@usegraft/tokens 0.1.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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +25 -0
  3. package/tokens.css +220 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anderson Joseph
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@usegraft/tokens",
3
+ "version": "0.1.0",
4
+ "description": "Graft design tokens — one stylesheet, consumed by the docs site and the Studio.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/AndersonDesign1/graft.git",
9
+ "directory": "packages/tokens"
10
+ },
11
+ "files": [
12
+ "*.css"
13
+ ],
14
+ "type": "module",
15
+ "exports": {
16
+ "./tokens.css": "./tokens.css",
17
+ "./package.json": "./package.json"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "engines": {
23
+ "node": ">=22.16"
24
+ }
25
+ }
package/tokens.css ADDED
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Graft design tokens — the single source for color, type, space, motion.
3
+ *
4
+ * Lives in its own package because it has two consumers: the docs site and
5
+ * the Studio. It used to be a hand-copied duplicate in both, which is a
6
+ * design system with two sources of truth — the exact thing tokens exist to
7
+ * prevent. Import it as `@usegraft/tokens/tokens.css`.
8
+ *
9
+ * Thesis: editorial print, monochrome. A black stage, off-white type, and
10
+ * elevation carried entirely by *lightness* — surfaces step up from the stage
11
+ * rather than being drawn in with borders. There is no brand accent. Emphasis
12
+ * is "bright": the mark, the union, the active state and the primary button are
13
+ * simply the lightest thing on screen. The only chromatic color in the system
14
+ * is the error red, and it is function, not identity.
15
+ *
16
+ * Why no accent (this is measured, not vibes):
17
+ * A colored accent on a black stage is a trap. Solving for the lightness a
18
+ * hue needs to pass APCA 75 as text on black, red/blue/magenta collapse to
19
+ * pastels (chroma 0.067-0.091); only green survives, and green reads as
20
+ * "success", not as a brand. Meanwhile every CMS competitor is already
21
+ * holding a hue (BaseHub orange #ff6c02, Contentful blue, Strapi indigo).
22
+ * Monochrome is the one position nobody occupies, and it is the one that
23
+ * makes the *content* — code, terminals, diffs — the only colored thing.
24
+ *
25
+ * Structure (the shadcn pattern):
26
+ *
27
+ * 1. PALETTE raw values, authored in OKLCH (perceptually uniform: equal L
28
+ * steps = equal brightness). One neutral ramp, one danger red.
29
+ * The only place a literal color is allowed.
30
+ * 2. SEMANTIC what a color *means* (--background, --primary, --danger).
31
+ * Every value is light-dark(), so one set of names themes both
32
+ * schemes. Components MUST consume this layer, never layer 1.
33
+ * 3. ALIASES the house vocabulary (--paper/--ink/--mark) pointing at layer
34
+ * 2, so component CSS reads editorially.
35
+ *
36
+ * Contrast: every pairing is APCA-verified against its real background (Lc
37
+ * noted inline). Body text >= 75; small non-body marks >= 60.
38
+ */
39
+
40
+ :root {
41
+ color-scheme: light dark;
42
+
43
+ /* ====================================================== 1. palette === */
44
+
45
+ /* The neutral ramp. Hue 95 at chroma 0.003 — a whisper of warmth, so this
46
+ reads as ink-on-paper rather than a cold UI gray. Everything is one hue;
47
+ only L moves. Elevation IS lightness. */
48
+ --n-0: #000000; /* the stage */
49
+ --n-1: oklch(0.17 0.003 95); /* #100f0e — sunken: wells, terminals, code */
50
+ --n-2: oklch(0.22 0.003 95); /* #1b1b19 — raised: cards, embedded UI frames */
51
+ --n-3: oklch(0.27 0.003 95); /* #272625 — elevated: hover, popovers */
52
+ --n-4: oklch(0.32 0.003 95); /* #333331 — the brightest surface */
53
+
54
+ --n-6: oklch(0.62 0.003 95); /* #878684 — light-scheme faint */
55
+ --n-7: oklch(0.79 0.003 95); /* #bcbcb9 — dark-scheme faint — Lc -65 on black */
56
+ --n-8: oklch(0.86 0.003 95); /* #d1d1cf — dark-scheme muted — Lc -79 on black */
57
+ --n-9: oklch(0.96 0.003 95); /* #f2f2ef — dark-scheme text/bright — Lc -99 */
58
+
59
+ /* light scheme neutrals — warm ivory paper, warm near-black ink */
60
+ --paper-0: #f7f5f0; /* the page */
61
+ --paper-1: #ffffff; /* raised */
62
+ --paper-2: #eeebe3; /* sunken */
63
+ --ink-0: oklch(0.2 0.006 95); /* #1b1a16 — text, and the light-scheme "bright" */
64
+ --ink-0-hover: oklch(0.32 0.006 95); /* one step off the fill, not a gray jump */
65
+ --ink-1: oklch(0.44 0.006 95); /* #5c5a52 — muted */
66
+ --ink-2: oklch(0.56 0.005 95); /* #7a7871 — faint */
67
+
68
+ /* danger — hue 25. The ONLY hue in the system; errors only, never brand.
69
+ A red bright enough for APCA 75 on black would be pale pink, so error
70
+ text targets the non-body floor (Lc 60). */
71
+ --danger-300: oklch(0.77 0.123 25); /* on black #f9948c — Lc -60 */
72
+ --danger-600: oklch(0.5 0.183 25); /* on ivory #b42027 — Lc 75 */
73
+
74
+ /* syntax — GitHub palette (matches shiki github-light / github-dark).
75
+ Page chrome stays monochrome; code is the one place hue is welcome. */
76
+ --gh-string-light: #032f62;
77
+ --gh-string-dark: #9ecbff;
78
+ --gh-entity-light: #6f42c1;
79
+ --gh-entity-dark: #b392f0;
80
+ --gh-constant-light: #005cc5;
81
+ --gh-constant-dark: #79b8ff;
82
+ --gh-inserted-light: #22863a;
83
+ --gh-inserted-dark: #85e89d;
84
+ --gh-comment-light: #6a737d;
85
+ --gh-comment-dark: #8b949e;
86
+ --gh-keyword-light: #d73a49;
87
+ --gh-keyword-dark: #f97583;
88
+
89
+ /* ===================================================== 2. semantic === */
90
+
91
+ --background: light-dark(var(--paper-0), var(--n-0));
92
+ --muted: light-dark(var(--paper-2), var(--n-1)); /* wells, terminals */
93
+ --card: light-dark(var(--paper-1), var(--n-2)); /* cards, embedded UI */
94
+ --elevated: light-dark(var(--paper-1), var(--n-3)); /* hover, popovers */
95
+
96
+ --foreground: light-dark(var(--ink-0), var(--n-9));
97
+ --muted-foreground: light-dark(var(--ink-1), var(--n-8)); /* passes body 75 */
98
+ --faint-foreground: light-dark(var(--ink-2), var(--n-7)); /* passes non-body 60 */
99
+
100
+ --border: light-dark(rgb(27 26 22 / 0.14), rgb(242 242 239 / 0.11));
101
+ --border-strong: light-dark(rgb(27 26 22 / 0.34), rgb(242 242 239 / 0.26));
102
+
103
+ /* "Primary" is not a hue — it is BRIGHT. Marks, unions, the active state,
104
+ the filled button. In dark it is the lightest neutral; in light it is the
105
+ darkest. The fill and its label simply invert. */
106
+ --primary: light-dark(var(--ink-0), var(--n-9));
107
+ --primary-hover: light-dark(
108
+ var(--ink-0-hover),
109
+ #ffffff
110
+ ); /* fill hover: one step, same direction */
111
+ --primary-foreground: light-dark(var(--paper-0), var(--n-0));
112
+ /* Alpha variants derive from --primary, so they follow it.
113
+ light-dark() must wrap a whole <color> — it cannot sit inside rgb()'s
114
+ channel list — hence color-mix rather than an --rgb channel triple. */
115
+ --primary-tint: color-mix(in srgb, var(--primary) 8%, transparent); /* washes, hot rows */
116
+ --primary-wash: color-mix(in srgb, var(--primary) 40%, transparent); /* soft borders */
117
+ --primary-underline: color-mix(in srgb, var(--primary) 35%, transparent);
118
+
119
+ /* The graft took: success is simply bright. One system, no traffic lights. */
120
+ --success: var(--primary);
121
+ --danger: light-dark(var(--danger-600), var(--danger-300));
122
+
123
+ --code-string: light-dark(var(--gh-string-light), var(--gh-string-dark));
124
+ --code-type: light-dark(var(--gh-entity-light), var(--gh-entity-dark));
125
+ --code-key: light-dark(var(--gh-inserted-light), var(--gh-inserted-dark));
126
+ --code-ok: light-dark(var(--gh-inserted-light), var(--gh-inserted-dark));
127
+ --code-comment: light-dark(var(--gh-comment-light), var(--gh-comment-dark));
128
+ --code-prompt: light-dark(var(--gh-constant-light), var(--gh-constant-dark));
129
+ --code-keyword: light-dark(var(--gh-keyword-light), var(--gh-keyword-dark));
130
+
131
+ /* keep short aliases used by older hand-painted tokens */
132
+ --amber-400: var(--code-string);
133
+ --azure-400: var(--code-prompt);
134
+ --green-400: var(--code-ok);
135
+ --violet-400: var(--code-type);
136
+
137
+ --selection-background: var(--primary);
138
+ --selection-foreground: var(--primary-foreground);
139
+ --focus-ring: var(--primary);
140
+
141
+ /* ====================================================== 3. aliases === */
142
+ /* House vocabulary. Component CSS reads these; they resolve to layer 2. */
143
+
144
+ --paper: var(--background);
145
+ --paper-raised: var(--card);
146
+ --paper-sunken: var(--muted);
147
+ --ink: var(--foreground);
148
+ --ink-muted: var(--muted-foreground);
149
+ --ink-faint: var(--faint-foreground);
150
+ --hairline: var(--border);
151
+ --hairline-strong: var(--border-strong);
152
+ --mark: var(--primary); /* the editorial mark — bright, not colored */
153
+ --mark-strong: var(--primary-hover);
154
+ --mark-tint: var(--primary-tint);
155
+
156
+ /* ---- type ---- */
157
+ --font-display: "Instrument Serif", "Iowan Old Style", georgia, serif;
158
+ --font-sans: "Instrument Sans Variable", "Instrument Sans", system-ui, sans-serif;
159
+ --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;
160
+
161
+ /* Scale tuned by hand, not a formula. Display sizes are for Instrument
162
+ Serif at weight 400 with tight tracking — big and light. */
163
+ --text-xs: 0.75rem; /* 12 — mono labels, uppercase, tracked wide */
164
+ --text-sm: 0.875rem; /* 14 — UI chrome, sidebar */
165
+ --text-base: 1rem; /* 16 — body */
166
+ --text-md: 1.125rem; /* 18 — docs body / lede */
167
+ --text-lg: 1.5rem; /* 24 — section intros */
168
+ --text-xl: 2.25rem; /* 36 — doc h1 */
169
+ --text-2xl: 3.5rem; /* 56 — page display */
170
+ --text-3xl: clamp(3.5rem, 9vw, 6.5rem); /* hero display */
171
+
172
+ --leading-tight: 1.08;
173
+ --leading-snug: 1.3;
174
+ --leading-body: 1.65;
175
+ --tracking-display: -0.03em; /* serif display, sizes ≥ --text-2xl */
176
+ --tracking-label: 0.08em; /* uppercase mono labels */
177
+
178
+ /* ---- space (4px base) ---- */
179
+ --space-1: 0.25rem;
180
+ --space-2: 0.5rem;
181
+ --space-3: 0.75rem;
182
+ --space-4: 1rem;
183
+ --space-6: 1.5rem;
184
+ --space-8: 2rem;
185
+ --space-12: 3rem;
186
+ --space-16: 4rem;
187
+ --space-24: 6rem;
188
+
189
+ /* ---- geometry: squared, concentric (outer = inner + padding) ---- */
190
+ --radius-inner: 3px; /* buttons, inputs, code spans */
191
+ --radius: 6px; /* cards, code blocks */
192
+ --radius-outer: 10px; /* bento cells, embedded UI frames */
193
+
194
+ /* ---- elevation: lightness first, shadow second. On a black stage a
195
+ shadow is invisible; the surface itself must get brighter. ---- */
196
+ --shadow-raised:
197
+ 0 0 0 1px var(--border), 0 1px 2px light-dark(rgb(27 26 22 / 0.05), rgb(0 0 0 / 0.6)),
198
+ 0 4px 12px light-dark(rgb(27 26 22 / 0.06), rgb(0 0 0 / 0.5));
199
+ --shadow-floating:
200
+ 0 0 0 1px var(--border), 0 2px 6px light-dark(rgb(27 26 22 / 0.07), rgb(0 0 0 / 0.65)),
201
+ 0 12px 32px light-dark(rgb(27 26 22 / 0.1), rgb(0 0 0 / 0.7));
202
+ /* Image outlines: pure black/white — tinted neutrals read as dirt. */
203
+ --outline-image: light-dark(rgb(0 0 0 / 0.1), rgb(255 255 255 / 0.1));
204
+
205
+ /* ---- motion (custom curves; the CSS built-ins are too weak) ---- */
206
+ --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
207
+ --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
208
+ --duration-press: 140ms;
209
+ --duration-fast: 180ms;
210
+ --duration-base: 240ms;
211
+ --press-scale: 0.97;
212
+ }
213
+
214
+ /* A theme toggle stamps data-theme on <html>; it must beat the OS setting. */
215
+ :root[data-theme="light"] {
216
+ color-scheme: light;
217
+ }
218
+ :root[data-theme="dark"] {
219
+ color-scheme: dark;
220
+ }