Foundations
Design tokens
Where tokens come fromLink to the section Where tokens come from
The source is W3C design token files in tokens/. Style Dictionary builds them into tokens.css, the CSS variables every layer reads, and src/tokens/values.ts, a typed object for code that needs a value, such as a chart. npm test fails when the built files are stale.
# edit tokens/*.tokens.json, then
npm run tokens
npm testimport { tokens } from '@skribly/ui/tokens'
tokens.dark.accent // '#f5b653'
tokens.foundation['radius-lg'] // '0.625rem'ColourLink to the section Colour
Each column below is drawn in its own theme. Use the semantic name, never the value: bg-surface is white in light, near black in dark and pure black in high contrast. accent-text is the accent for text, because accent fails 4.5:1 as text on some surfaces.
Surfaces
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-bgbg-bg | #fffdf8 | #111318 | #000000 |
| --sk-surfacebg-surface | #ffffff | #16191f | #000000 |
| --sk-sunkenbg-sunken | #faf6ee | #0f1115 | #000000 |
| --sk-sidebg-side | #f6f1e7 | #0d0f13 | #000000 |
| --sk-barbg-bar | #efe8da | #0a0c0f | #000000 |
| --sk-inputbg-input | #ffffff | #16191f | #000000 |
| --sk-hoverbg-hover | #f1eadb | #1a1d23 | #1a1a1a |
| --sk-selectedbg-selected | #f3e2c7 | #2a2215 | #3d3200 |
Text and lines
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-fgtext-fg | #14171d | #ecedf0 | #ffffff |
| --sk-mutedtext-muted | #5e6573 | #9aa1ad | #e6e6e6 |
| --sk-borderborder-border | #e3dbcb | #252a33 | #ffffff |
| --sk-border-strongborder-border-strong | #7f838c | #6b7280 | #ffffff |
Accent
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-accentbg-accent | #b45309 | #f5b653 | #ffd60a |
| --sk-accent-strongbg-accent-strong | #92400e | #f8c97a | #ffe566 |
| --sk-accent-fgtext-accent-fg | #ffffff | #14171d | #000000 |
| --sk-accent-texttext-accent-text | #92400e | #f5b653 | #ffd60a |
| --sk-accent-softbg-accent-soft | #fdf6ec | #2a2215 | #3d3200 |
| --sk-accent-ringbg-accent-ring | rgb(180 83 9 / 22%) | rgb(245 182 83 / 28%) | rgb(255 214 10 / 60%) |
Status
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-dangerbg-danger | #b42318 | #f97066 | #ff8a8a |
| --sk-danger-fgtext-danger-fg | #ffffff | #14171d | #000000 |
| --sk-danger-softbg-danger-soft | #fdecea | #331715 | #1a1a1a |
| --sk-warnbg-warn | #a84207 | #fdb022 | #ffc247 |
| --sk-warn-fgtext-warn-fg | #ffffff | #14171d | #000000 |
| --sk-warn-softbg-warn-soft | #fef3e6 | #2e2410 | #1a1a1a |
| --sk-okbg-ok | #067647 | #47cd89 | #6fe3a8 |
| --sk-ok-fgtext-ok-fg | #ffffff | #14171d | #000000 |
| --sk-ok-softbg-ok-soft | #e6f4ed | #122a1f | #1a1a1a |
| --sk-infobg-info | #1d4ed8 | #7ca9ff | #a4c4ff |
| --sk-info-fgtext-info-fg | #ffffff | #14171d | #000000 |
| --sk-info-softbg-info-soft | #e8eefc | #18233a | #1a1a1a |
Dark sidebar
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-navbg-nav | #14171d | #0a0c0f | #000000 |
| --sk-nav-fgtext-nav-fg | #d6d8dd | #d6d8dd | #ffffff |
| --sk-nav-mutedbg-nav-muted | #a2a8b3 | #a2a8b3 | #e6e6e6 |
| --sk-nav-hoverbg-nav-hover | rgb(255 255 255 / 5%) | rgb(255 255 255 / 5%) | #1a1a1a |
| --sk-nav-activebg-nav-active | rgb(255 255 255 / 10%) | rgb(255 255 255 / 10%) | #3d3200 |
| --sk-nav-accentbg-nav-accent | #f5b653 | #f5b653 | #ffd60a |
Tool colours
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-tool-agenttext-tool-agent | #c2410c | #fb923c | #fdba74 |
| --sk-tool-trackertext-tool-tracker | #0369a1 | #38bdf8 | #7dd3fc |
| --sk-tool-skillstext-tool-skills | #6d28d9 | #a78bfa | #c4b5fd |
| --sk-tool-docstext-tool-docs | #0f766e | #2dd4bf | #5eead4 |
| --sk-tool-uitext-tool-ui | #a21caf | #e879f9 | #f0abfc |
Scrim and focus
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-scrimbg-scrim | rgb(0 0 0 / 35%) | rgb(0 0 0 / 55%) | rgb(0 0 0 / 80%) |
| --sk-focusbg-focus | #b45309 | #f5b653 | #ffd60a |
Shadows
| Token | Light | Dark | High contrast |
|---|---|---|---|
| --sk-shadow-smshadow-sm | 0 1px 2px rgb(20 23 29 / 8%) | 0 1px 2px rgb(0 0 0 / 40%) | 0 0 0 1px #ffffff |
| --sk-shadow-mdshadow-md | 0 1px 2px rgb(20 23 29 / 8%), 0 6px 16px rgb(20 23 29 / 8%) | 0 1px 2px rgb(0 0 0 / 40%), 0 6px 16px rgb(0 0 0 / 35%) | 0 0 0 1px #ffffff |
| --sk-shadow-lgshadow-lg | 0 2px 6px rgb(20 23 29 / 10%), 0 18px 48px rgb(20 23 29 / 20%) | 0 2px 6px rgb(0 0 0 / 50%), 0 18px 48px rgb(0 0 0 / 60%) | 0 0 0 2px #ffffff |
TypeLink to the section Type
Sizes are in rem, so text grows with the reader’s settings. Body text is 14px, and fields use 16px below 640px so phones do not zoom in.
| Token | Value | Tailwind | Preview |
|---|---|---|---|
| --sk-font-sans | "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif | font-sans | Skribly 0123 |
| --sk-font-mono | "JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, "Liberation Mono", monospace | font-mono | Skribly 0123 |
| --sk-font-display | "Bricolage Grotesque", "Inter", "Arial Narrow", Arial, sans-serif | font-display | Skribly 0123 |
| --sk-text-label | 0.6875rem | text-label | Aa Skribly |
| --sk-text-caption | 0.75rem | text-caption | Aa Skribly |
| --sk-text-body | 0.875rem | text-body | Aa Skribly |
| --sk-text-title | 0.9375rem | text-title | Aa Skribly |
| --sk-text-heading | 1.25rem | text-heading | Aa Skribly |
| --sk-text-page | 1.5rem | text-page | Aa Skribly |
| --sk-text-display | 1.75rem | text-display | Aa Skribly |
| --sk-leading-tight | 1.25 | [--sk-leading-tight] | Two lines of text show the line height. |
| --sk-leading-body | 1.5 | [--sk-leading-body] | Two lines of text show the line height. |
| --sk-leading-prose | 1.65 | [--sk-leading-prose] | Two lines of text show the line height. |
| --sk-tracking-tight | -0.01em | [--sk-tracking-tight] | Label |
| --sk-tracking-label | 0.07em | [--sk-tracking-label] | Label |
Space and controlsLink to the section Space and controls
Space follows Tailwind’s 4px steps. Control heights are tokens of their own, so data-density="compact" can shrink them.
| Token | Value | Tailwind | Preview |
|---|---|---|---|
| --sk-space-1 | 0.25rem | gap-1 | |
| --sk-space-2 | 0.5rem | gap-2 | |
| --sk-space-3 | 0.75rem | gap-3 | |
| --sk-space-4 | 1rem | gap-4 | |
| --sk-space-5 | 1.25rem | gap-5 | |
| --sk-space-6 | 1.5rem | gap-6 | |
| --sk-space-8 | 2rem | gap-8 | |
| --sk-space-10 | 2.5rem | gap-10 | |
| --sk-space-12 | 3rem | gap-12 | |
| --sk-control-sm | 2rem | h-control-sm | |
| --sk-control-md | 2.25rem | h-control-md | |
| --sk-control-lg | 2.75rem | h-control-lg |
RadiusLink to the section Radius
Small for chips, medium for controls, large for cards, extra large for dialogs.
| Token | Value | Tailwind | Preview |
|---|---|---|---|
| --sk-radius-sm | 0.25rem | rounded-sm | |
| --sk-radius-md | 0.4375rem | rounded-md | |
| --sk-radius-lg | 0.625rem | rounded-lg | |
| --sk-radius-xl | 0.875rem | rounded-xl | |
| --sk-radius-full | 999px | rounded-full |
MotionLink to the section Motion
Short and calm. Under prefers-reduced-motion the durations drop to almost nothing and loops stop.
| Token | Value | Tailwind | Preview |
|---|---|---|---|
| --sk-duration-fast | 120ms | duration-(--sk-duration-fast) | |
| --sk-duration-base | 160ms | duration-(--sk-duration-base) | |
| --sk-duration-slow | 250ms | duration-(--sk-duration-slow) | |
| --sk-ease-out | cubic-bezier(0.22, 1, 0.36, 1) | ease-out | |
| --sk-ease-in-out | cubic-bezier(0.65, 0, 0.35, 1) | ease-in-out |
Focus and layersLink to the section Focus and layers
The focus ring lives in base.css. Layers keep sticky bars, sidebars, dialogs, toasts and tours in a fixed order.
| Token | Value | Tailwind | Preview |
|---|---|---|---|
| --sk-focus-width | 2px | base.css | |
| --sk-focus-offset | 2px | base.css | |
| --sk-z-sticky | 10 | z-(--sk-z-sticky) | 10 |
| --sk-z-sidebar | 40 | z-(--sk-z-sidebar) | 40 |
| --sk-z-modal | 100 | z-(--sk-z-modal) | 100 |
| --sk-z-toast | 200 | z-(--sk-z-toast) | 200 |
| --sk-z-tour | 300 | z-(--sk-z-tour) | 300 |