@handamade/psi-tokens 0.3.0

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.
@@ -0,0 +1,169 @@
1
+ @layer psi.utilities {
2
+ .psi-gap-0 { gap: var(--psi-space-0); }
3
+ .psi-gap-2 { gap: var(--psi-space-2); }
4
+ .psi-gap-4 { gap: var(--psi-space-4); }
5
+ .psi-gap-6 { gap: var(--psi-space-6); }
6
+ .psi-gap-8 { gap: var(--psi-space-8); }
7
+ .psi-gap-12 { gap: var(--psi-space-12); }
8
+ .psi-gap-16 { gap: var(--psi-space-16); }
9
+ .psi-gap-20 { gap: var(--psi-space-20); }
10
+ .psi-gap-24 { gap: var(--psi-space-24); }
11
+ .psi-gap-32 { gap: var(--psi-space-32); }
12
+ .psi-gap-40 { gap: var(--psi-space-40); }
13
+ .psi-gap-48 { gap: var(--psi-space-48); }
14
+ .psi-gap-64 { gap: var(--psi-space-64); }
15
+ .psi-gap-80 { gap: var(--psi-space-80); }
16
+ .psi-gap-96 { gap: var(--psi-space-96); }
17
+ .psi-gap-120 { gap: var(--psi-space-120); }
18
+ .psi-gap-144 { gap: var(--psi-space-144); }
19
+
20
+ .psi-p-0 { padding: var(--psi-space-0); }
21
+ .psi-px-0 { padding-inline: var(--psi-space-0); }
22
+ .psi-py-0 { padding-block: var(--psi-space-0); }
23
+ .psi-p-2 { padding: var(--psi-space-2); }
24
+ .psi-px-2 { padding-inline: var(--psi-space-2); }
25
+ .psi-py-2 { padding-block: var(--psi-space-2); }
26
+ .psi-p-4 { padding: var(--psi-space-4); }
27
+ .psi-px-4 { padding-inline: var(--psi-space-4); }
28
+ .psi-py-4 { padding-block: var(--psi-space-4); }
29
+ .psi-p-6 { padding: var(--psi-space-6); }
30
+ .psi-px-6 { padding-inline: var(--psi-space-6); }
31
+ .psi-py-6 { padding-block: var(--psi-space-6); }
32
+ .psi-p-8 { padding: var(--psi-space-8); }
33
+ .psi-px-8 { padding-inline: var(--psi-space-8); }
34
+ .psi-py-8 { padding-block: var(--psi-space-8); }
35
+ .psi-p-12 { padding: var(--psi-space-12); }
36
+ .psi-px-12 { padding-inline: var(--psi-space-12); }
37
+ .psi-py-12 { padding-block: var(--psi-space-12); }
38
+ .psi-p-16 { padding: var(--psi-space-16); }
39
+ .psi-px-16 { padding-inline: var(--psi-space-16); }
40
+ .psi-py-16 { padding-block: var(--psi-space-16); }
41
+ .psi-p-20 { padding: var(--psi-space-20); }
42
+ .psi-px-20 { padding-inline: var(--psi-space-20); }
43
+ .psi-py-20 { padding-block: var(--psi-space-20); }
44
+ .psi-p-24 { padding: var(--psi-space-24); }
45
+ .psi-px-24 { padding-inline: var(--psi-space-24); }
46
+ .psi-py-24 { padding-block: var(--psi-space-24); }
47
+ .psi-p-32 { padding: var(--psi-space-32); }
48
+ .psi-px-32 { padding-inline: var(--psi-space-32); }
49
+ .psi-py-32 { padding-block: var(--psi-space-32); }
50
+ .psi-p-40 { padding: var(--psi-space-40); }
51
+ .psi-px-40 { padding-inline: var(--psi-space-40); }
52
+ .psi-py-40 { padding-block: var(--psi-space-40); }
53
+ .psi-p-48 { padding: var(--psi-space-48); }
54
+ .psi-px-48 { padding-inline: var(--psi-space-48); }
55
+ .psi-py-48 { padding-block: var(--psi-space-48); }
56
+ .psi-p-64 { padding: var(--psi-space-64); }
57
+ .psi-px-64 { padding-inline: var(--psi-space-64); }
58
+ .psi-py-64 { padding-block: var(--psi-space-64); }
59
+ .psi-p-80 { padding: var(--psi-space-80); }
60
+ .psi-px-80 { padding-inline: var(--psi-space-80); }
61
+ .psi-py-80 { padding-block: var(--psi-space-80); }
62
+ .psi-p-96 { padding: var(--psi-space-96); }
63
+ .psi-px-96 { padding-inline: var(--psi-space-96); }
64
+ .psi-py-96 { padding-block: var(--psi-space-96); }
65
+ .psi-p-120 { padding: var(--psi-space-120); }
66
+ .psi-px-120 { padding-inline: var(--psi-space-120); }
67
+ .psi-py-120 { padding-block: var(--psi-space-120); }
68
+ .psi-p-144 { padding: var(--psi-space-144); }
69
+ .psi-px-144 { padding-inline: var(--psi-space-144); }
70
+ .psi-py-144 { padding-block: var(--psi-space-144); }
71
+
72
+ .psi-m-0 { margin: var(--psi-space-0); }
73
+ .psi-mx-0 { margin-inline: var(--psi-space-0); }
74
+ .psi-my-0 { margin-block: var(--psi-space-0); }
75
+ .psi-m-2 { margin: var(--psi-space-2); }
76
+ .psi-mx-2 { margin-inline: var(--psi-space-2); }
77
+ .psi-my-2 { margin-block: var(--psi-space-2); }
78
+ .psi-m-4 { margin: var(--psi-space-4); }
79
+ .psi-mx-4 { margin-inline: var(--psi-space-4); }
80
+ .psi-my-4 { margin-block: var(--psi-space-4); }
81
+ .psi-m-6 { margin: var(--psi-space-6); }
82
+ .psi-mx-6 { margin-inline: var(--psi-space-6); }
83
+ .psi-my-6 { margin-block: var(--psi-space-6); }
84
+ .psi-m-8 { margin: var(--psi-space-8); }
85
+ .psi-mx-8 { margin-inline: var(--psi-space-8); }
86
+ .psi-my-8 { margin-block: var(--psi-space-8); }
87
+ .psi-m-12 { margin: var(--psi-space-12); }
88
+ .psi-mx-12 { margin-inline: var(--psi-space-12); }
89
+ .psi-my-12 { margin-block: var(--psi-space-12); }
90
+ .psi-m-16 { margin: var(--psi-space-16); }
91
+ .psi-mx-16 { margin-inline: var(--psi-space-16); }
92
+ .psi-my-16 { margin-block: var(--psi-space-16); }
93
+ .psi-m-20 { margin: var(--psi-space-20); }
94
+ .psi-mx-20 { margin-inline: var(--psi-space-20); }
95
+ .psi-my-20 { margin-block: var(--psi-space-20); }
96
+ .psi-m-24 { margin: var(--psi-space-24); }
97
+ .psi-mx-24 { margin-inline: var(--psi-space-24); }
98
+ .psi-my-24 { margin-block: var(--psi-space-24); }
99
+ .psi-m-32 { margin: var(--psi-space-32); }
100
+ .psi-mx-32 { margin-inline: var(--psi-space-32); }
101
+ .psi-my-32 { margin-block: var(--psi-space-32); }
102
+ .psi-m-40 { margin: var(--psi-space-40); }
103
+ .psi-mx-40 { margin-inline: var(--psi-space-40); }
104
+ .psi-my-40 { margin-block: var(--psi-space-40); }
105
+ .psi-m-48 { margin: var(--psi-space-48); }
106
+ .psi-mx-48 { margin-inline: var(--psi-space-48); }
107
+ .psi-my-48 { margin-block: var(--psi-space-48); }
108
+ .psi-m-64 { margin: var(--psi-space-64); }
109
+ .psi-mx-64 { margin-inline: var(--psi-space-64); }
110
+ .psi-my-64 { margin-block: var(--psi-space-64); }
111
+ .psi-m-80 { margin: var(--psi-space-80); }
112
+ .psi-mx-80 { margin-inline: var(--psi-space-80); }
113
+ .psi-my-80 { margin-block: var(--psi-space-80); }
114
+ .psi-m-96 { margin: var(--psi-space-96); }
115
+ .psi-mx-96 { margin-inline: var(--psi-space-96); }
116
+ .psi-my-96 { margin-block: var(--psi-space-96); }
117
+ .psi-m-120 { margin: var(--psi-space-120); }
118
+ .psi-mx-120 { margin-inline: var(--psi-space-120); }
119
+ .psi-my-120 { margin-block: var(--psi-space-120); }
120
+ .psi-m-144 { margin: var(--psi-space-144); }
121
+ .psi-mx-144 { margin-inline: var(--psi-space-144); }
122
+ .psi-my-144 { margin-block: var(--psi-space-144); }
123
+
124
+ .psi-text-12-16-regular { font: var(--psi-text-12-16-regular); }
125
+ .psi-text-12-16-medium { font: var(--psi-text-12-16-medium); }
126
+ .psi-text-14-20-regular { font: var(--psi-text-14-20-regular); }
127
+ .psi-text-14-20-medium { font: var(--psi-text-14-20-medium); }
128
+ .psi-text-16-20-regular { font: var(--psi-text-16-20-regular); }
129
+ .psi-text-16-24-regular { font: var(--psi-text-16-24-regular); }
130
+ .psi-text-16-24-medium { font: var(--psi-text-16-24-medium); }
131
+ .psi-text-18-28-medium { font: var(--psi-text-18-28-medium); }
132
+ .psi-text-20-28-semibold { font: var(--psi-text-20-28-semibold); }
133
+ .psi-text-24-32-medium { font: var(--psi-text-24-32-medium); }
134
+ .psi-text-30-36-bold { font: var(--psi-text-30-36-bold); }
135
+ .psi-text-serif-18-28-regular { font: var(--psi-text-serif-18-28-regular); }
136
+ .psi-text-serif-20-30-regular { font: var(--psi-text-serif-20-30-regular); }
137
+ .psi-text-serif-20-32-regular { font: var(--psi-text-serif-20-32-regular); }
138
+ .psi-text-serif-24-36-regular { font: var(--psi-text-serif-24-36-regular); }
139
+ .psi-text-serif-28-40-regular { font: var(--psi-text-serif-28-40-regular); }
140
+ .psi-text-mono-13-20-regular { font: var(--psi-text-mono-13-20-regular); }
141
+ .psi-text-mono-14-20-regular { font: var(--psi-text-mono-14-20-regular); }
142
+ .psi-text-mono-15-24-regular { font: var(--psi-text-mono-15-24-regular); }
143
+ .psi-text-mono-15-24-medium { font: var(--psi-text-mono-15-24-medium); }
144
+
145
+ .psi-display-56-128-black { font: var(--psi-display-56-128-black); letter-spacing: -0.02em; text-transform: uppercase; }
146
+ .psi-display-36-64-black { font: var(--psi-display-36-64-black); letter-spacing: -0.02em; text-transform: uppercase; }
147
+ .psi-display-32-32-extrabold { font: var(--psi-display-32-32-extrabold); letter-spacing: -0.01em; text-transform: uppercase; }
148
+
149
+ .psi-container { max-width: var(--psi-container-max); margin-inline: auto; padding-inline: var(--psi-gutter); }
150
+ /* Gutter narrows under md — breakpoint baked at build time (D31). */
151
+ @media (max-width: 960px) {
152
+ :root { --psi-gutter: 1.5rem; }
153
+ }
154
+
155
+ .psi-media-tint { filter: var(--psi-media-tint); transition: filter var(--psi-duration-450) var(--psi-ease-soft); }
156
+ .psi-media-tint:hover, .psi-media-tint:focus-visible { filter: none; }
157
+
158
+ /* Reduced motion (D30): zero every duration token; anything driven by
159
+ --psi-duration-* complies for free. psi.utilities wins over psi.base. */
160
+ @media (prefers-reduced-motion: reduce) {
161
+ :root {
162
+ --psi-duration-150: 0.01ms;
163
+ --psi-duration-200: 0.01ms;
164
+ --psi-duration-350: 0.01ms;
165
+ --psi-duration-450: 0.01ms;
166
+ --psi-duration-600: 0.01ms;
167
+ }
168
+ }
169
+ }
package/llms.txt ADDED
@@ -0,0 +1,46 @@
1
+ # @handamade/psi-tokens
2
+
3
+ OKLCH formula-based design tokens. Code is the source of truth; all artifacts generated.
4
+
5
+ ## Machine-readable artifacts
6
+ - dist/resolved/{light,dark,acme,ember}.json: every token with OKLCH, hex, and derivation formula
7
+ - dist/guidance.json: variant intent, usage rules, state derivation, typography defaults, motion/layout/fonts notes, recipes
8
+ - dist/dtcg/{theme}.json: W3C DTCG format export
9
+ - dist/components/{name}.vars.css: component-token declarations (--psi-{component}-*)
10
+
11
+ ## Rules for generated code
12
+ - Use semantic tokens (--psi-bg-*, --psi-fg-*, --psi-fill-*, --psi-border-*) in app CSS.
13
+ - Override --psi-{component}-* tokens for one-off component theming; never fork semantic tokens.
14
+ - Scales are pixel-true: --psi-space-8 is 8px (0.5rem). Typography: --psi-text-{size}-{lh}-{weight}.
15
+ - Themes: import base.css + {theme}.css + components.css + utilities.css; set data-psi-theme="{name}" ({light,dark,acme,ember}).
16
+
17
+ ## Motion (--psi-duration-*, --psi-ease-*)
18
+ - Durations: 150/200/350/450/600ms. Easings: --psi-ease-standard, --psi-ease-in-out, --psi-ease-soft.
19
+ - Always drive transitions/animations with duration/ease tokens; never hardcode times or curves.
20
+ - Under prefers-reduced-motion, all --psi-duration-* zero out (D30) — no per-component opt-out needed.
21
+
22
+ ## Layout (--psi-container-max, --psi-gutter, --psi-z-*)
23
+ - .psi-container: max-width 1312px, gutter 40px stepping to 24px under the md breakpoint.
24
+ - --psi-z-nav (100), --psi-z-overlay (1000), --psi-z-tooltip (1100).
25
+ - Breakpoints (sm 560 / md 960) are build-time constants (D31), not CSS vars: `import { breakpoints } from "@handamade/psi-tokens/types"` for any @media-driven JS/CSS-in-JS.
26
+
27
+ ## Typography: display, serif, mono
28
+ - Fluid display tier: --psi-display-{min}-{max}-{weight} (e.g. --psi-display-56-128-black), clamp-based and pixel-true at both endpoints; pair with the matching .psi-display-* utility class for tracking/uppercase.
29
+ - Serif and mono are first-class combo roles alongside sans: --psi-text-serif-{size}-{lh}-{weight} and --psi-text-mono-{size}-{lh}-{weight}, same naming shape as the sans scale.
30
+
31
+ ## Scrims and borders
32
+ - --psi-scrim-soft/medium/heavy: bgPrimary-derived translucent overlays (0.25/0.35/0.82 alpha) for text-over-media legibility.
33
+ - --psi-border-faint: low-contrast hairline border (fgPrimary at 0.08 alpha), used by Card/NavBar and other bordered surfaces.
34
+
35
+ ## Media tint (--psi-media-tint, .psi-media-tint)
36
+ - --psi-media-tint is a brand-defined filter chain (D35); apply the .psi-media-tint utility class to media elements to get a tinted-at-rest, true-color-on-hover/focus treatment, transitioning over --psi-duration-450 / --psi-ease-soft.
37
+
38
+ ## Dark-first brands
39
+ - A brand theme can declare `base: "dark"` (see ember) so its formulas derive from the dark palette instead of light — use this for brands whose primary surface is a dark canvas rather than treating dark as an inverted afterthought.
40
+
41
+ ## Brand fonts (D29)
42
+ - Font roles (sans/serif/mono/display) are brand-level. The DS ships no font files — consumers load each brand's webfonts themselves (e.g. ember: Archivo for display/sans, IBM Plex Serif/Mono).
43
+ - Brand font stacks fully apply when data-psi-theme is set on the root <html> element. In nested subtree theming, --psi-text-*/--psi-display-* combos keep the default stacks, since unregistered custom properties only substitute at :root.
44
+
45
+ ## Component overrides (D34)
46
+ - A brand's theme file may declare `componentOverrides: Record<string, string>` (e.g. ember: button-font → mono, card-radius → 0, media-tint → its filter chain), emitted in the brand's own theme file (e.g. dist/ember.css) inside `@layer psi.components`. Prefer this over forking a component's CSS Module for brand-specific token values.
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@handamade/psi-tokens",
3
+ "version": "0.3.0",
4
+ "type": "module",
5
+ "exports": {
6
+ "./*.css": "./dist/*.css",
7
+ "./components/*": "./dist/components/*",
8
+ "./resolved/*": "./dist/resolved/*",
9
+ "./dtcg/*": "./dist/dtcg/*",
10
+ "./types": {
11
+ "types": "./dist/types/index.d.ts",
12
+ "import": "./dist/types/index.js"
13
+ },
14
+ "./guidance.json": "./dist/guidance.json"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "llms.txt"
20
+ ],
21
+ "dependencies": {
22
+ "culori": "^4.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "tsx": "^4.19.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsx scripts/build.ts",
29
+ "dev": "tsx --watch scripts/build.ts --watch",
30
+ "new-theme": "tsx scripts/new-theme.ts"
31
+ }
32
+ }