Vouch
Design system
Every primitive in the product, in every state, measured against the live theme. Contrast ratios below are read out of the DOM after paint — not asserted — so a token that fails in one mode cannot pass here in the other. Switch the theme above and the numbers re-measure.
Color
Six ramps and a semantic layer on top of them. The number in each swatch is its best contrast against white or ink; the pairings below are the ones components actually use.
Blue
Primary. Competence hue, deliberately low saturation.
50
—
100
—
200
—
300
—
400
—
500
—
600
—
700
—
800
—
900
—
950
—
Gold
Accent only — never a status color.
50
—
100
—
200
—
300
—
400
—
500
—
600
—
700
—
800
—
900
—
950
—
Green
Success.
50
—
100
—
200
—
300
—
400
—
500
—
600
—
700
—
800
—
900
—
950
—
Orange
Warning. Kept apart from gold by usage, not hue.
50
—
100
—
200
—
300
—
400
—
500
—
600
—
700
—
800
—
900
—
950
—
Red
Danger.
50
—
100
—
200
—
300
—
400
—
500
—
600
—
700
—
800
—
900
—
950
—
Neutral
Cool-tinted. Darkest step is #131820, never black.
50
—
100
—
200
—
300
—
400
—
500
—
600
—
700
—
800
—
900
—
950
—
Semantic pairings, measured
text on bg
needs 4.5:1 · body text
text on surface
needs 4.5:1 · body text
text on surface-2
needs 4.5:1 · body text
text-secondary on surface
needs 4.5:1 · body text
text-muted on surface
needs 4.5:1 · body text
text-muted on bg
needs 4.5:1 · body text
primary link on surface
needs 4.5:1 · body text
primary-fg on primary-fill
needs 4.5:1 · body text
primary-subtle pairing
needs 4.5:1 · body text
success-fg on success-fill
needs 4.5:1 · body text
success-text on subtle
needs 4.5:1 · body text
warning-fg on warning-fill
needs 4.5:1 · body text
warning-text on subtle
needs 4.5:1 · body text
danger-fg on danger-fill
needs 4.5:1 · body text
danger-text on subtle
needs 4.5:1 · body text
gold-fg on gold fill
needs 4.5:1 · body text
gold-text on surface
needs 4.5:1 · body text
gold-text on gold-subtle
needs 4.5:1 · body text
border-strong on surface
needs 3:1 · large text / UI
focus-ring on bg
needs 3:1 · large text / UI
gold fill on surface (boundary)
needs 3:1 · large text / UI
avatar tint 1
needs 4.5:1 · body text
avatar tint 2
needs 4.5:1 · body text
avatar tint 3
needs 4.5:1 · body text
avatar tint 4
needs 4.5:1 · body text
avatar tint 5
needs 4.5:1 · body text
avatar tint 6
needs 4.5:1 · body text
Type
Inter, variable. A real scale rather than ad-hoc sizes, with tracking tightening as size grows. Numbers that get compared use tabular figures.
text-6xlFind the right agent
text-5xlFind the right agent
text-4xlFind the right agent
text-3xlFind the right agent
text-2xlFind the right agent
text-xlFind the right agent
text-lgFind the right agent
text-baseA referral reflects on you. Judge the person.
text-smA referral reflects on you. Judge the person.
text-xsA referral reflects on you. Judge the person.
text-2xsSelf-reported
Tabular figures
Volume, price and ratings must align in a column.
Text inputs
Field owns the label, hint and error, and wires aria-describedby, aria-invalid and required into whatever control it wraps. Controls used bare still work.
As it appears on your license.
Enter a valid email address.
Percentage of the commission on the far end.
Locked while your license is being verified.
sm · md · lg
What a referring agent needs to know about you.
This field is required before publishing.
Determines which specialties you can pick.
Pick at least one specialty.
Selection controls
Checkbox, Radio and Switch each keep a real native input under the drawn control, so arrow keys, form submission and screen-reader semantics are the platform's, not an imitation.
Segmented control
Small / full width
Disabled
Range slider
Two independent thumbs, each its own role=slider with live aria bounds. They cannot cross, and Home/End respect the other thumb rather than jumping past it.
Badges, chips, cards, avatars
Status never rides on color alone — every tone carries an icon or a word as well.
Badges
Chips — toggle
aria-pressed plus a heavier border, not just a fill.
Chips — removable
Two targets: reopen the filter, or remove it.
Default card
Decorative hairline. Not clickable, so a 1.5:1 border is fine.
Interactive card
border-strong, because the outline is now a UI boundary.
Selected card
Border plus an inset ring.
Marisol Ochoa
Cardinal & Vale Realty · Austin, TX
Avatar sizes
Deterministic tint from the agent id.
Avatar tints
Six pairings, each contrast-verified.
Avatar extras
Star rating
SPEC §10: Zillow has no public API for third-party apps, so every rating in this product is typed in by the agent. The disclosure is a real control, not a footnote, and it links out so the number can be checked.
Display
One role=img with a spelled-out label.
Sizes
Compact
hideDisclosure for map pins and dense rows — pair it with the disclosure elsewhere on the screen.
Interactive
Real radios: arrow keys work, and each star's name is a phrase.
You are entering this yourself. It will be shown as self-reported until verification exists.
Interactive — disabled
Loading, progress, empty, toast
A blank div is never a state. Every one of these announces itself to a screen reader.
Spinner
Skeleton
Skeleton list — the results-list loading state
Progress
Add a video pitch to reach Strong.
No agents in this area
Try zooming out, or clearing a filter or two.
Nothing to review
You have made a call on every agent in this search. Nicely done.
Could not load the map
The basemap did not respond. Your filters are still saved.
Empty state sizes
Small
Medium
Large
Toast
Errors go to an assertive region; everything else is polite. Hovering pauses the timer.
Overlays
Modal and Drawer trap focus, lock scroll, close on Escape and return focus to the trigger. None of them uses a native <dialog>, so a tooltip or popover opened inside one still renders above it.
Modal
Drawer
Tooltip
Describes the trigger; never replaces its name.
Popover
Non-modal: the page behind stays live. Tab out to dismiss.
Dropdown menu
Arrow keys, Home/End, typeahead, roving tabindex.