Using the system in different stacks
Everything comes from the package @abhishekshankar/ds. Its single source is tokens/tokens.json in the DS repo (DTCG), and every file below is generated from it, so never copy values into a second place. Run npx @abhishekshankar/ds init to wire a project up automatically.
| Import | What it is |
|---|---|
@abhishekshankar/ds/tokens.css |
CSS custom properties (light by default, dark via data-theme), plus base body and reduced-motion styles |
@abhishekshankar/ds/fonts.css |
Switzer @font-face (self-hosted on ds.abhishekshankar.com, Fontshare fallback) |
@abhishekshankar/ds/components.css |
Plain-CSS reference components (ds- classes) |
@abhishekshankar/ds/tailwind.css |
Tailwind v4 theme (imports tokens.css) |
@abhishekshankar/ds/tokens.json |
DTCG tokens for native, Figma, Style Dictionary |
@abhishekshankar/ds/tokens.js |
tokens map and motion numbers for JS (Motion, GSAP) |
@abhishekshankar/ds/stylelint, /eslint |
Guardrail configs |
Any web stack (default)
npm i @abhishekshankar/ds, then import once, globally:@import "@abhishekshankar/ds/tokens.css"; @import "@abhishekshankar/ds/fonts.css"; @import "@abhishekshankar/ds/components.css"; /* optional */- Newsreader (long-form serif) comes from Google Fonts, only on pages with prose.
- Light is the default. If the product offers dark, set
data-theme="dark"on<html>when the user picks it and persist the choice (set it in an inline<head>script to avoid a flash).data-theme="system"follows the OS for users who choose that. - Write component styles with
var(--token). Never hardcode a hex, radius, shadow, duration, easing or font family:ds lint(Stylelint) fails the build if you do. Token definition files (tokens.css,*.tokens.css) are the only exception.
Tailwind v4
@import "tailwindcss";
@import "@abhishekshankar/ds/fonts.css";
@import "@abhishekshankar/ds/tailwind.css";
The theme clears Tailwind's default colors, fonts, type scale, radii, shadows, easings and breakpoints (--color-*: initial …), so only DS values exist. Colors are runtime aliases in @theme inline (dark mode keeps working); no alias references itself. The dark: variant is remapped to [data-theme="dark"], so it follows the user's choice, not the OS. You rarely need it, because the color utilities already flip.
| Utility | Maps to |
|---|---|
text-primary / text-secondary / text-danger |
text colors |
bg-page / bg-raised / bg-inverse, text-on-inverse(-secondary) |
surfaces |
border-line / border-line-strong / border-line-inverse, border-rule |
borders, dividers |
bg-hover, ring-halo |
3% hover fill, focus halo |
bg-teal-subtle, border-teal-line, text-teal |
accent (sparingly) |
text-display text-subtitle text-h2 text-title text-h3 text-body-lg text-body text-small text-caption text-eyebrow text-prose |
type scale (display/subtitle/h2 carry weight, tracking and leading) |
tracking-heading, tracking-eyebrow |
letter spacing |
rounded-xs/sm/md/lg/xl/pill |
radius 4/6/10/16/20/999 |
ease-standard/enter/exit/spring, duration-fast/moderate/page/page-exit/reveal |
motion |
py-section, py-section-compact, size-tap, max-w-center, max-w-reading, max-w-wide |
layout |
sm 560 · md 768 · lg 1024 · xl 1280 |
breakpoints |
font-sans / font-serif / font-mono |
families |
Shadows have no named utility because Tailwind inlines shadow values, which would freeze them to light mode. Use shadow-(--shadow-float). Any other token without a utility uses the variable form: max-w-(--reading-column-width).
Banned by ds/no-arbitrary-tailwind (ESLint for JS/TS, ds lint for markup): arbitrary colors (bg-[#fff]), arbitrary values on design utilities (text-[13px], rounded-[7px], duration-[400ms]) unless they are var(--token), default palette classes (text-gray-500, bg-white), transition-all, ease-linear/in/out, font-bold, shadow-sm…2xl, and durations other than the token values. Layout arbitrary values (grid-cols-[…], w-[…]) are allowed.
Tailwind v3: put the color aliases in theme.extend.colors as 'var(--…)' strings.
React / Next / Vue / Svelte / Solid / Astro
- Import tokens.css in the root layout or entry.
- Build components with the anatomy, variants and states from
components.md. Map variants to props (tone="inverse",size="compact") and states to native pseudo-classes or ARIA attributes (aria-expanded,aria-current). - Scoped styles (CSS Modules, Vue/Svelte
<style scoped>, Astro) consume the same variables. - CSS-in-JS (styled-components, Emotion, vanilla-extract, StyleX): reference
var(--token)strings rather than copying values. vanilla-extract can type them withcreateGlobalThemeContract. - Motion libraries (Framer Motion / Motion One / GSAP): pass the same numbers. Durations come from the table in interaction-motion.md (0.15 / 0.2 / 0.28 / 0.18s) and easings as arrays, for example enter
[0.22, 1, 0.36, 1], exit[0.4, 0, 1, 1], standard[0.4, 0, 0.2, 1]. Use the library's reduced-motion hook (useReducedMotion). - Page transitions: use the View Transitions API (
document.startViewTransition, Next/Astro/SvelteKit integrations) with the direction-aware pattern.
Native and other platforms
Read @abhishekshankar/ds/tokens.json (W3C DTCG-style: $type, $value, dark values under $extensions.mode.dark). Px values become pt/dp 1:1.
- SwiftUI: a
Colorextension per token with light/dark from the asset catalog orUIColor { traits in … }. Font: bundle Switzer, fallback.system. Motion:.timingCurve(0.22, 1, 0.36, 1, duration: 0.28), and respect@Environment(\.accessibilityReduceMotion). - Jetpack Compose:
CubicBezierEasing(0.22f, 1f, 0.36f, 1f),tween(280, easing = …), colors in aColorScheme. - Figma: create variable collections (Color with Light/Dark modes, Spacing, Radius, Motion as number/string) from tokens.json, with the same names using
/groups (color/text-primary). - Style Dictionary: tokens.json is a valid source. Point a config at it to emit Swift, Kotlin, SCSS, etc.
Coexisting with another design system
If the project already uses a component library (shadcn, MUI, Radix Themes), theme that library with these tokens rather than rebuilding everything. Map its color/radius/font variables to ours, then apply the type scale, hairline borders and motion rules on top.