@serenis/design-tokens

中文文档
0.41.0发布于 2026/11/02LicenseRef-PolyForm-Noncommercial-1.0.0TypeScriptESM + CJS零依赖

Design tokens for the Serenis design system. Generates typed JS constants, CSS custom properties, cssvar accessor helpers, and CSS utility classes — all from a single set of DTCG JSON files.

原始使用文档
文档目录16 节
  1. Install
  2. Quick start
  3. Directory structure
  4. Documentation pages
  5. Writing an Overview page
  6. How to consume
  7. 1. JS constants
  8. 2. CSS custom properties
  9. 3. Cssvar accessor helpers
  10. 4. CSS utility classes
  11. How to add a token
  12. How to add a theme
  13. Token types
  14. Build pipeline
  15. Scripts
  16. License

@serenis/design-tokens

Design tokens for the Serenis design system. Generates typed JS constants, CSS custom properties, cssvar accessor helpers, and CSS utility classes — all from a single set of DTCG JSON files.

See ADR-0011 for architectural decisions.

Install

npm install @serenis/design-tokens

Import the CSS bundle in your app entry point:

import '@serenis/design-tokens/css'

Sub-paths for advanced use cases: @serenis/design-tokens/css/base, @serenis/design-tokens/css/design-tokens, @serenis/design-tokens/css/serenis-light, @serenis/design-tokens/css/serenis-dark, @serenis/design-tokens/css/global-style, @serenis/design-tokens/utilities.

Quick start

# Build all outputs (codegen + CSS + JS bundle)
yarn workspace @serenis/design-tokens build

# Run tests (includes golden snapshot tests)
yarn workspace @serenis/design-tokens test

# Update golden snapshots after intentional changes
yarn workspace @serenis/design-tokens test -- -u

Directory structure

libraries/design-tokens/
├── tokens/                    # DTCG JSON source files
│   ├── root.json              # Base tokens (all types)
│   ├── serenis-light.json     # Light theme overrides
│   ├── serenis-dark.json      # Dark theme overrides
│   ├── aliases.json           # Semantic alias tokens
│   └── md.json                # Responsive overrides (≥601px)
├── src/
│   ├── global-style.css       # Hand-written global reset + markdown semantic classes (→ dist/global-style.css)
│   ├── stories/               # Storybook stories and documentation (colocated)
│   │   ├── Breakpoints/
│   │   ├── Color/             # Color stories + Overview.mdx doc page
│   │   ├── Dimensions/
│   │   ├── Effects/
│   │   ├── Typography/
│   │   ├── UtilityClasses/
│   │   ├── components/        # Shared story helper components
│   │   └── docs/              # Domain-specific doc helpers for MDX pages
│   │       ├── components/    # Category-specific doc components (ColorPairing, etc.)
│   │       └── utils/         # Token description extractors (reads $description from JSON)
│   └── utils/                 # Hand-written runtime helpers (compiled → dist/utils/)
├── scripts/
│   ├── pipeline/              # Build pipeline entry + Style Dictionary config
│   ├── codegen/               # TypeScript module generators
│   ├── cssUtilities/          # CSS utility class generator
│   └── shared/                # JSON reader, token key sorting
├── dist/                      # Generated JS/DTS modules + CSS (gitignored)
│   ├── types/                 # Token name arrays + union types
│   ├── constants/             # UPPER_SNAKE_CASE constants (var refs + raw values)
│   ├── cssvar/                # Typed accessor functions
│   ├── utils/                 # Compiled from src/utils/
│   └── *.css                  # CSS custom properties + utility classes
└── package.json

Documentation pages

MDX files under src/stories/ are picked up by Storybook as doc pages (configured in apps/design-system/.storybook/main.ts). These pages use design-token-based typography via docs-reset.css, which overrides Storybook's built-in styles.

Reusable building blocks for doc pages (SectionIntro, DosDonts, Table/Row, CodeExample) come from the storybook-docs library. Category-specific visual components (e.g. ColorPairing) live in src/stories/docs/components/. Token $description metadata is read by src/stories/docs/utils/tokenDescriptions.ts, so doc pages stay in sync with the JSON source automatically.

Story sort order puts "Overview" pages first, then "Playground", then everything else.

Writing an Overview page

Every token category should have an Overview.mdx file that follows a consistent structure. Use Color/Overview.mdx as the reference implementation.

Expected sections:

  1. Title + intro — Category name as # heading, one-line summary, then <SectionIntro> pulling $description from the token JSON source.
  2. Theme and responsive behavior — How these tokens behave across themes and viewports. Every Overview page includes this section for consistency.
  3. Conceptual model — How the tokens in this category are organized (layers, scales, naming logic). Use <Table> for structured breakdowns.
  4. Token-by-token guide with code examples — Walk through the main tokens/groups with usage guidance, rendered values, <CodeExample> blocks, and <DosDonts>. Code examples and guidelines are interleaved within each token subsection rather than gathered at the end.
  5. Accessibility / pairing notes (when relevant) — Contrast, motion, sizing, or other a11y considerations for this category. Not every category needs a dedicated section.

Imports:

import { Meta } from '@storybook/addon-docs/blocks'
import { CodeExample, DosDonts, Row, SectionIntro, Table } from 'storybook-docs'
import { rootDescriptions } from '../docs'
// For themed tokens (Color, Effects):
import { rootDescriptions, lightDescriptions, darkDescriptions } from '../docs'

Generic building blocks (SectionIntro, DosDonts, Table/Row, CodeExample) come from the storybook-docs library. Category-specific components (e.g. ColorPairing) live in src/stories/docs/components/. Token descriptions are pre-computed maps in src/stories/docs/utils/tokenDescriptions.ts — use rootDescriptions.spacing, lightDescriptions.color, etc. to get $description strings. This keeps docs in sync with the JSON source automatically.

How to consume

1. JS constants

For values that must live in TypeScript, such as third-party APIs:

import { COLOR_PRIMARY, SPACING_MD } from '@serenis/design-tokens'

const appearance = {
  variables: { colorPrimary: COLOR_PRIMARY, spacingUnit: SPACING_MD },
}

Each constant resolves to var(--srns-spacing-md). A _VALUE companion holds the raw CSS value:

SPACING_MD // "var(--srns-spacing-md)"
SPACING_MD_VALUE // "16px"

2. CSS custom properties

@import '@serenis/design-tokens/css'; /* base + both themes + utility classes */
/* or granular: */
@import '@serenis/design-tokens/css/base'; /* root + responsive overrides */
@import '@serenis/design-tokens/css/design-tokens'; /* base + both themes (no utilities; same as CDN bundle) */
@import '@serenis/design-tokens/css/serenis-light'; /* base + light theme (standalone) */
@import '@serenis/design-tokens/css/serenis-dark'; /* base + dark theme (standalone) */

All variables are prefixed --srns- and scoped to :root (base) or .srns-serenis-light / .srns-serenis-dark (themes).

3. Cssvar accessor helpers

import { cssvarSpacing } from '@serenis/design-tokens'

cssvarSpacing('md')
// → { name: 'srns-spacing-md', value: 'var(--srns-spacing-md)' }

Typed: cssvarSpacing only accepts valid SpacingName values.

4. CSS utility classes

/* app.css */
@import '@serenis/design-tokens/utilities';

This gives you utility classes mapped to design tokens: bg-primary-50, p-md, px-lg, mb-sm, rounded-lg, elevation-sm, font-600, etc. Typography composites are available as typography-paragraph, typography-public-h1, etc. Directional spacing uses short Tailwind-style names (px/py/pl/pr/pt/pb for padding, mx/my/ml/mr/mt/mb for margin), mapped to CSS logical properties under the hood. No external CSS framework required; the output is plain CSS compatible with Safari 16+.

Responsive variants

Some utility classes are available with a mobile-first breakpoint prefix, following the pattern {bp}\:{utility}. Three breakpoints are supported:

PrefixMedia query
mdmin-width: 601px
lgmin-width: 1025px
xlmin-width: 1441px

For example, md\:flex applies display: flex when the viewport is at least 601px wide.

Utilities with responsive variants:

  • Spacing padding (p-*, px-*, py-*, pl-*, pr-*, pt-*, pb-*)
  • Spacing margin (m-*, mx-*, my-*, ml-*, mr-*, mt-*, mb-*)
  • Spacing gap (gap-*, gap-x-*, gap-y-*)
  • Display (flex, grid, block, inline-flex, inline-block, hidden)
  • Flex direction (flex-row, flex-col, flex-row-reverse, flex-col-reverse)
  • Alignment (items-center, items-start, items-end, items-stretch, items-baseline)
  • Justify (justify-center, justify-between, justify-around, justify-start, justify-end)
  • Flex wrap (flex-wrap, flex-nowrap)
  • Flex shorthand (flex-auto, flex-initial, flex-none, flex-1)
  • Flex grow/shrink (grow, grow-0, shrink, shrink-0)
  • Flex basis (basis-1/12–basis-11/12, basis-full, basis-auto, basis-0, basis-{container})

Utilities without responsive variants: legacy-flex (the default styles of Flex), spacing sizing (w-*, h-*, size-* from spacing 0 only), container width (w-{container}, min-w-{container}, max-w-{container}), auto margin (mx-auto), border radius (rounded-*), colors (bg-*, text-{color}, border-{color}), border width (border-*), elevation (elevation-*), z-index (z-*), font weight, font family, opacity (opacity-*), typography composites (typography-*), position, overflow (overflow-*, truncate), scroll behavior (scroll-smooth), line clamp (line-clamp-1–line-clamp-6, line-clamp-none), sr-only, text-align, text-transform (uppercase, lowercase, capitalize, normal-case), text-decoration (underline, line-through), font-style (italic), white-space (whitespace-*), word-break (break-word), border-style, and transition.

cx helper

The build also generates a cx() helper for composing utility classes. It is typed via UtilityClassName, so you get autocomplete and compile-time checks. It also accepts object arguments for conditional classes — keys whose value is truthy are included in the output. Literal keys are checked against UtilityClassName as well; computed keys (e.g. CSS-module classes or a nested cx()) pass through:

import { cx } from '@serenis/design-tokens'
;<div className={cx('p-md', 'bg-primary-50', 'rounded-lg')} />
;<div className={cx('pt-md', { [cx('pt-0', 'bg-primary-50')]: isLarge })} />

Linting: Three ESLint rules enforce cx() usage across the monorepo. enforce-cx-classname requires cx() for all className composition (no raw strings or template literals). cx-classnames-order enforces a deterministic CSS-property-based ordering of class arguments and removes duplicates — this rule is auto-fixable. cx-prefer-object-notation requires object arguments for conditional classes (no ternaries or &&).

How to add a token

  1. Add to JSON: Edit the appropriate file in tokens/ (usually root.json). Follow the DTCG format:

    {
      "my-branch": {
        "$type": "dimension",
        "$description": "Description of this token group",
        "my-token": { "$value": { "value": 8, "unit": "px" } }
      }
    }
    
  2. Build: Run yarn workspace @serenis/design-tokens build. The pipeline auto-discovers branches by $type.

  3. Update snapshots: Run yarn workspace @serenis/design-tokens test -- -u to update golden snapshots.

  4. The new token is immediately available as:

    • JS constant: MY_BRANCH_MY_TOKEN / MY_BRANCH_MY_TOKEN_VALUE
    • CSS variable: --srns-my-branch-my-token
    • CSS utility class (e.g. my-branch-my-token)

How to add a theme

  1. Create a new JSON file in tokens/ with color/elevation overrides.

  2. Register it in scripts/pipeline/themeTokenRegistry.ts:

    { id: 'my-theme', tokenJsonFileName: 'my-theme.json', colorScheme: 'light' }
    
  3. Rebuild. The pipeline produces dist/my-theme.css and theme-specific JS constants automatically.

Token types

DTCG $typeToken branchesCodegen
dimensionspacing, container, border-radius, border-width, font-size, line-height, letter-spacing, breakpointRegistry (simple)
durationtimeRegistry (simple)
fontFamilyfont-familyRegistry (simple)
fontWeightfont-weightRegistry (simple)
numberopacity, z-indexRegistry (simple)
stringmedia-queryRegistry (simple)
shadowelevationDedicated (theme layers)
colorcolorDedicated (theme layers)
typographytypography, typography.publicDedicated (composite)

"Registry (simple)" types share a unified codegen path via SIMPLE_TOKEN_RESOLVERS. Adding a new simple type means adding one entry to the registry in scripts/codegen/tokenTypeResolvers.ts.

Build pipeline

tokens/*.json
    │
    ├──▶ Custom codegen ──▶ dist/{types,constants,cssvar,utils}/*.{js,d.ts}
    │       │
    │       └──▶ esbuild CJS bundle ──▶ dist/index.cjs
    │
    ├──▶ Style Dictionary ──▶ dist/root.css, dist/{theme}.css, dist/md.css
    │       │
    │       └──▶ Minify + assemble ──▶ dist/base.css, dist/serenis-light.css, dist/serenis-dark.css
    │
    ├──▶ Style Dictionary (utility classes format) ──▶ dist/utilities.css
    │       │
    │       └──▶ Extract class names ──▶ dist/types/utility-class.{js,d.ts} (UtilityClassName)
    │                                  + dist/utils/cx.{js,d.ts} (cx helper)
    │
    └──▶ Concat base + themes + minify ──▶ dist/design-tokens.css
         Concat base + themes + utilities + minify ──▶ dist/index.css

Scripts

CommandDescription
buildFull build: codegen + Style Dictionary CSS + JS bundle
testRun all tests (includes golden snapshot tests)
test:watchWatch mode
typecheckTypeScript validation
lintESLint
lint:fixESLint with auto-fix
prettifyoxfmt formatting
prettify:checkCheck oxfmt formatting without writing
cleanRemove .turbo, build, dist, node_modules

License

PolyForm Noncommercial 1.0.0 — see LICENSE for the full text.

版本与发布记录

@serenis/design-tokens 共发布 21 个版本,首次发布于 2026/07/03;过去 12 个月发布了 21 个版本。

版本发布日期距今
0.41.0latest2026/11/021 天前
0.40.02026/10/268 天前
0.39.02026/10/268 天前
0.38.02026/10/268 天前
0.37.02026/10/2310 天前
0.36.02026/10/2311 天前

关于 @serenis/design-tokens 的常见问题

@serenis/design-tokens 是什么?

为Serenis设计系统设计令牌。生成类型化的JS常量、CSS自定义属性、cssvar访问器帮助器和CSS实用程序类——所有这些都来自一组 DTCG JSON文件。

如何安装 @serenis/design-tokens?

在项目目录运行 npm install @serenis/design-tokens,或使用 pnpm add / yarn add / bun add。

@serenis/design-tokens 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@serenis/design-tokens 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@serenis/design-tokens 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@serenis/design-tokens 可以商用吗?

@serenis/design-tokens 使用 LicenseRef-PolyForm-Noncommercial-1.0.0 许可。声明的许可为 LicenseRef-PolyForm-Noncommercial-1.0.0,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@serenis/design-tokens 还在维护吗?

最新版本 0.41.0 发布于 2026/11/02(1 天前);至今共发布 21 个版本,其中过去 12 个月发布了 21 个;首次发布于 2026/07/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。