Skip to content

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)

  1. npm i @abhishekshankar/ds, then import once, globally:
    @import "@abhishekshankar/ds/tokens.css";
    @import "@abhishekshankar/ds/fonts.css";
    @import "@abhishekshankar/ds/components.css"; /* optional */
    
  2. Newsreader (long-form serif) comes from Google Fonts, only on pages with prose.
  3. 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.
  4. 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 with createGlobalThemeContract.
  • 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 Color extension per token with light/dark from the asset catalog or UIColor { 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 a ColorScheme.
  • 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.