feat: add token system and ui contracts

This commit is contained in:
2026-03-19 14:21:13 +08:00
parent 937855362b
commit 3960e0a0e7
14 changed files with 1389 additions and 32 deletions
+170 -2
View File
@@ -1,8 +1,176 @@
export const themeNames = ["light", "dark"] as const;
export const themeNames = ["light", "dark", "brand"] as const;
export type ThemeName = (typeof themeNames)[number];
export const defaultTheme: ThemeName = "light";
export const themeDetails = {
light: {
label: "Light",
note: "Warm editorial default"
},
dark: {
label: "Dark",
note: "Warm charcoal default"
},
brand: {
label: "Brand",
note: "Verdant accent scaffold"
}
} as const satisfies Record<ThemeName, { label: string; note: string }>;
export const motionModeNames = ["system", "reduced"] as const;
export type MotionModeName = (typeof motionModeNames)[number];
export const defaultMotionMode: MotionModeName = "system";
export const motionScale = {
instant: "var(--dur-instant)",
fast: "var(--dur-fast)",
base: "var(--dur-base)",
slow: "var(--dur-slow)"
slow: "var(--dur-slow)",
deliberate: "var(--dur-deliberate)"
} as const;
export const colorTokens = [
{ name: "background", cssVar: "--color-background", role: "Application canvas" },
{ name: "foreground", cssVar: "--color-foreground", role: "Primary text and icons" },
{ name: "surface", cssVar: "--color-surface", role: "Secondary surface backgrounds" },
{
name: "surface-strong",
cssVar: "--color-surface-strong",
role: "Elevated surface emphasis"
},
{ name: "card", cssVar: "--color-card", role: "Cards and floating panels" },
{ name: "border", cssVar: "--color-border", role: "Default dividers and input borders" },
{
name: "border-strong",
cssVar: "--color-border-strong",
role: "Higher emphasis dividers"
},
{ name: "primary", cssVar: "--color-primary", role: "Primary actions and highlights" },
{
name: "secondary",
cssVar: "--color-secondary",
role: "Secondary fills and supporting actions"
},
{ name: "muted", cssVar: "--color-muted", role: "Subtle supporting surfaces" },
{
name: "muted-foreground",
cssVar: "--color-muted-foreground",
role: "Secondary text and captions"
},
{ name: "accent", cssVar: "--color-accent", role: "Moments of emphasis or delight" },
{ name: "success", cssVar: "--color-success", role: "Success feedback" },
{ name: "warning", cssVar: "--color-warning", role: "Warning feedback" },
{ name: "destructive", cssVar: "--color-destructive", role: "Destructive actions" }
] as const;
export const typographyTokens = [
{
name: "caption",
fontVar: "--text-xs",
lineHeightVar: "--leading-normal",
familyVar: "--font-sans",
sample: "Small labels, metadata, and supporting notes."
},
{
name: "body",
fontVar: "--text-base",
lineHeightVar: "--leading-normal",
familyVar: "--font-sans",
sample: "Body copy stays warm, readable, and stable across themes."
},
{
name: "lead",
fontVar: "--text-xl",
lineHeightVar: "--leading-loose",
familyVar: "--font-sans",
sample: "Lead text introduces a surface without becoming display copy."
},
{
name: "display",
fontVar: "--text-4xl",
lineHeightVar: "--leading-tight",
familyVar: "--font-display",
sample: "Display text carries the editorial voice of the system."
}
] as const;
export const radiusTokens = [
{ name: "xs", cssVar: "--radius-xs" },
{ name: "sm", cssVar: "--radius-sm" },
{ name: "md", cssVar: "--radius-md" },
{ name: "lg", cssVar: "--radius-lg" },
{ name: "xl", cssVar: "--radius-xl" },
{ name: "full", cssVar: "--radius-full" }
] as const;
export const shadowTokens = [
{ name: "xs", cssVar: "--shadow-xs" },
{ name: "sm", cssVar: "--shadow-sm" },
{ name: "md", cssVar: "--shadow-md" },
{ name: "lg", cssVar: "--shadow-lg" }
] as const;
export const motionTokens = {
durations: [
{ name: "instant", cssVar: "--dur-instant" },
{ name: "fast", cssVar: "--dur-fast" },
{ name: "base", cssVar: "--dur-base" },
{ name: "slow", cssVar: "--dur-slow" },
{ name: "deliberate", cssVar: "--dur-deliberate" }
],
easings: [
{ name: "standard", cssVar: "--ease-standard" },
{ name: "emphasized", cssVar: "--ease-emphasized" },
{ name: "exit", cssVar: "--ease-exit" }
],
distances: [
{ name: "xs", cssVar: "--distance-xs" },
{ name: "sm", cssVar: "--distance-sm" },
{ name: "md", cssVar: "--distance-md" },
{ name: "lg", cssVar: "--distance-lg" }
],
scales: [
{ name: "press", cssVar: "--scale-press" },
{ name: "hover", cssVar: "--scale-hover" },
{ name: "pop", cssVar: "--scale-pop" }
]
} as const;
function getTargetElement(root?: HTMLElement) {
if (root) {
return root;
}
if (typeof document === "undefined") {
return undefined;
}
return document.documentElement;
}
export function setTheme(theme: ThemeName, root?: HTMLElement) {
const target = getTargetElement(root);
if (!target) {
return;
}
target.dataset.theme = theme;
}
export function setMotionMode(mode: MotionModeName, root?: HTMLElement) {
const target = getTargetElement(root);
if (!target) {
return;
}
if (mode === "system") {
delete target.dataset.motion;
return;
}
target.dataset.motion = mode;
}