@asafarim/design-tokens 0.3.6 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/tokens.base.css +50 -9
- package/package.json +7 -2
- package/src/build/index.ts +5 -0
- package/src/build/toCssVars.ts +31 -0
- package/src/build/toJson.ts +20 -0
- package/src/build/validate.ts +90 -0
- package/src/core/types.ts +29 -0
- package/src/core/utils.ts +42 -0
- package/src/index.ts +60 -0
- package/src/themes/dark.ts +64 -0
- package/src/themes/densityComfortable.ts +11 -0
- package/src/themes/densityCompact.ts +11 -0
- package/src/themes/highContrast.ts +60 -0
- package/src/themes/index.ts +21 -0
- package/src/themes/light.ts +64 -0
- package/src/tokens/color/accent.ts +20 -0
- package/src/tokens/color/brand.ts +41 -0
- package/src/tokens/color/dataViz.ts +20 -0
- package/src/tokens/color/gradients.ts +13 -0
- package/src/tokens/color/index.ts +7 -0
- package/src/tokens/color/neutral.ts +32 -0
- package/src/tokens/color/overlays.ts +21 -0
- package/src/tokens/color/semantic.ts +36 -0
- package/src/tokens/effects/blur.ts +7 -0
- package/src/tokens/effects/index.ts +4 -0
- package/src/tokens/effects/opacity.ts +11 -0
- package/src/tokens/effects/shadows.ts +20 -0
- package/src/tokens/effects/textures.ts +11 -0
- package/src/tokens/iconography/index.ts +2 -0
- package/src/tokens/iconography/sizes.ts +9 -0
- package/src/tokens/iconography/strokeWidths.ts +7 -0
- package/src/tokens/index.ts +21 -0
- package/src/tokens/motion/duration.ts +7 -0
- package/src/tokens/motion/easing.ts +8 -0
- package/src/tokens/motion/index.ts +4 -0
- package/src/tokens/motion/reducedMotion.ts +9 -0
- package/src/tokens/motion/transitions.ts +16 -0
- package/src/tokens/shape/borders.ts +7 -0
- package/src/tokens/shape/index.ts +3 -0
- package/src/tokens/shape/radii.ts +9 -0
- package/src/tokens/shape/strokes.ts +7 -0
- package/src/tokens/spacing/density.ts +12 -0
- package/src/tokens/spacing/grid.ts +28 -0
- package/src/tokens/spacing/index.ts +5 -0
- package/src/tokens/spacing/layout.ts +20 -0
- package/src/tokens/spacing/rtl.ts +20 -0
- package/src/tokens/spacing/scale.ts +15 -0
- package/src/tokens/typography/families.ts +14 -0
- package/src/tokens/typography/headings.ts +29 -0
- package/src/tokens/typography/index.ts +7 -0
- package/src/tokens/typography/letterSpacing.ts +7 -0
- package/src/tokens/typography/lineHeights.ts +13 -0
- package/src/tokens/typography/responsive.ts +21 -0
- package/src/tokens/typography/sizes.ts +14 -0
- package/src/tokens/typography/weights.ts +13 -0
- package/src/tokens/ui/button.ts +25 -0
- package/src/tokens/ui/focus.ts +7 -0
- package/src/tokens/ui/index.ts +5 -0
- package/src/tokens/ui/input.ts +12 -0
- package/src/tokens/ui/overlay.ts +6 -0
- package/src/tokens/ui/surface.ts +9 -0
- package/src/tokens/zindex/index.ts +1 -0
- package/src/tokens/zindex/scale.ts +12 -0
package/css/tokens.base.css
CHANGED
|
@@ -1,10 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/* ------------------------------------------------------------------ */
|
|
2
|
+
/* 20 fonts (100-900) Google Fonts */
|
|
3
|
+
/* ------------------------------------------------------------------ */
|
|
4
|
+
@import url('https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Figtree:ital,wght@0,300..900;1,300..900&family=Jost:ital,wght@0,100..900;1,100..900&family=Karla:ital,wght@0,200..800;1,200..800&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Lexend:wght@100..900&family=Manrope:wght@200..800&family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Overpass:ital,wght@0,100..900;1,100..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Public+Sans:ital,wght@0,100..900;1,100..900&family=Quicksand:wght@300..700&family=Red+Hat+Display:ital,wght@0,300..900;1,300..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Rubik:ital,wght@0,300..900;1,300..900&family=Sora:wght@100..800&family=Space+Grotesk:wght@300..700&family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap');
|
|
5
|
+
@import url('https://fonts.googleapis.com/css2?family=Barriecito&family=Borel&family=DynaPuff:wght@400..700&family=Gabarito:wght@400..900&family=Kranky&family=Macondo&family=Playpen+Sans:wght@100..800&family=Rubik+Spray+Paint&family=Shadows+Into+Light+Two&family=Zalando+Sans:ital,wght@0,200..900;1,200..900&display=swap');
|
|
4
6
|
|
|
5
7
|
:root {
|
|
6
|
-
--asm-font-family-primary: Inter, ui-sans-serif, system-ui, -apple-system,
|
|
7
|
-
|
|
8
|
+
--asm-font-family-primary: Inter, ui-sans-serif, system-ui, -apple-system,
|
|
9
|
+
Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
|
|
10
|
+
--asm-font-family-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo,
|
|
11
|
+
Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
12
|
+
--asm-font-family-poppins: "Poppins", ui-sans-serif, system-ui, sans-serif;
|
|
13
|
+
--asm-font-family-open-sans: "Open Sans", ui-sans-serif, system-ui, sans-serif;
|
|
14
|
+
--asm-font-family-lato: "Lato", ui-sans-serif, system-ui, sans-serif;
|
|
15
|
+
--asm-font-family-rubik: "Rubik", ui-sans-serif, system-ui, sans-serif;
|
|
16
|
+
--asm-font-family-nunito: "Nunito", ui-sans-serif, system-ui, sans-serif;
|
|
17
|
+
--asm-font-family-work-sans: "Work Sans", ui-sans-serif, system-ui, sans-serif;
|
|
18
|
+
--asm-font-family-manrope: "Manrope", ui-sans-serif, system-ui, sans-serif;
|
|
19
|
+
--asm-font-family-figtree: "Figtree", ui-sans-serif, system-ui, sans-serif;
|
|
20
|
+
--asm-font-family-sora: "Sora", ui-sans-serif, system-ui, sans-serif;
|
|
21
|
+
--asm-font-family-outfit: "Outfit", ui-sans-serif, system-ui, sans-serif;
|
|
22
|
+
--asm-font-family-lexend: "Lexend", ui-sans-serif, system-ui, sans-serif;
|
|
23
|
+
--asm-font-family-public-sans: "Public Sans", ui-sans-serif, system-ui,
|
|
24
|
+
sans-serif;
|
|
25
|
+
--asm-font-family-dm-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif;
|
|
26
|
+
--asm-font-family-overpass: "Overpass", ui-sans-serif, system-ui, sans-serif;
|
|
27
|
+
--asm-font-family-quicksand: "Quicksand", ui-sans-serif, system-ui, sans-serif;
|
|
28
|
+
--asm-font-family-karla: "Karla", ui-sans-serif, system-ui, sans-serif;
|
|
29
|
+
--asm-font-family-barlow: "Barlow", ui-sans-serif, system-ui, sans-serif;
|
|
30
|
+
--asm-font-family-space-grotesk: "Space Grotesk", ui-sans-serif, system-ui,
|
|
31
|
+
sans-serif;
|
|
32
|
+
--asm-font-family-red-hat: "Red Hat Display", ui-sans-serif, system-ui,
|
|
33
|
+
sans-serif;
|
|
34
|
+
--asm-font-family-jost: "Jost", ui-sans-serif, system-ui, sans-serif;
|
|
35
|
+
|
|
36
|
+
--asm-font-family-barriecito: "Barriecito", cursive, sans-serif;
|
|
37
|
+
--asm-font-family-borel: "Borel", cursive, sans-serif;
|
|
38
|
+
--asm-font-family-dynapuff: "DynaPuff", cursive, sans-serif;
|
|
39
|
+
--asm-font-family-gabarito: "Gabarito", sans-serif;
|
|
40
|
+
--asm-font-family-kranky: "Kranky", cursive, sans-serif;
|
|
41
|
+
--asm-font-family-macondo: "Macondo", cursive, sans-serif;
|
|
42
|
+
--asm-font-family-playpen-sans: "Playpen Sans", cursive, sans-serif;
|
|
43
|
+
--asm-font-family-rubik-spray: "Rubik Spray Paint", cursive, sans-serif;
|
|
44
|
+
--asm-font-family-shadows-light: "Shadows Into Light Two", cursive, sans-serif;
|
|
45
|
+
--asm-font-family-zalando: "Zalando Sans", ui-sans-serif, system-ui, sans-serif;
|
|
8
46
|
|
|
9
47
|
--asm-font-weight-100: 100;
|
|
10
48
|
--asm-font-weight-200: 200;
|
|
@@ -79,7 +117,7 @@
|
|
|
79
117
|
--asm-border-thick: 3px;
|
|
80
118
|
|
|
81
119
|
--asm-effect-shadow-sm: 0 1px 2px rgba(17, 24, 39, 0.06);
|
|
82
|
-
--asm-effect-shadow-md: 0 6px 20px rgba(17, 24, 39, 0.
|
|
120
|
+
--asm-effect-shadow-md: 0 6px 20px rgba(17, 24, 39, 0.1);
|
|
83
121
|
--asm-effect-shadow-lg: 0 16px 40px rgba(17, 24, 39, 0.14);
|
|
84
122
|
--asm-effect-shadow-xl: 0 24px 60px rgba(17, 24, 39, 0.18);
|
|
85
123
|
|
|
@@ -96,9 +134,12 @@
|
|
|
96
134
|
--asm-motion-easing-entrance: cubic-bezier(0, 0, 0, 1);
|
|
97
135
|
--asm-motion-easing-exit: cubic-bezier(0.4, 0, 1, 1);
|
|
98
136
|
|
|
99
|
-
--asm-transition-fade: opacity var(--asm-motion-duration-normal)
|
|
100
|
-
|
|
101
|
-
--asm-transition-
|
|
137
|
+
--asm-transition-fade: opacity var(--asm-motion-duration-normal)
|
|
138
|
+
var(--asm-motion-easing-standard);
|
|
139
|
+
--asm-transition-slide: transform var(--asm-motion-duration-normal)
|
|
140
|
+
var(--asm-motion-easing-standard);
|
|
141
|
+
--asm-transition-scale: transform var(--asm-motion-duration-fast)
|
|
142
|
+
var(--asm-motion-easing-emphasized);
|
|
102
143
|
|
|
103
144
|
--asm-z-base: 0;
|
|
104
145
|
--asm-z-dropdown: 100;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asafarim/design-tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "ASafariM design tokens (CSS variables) with multi-theme support.",
|
|
5
|
+
"description": "ASafariM design tokens (CSS variables) with multi-theme support and 30 variable fonts (100-900).",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "ASafariM",
|
|
8
8
|
"repository": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"style": "css/index.css",
|
|
23
23
|
"files": [
|
|
24
24
|
"css",
|
|
25
|
+
"src",
|
|
25
26
|
"README.md",
|
|
26
27
|
"CHANGELOG.md",
|
|
27
28
|
"LICENSE"
|
|
@@ -30,6 +31,10 @@
|
|
|
30
31
|
"css/*.css"
|
|
31
32
|
],
|
|
32
33
|
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"import": "./src/index.ts",
|
|
36
|
+
"default": "./src/index.ts"
|
|
37
|
+
},
|
|
33
38
|
"./css": {
|
|
34
39
|
"style": "./css/index.css",
|
|
35
40
|
"default": "./css/index.css"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TokenTree } from "../core/types";
|
|
2
|
+
import { toKebabCase, walkTokenTree } from "../core/utils";
|
|
3
|
+
|
|
4
|
+
export type CssVarDict = Record<string, string>;
|
|
5
|
+
|
|
6
|
+
export function tokenPathToCssVarName(path: string[]): string {
|
|
7
|
+
const kebabParts = path.map(toKebabCase);
|
|
8
|
+
return `--asm-${kebabParts.join("-")}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function toCssVars(tree: TokenTree): CssVarDict {
|
|
12
|
+
const out: CssVarDict = {};
|
|
13
|
+
|
|
14
|
+
walkTokenTree(tree, ({ path, leaf }) => {
|
|
15
|
+
const cssVar = tokenPathToCssVarName(path);
|
|
16
|
+
out[cssVar] = String(leaf.value);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function toCssVarNames(tree: TokenTree): Record<string, string> {
|
|
23
|
+
const out: Record<string, string> = {};
|
|
24
|
+
|
|
25
|
+
walkTokenTree(tree, ({ path }) => {
|
|
26
|
+
const dotPath = path.join(".");
|
|
27
|
+
out[dotPath] = tokenPathToCssVarName(path);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TokenTree } from "../core/types";
|
|
2
|
+
import { walkTokenTree } from "../core/utils";
|
|
3
|
+
|
|
4
|
+
export type FlatTokenJson = Record<string, { value: string | number; description: string; meta?: Record<string, unknown> }>;
|
|
5
|
+
|
|
6
|
+
export function toJson(tree: TokenTree): FlatTokenJson {
|
|
7
|
+
const out: FlatTokenJson = {};
|
|
8
|
+
|
|
9
|
+
walkTokenTree(tree, ({ path, leaf }) => {
|
|
10
|
+
const key = path.join(".");
|
|
11
|
+
if (leaf.meta) {
|
|
12
|
+
out[key] = { value: leaf.value, description: leaf.description, meta: leaf.meta };
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
out[key] = { value: leaf.value, description: leaf.description };
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ThemeDefinition, TokenTree, ValidateResult } from "../core/types";
|
|
2
|
+
import { walkTokenTree } from "../core/utils";
|
|
3
|
+
import { tokenPathToCssVarName } from "./toCssVars";
|
|
4
|
+
|
|
5
|
+
const HEX_COLOR = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
6
|
+
|
|
7
|
+
export function validateTokens(args: {
|
|
8
|
+
tokens: TokenTree;
|
|
9
|
+
themes: ThemeDefinition[];
|
|
10
|
+
}): ValidateResult {
|
|
11
|
+
const errors: string[] = [];
|
|
12
|
+
const warnings: string[] = [];
|
|
13
|
+
|
|
14
|
+
const seenDotKeys = new Set<string>();
|
|
15
|
+
const seenCssVars = new Set<string>();
|
|
16
|
+
|
|
17
|
+
walkTokenTree(args.tokens, ({ path, leaf }) => {
|
|
18
|
+
const dotKey = path.join(".");
|
|
19
|
+
if (seenDotKeys.has(dotKey)) {
|
|
20
|
+
errors.push(`Duplicate token key: ${dotKey}`);
|
|
21
|
+
} else {
|
|
22
|
+
seenDotKeys.add(dotKey);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!leaf.description || leaf.description.trim().length === 0) {
|
|
26
|
+
errors.push(`Missing description for token: ${dotKey}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const cssVar = tokenPathToCssVarName(path);
|
|
30
|
+
if (!cssVar.startsWith("--asm-")) {
|
|
31
|
+
errors.push(`Invalid CSS var prefix (must be --asm-): ${cssVar}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (seenCssVars.has(cssVar)) {
|
|
35
|
+
errors.push(`Duplicate CSS var name generated: ${cssVar} (from ${dotKey})`);
|
|
36
|
+
} else {
|
|
37
|
+
seenCssVars.add(cssVar);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const v = leaf.value;
|
|
41
|
+
if (typeof v === "string" && v.startsWith("#") && !HEX_COLOR.test(v)) {
|
|
42
|
+
errors.push(`Invalid hex color for ${dotKey}: ${v}`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const requiredThemeVars = [
|
|
47
|
+
"--asm-color-bg",
|
|
48
|
+
"--asm-color-surface",
|
|
49
|
+
"--asm-color-text",
|
|
50
|
+
"--asm-color-border",
|
|
51
|
+
"--asm-color-button-primary-bg",
|
|
52
|
+
"--asm-color-input-border-focus",
|
|
53
|
+
"--asm-color-focus-ring",
|
|
54
|
+
"--asm-color-overlay-scrim"
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const themeNames = new Set(args.themes.map(t => t.name));
|
|
58
|
+
for (const name of ["light", "dark", "high-contrast"] as const) {
|
|
59
|
+
if (!themeNames.has(name)) {
|
|
60
|
+
errors.push(`Missing required theme: ${name}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const theme of args.themes) {
|
|
65
|
+
if (theme.name !== "light" && theme.name !== "dark" && theme.name !== "high-contrast") {
|
|
66
|
+
warnings.push(`Theme has non-standard name: ${theme.name}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const varName of requiredThemeVars) {
|
|
70
|
+
if (!(varName in theme.tokens)) {
|
|
71
|
+
errors.push(`Theme '${theme.name}' missing required token override: ${varName}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const [key, value] of Object.entries(theme.tokens)) {
|
|
76
|
+
if (!key.startsWith("--asm-")) {
|
|
77
|
+
errors.push(`Theme '${theme.name}' contains non --asm- var: ${key}`);
|
|
78
|
+
}
|
|
79
|
+
if (typeof value === "string" && value.startsWith("#") && !HEX_COLOR.test(value)) {
|
|
80
|
+
errors.push(`Theme '${theme.name}' invalid hex for ${key}: ${value}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
ok: errors.length === 0,
|
|
87
|
+
errors,
|
|
88
|
+
warnings
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type Token<T extends string | number = string> = {
|
|
2
|
+
value: T;
|
|
3
|
+
description: string;
|
|
4
|
+
meta?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type ThemeName = "light" | "dark" | "high-contrast";
|
|
8
|
+
|
|
9
|
+
export type ThemeDefinition = {
|
|
10
|
+
name: ThemeName;
|
|
11
|
+
tokens: Record<string, string | number>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type DensityName = "compact" | "comfortable";
|
|
15
|
+
|
|
16
|
+
export type DensityDefinition = {
|
|
17
|
+
name: DensityName;
|
|
18
|
+
tokens: Record<string, string | number>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type TokenTree = Record<string, unknown>;
|
|
22
|
+
|
|
23
|
+
export type TokenLeaf = Token<string | number>;
|
|
24
|
+
|
|
25
|
+
export type ValidateResult = {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
errors: string[];
|
|
28
|
+
warnings: string[];
|
|
29
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { TokenLeaf, TokenTree } from "./types";
|
|
2
|
+
|
|
3
|
+
export function isTokenLeaf(value: unknown): value is TokenLeaf {
|
|
4
|
+
return (
|
|
5
|
+
typeof value === "object" &&
|
|
6
|
+
value !== null &&
|
|
7
|
+
"value" in value &&
|
|
8
|
+
"description" in value &&
|
|
9
|
+
(typeof (value as any).value === "string" || typeof (value as any).value === "number") &&
|
|
10
|
+
typeof (value as any).description === "string"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function toKebabCase(input: string): string {
|
|
15
|
+
return input
|
|
16
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
17
|
+
.replace(/[_\s]+/g, "-")
|
|
18
|
+
.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function joinPath(path: string[], sep = "."): string {
|
|
22
|
+
return path.filter(Boolean).join(sep);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function walkTokenTree(
|
|
26
|
+
tree: TokenTree,
|
|
27
|
+
visitor: (args: { path: string[]; leaf: TokenLeaf }) => void,
|
|
28
|
+
path: string[] = []
|
|
29
|
+
) {
|
|
30
|
+
for (const [key, value] of Object.entries(tree)) {
|
|
31
|
+
const nextPath = [...path, key];
|
|
32
|
+
|
|
33
|
+
if (isTokenLeaf(value)) {
|
|
34
|
+
visitor({ path: nextPath, leaf: value });
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof value === "object" && value !== null) {
|
|
39
|
+
walkTokenTree(value as TokenTree, visitor, nextPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DensityDefinition, ThemeDefinition, Token, ValidateResult } from "./core/types";
|
|
2
|
+
import { themes } from "./themes";
|
|
3
|
+
import { tokens } from "./tokens";
|
|
4
|
+
import { toCssVars, toCssVarNames } from "./build/toCssVars";
|
|
5
|
+
import { validateTokens as validateTokensInternal } from "./build/validate";
|
|
6
|
+
|
|
7
|
+
export type { ThemeDefinition, Token };
|
|
8
|
+
export type { ValidateResult };
|
|
9
|
+
|
|
10
|
+
export { tokens };
|
|
11
|
+
export { themes };
|
|
12
|
+
|
|
13
|
+
export const cssVarNames = toCssVarNames(tokens as any);
|
|
14
|
+
|
|
15
|
+
export { toCssVars };
|
|
16
|
+
|
|
17
|
+
export type ApplyThemeOptions = {
|
|
18
|
+
overrides?: Record<string, string | number>;
|
|
19
|
+
cleanup?: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function applyThemeToElement(
|
|
23
|
+
element: { style: { setProperty: (k: string, v: string) => void; removeProperty: (k: string) => void } },
|
|
24
|
+
theme: ThemeDefinition | DensityDefinition,
|
|
25
|
+
options?: ApplyThemeOptions
|
|
26
|
+
) {
|
|
27
|
+
const cleanup = options?.cleanup ?? false;
|
|
28
|
+
|
|
29
|
+
const keys = Object.keys(theme.tokens);
|
|
30
|
+
if (cleanup) {
|
|
31
|
+
for (const k of keys) {
|
|
32
|
+
element.style.removeProperty(k);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const [k, v] of Object.entries(theme.tokens)) {
|
|
37
|
+
element.style.setProperty(k, String(v));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options?.overrides) {
|
|
41
|
+
for (const [k, v] of Object.entries(options.overrides)) {
|
|
42
|
+
element.style.setProperty(k, String(v));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function prefersColorScheme(): "light" | "dark" | undefined {
|
|
48
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function validateTokens(): ValidateResult {
|
|
56
|
+
return validateTokensInternal({
|
|
57
|
+
tokens: tokens as any,
|
|
58
|
+
themes: [themes.light, themes.dark, themes.highContrast]
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ThemeDefinition } from "../core/types";
|
|
2
|
+
import { brand } from "../tokens/color/brand";
|
|
3
|
+
import { neutral } from "../tokens/color/neutral";
|
|
4
|
+
import { semantic } from "../tokens/color/semantic";
|
|
5
|
+
import { overlays } from "../tokens/color/overlays";
|
|
6
|
+
|
|
7
|
+
export const dark: ThemeDefinition = {
|
|
8
|
+
name: "dark",
|
|
9
|
+
tokens: {
|
|
10
|
+
"--asm-color-bg": "#0B1220",
|
|
11
|
+
"--asm-color-surface": "#0F172A",
|
|
12
|
+
"--asm-color-surface-muted": "#111B2E",
|
|
13
|
+
"--asm-color-panel": "#0F172A",
|
|
14
|
+
"--asm-color-modal": "#0F172A",
|
|
15
|
+
|
|
16
|
+
"--asm-color-text": "#E5E7EB",
|
|
17
|
+
"--asm-color-text-muted": "#9CA3AF",
|
|
18
|
+
"--asm-color-border": "#1F2937",
|
|
19
|
+
|
|
20
|
+
"--asm-color-brand-primary-50": brand.primary50.value,
|
|
21
|
+
"--asm-color-brand-primary-500": brand.primary500.value,
|
|
22
|
+
"--asm-color-brand-primary-600": brand.primary500.value,
|
|
23
|
+
"--asm-color-brand-primary-700": brand.primary600.value,
|
|
24
|
+
|
|
25
|
+
"--asm-color-semantic-success": semantic.success.value,
|
|
26
|
+
"--asm-color-semantic-warning": semantic.warning.value,
|
|
27
|
+
"--asm-color-semantic-error": semantic.error.value,
|
|
28
|
+
"--asm-color-semantic-info": semantic.info.value,
|
|
29
|
+
|
|
30
|
+
"--asm-color-button-primary-bg": brand.primary500.value,
|
|
31
|
+
"--asm-color-button-primary-bg-hover": brand.primary600.value,
|
|
32
|
+
"--asm-color-button-primary-bg-active": brand.primary600.value,
|
|
33
|
+
"--asm-color-button-primary-text": neutral.white.value,
|
|
34
|
+
|
|
35
|
+
"--asm-color-button-secondary-bg": "#111B2E",
|
|
36
|
+
"--asm-color-button-secondary-text": "#E5E7EB",
|
|
37
|
+
|
|
38
|
+
"--asm-color-button-ghost-bg-hover": "rgba(229, 231, 235, 0.08)",
|
|
39
|
+
|
|
40
|
+
"--asm-color-button-destructive-bg": "#F87171",
|
|
41
|
+
"--asm-color-button-destructive-text": "#0B1220",
|
|
42
|
+
|
|
43
|
+
"--asm-color-button-disabled-bg": "#1F2937",
|
|
44
|
+
"--asm-color-button-disabled-text": "#9CA3AF",
|
|
45
|
+
|
|
46
|
+
"--asm-color-input-bg": "#0B1220",
|
|
47
|
+
"--asm-color-input-text": "#E5E7EB",
|
|
48
|
+
"--asm-color-input-border": "#1F2937",
|
|
49
|
+
"--asm-color-input-border-hover": "#374151",
|
|
50
|
+
"--asm-color-input-border-focus": brand.primary500.value,
|
|
51
|
+
"--asm-color-input-placeholder": "#9CA3AF",
|
|
52
|
+
"--asm-color-input-error-border": "#F87171",
|
|
53
|
+
"--asm-color-input-success-border": "#22C55E",
|
|
54
|
+
|
|
55
|
+
"--asm-color-focus-ring": brand.primary500.value,
|
|
56
|
+
|
|
57
|
+
"--asm-color-overlay-scrim": overlays.scrimStrong.value,
|
|
58
|
+
|
|
59
|
+
"--asm-color-dataviz-gridline": "#1F2937",
|
|
60
|
+
"--asm-color-dataviz-axis": "#9CA3AF",
|
|
61
|
+
"--asm-color-dataviz-tooltip-bg": "#111827",
|
|
62
|
+
"--asm-color-dataviz-tooltip-text": "#F9FAFB"
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DensityDefinition } from "../core/types";
|
|
2
|
+
|
|
3
|
+
export const densityComfortable: DensityDefinition = {
|
|
4
|
+
name: "comfortable",
|
|
5
|
+
tokens: {
|
|
6
|
+
"--asm-density-factor": 1.05,
|
|
7
|
+
"--asm-space-component-gap": "18px",
|
|
8
|
+
"--asm-space-control-padding-y": "10px",
|
|
9
|
+
"--asm-space-control-padding-x": "14px"
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DensityDefinition } from "../core/types";
|
|
2
|
+
|
|
3
|
+
export const densityCompact: DensityDefinition = {
|
|
4
|
+
name: "compact",
|
|
5
|
+
tokens: {
|
|
6
|
+
"--asm-density-factor": 0.9,
|
|
7
|
+
"--asm-space-component-gap": "12px",
|
|
8
|
+
"--asm-space-control-padding-y": "6px",
|
|
9
|
+
"--asm-space-control-padding-x": "10px"
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ThemeDefinition } from "../core/types";
|
|
2
|
+
|
|
3
|
+
export const highContrast: ThemeDefinition = {
|
|
4
|
+
name: "high-contrast",
|
|
5
|
+
tokens: {
|
|
6
|
+
"--asm-color-bg": "#000000",
|
|
7
|
+
"--asm-color-surface": "#000000",
|
|
8
|
+
"--asm-color-surface-muted": "#000000",
|
|
9
|
+
"--asm-color-panel": "#000000",
|
|
10
|
+
"--asm-color-modal": "#000000",
|
|
11
|
+
|
|
12
|
+
"--asm-color-text": "#FFFFFF",
|
|
13
|
+
"--asm-color-text-muted": "#FFFFFF",
|
|
14
|
+
"--asm-color-border": "#FFFFFF",
|
|
15
|
+
|
|
16
|
+
"--asm-color-brand-primary-50": "#FFFFFF",
|
|
17
|
+
"--asm-color-brand-primary-500": "#FFFFFF",
|
|
18
|
+
"--asm-color-brand-primary-600": "#FFFFFF",
|
|
19
|
+
"--asm-color-brand-primary-700": "#FFFFFF",
|
|
20
|
+
|
|
21
|
+
"--asm-color-semantic-success": "#00FF00",
|
|
22
|
+
"--asm-color-semantic-warning": "#FFFF00",
|
|
23
|
+
"--asm-color-semantic-error": "#FF0000",
|
|
24
|
+
"--asm-color-semantic-info": "#00FFFF",
|
|
25
|
+
|
|
26
|
+
"--asm-color-button-primary-bg": "#FFFFFF",
|
|
27
|
+
"--asm-color-button-primary-bg-hover": "#FFFFFF",
|
|
28
|
+
"--asm-color-button-primary-bg-active": "#FFFFFF",
|
|
29
|
+
"--asm-color-button-primary-text": "#000000",
|
|
30
|
+
|
|
31
|
+
"--asm-color-button-secondary-bg": "#000000",
|
|
32
|
+
"--asm-color-button-secondary-text": "#FFFFFF",
|
|
33
|
+
|
|
34
|
+
"--asm-color-button-ghost-bg-hover": "#FFFFFF",
|
|
35
|
+
|
|
36
|
+
"--asm-color-button-destructive-bg": "#FF0000",
|
|
37
|
+
"--asm-color-button-destructive-text": "#000000",
|
|
38
|
+
|
|
39
|
+
"--asm-color-button-disabled-bg": "#000000",
|
|
40
|
+
"--asm-color-button-disabled-text": "#FFFFFF",
|
|
41
|
+
|
|
42
|
+
"--asm-color-input-bg": "#000000",
|
|
43
|
+
"--asm-color-input-text": "#FFFFFF",
|
|
44
|
+
"--asm-color-input-border": "#FFFFFF",
|
|
45
|
+
"--asm-color-input-border-hover": "#FFFFFF",
|
|
46
|
+
"--asm-color-input-border-focus": "#FFFFFF",
|
|
47
|
+
"--asm-color-input-placeholder": "#FFFFFF",
|
|
48
|
+
"--asm-color-input-error-border": "#FF0000",
|
|
49
|
+
"--asm-color-input-success-border": "#00FF00",
|
|
50
|
+
|
|
51
|
+
"--asm-color-focus-ring": "#FFFFFF",
|
|
52
|
+
|
|
53
|
+
"--asm-color-overlay-scrim": "rgba(255, 255, 255, 0.9)",
|
|
54
|
+
|
|
55
|
+
"--asm-color-dataviz-gridline": "#FFFFFF",
|
|
56
|
+
"--asm-color-dataviz-axis": "#FFFFFF",
|
|
57
|
+
"--asm-color-dataviz-tooltip-bg": "#FFFFFF",
|
|
58
|
+
"--asm-color-dataviz-tooltip-text": "#000000"
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { light } from "./light";
|
|
2
|
+
export { dark } from "./dark";
|
|
3
|
+
export { highContrast } from "./highContrast";
|
|
4
|
+
export { densityCompact } from "./densityCompact";
|
|
5
|
+
export { densityComfortable } from "./densityComfortable";
|
|
6
|
+
|
|
7
|
+
import { light } from "./light";
|
|
8
|
+
import { dark } from "./dark";
|
|
9
|
+
import { highContrast } from "./highContrast";
|
|
10
|
+
import { densityCompact } from "./densityCompact";
|
|
11
|
+
import { densityComfortable } from "./densityComfortable";
|
|
12
|
+
|
|
13
|
+
export const themes = {
|
|
14
|
+
light,
|
|
15
|
+
dark,
|
|
16
|
+
highContrast,
|
|
17
|
+
density: {
|
|
18
|
+
compact: densityCompact,
|
|
19
|
+
comfortable: densityComfortable
|
|
20
|
+
}
|
|
21
|
+
} as const;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ThemeDefinition } from "../core/types";
|
|
2
|
+
import { brand } from "../tokens/color/brand";
|
|
3
|
+
import { neutral } from "../tokens/color/neutral";
|
|
4
|
+
import { semantic } from "../tokens/color/semantic";
|
|
5
|
+
import { overlays } from "../tokens/color/overlays";
|
|
6
|
+
|
|
7
|
+
export const light: ThemeDefinition = {
|
|
8
|
+
name: "light",
|
|
9
|
+
tokens: {
|
|
10
|
+
"--asm-color-bg": neutral.gray50.value,
|
|
11
|
+
"--asm-color-surface": neutral.white.value,
|
|
12
|
+
"--asm-color-surface-muted": "#F3F4F6",
|
|
13
|
+
"--asm-color-panel": neutral.white.value,
|
|
14
|
+
"--asm-color-modal": neutral.white.value,
|
|
15
|
+
|
|
16
|
+
"--asm-color-text": neutral.gray900.value,
|
|
17
|
+
"--asm-color-text-muted": neutral.gray500.value,
|
|
18
|
+
"--asm-color-border": neutral.gray300.value,
|
|
19
|
+
|
|
20
|
+
"--asm-color-brand-primary-50": brand.primary50.value,
|
|
21
|
+
"--asm-color-brand-primary-500": brand.primary500.value,
|
|
22
|
+
"--asm-color-brand-primary-600": brand.primary600.value,
|
|
23
|
+
"--asm-color-brand-primary-700": brand.primary700.value,
|
|
24
|
+
|
|
25
|
+
"--asm-color-semantic-success": semantic.success.value,
|
|
26
|
+
"--asm-color-semantic-warning": semantic.warning.value,
|
|
27
|
+
"--asm-color-semantic-error": semantic.error.value,
|
|
28
|
+
"--asm-color-semantic-info": semantic.info.value,
|
|
29
|
+
|
|
30
|
+
"--asm-color-button-primary-bg": brand.primary600.value,
|
|
31
|
+
"--asm-color-button-primary-bg-hover": brand.primary700.value,
|
|
32
|
+
"--asm-color-button-primary-bg-active": brand.primary700.value,
|
|
33
|
+
"--asm-color-button-primary-text": neutral.white.value,
|
|
34
|
+
|
|
35
|
+
"--asm-color-button-secondary-bg": neutral.white.value,
|
|
36
|
+
"--asm-color-button-secondary-text": neutral.gray900.value,
|
|
37
|
+
|
|
38
|
+
"--asm-color-button-ghost-bg-hover": "rgba(17, 24, 39, 0.06)",
|
|
39
|
+
|
|
40
|
+
"--asm-color-button-destructive-bg": semantic.error.value,
|
|
41
|
+
"--asm-color-button-destructive-text": neutral.white.value,
|
|
42
|
+
|
|
43
|
+
"--asm-color-button-disabled-bg": neutral.gray300.value,
|
|
44
|
+
"--asm-color-button-disabled-text": neutral.gray700.value,
|
|
45
|
+
|
|
46
|
+
"--asm-color-input-bg": neutral.white.value,
|
|
47
|
+
"--asm-color-input-text": neutral.gray900.value,
|
|
48
|
+
"--asm-color-input-border": neutral.gray300.value,
|
|
49
|
+
"--asm-color-input-border-hover": neutral.gray500.value,
|
|
50
|
+
"--asm-color-input-border-focus": brand.primary600.value,
|
|
51
|
+
"--asm-color-input-placeholder": neutral.gray500.value,
|
|
52
|
+
"--asm-color-input-error-border": semantic.error.value,
|
|
53
|
+
"--asm-color-input-success-border": semantic.success.value,
|
|
54
|
+
|
|
55
|
+
"--asm-color-focus-ring": brand.primary500.value,
|
|
56
|
+
|
|
57
|
+
"--asm-color-overlay-scrim": overlays.scrim.value,
|
|
58
|
+
|
|
59
|
+
"--asm-color-dataviz-gridline": neutral.gray300.value,
|
|
60
|
+
"--asm-color-dataviz-axis": neutral.gray500.value,
|
|
61
|
+
"--asm-color-dataviz-tooltip-bg": neutral.gray900.value,
|
|
62
|
+
"--asm-color-dataviz-tooltip-text": neutral.gray50.value
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Token } from "../../core/types";
|
|
2
|
+
|
|
3
|
+
export const accent = {
|
|
4
|
+
purple500: {
|
|
5
|
+
value: "#8B5CF6",
|
|
6
|
+
description: "Accent purple for special contexts (e.g., highlights)."
|
|
7
|
+
} satisfies Token,
|
|
8
|
+
cyan500: {
|
|
9
|
+
value: "#06B6D4",
|
|
10
|
+
description: "Accent cyan for special contexts (e.g., data accents)."
|
|
11
|
+
} satisfies Token,
|
|
12
|
+
pink500: {
|
|
13
|
+
value: "#EC4899",
|
|
14
|
+
description: "Accent pink for special contexts."
|
|
15
|
+
} satisfies Token,
|
|
16
|
+
lime500: {
|
|
17
|
+
value: "#84CC16",
|
|
18
|
+
description: "Accent lime for special contexts."
|
|
19
|
+
} satisfies Token
|
|
20
|
+
} as const;
|