Skip to documentation
StardwstDESIGN SYSTEM

Foundations

Foundations

Choose tokens for color, type, spacing, shape, and motion. Use semantic tokens for interface roles and palette values when defining a theme.

Overview

Choose tokens by purpose

Use shared tokens such as --color__surface and--spacing__regular across components. Recipe properties such as--button__background apply to a single component; override them locally.

WorkspaceReady to publish

Your changes have passed every check.


Layout

Layout

Use .container to bound a page and .grid.responsive for cards that reflow with the available width.

Start with the layout primitives

<main class="stardwst">
  <div class="container"
    style="--container__max-width: 72rem;
      --container__padding: clamp(1rem, 4vw, 3rem)">
    <div class="grid responsive"
      style="--grid__min-width: 18rem;
        --grid__gap: var(--spacing__xl)">
      <article class="card">...</article>
      <article class="card">...</article>
    </div>
  </div>
</main>

Color

Color

Use neutral surfaces for content, accent color for actions, and danger color for errors or destructive actions.

Choose semantic tokens before selecting a palette step. Pair accent backgrounds with --color__on-accent, and check text and control contrast on the actual surface. A darker or lighter step alone does not establish an accessible combination.

Pair status color with text, an icon, or another cue. Never make hue the only way to understand a state.

--color__white#ffffff
--color__black#000000

Brown

--color__brown-<step>
50#efebe9
100#d7ccc8
200#bcaaa4
300#a1887f
400#8d6e63
500#795548
600#6d4c41
700#5d4037
800#4e342e
900#3e2723
950#0c0a09

Grey

--color__grey-<step>
50#fafafa
100#f5f5f5
200#eeeeee
300#e0e0e0
400#bdbdbd
500#9e9e9e
600#757575
700#616161
800#424242
900#212121
950#030712

Blue Grey

--color__blue-grey-<step>
50#eceff1
100#cfd8dc
200#b0bec5
300#90a4ae
400#78909c
500#607d8b
600#546e7a
700#455a64
800#37474f
900#263238
950#020617

Slate

--color__slate-<step>
50#f8fafc
100#f1f5f9
200#e2e8f0
300#cfd8e3
400#94a3b8
500#64748b
600#475569
700#334155
800#1e293b
900#0f172a
950#020617

Zinc

--color__zinc-<step>
50#fafafa
100#f4f4f5
200#e4e4e7
300#d4d4d8
400#a1a1aa
500#71717a
600#52525b
700#3f3f46
800#27272a
900#18181b
950#09090b

Stone

--color__stone-<step>
50#fafaf9
100#f5f5f4
200#e7e5e4
300#d6d3d1
400#a8a29e
500#78716c
600#57534e
700#44403c
800#292524
900#1c1917
950#0c0a09

Red

--color__red-<step>
50#ffebee
100#ffcdd2
200#ef9a9a
300#e57373
400#ef5350
500#f44336
600#e53935
700#d32f2f
800#c62828
900#b71c1c
950#450a0a
A100#ff8a80
A200#ff5252
A400#ff1744
A700#d50000

Pink

--color__pink-<step>
50#fce4ec
100#f8bbd0
200#f48fb1
300#f06292
400#ec407a
500#e91e63
600#d81b60
700#c2185b
800#ad1457
900#880e4f
950#500724
A100#ff80ab
A200#ff4081
A400#f50057
A700#c51162

Purple

--color__purple-<step>
50#f3e5f5
100#e1bee7
200#ce93d8
300#ba68c8
400#ab47bc
500#9c27b0
600#8e24aa
700#7b1fa2
800#6a1b9a
900#4a148c
950#3b0764
A100#ea80fc
A200#e040fb
A400#d500f9
A700#aa00ff

Deep Purple

--color__deep-purple-<step>
50#ede7f6
100#d1c4e9
200#b39ddb
300#9575cd
400#7e57c2
500#673ab7
600#5e35b1
700#512da8
800#4527a0
900#311b92
950#2e1065
A100#b388ff
A200#7c4dff
A400#651fff
A700#6200ea

Indigo

--color__indigo-<step>
50#e8eaf6
100#c5cae9
200#9fa8da
300#7986cb
400#5c6bc0
500#3f51b5
600#3949ab
700#303f9f
800#283593
900#1a237e
950#1e1b4b
A100#8c9eff
A200#536dfe
A400#3d5afe
A700#304ffe

Blue

--color__blue-<step>
50#e3f2fd
100#bbdefb
200#90caf9
300#64b5f6
400#42a5f5
500#2196f3
600#1e88e5
700#1976d2
800#1565c0
900#0d47a1
950#172554
A100#82b1ff
A200#448aff
A400#2979ff
A700#2962ff

Light Blue

--color__light-blue-<step>
50#e1f5fe
100#b3e5fc
200#81d4fa
300#4fc3f7
400#29b6f6
500#03a9f4
600#039be5
700#0288d1
800#0277bd
900#01579b
950#082f49
A100#80d8ff
A200#40c4ff
A400#00b0ff
A700#0091ea

Cyan

--color__cyan-<step>
50#e0f7fa
100#b2ebf2
200#80deea
300#4dd0e1
400#26c6da
500#00bcd4
600#00acc1
700#0097a7
800#00838f
900#006064
950#083344
A100#84ffff
A200#18ffff
A400#00e5ff
A700#00b8d4

Teal

--color__teal-<step>
50#e0f2f1
100#b2dfdb
200#80cbc4
300#4db6ac
400#26a69a
500#009688
600#00897b
700#00796b
800#00695c
900#004d40
950#042f2e
A100#a7ffeb
A200#64ffda
A400#1de9b6
A700#00bfa5

Green

--color__green-<step>
50#e8f5e9
100#c8e6c9
200#a5d6a7
300#81c784
400#66bb6a
500#4caf50
600#43a047
700#388e3c
800#2e7d32
900#1b5e20
950#052e16
A100#b9f6ca
A200#69f0ae
A400#00e676
A700#00c853

Light Green

--color__light-green-<step>
50#f1f8e9
100#dcedc8
200#c5e1a5
300#aed581
400#9ccc65
500#8bc34a
600#7cb342
700#689f38
800#558b2f
900#33691e
950#1a2e05
A100#ccff90
A200#b2ff59
A400#76ff03
A700#64dd17

Lime

--color__lime-<step>
50#f9fbe7
100#f0f4c3
200#e6ee9c
300#dce775
400#d4e157
500#cddc39
600#c0ca33
700#afb42b
800#9e9d24
900#827717
950#1a2e05
A100#f4ff81
A200#eeff41
A400#c6ff00
A700#aeea00

Yellow

--color__yellow-<step>
50#fffde7
100#fff9c4
200#fff59d
300#fff176
400#ffee58
500#ffeb3b
600#fdd835
700#fbc02d
800#f9a825
900#f57f17
950#422006
A100#ffff8d
A200#ffff00
A400#ffea00
A700#ffd600

Amber

--color__amber-<step>
50#fff8e1
100#ffecb3
200#ffe082
300#ffd54f
400#ffca28
500#ffc107
600#ffb300
700#ffa000
800#ff8f00
900#ff6f00
950#451a03
A100#ffe57f
A200#ffd740
A400#ffc400
A700#ffab00

Orange

--color__orange-<step>
50#fff3e0
100#ffe0b2
200#ffcc80
300#ffb74d
400#ffa726
500#ff9800
600#fb8c00
700#f57c00
800#ef6c00
900#e65100
950#431407
A100#ffd180
A200#ffab40
A400#ff9100
A700#ff6d00

Deep Orange

--color__deep-orange-<step>
50#fbe9e7
100#ffccbc
200#ffab91
300#ff8a65
400#ff7043
500#ff5722
600#f4511e
700#e64a19
800#d84315
900#bf360c
950#431407
A100#ff9e80
A200#ff6e40
A400#ff3d00
A700#dd2c00

Palette names combine a family and a shade, such as --color__cyan-900. Higher numbers are darker. Accent shades use a capital A, as in --color__cyan-A400.

Semantic color scales

Primary uses Cyan, negative uses Red, and text uses Zinc. Use the role tokens below for common surfaces and states; choose a scale step when those roles do not cover the need.

Primary

--color__primary-<step>
50Cyan 50
100Cyan 100
200Cyan 200
300Cyan 300
400Cyan 400
500Cyan 500
600Cyan 600
700Cyan 700
800Cyan 800
900Cyan 900
950Cyan 950
A100Cyan A100
A200Cyan A200
A400Cyan A400
A700Cyan A700

Negative

--color__negative-<step>
50Red 50
100Red 100
200Red 200
300Red 300
400Red 400
500Red 500
600Red 600
700Red 700
800Red 800
900Red 900
950Red 950
A100Red A100
A200Red A200
A400Red A400
A700Red A700

Text

--color__text-<step>
50Zinc 50
100Zinc 100
200Zinc 200
300Zinc 300
400Zinc 400
500Zinc 500
600Zinc 600
700Zinc 700
800Zinc 800
900Zinc 900
950Zinc 950
Background--color__backgroundvar(--color__zinc-50)
Surface--color__surfacevar(--color__white)
Subtle surface--color__surface-subtlevar(--color__zinc-100)
Muted surface--color__surface-mutedvar(--color__slate-100)
Subtle border--color__border-subtlevar(--color__zinc-200)
Border--color__bordervar(--color__zinc-300)
Muted border--color__border-mutedvar(--color__slate-200)
Strong border--color__border-strongvar(--color__slate-300)
Subtle accent--color__accent-subtlevar(--color__primary-50)
Accent--color__accentvar(--color__primary-900)
Accent hover--color__accent-hovervar(--color__primary-800)
Accent focus--color__accent-focusvar(--color__primary-700)
Subtle info--color__info-subtlevar(--color__blue-50)
Info--color__infovar(--color__blue-800)
Strong info--color__info-strongvar(--color__blue-900)
On info--color__on-infovar(--color__white)
Subtle success--color__success-subtlevar(--color__green-50)
Success--color__successvar(--color__green-800)
Strong success--color__success-strongvar(--color__green-900)
On success--color__on-successvar(--color__white)
Subtle warning--color__warning-subtlevar(--color__orange-50)
Warning--color__warningvar(--color__orange-900)
Strong warning--color__warning-strongvar(--color__deep-orange-900)
On warning--color__on-warningvar(--color__zinc-950)
Subtle danger--color__danger-subtlevar(--color__negative-50)
Danger--color__dangervar(--color__negative-700)
Strong danger--color__danger-strongvar(--color__negative-800)
On danger--color__on-dangervar(--color__white)
On accent--color__on-accentvar(--color__white)
Muted on accent--color__on-accent-mutedvar(--color__slate-50)
Text on subtle accent--color__text-on-accent-subtlevar(--color__slate-600)

Typography

Typography

Use display text for titles, sans-serif for body copy and controls, and monospace for code. Heading levels follow document structure, regardless of visual size.

The family tokens use generic browser fonts, so appearance varies by operating system. Use --tracking-label for compact uppercase labels.

Display--font-family__display

Make the important thing easy to find.

Tracking: --tracking-display
Sans-serif--font-family__sans-serif

Body text carries instructions and explanations. Keep line length comfortable and make the useful detail easy to scan.

Default line height: 1.6
Monospace--font-family__monospace

pnpm --dir design-system docs:build

Use for code, keyboard input, and fixed-width data.

Spacing

Spacing

Keep labels close to controls and larger gaps between groups. For shared layout spacing, choose --spacing__narrow, --spacing__regular, or --spacing__wide.

2xs--spacing__2xs

0.25rem 4 px

xs--spacing__xs

0.5rem 8 px

sm--spacing__sm

0.75rem 12 px

md--spacing__md

1rem 16 px

lg--spacing__lg

1.25rem 20 px

xl--spacing__xl

1.5rem 24 px

2xl--spacing__2xl

1.75rem 28 px


Shape and depth

Shape and depth

Separate content with spacing and borders. Reserve stronger shadows for overlays; use full roundness for circles and pills.

Start with --roundness for corners and --shadow for depth. When surfaces overlap, use the shared --z-index__ scale to define their order.

None--roundness__none
Default--roundness
Full--roundness__full
None--shadow__none
Inner--shadow__inner
Level 1--shadow__level-1
Level 2--shadow__level-2
Level 3--shadow__level-3

Motion

Motion

Motion should clarify a relationship or acknowledge an action. Stardwst pairs --transition__duration with --easing__out-expo for quick state changes. Hover the example to see the default transition.

Transition--transition__duration

175ms


Theming

Theming

Set data-theme on the island or an ancestor, then set any product-specific overrides on the .stardwst boundary that contains the interface. Dark mode changes semantic roles, while named palette values remain stable. Review the theme across buttons, fields, menus, and dialogs, including hover, focus, disabled, and error states.

Current theme

The default theme uses compact corners, cyan accents, and light neutral surfaces.

<main data-theme="dark">
  <section class="stardwst product-theme">
    <button class="button filled">Save</button>
  </section>
</main>

<style>
  .product-theme {
    --roundness: var(--roundness__sm);
  }
</style>

The ancestor supplies the dark semantic roles; the island keeps the product's shape choice local. Use a recipe override only when the adjustment belongs to that one component.


Token reference

Look up a token

Find exact names and values for typography, spacing, shape, motion, and accessibility. For color tokens, use the palette swatches above.

Typography

--font__ratio
1.618
--font-family__display
sans-serif
--font-family__serif
serif
--font-family__sans-serif
sans-serif
--font-family__monospace
monospace
--font-size__2xs
0.5rem
--font-size__xs
0.75rem
--font-size__sm
0.875rem
--font-size__md
1rem
--font-size__lg
1.25rem
--font-size__xl
1.75rem
--font-size__2xl
2.5rem
--font-size__3xl
3.5rem
--tracking-display
-0.025em
--tracking-label
0.08em

Spacing

--spacing__2xs
0.25rem
--spacing__xs
0.5rem
--spacing__sm
0.75rem
--spacing__md
1rem
--spacing__lg
1.25rem
--spacing__xl
1.5rem
--spacing__2xl
1.75rem
--spacing__narrow
var(--spacing__sm)
--spacing__regular
var(--spacing__md)
--spacing__wide
var(--spacing__lg)

Shape and depth

--roundness__none
0
--roundness__full
9999px
--roundness__2xs
0.125rem
--roundness__xs
0.25rem
--roundness__sm
0.375rem
--roundness__md
0.5rem
--roundness__lg
0.625rem
--roundness__xl
0.75rem
--roundness__2xl
0.875rem
--shadow__none
0 0 #0000
--shadow__inner
inset 0 2px 4px 0 rgb(0 0 0 / 0.05)
--shadow__level-1
0 1px 2px 0 rgb(0 0 0 / 0.05)
--shadow__level-2
0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)
--shadow__level-3
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)
--z-index__auto
auto
--z-index__0
0
--z-index__10
10
--z-index__20
20
--z-index__30
30
--z-index__40
40
--z-index__50
50
--z-index__-50
-50
--z-index__-40
-40
--z-index__-30
-30
--z-index__-20
-20
--z-index__-10
-10
--roundness
var(--roundness__2xs)
--thickness
1px
--shadow
var(--shadow__level-1)

Motion

--easing__in-sine
cubic-bezier(0.12, 0, 0.39, 0)
--easing__out-sine
cubic-bezier(0.61, 1, 0.88, 1)
--easing__in-out-sine
cubic-bezier(0.37, 0, 0.63, 1)
--easing__in-quad
cubic-bezier(0.11, 0, 0.5, 0)
--easing__out-quad
cubic-bezier(0.5, 1, 0.89, 1)
--easing__in-out-quad
cubic-bezier(0.45, 0, 0.55, 1)
--easing__in-cubic
cubic-bezier(0.32, 0, 0.67, 0)
--easing__out-cubic
cubic-bezier(0.33, 1, 0.68, 1)
--easing__in-out-cubic
cubic-bezier(0.65, 0, 0.35, 1)
--easing__in-quart
cubic-bezier(0.5, 0, 0.75, 0)
--easing__out-quart
cubic-bezier(0.25, 1, 0.5, 1)
--easing__in-out-quart
cubic-bezier(0.76, 0, 0.24, 1)
--easing__in-quint
cubic-bezier(0.64, 0, 0.78, 0)
--easing__out-quint
cubic-bezier(0.22, 1, 0.36, 1)
--easing__in-out-quint
cubic-bezier(0.83, 0, 0.17, 1)
--easing__in-expo
cubic-bezier(0.7, 0, 0.84, 0)
--easing__out-expo
cubic-bezier(0.16, 1, 0.3, 1)
--easing__in-out-expo
cubic-bezier(0.87, 0, 0.13, 1)
--easing__in-circ
cubic-bezier(0.55, 0, 1, 0.45)
--easing__out-circ
cubic-bezier(0, 0.55, 0.45, 1)
--easing__in-out-circ
cubic-bezier(0.85, 0, 0.15, 1)
--easing__in-back
cubic-bezier(0.36, 0, 0.66, -0.56)
--easing__out-back
cubic-bezier(0.34, 1.56, 0.64, 1)
--easing__in-out-back
cubic-bezier(0.68, -0.6, 0.32, 1.6)
--transition__easing
var(--easing__out-expo)
--transition__duration
175ms

Accessibility

--disabled-opacity
0.5

Accessibility

Accessibility

The foundation supplies visible keyboard focus, scoped selection colors, reduced-motion behavior, and a visually hidden utility. These defaults help, but each component still needs meaningful structure, labels, and states.

Use --disabled-opacity for a consistent disabled appearance. Preserve the native disabled state and explain why an action is unavailable when it may be unclear.

01

Keep focus visible

The default focus ring is 2 px with a 3 px offset. Check that it stays visible against each surface and is not clipped by surrounding containers.

02

Respect motion preferences

When reduced motion is requested, the base styles shorten CSS animation and transition durations. Keep state changes understandable without movement and check any custom JavaScript animation separately.

03

Preserve accessible names

Use .visually-hidden when useful text should remain available to assistive technology.

04

Test every combination

Complete the task with a keyboard and at 200% zoom. Check contrast in every theme and state, then review names, reading order, and announcements with a screen reader.