Skip to main content
UDSUrban Design SystemBuild a multi-component HTML demoSITE 2026.06.10.1
Color
Brand
Font
Scale
Density

Design Language

How UDS components look in each state. Every preview on this page is a real component — the colors come from the same tokens as production. Where components disagree on the same state, both are shown so we can pick one.

lightbulb Reflects how every UDS component handles state today. Five differences between components are flagged at the bottom for us to talk through.

Interaction states

Four states an interactive element moves through: at rest, pointer-over, pressed, and keyboard-focused. Each preview below is a real UDS component rendered in that state.

Default

rest
Live preview
Primary
surface-mainbgborder-primarybordertext-primaryvaluetext-secondaryplaceholder

Hover

pointer over
Live preview (forced state)

Hover over a real button on the Button page to see it animated.

border-outline-hoverfield bordersurface-interactive-subtle-hoverghost / tab bgsurface-interactive-hoverprimary button bg

inconsistency Four different form-field hover patterns coexist today — see §12.2.

Focus

keyboard focus
Live preview (forced state)

Tab through any real field on the Text Input page to see it.

border-outline-focus-visiblering colorsurface-maingap (theme-safe)

Three constructions exist — outline+offset, stacked box-shadow, single-ring. See §6 deep dive.

Pressed

:active
Live preview
Pressed primary Pressed secondary

→ Mouse-down on a real button to see this state.

surface-interactive-activeprimary bgsurface-interactive-subtle-activeghost bg

Validation states

How fields, notifications, and badges communicate success, warning, error, and info.

Error

red, bold
Live preview
Something went wrong.
border-errortext-erroricon-errorsurface-error-subtlenotification bg

Info

blue
Live preview
Heads up — info notification. Info badge
surface-info-subtletext-infosurface-infoprominent

Success

green
Live preview
All set — your changes saved. Done
surface-success-subtletext-successsurface-successprominent

Warning

orange
Live preview
Heads up — something needs attention. Pending
surface-warning-subtletext-warningsurface-warningprominent

Availability states

Disabled means “you can’t act here right now.” Read-only means “this value is what it is.” They look different on purpose.

Disabled

non-interactive
Live preview
Submit Cancel
surface-interactive-disabledfilled bgtext-disabled-boldvalue texttext-disabledgeneral copyborder-disabledoutlined elements

Read-only

value is fixed
Live preview (proposed)

User can read + copy the value but not edit. proposed

surface-subtlebg tintborder-primarykeep visibletext-primaryfull opacity

Loading

deferred
Status

Not specified in UDS today. Components that need a loading look currently fall back to Disabled as the closest approximation.

deferred Waiting on designer review.

Selection states

How “this is the chosen one” reads visually. Filled elements (chip, toggle, checkbox) go more saturated. Outlined elements (tile, tab, radio) gain a colored marker and recolor text to text-interactive.

Checkbox · checked

filled
Live preview
Marketing emails Promotional offers
surface-interactive-defaultfilled boxicon-inversecheckmark

Radio · selected

dot fills + label tints
Live preview
Yes, send updates No thanks
icon-interactivecenter dottext-interactiveselected label

Chip · selected

filled, inverse text
Live preview
All Open Closed
surface-interactive-activebg + bordertext-inverselabel

Disabled vs Read-only — quick reference

Confusing these two is the most common spec-writing mistake. Use this table.

QuestionDisabledRead-only
User can read the value?technically yes, but greyedyes, full primary text
User can copy the value?usually not (cursor not-allowed)yes
User can focus the element?no (skipped by Tab)yes
User can edit?nono
Communicates “temporarily unavailable”?yesno — “this is fixed information”
Communicates “you can’t change this”?yesyes (because the system owns it)
priority_highCommon confusion: applying Disabled to “this field is auto-filled and shouldn’t be edited” — that’s Read-only. Disabled is for “the action isn’t currently available” (e.g. a “Save” button while the form is invalid).

Focus ring construction

UDS has three focus-ring constructions in the current CSS. They’re not interchangeable — pick one by the rules below, not by aesthetic preference.

A. outline + outline-offset recommended

Outline with a transparent offset. The gap is genuinely empty space — always blends with whatever’s behind. Default for new code.

Focused link

Used by: checkbox, radio, link, toggle, tabs, text-area, dropdown items, dialog close, notification close.

B. Stacked box-shadow

Two box-shadows: a surface-colored “gap” layer + the focus ring outside it. Use when an outline would be clipped (form field with overflow:hidden). Theme-safe ONLY when the gap binds to surface-main, not surface-white.

Used by: chip, search (theme-safe). Bug: button, text-input, dropdown bind the gap to surface-white — visible white ring in dark mode. See §12.1.

C. Single ring, no gap

A direct ring on the element with no separating gap. Reserved for large containers where extra visual weight competes with neighbors.

Focused tile

Used by: tile only.

Decision tree

Is the element inside a clipped container (overflow:hidden)? ├── Yes → Use construction B (box-shadow stacked) bound to surface-main └── No │ Is the element a large card / tile? ├── Yes → Use construction C (single ring, no gap) └── No → Use construction A (outline + offset) ← default

Universal rules

  • Ring color: always border-outline-focus-visible
  • Ring thickness: 2 px for inline controls and form fields, 3 px for tiles
  • Gap (in A or B): 1–2 px
  • Selector: :focus-visible, never :focus — the latter fires on every click
  • Error doesn’t recolor the focus ring — it stays blue (search is the only exception today)

Surface treatments

Page surfaces, content surfaces, interactive surfaces, status surfaces. Full chip set with resolved colors. For the full token reference see Semantic Colors.

Content surfaces

surface-mainsurface-subtlesurface-altsurface-boldsurface-xboldsurface-xxboldsurface-inversesurface-none

Interactive surfaces

interactive-defaultinteractive-hoverinteractive-activeinteractive-subtleinteractive-subtle-hoverinteractive-subtle-activeinteractive-disabled

Status surfaces

infoinfo-subtlesuccesssuccess-subtleerrorerror-subtlewarningwarning-subtle
priority_highsurface-white and surface-black are literal white and black in every theme. Almost never the right token — usually you want surface-main. Three components in UDS use surface-white incorrectly for focus-ring gaps; see §12.1.

Elevation / shadow scale

Three depth levels plus a bento variant for dashboards. Pick by purpose, not by size. Live preview of each shadow below.

depth-100
depth-300
depth-500
bento
TokenUse when…Live examples
shadow-depth-100A small element is lifted off its surfaceToggle thumb
shadow-depth-300A transient overlay near its triggerTooltips, dropdown menus, popovers
shadow-depth-500A modal that floats above the entire pageDialogs, sheets
shadow-bentoBento-style card grouping (dashboards)Dashboard cards
priority_highDon’t bind elevation tokens to non-elevation effects. The shadow scale exists to communicate depth, not decoration.

Border radius scale

Two families: input for things you interact with, container for surfaces holding content.

container-sm
input · 8 px
container
container-md
container-lg
container-xl · 24 px
container-full
TokenLive examples in UDS
border-radius-inputtext-input, dropdown trigger, button, dialog close, tile, dropdown items
border-radius-container-smnotification, badge, link focus, tab focus
border-radius-containertooltip, chip
border-radius-container-xldialog (24 px)
border-radius-container-fulltoggle control + thumb

Density / sizing

Two density tracks: default (comfortable) and small (filter rows, dense forms). Side-by-side live preview.

Default (comfortable)

Submit Cancel

Button: padding: space-150 space-200 (12 × 16 px)
Tab: padding: space-200 space-300 (16 × 24 px)

Small (size="sm")

Submit Cancel

Button: padding: space-075 space-150 (6 × 12 px)
Tab: padding-top/bottom: space-100 (8 px)

infoNo lg size in UDS yet. Components that need a bigger size should check with the designer before adding one.

Checked vs Selected vs Active — naming guidance

Three words that get confused. Use the right one for the right element.

TermUsed byPair with
Checkedcheckbox, radio, toggle<input checked> / aria-checked="true"
Selectedtabs, dropdown items, chip, tilearia-selected="true" / data-selected="true"
Active (current)link, button ([data-selected])aria-current="page"
Active (pressed)every interactive control while held down:active (CSS pseudo)
priority_highThe two meanings of “Active” collide. Prefer “Pressed” for the mouse-down meaning and “Current” for the selected-as-nav meaning when writing specs.

Known inconsistencies needs review

Five places where components disagree with each other today. Each one needs a quick decision from the designer. Until that happens, new components default to whichever option is the safest across themes.

12.1 Focus ring gap binding

ComponentGap bindingTheme-safe?
buttonsurface-whiteno — white in dark mode
text-inputsurface-whiteno
dropdown triggersurface-whiteno
chipsurface-mainyes
searchsurface-mainyes

Suggested fix: bind the gap to surface-main everywhere. Three components need a small CSS update.

12.2 Hover treatment for form fields

ComponentHover treatment
text-input(no hover state defined)
dropdown triggerbg → interactive-subtle-hover, border → border-interactive
search fieldbg → surface-subtle
text-areaborder → border-outline-hover

Suggested fix: use text-area’s border-outline-hover everywhere. It reads as hover without competing with the focus ring.

12.3 Disabled border treatment

ComponentDisabled border
button (primary)same as bg — invisible border
button (secondary), text-inputtransparent
dropdown, checkbox, radio, tileborder-disabled

Suggested fix: use border-disabled on outlined elements (form controls) and transparent on filled ones. Both treatments are fine — they say slightly different things about disabled.

12.4 Toggle’s opacity: 0.7 for disabled

Toggle is the only component that drops opacity for disabled. Either everyone else should match it, or toggle should drop the opacity. Designer call.

12.5 Required dot color token

ComponentRequired dot color
text-input, dropdown, checkbox, radioicon-error
tiletext-error

Both resolve to the same red. Pick one and use it everywhere.

A few rules that apply across the board

  • Stroke alignment is INSIDE for form fields so border weight doesn’t affect outer dimensions.
  • Border weight stays at 1 px through state transitions for most components. Exceptions: tile thickens to 2 px on selection; tabs use 2 px bottom border.
  • :focus-visible is the focus selector, never :focus.
  • Transitions are 120ms ease for color and border, 200ms ease for transforms.
  • Disabled elements need pointer-events: none AND cursor: not-allowed — both are required.

Related pages

  • Semantic Colors — the full token vocabulary the constructions here resolve to.
  • Spacing — the space-* scale referenced in density patterns.
  • Text Styles — typography decisions that pair with these visual treatments.
  • Cursor Workflows — the same content for AI agents lives in .cursor/rules/uds-design-language.mdc.