@dloizides/design-tokens

中文文档
1.20.0发布于 2026/11/10MITTypeScriptESM + CJS零依赖近 30 天下载 3595

Framework-agnostic design-token SOURCE for the dloizides.com portfolio. One typed token set per app (aml / erevna / katalogos), seeded byte-for-byte from each app's CURRENT values, emitted in three forms from one source: CSS custom properties (:root) for

原始使用文档
文档目录11 节
  1. The four output forms
  2. toUiTheme is a LOSSLESS carrier
  3. The per-app presets
  4. FINREG module colours ( FINREG_MODULE_COLORS , 1.5.0)
  5. Motion tokens ( DEFAULT_MOTION , 1.7.0)
  6. The token shape (aligned to TenantThemeConfig )
  7. The cross-stack naming contract (#78b)
  8. AML name-mapping table (core → ui.css variable)
  9. Open naming decisions (need a human call before wider adopt…
  10. Adoption (separate tasks — this package ships values only)
  11. Scripts

@dloizides/design-tokens

Framework-agnostic design-token source for the dloizides.com portfolio — the cross-stack unifier (task #78). One typed token set per app, seeded byte-for-byte from each app's current values, emitted in three forms from one source.

#1 rule — behaviour-preserving extraction, not a redesign. Every preset mirrors the app's existing look. Adopting this package changes nothing visually. Runtime tests assert the RN output deep-equals the app's current DEFAULT_THEME_CONFIG and the AML CSS reproduces ui.css's :root verbatim.

Values only — no components. (Shared React components live in @dloizides/ui-layout, ui-forms, ui-feedback, ui-icons, ui-tables.)

The four output forms

One DesignTokens object per app feeds four pure emitters:

FormEmitterConsumer stackOutput
(a) CSS custom propertiestoCssVars(tokens, names, opts?)Stack-A vanilla-JS (AML console)a :root { --…: …; } string (+ optional dark-mode block)
(b) RN-web theme objecttoRnTheme(tokens)RN-web (erevna, katalogos)a TenantThemeConfig-compatible object
(c) Tailwind presettoTailwindPreset(tokens)DOM-React (SyncfusionThemeStudio){ theme: { extend: { colors, borderRadius, boxShadow, fontFamily } } }
(d) Shared UI kit themetoUiTheme(tokens, mode?)RN-web on the @dloizides/ui-* kit (ichnos, kefi)a UiTheme for ui-feedback's UiProvider
import {
  AML_TOKENS, EREVNA_TOKENS, KATALOGOS_TOKENS, PRESETS,
  toCssVars, toRnTheme, toTailwindPreset, toUiTheme,
  AML_CSS_VAR_NAMES, DEFAULT_CSS_VAR_NAMES,
} from '@dloizides/design-tokens';

// (a) AML console — reproduces wwwroot/shared/ui.css :root exactly
const css = toCssVars(AML_TOKENS, AML_CSS_VAR_NAMES);

// (b) RN-web — drop in as the tenant-theme fallback (deep-equals the current default)
const rnDefault = toRnTheme(EREVNA_TOKENS);

// (c) DOM-React ThemeStudio — spread into tailwind.config.presets
const tw = toTailwindPreset(KATALOGOS_TOKENS);

// (d) Shared UI kit — hand straight to <UiProvider theme={…}>
const uiTheme = toUiTheme(MY_TOKENS);

toUiTheme is a LOSSLESS carrier

toUiTheme exists because the naive projection an app writes by hand — value => ({ '100': v, '500': v, '700': v }) — collapses a scale to one value, silently discarding the deeper brand step and every status background tint. A provider fed that way cannot reproduce the palette it was built from, which is what stops a shared component (StatCard, ThemedTextInput, Field) from rendering on-brand in a consuming app.

Every token lands in an addressable slot, so an app can project its whole palette back out of the live provider rather than keeping a static parallel copy:

TokenSlot
primary / secondary / accentpalette.primary / .secondary / .accent at UI_MAIN_STEP
onBrandpalette.onBrand
light.surfaceMuted (or dark.*, per mode)palette.surfaceMuted
semantic.success …semantic.success at UI_MAIN_STEP
semantic.successBg …semantic.success at UI_TINT_STEP

The UiTheme shape is declared structurally in this package (as RnThemeConfig declares TenantThemeConfig), so the zero-dependency / zero-peer guarantee holds: the token source never depends on the kit it feeds.

The per-app presets

Each preset is seeded from that app's current source of truth:

PresetSeeded fromNotes
EREVNA_TOKENSerevna-web/src/theme/presets/default.ts + theme/utils/palette.tsprimary #005f73, secondary #94d2bd, accent #008d5c; full light+dark mode
KATALOGOS_TOKENSkatalogos-web/src/theme/presets/default.tscurrently identical to Erevna; kept separate so they can diverge
AML_TOKENSPROOViD/AMLService/.../wwwroot/shared/ui.css :rootbrand #4f46e5; dark app-shell + light workspace + verdict palette + radii/shadows/fonts
INTERVIEWPREP_TOKENSInterviewPrep/sites/portal/theme.css (dark :root + [data-theme='light'])DARK-FIRST: emit with toCssVars(INTERVIEWPREP_TOKENS, INTERVIEWPREP_CSS_VAR_NAMES, { lightSelector: ":root[data-theme='light']" }) (1.12.0); per-mode brandFill/dangerFill/... carry the site colours

RN presets carry no typography/radii/shadows/extensions — the RN theme has no centralized tokens for those today, so they are omitted (nothing to emit → zero change). The AML preset carries the richer superset (dark app-shell, ink scale, verdict palette, radii, shadows, fonts) in its extensions group.

FINREG module colours (FINREG_MODULE_COLORS, 1.5.0)

FINREG is a multi-module product (Payments / Accounting / CRM). Its interim EDA logo binds a colour to each module — the three orbits are the three modules — so colour reads as module identity rather than as one app's brand primary. Those tokens therefore sit beside the per-app presets, not inside one:

Modulebase (fill)on whiteonWhite (AA text/icon)on white
Payments (cyan)#4FD8F01.69:1 ✗#0C7D914.82:1 ✓
Accounting (magenta)#E85FBF3.08:1 ✗#D11E9B4.81:1 ✓
CRM (violet)#7B8FF72.95:1 ✗#4763F44.82:1 ✓

The base hexes are a decided design input (the logo), transcribed verbatim. They are light fills — none clears the WCAG AA 4.5:1 text floor on white — so anything painting a module colour as text or an icon on white uses onWhite, the darkened same-hue companion. The onWhite values were derived (lightest shade of the hue that still clears AA, carried a little past the floor) and both halves are pinned by finregModules.test.ts with their measured ratios, so a drift below AA fails the build. This is the SOURCE of these hexes — consumers read the token, they do not re-declare the literal (no-raw-color-literal).

Motion tokens (DEFAULT_MOTION, 1.7.0)

Motion is uniform across every portal, so — unlike colour — there is ONE shipped default rather than one per preset. DEFAULT_MOTION carries durations (ms) and easings:

DurationmsEasingcontrol pointscss
instant0standard[0.2,0,0,1]cubic-bezier(0.2,0,0,1)
fast140decelerate[0,0,0,1]cubic-bezier(0,0,0,1)
base200accelerate[0.3,0,1,1]cubic-bezier(0.3,0,1,1)
slow300

instant (0) is the reduced-motion collapse target. Each easing carries both a web css cubic-bezier(...) string and the bezier [x1,y1,x2,y2] control points, so @dloizides/ui-motion can build a matching RN Easing.bezier(...) and web + native animate on the same curve. motion is an OPTIONAL field on DesignTokens (a preset MAY override it), so existing presets/emitters are byte-identical.

The token shape (aligned to TenantThemeConfig)

The core fields intentionally mirror @dloizides/tenant-theme-web's TenantThemeConfig (primary, secondary, accent, semantic, light, dark, typography, branding) so a per-tenant theme override layers cleanly on top of a preset — the tenant blob and the preset speak the same field names. Fields the core cannot express (a dark app-shell, an ink scale, sanctions verdict triads) live in an optional extensions group and are documented as Stack-A supersets, not part of the TenantThemeConfig contract.

Three additions (1.2.0) are core rather than extensions, because they are needed by any app with a status badge or a filled brand button — not by one stack:

FieldWhy it needs its own slot
light.surfaceMuted / dark.surfaceMutedThe inset surface (table header rows, chips, wells) recedes; surface and surfaceElevated both lift. Neither substitutes.
onBrandThe foreground on a primary fill. A contrast partner of primary, so it sits beside it, not in a mode block. Defaults to white; a pale primary must set it.
semantic.successBg / warningBg / errorBg / infoBgA status tint is hand-picked for contrast, not derivable from its base — so it cannot be computed and must be carried.

All optional, and every emitter emits them only when a preset declares them, so presets that predate 1.2.0 produce byte-identical output.

The cross-stack naming contract (#78b)

Different stacks use different CSS-variable names for the same token. toCssVars takes a name map (CssVarNames) that says which CSS variable each token field emits as; a field mapped to undefined is skipped. Two maps ship:

  • DEFAULT_CSS_VAR_NAMES — canonical kebab names (--primary, --background, --radius, …), with a dark-mode block. Adopt these for a fresh consumer.
  • AML_CSS_VAR_NAMES — the AML console's existing ui.css names, so nothing there changes.

AML name-mapping table (core → ui.css variable)

Token fieldAML CSS varNote
primary--brand
secondary, accent(skipped)AML is single-brand; ui.css never reads them
semantic.success/warning/error(via --ok/--warn/--bad)emitted from the verdict extension
semantic.info(skipped)no AML variable
light.background--bg
light.surface--surface
light.surfaceElevated(via --surface-2)emitted from workspace.surfaceAlt
light.text(via --ink)emitted from ink.base
light.textSecondary(via --ink-soft)emitted from ink.soft
light.border--line
light.divider(skipped)AML reuses --line
dark.*(skipped)AML has no light/dark toggle; its dark shell is the shell extension in :root
radii.{xs,sm,md,lg,pill}--r-xs, --r-sm, --r, --r-lg, --r-pill
shadows.{sm,md,pop}--sh-1, --sh-2, --sh-pop
typography.fontFamily / monoFamily--font / --mono
extensions.shell.*--shell, --shell-2, --shell-line, --on-shell, --on-shell-dim
extensions.brand.*--brand-press, --brand-soft, --brand-ring
extensions.ink.*--ink, --ink-soft, --ink-faint, --ink-2
extensions.workspace.*--surface-2, --line-strong
extensions.verdict.*--ok*, --warn*, --bad* (base/soft/ink)

Open naming decisions (need a human call before wider adoption)

These were resolved conservatively to guarantee zero change, but a human should confirm the intended long-term semantics:

  1. AML secondary/accent — AML has a single brand. The preset mirrors --brand into both so the core shape is complete, but ui.css reads neither. If AML ever gains a real secondary brand, set it here.
  2. AML dark = the app-shell. AML has a dark shell over a light workspace, not a light/dark mode toggle. The preset seeds dark from the shell so the shape is complete, and the AML name map omits dark* so no dark-mode block is emitted (the shell is emitted from the shell extension in :root). If a real AML dark mode is introduced, this needs revisiting.
  3. semantic.info — AML has no info colour; it mirrors --brand. Confirm before relying on info in AML.
  4. RN radii/shadows. The RN apps have no centralized radius/shadow tokens today, so the RN presets omit them. If RN adopts a radius/shadow scale later, decide whether it should share AML's values or define its own.

Adoption (separate tasks — this package ships values only)

This package does not modify any consuming app. Adoption is tracked separately:

  • AML: replace the hand-written :root in ui.css with toCssVars(AML_TOKENS, AML_CSS_VAR_NAMES) output.
  • erevna/katalogos: import toRnTheme(EREVNA_TOKENS) as the defaultThemeConfig fallback.
  • ThemeStudio: spread toTailwindPreset(...) into tailwind.config's presets.

Scripts

npm run build (tsup → CJS+ESM+d.ts), npm test (jest, 100% coverage), npm run lint, npm run typecheck. Publish via ./publish.ps1 patch|minor|major.

版本与发布记录

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

版本发布日期距今
1.20.0latest2026/11/101 天前
1.19.02026/11/0210 天前
1.18.02026/10/2914 天前
1.17.02026/10/2815 天前
1.16.02026/10/2815 天前
1.15.02026/10/2518 天前

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

@dloizides/design-tokens 是什么?

Framework-agnostic design-token SOURCE for the dloizides.com portfolio. One typed token set per app (aml / erevna / katalogos), seeded byte-for-byte from each app's CURRENT values, emitted in three forms from one source: CSS custom properties (:root) for

如何安装 @dloizides/design-tokens?

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

@dloizides/design-tokens 支持 TypeScript 吗?

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

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

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

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

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

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

@dloizides/design-tokens 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dloizides/design-tokens 的下载量有多少?

根据 npm 官方下载统计,@dloizides/design-tokens 在 2026/10/13 – 2026/11/11 这 30 天内被下载 3,595 次,日均约 120 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

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

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