The visual language for SmallWorld's React-based pages — extracted from the Connector Dashboard, Target Companies, and navigation redesign. Use this as the reference for all new frontend work.
Color System
Foundation
Primary
Blue 500
#3f84e5
Primary CTA, active states, links
Blue 600
#2e6ec5
Hover state for primary
Blue 50
#F1F7FF
Active filter bg, selected states
Blue 100
#dbeafe
Info badges, light blue bg
Blue 700
#2563eb
Badge text on blue bg
Neutrals
Gray 900
#222222
Headings, primary text
Gray 700
#444444
Strong secondary text
Gray 500
#77797B
Body text, descriptions, labels
Gray 400
#9DA1A5
Placeholder, muted text, icons
Gray 200
#e1e5e9
Borders, dividers, tab rules
Surfaces
Page Background
#FBFCFE
Body background
Surface Muted
#f5f7fa
Alternate bg, footer
Surface White
#FFFFFF
Cards, header, inputs
Surface Subtle
#F1F5F9
Secondary buttons, inactive badges
Table Header
#fafbfc
Table header row bg
Semantic
Success
#16a34a
Positive outcomes, confirmations
Warning
#d97706
Pending, attention needed
Danger
#dc2626
Errors, destructive actions, alerts
Danger Vivid
#ff0007
Destructive confirm buttons
Typography
Foundation
Primary Font
Montserrat
Used for everything — headings, body, labels, buttons, inputs. Weights: 300–800.
Design Philosophy
Single font family creates cohesion. Weight and size create hierarchy — not font changes. Light (300) for large headlines, regular (400–500) for body, semibold (600) for labels, bold (700) for emphasis.
Page Title
20px / 400 / -0.2px
Can you help SmallWorld get an introduction?
Hero Title
28px / 300 / -0.3px
Who do you want to meet today?
Section Label
13px / 600 / 0.8px UC
Your Action Items
Tab
13px / 600
Target Companies
Nav Item
14px / 500
Relationship Leads
Table Header
11px / 600 / 0.5px UC
COMPANY PRIORITY TYPE HELP REQUESTED
Body
12-13px / 400-500
Richard Perrott — Sr. Director, Engineering at Cloudflare
Caption
11px / 500
10mo ago · Targeted by David Rush
Button
12px / 600-700
Share Dashboard Offer Help Relationship AI
Buttons
Components
Variants
Sizes
Contextual
Filter Buttons
Badges & Pills
Components
Status Badges
New OfferAction NeededUpdatedPendingNot Rated
Tab Count Badges
4132
Nav Badges
312
Type Badges
New BusinessExpansionRetention
Tabs
Components
Active tab: blue text + blue bottom border (2px). Inactive: gray text. Badge colors are contextual — alert red for counts needing attention, blue for informational.
Data Display
Components
Relevance Bars vs. Strength — DO NOT MIX
Relevance (how well a record matches the user’s filters/ICP) is shown as signal bars.
Relationship strength (how well a connector knows a prospect) is shown as emoji + label.
These visuals look superficially similar but represent different concepts — never use bars for strength, never use the strength emojis for non-strength concepts. The single source of truth is STRENGTH_LEVELS in frontend/src/components/ConnectorDashboard/constants.ts (mirrored by STRENGTH_CONFIG in RelationshipLeads/StrengthBadge.tsx).
Relevance Bars (relevance only)
1/5 — Minimal
3/5 — Medium
5/5 — Very High
Strength Widget (canonical emoji set)
Rated — emoji + label, all five canonical values:
🔥Very Strongvery_strong
💪Strongstrong
🤝Averageavg
👎Weakweak
❓No Relationshipvery_weak
Unrated (interactive picker, low → high):
❓
👎
🤝
💪
🔥
Avatars
DR
MG
CR
Initials on colored background. Colors assigned per-person.
Table Pattern
⊞
Priority
Type
Company
Help Requested
Offer Help
Date Added
⭐
New Business
Cloudflare
Targeted by David Rush
10mo ago
Retention
Grafana Labs
Targeted by David Rush
8mo ago
Popovers & Confirmations
Components
Hide Cloudflare?
They won't appear in your list. You can recover hidden companies from filters.
Flag as incorrect?
Let us know this company's info appears out of date. We'll review and update it.
Layout & Spacing
Foundation
Max Width
1200px
All page content containers
Page Padding
20px 1rem
Dashboard main container
Card Border Radius
10px
Cards, modals, popovers
Button Border Radius
4px
All buttons, inputs, selects
Badge Border Radius
10px
Pill badges, count badges
Border Color
#e1e5e9
Default borders, card edges, tab rules
Card Border
#e8ebef
Slightly softer, used on cards specifically
Row Divider
#f0f2f5
Table row borders, subtle dividers
Hover Lift
translateY(-1px)
Card hover, row hover — subtle lift
Hover Shadow
0 2px 12px rgba(0,0,0,0.05)
Card hover shadow
Transition
all 0.15s
Default transition for interactive elements
Shadcn & Tailwind
Guidelines
Component Hierarchy
1. Shadcn first. Before building any interactive component (dialog, popover, tooltip, select, dropdown, tabs, accordion), check if Shadcn has it. Use yarn shadcn:add [component]. These are in src/components/ui/shadcn/ — never edit generated files directly. 2. Wrap with custom styles. Create thin wrappers in src/components/ui/ that apply SmallWorld styling via component CSS (not Tailwind utilities). Example: ui/button.tsx wraps Shadcn's button with our font/color/radius. 3. Custom only when Shadcn doesn't cover it. Strength widget, relevance bars, avatar — these have no Shadcn equivalent. Build them from scratch with Montserrat + our color tokens.
Shadcn Components in Use
Component
Shadcn Source
Used For
Dialog
@radix-ui/react-dialog
Offer Help modal, Offer Intro modal, Strength Info modal
Popover
@radix-ui/react-popover
Hide/Flag confirmation popovers
Tooltip
@radix-ui/react-tooltip
Strength emoji tooltips, relevance tooltip
Select
@radix-ui/react-select
Filter dropdowns (relevance, type, account owner)
Tailwind Usage
✓ Do
Use Tailwind for layout utilities in non-dashboard pages: flex, gap-2, grid, p-4, max-w-screen-xl. Tailwind is great for layout and spacing.
✕ Don't
Use Tailwind utilities to style Shadcn components — specificity conflicts with Tailwind v4's @layer system make this unreliable. Use component CSS or inline styles instead.
✓ Do
Write component-scoped CSS in a .css file next to the component (e.g., ConnectorDashboard.css). Use descriptive class names like .offer-help-modal-title.
✕ Don't
Mix Tailwind utilities with component CSS for the same element. Choose one approach per component — our dashboards use component CSS exclusively.
Styling Shadcn Overrides
Shadcn components have their own styles that are hard to override with Tailwind. Use these patterns instead:
/* ✓ Good — inline style on the Shadcn component */
<DialogContent style={{ border: '1px solid #e1e5e9', maxWidth: '480px' }}>
/* ✓ Good — className with component CSS */
<PopoverContent className="action-confirm-popover">
/* ✓ Good — className + custom render for complex overrides */
<DialogClose style={{ all: 'unset', position: 'absolute', ... }}>
/* ✕ Bad — Tailwind utilities get out-specificed */
<DialogContent className="border border-gray-200 max-w-md">
Gotchas & Patterns
Guidelines
✓ Do
Scope component styles to a parent class like .action-confirm-popover when using Radix portals (Dialog, Popover, Tooltip). These render at document.body, outside your component's DOM.
✕ Don't
Scope portal styles to .connector-dashboard or any page container — the portal renders outside it and the styles won't apply.
✓ Do
Use inline styles or component-level class names for Shadcn/Radix overrides. The Tailwind v4 + layer system makes specificity unpredictable with utility classes.
✕ Don't
Rely on Tailwind utility classes for Shadcn component overrides — they often get out-specificed by the component's own styles.
✓ Do
Use TanStack Query for all API data fetching. Invalidate queries after mutations to refresh lists. Optimistic updates for UI that needs to feel instant (like strength rating).
✕ Don't
Fire API calls without refreshing the UI afterward. If a hide/flag/offer action succeeds, queryClient.invalidateQueries must follow.
✓ Do
Use the turbolinks-visit-control: reload meta tag on React layouts. This ensures old Rails CSS is flushed when navigating from legacy pages.
✕ Don't
Assume React page styles are isolated from Rails asset pipeline styles. Turbolinks preserves the previous page's stylesheets during SPA navigation.