@cupped/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.
@@ -0,0 +1,153 @@
1
+ # Consuming @cupped/tokens from Phoenix LiveView
2
+
3
+ Three supported modes. All assume Phoenix 1.8 / Tailwind v4 (CSS-first config,
4
+ standalone Tailwind binary — no Node runtime needed at build beyond an
5
+ optional `npm install`).
6
+
7
+ ## Mode 0 — pinned CSS from the GitHub Release (recommended)
8
+
9
+ Each release attaches version-pinned CSS to its GitHub Release. Pin the version
10
+ (this string is your lockfile analog) and pull the files before the Tailwind
11
+ build — no npm, no `node_modules`:
12
+
13
+ ```bash
14
+ # e.g. in a mix alias or a small fetch script, run before tailwind build
15
+ export CUPPED_TOKENS_VERSION=0.1.0
16
+ mkdir -p assets/vendor/cupped
17
+ gh release download "v${CUPPED_TOKENS_VERSION}" \
18
+ --repo ybird-labs/cupped-design-system \
19
+ --pattern "cupped-tokens-${CUPPED_TOKENS_VERSION}.*" \
20
+ --dir assets/vendor/cupped --clobber
21
+ # verify integrity against the published checksum sidecar
22
+ ( cd assets/vendor/cupped && sha256sum -c "cupped-tokens-${CUPPED_TOKENS_VERSION}.sha256" )
23
+ # downloads: cupped-tokens-X.Y.Z.theme.css / .tokens.css / .components.css (+ .sha256)
24
+ ```
25
+
26
+ ```css
27
+ @import "tailwindcss" source(none);
28
+ @source "../css";
29
+ @source "../js";
30
+ @source "../../lib/my_app_web";
31
+
32
+ @import "../vendor/cupped/cupped-tokens-0.1.0.theme.css";
33
+ /* optional: */
34
+ @import "../vendor/cupped/cupped-tokens-0.1.0.tokens.css";
35
+ @import "../vendor/cupped/cupped-tokens-0.1.0.components.css";
36
+ ```
37
+
38
+ (`gh release download` needs an authenticated `gh` / token with read access to
39
+ the package repo. The filenames are stable: `cupped-tokens-<version>.<target>.css`.)
40
+
41
+ ## Mode 1 — npm git dependency
42
+
43
+ Create `assets/package.json` (or add to it):
44
+
45
+ ```jsonc
46
+ {
47
+ "dependencies": {
48
+ "@cupped/tokens": "github:ybird-labs/cupped-design-system#v0.1.0"
49
+ }
50
+ }
51
+ ```
52
+
53
+ Run `npm install` inside `assets/`. The Tailwind v4 binary resolves
54
+ `@import` against `node_modules`, so in `assets/css/app.css`:
55
+
56
+ ```css
57
+ @import "tailwindcss" source(none);
58
+ @source "../css";
59
+ @source "../js";
60
+ @source "../../lib/my_app_web";
61
+
62
+ /* Tailwind @theme tokens → utilities (bg-canvas, text-ink-secondary,
63
+ rounded-lg, shadow-primary, p-base, text-card-title…) + CSS variables. */
64
+ @import "@cupped/tokens/tailwind";
65
+
66
+ /* Optional: the alias-preserving raw variables (--ink, --xp: var(--sweet)…)
67
+ if you also write plain CSS against var() names. */
68
+ @import "@cupped/tokens/css";
69
+
70
+ /* Optional: the canonical web component classes (.btn, .input, .chip-flavor,
71
+ .banner, .chrome…) — reference styles, skip if you rebuild components in
72
+ HEEx + utilities. Requires the /css import above. */
73
+ @import "@cupped/tokens/css/components";
74
+ ```
75
+
76
+ HEEx then uses utilities or raw variables — no JS framework assumption:
77
+
78
+ ```heex
79
+ <button class="bg-primary-strong text-ink-inverse rounded-md shadow-primary
80
+ px-lg py-md min-h-(--hit-target-min) text-(length:--text-body)">
81
+ Log a coffee
82
+ </button>
83
+
84
+ <span style={"color: var(--ink-secondary)"}>SEATTLE · 12 MIN AGO · V60</span>
85
+ ```
86
+
87
+ ## Mode 2 — vendor mode (zero npm, from a source checkout)
88
+
89
+ Copy the generated files into the app on each upgrade (Mode 0 is the same idea
90
+ but pulls pinned files from the Release instead of a working checkout):
91
+
92
+ ```bash
93
+ cp dist/tailwind/theme.css <app>/assets/vendor/cupped/theme.css
94
+ cp dist/css/tokens.css <app>/assets/vendor/cupped/tokens.css # optional
95
+ cp dist/css/components.css <app>/assets/vendor/cupped/components.css # optional
96
+ ```
97
+
98
+ ```css
99
+ @import "tailwindcss" source(none);
100
+ @import "../vendor/cupped/theme.css";
101
+ ```
102
+
103
+ ## Fonts (self-hosted)
104
+
105
+ The package ships no font binaries. Self-host WOFF2 in `priv/static/fonts/`
106
+ (already whitelisted by `static_paths/0`) — Google Fonts CDN is discouraged
107
+ (GDPR; no shared-cache benefit since browser cache partitioning). Inter,
108
+ Instrument Serif, and JetBrains Mono are all OFL-licensed.
109
+
110
+ ```css
111
+ @font-face {
112
+ font-family: "Inter";
113
+ src: url("/fonts/InterVariable.woff2") format("woff2");
114
+ font-weight: 100 900;
115
+ font-display: swap;
116
+ }
117
+ @font-face {
118
+ font-family: "Instrument Serif";
119
+ src: url("/fonts/InstrumentSerif-Regular.woff2") format("woff2");
120
+ font-weight: 400;
121
+ font-display: swap;
122
+ }
123
+ @font-face {
124
+ font-family: "Instrument Serif";
125
+ src: url("/fonts/InstrumentSerif-Italic.woff2") format("woff2");
126
+ font-weight: 400;
127
+ font-style: italic;
128
+ font-display: swap;
129
+ }
130
+ @font-face {
131
+ font-family: "JetBrains Mono";
132
+ src: url("/fonts/JetBrainsMono[wght].woff2") format("woff2");
133
+ font-weight: 100 800;
134
+ font-display: swap;
135
+ }
136
+ ```
137
+
138
+ ## Enforcing "no raw values"
139
+
140
+ *Never hard-code a hex or pixel value that exists as a token.* There is no
141
+ turnkey HEEx linter for this; the practical recipe is a CI grep over
142
+ templates and CSS:
143
+
144
+ ```bash
145
+ # fails CI when a raw hex sneaks into templates/styles (tokens.css itself is generated)
146
+ grep -rnE '#[0-9a-fA-F]{3,8}\b' lib/my_app_web assets/css --include='*.heex' --include='*.css' \
147
+ | grep -v vendor/ && exit 1 || exit 0
148
+ ```
149
+
150
+ For JS in `assets/js`, port the `no-restricted-syntax` patterns from
151
+ [docs/expo.md](./expo.md).
152
+
153
+ A complete example lives at [examples/phoenix/app.css](../examples/phoenix/app.css).
@@ -0,0 +1,93 @@
1
+ # Releasing @cupped/tokens
2
+
3
+ The package is published to **GitHub Packages** (npm registry
4
+ `https://npm.pkg.github.com`, `@cupped` scope, `restricted` access) under
5
+ [`ybird-labs/cupped-design-system`](https://github.com/ybird-labs/cupped-design-system).
6
+ Releases are driven by [Changesets](https://github.com/changesets/changesets);
7
+ the [`release.yml`](../.github/workflows/release.yml) workflow does the publish
8
+ and attaches versioned CSS to the GitHub Release.
9
+
10
+ For *what* bump to choose, see [versioning.md](./versioning.md).
11
+
12
+ ## Day-to-day: adding a changeset
13
+
14
+ Every PR that changes tokens or generated output should include a changeset:
15
+
16
+ ```bash
17
+ npx changeset # pick the bump level, write a summary
18
+ git add .changeset/
19
+ ```
20
+
21
+ The summary becomes the `CHANGELOG.md` entry, so write it for a consumer.
22
+
23
+ ## Automated release flow
24
+
25
+ ```
26
+ PR with a .changeset/*.md merged to main
27
+
28
+ ▼ release.yml runs (npm ci → npm run check)
29
+ changesets/action sees pending changesets
30
+
31
+ ▼ opens / updates a "Version Packages" PR
32
+ (bumps package.json + writes CHANGELOG.md, deletes consumed changesets)
33
+
34
+ ▼ merge the Version Packages PR
35
+ release.yml runs again → no pending changesets, version not yet published
36
+
37
+ ▼ changesets/action runs `npm run release` (changeset publish)
38
+ changeset publish → publishes to GitHub Packages + creates the git tag vX.Y.Z
39
+ the changesets/action wrapper then creates the matching GitHub Release
40
+
41
+ ▼ upload step attaches the three CSS files to Release vX.Y.Z:
42
+ cupped-tokens-X.Y.Z.theme.css (Tailwind v4 @theme)
43
+ cupped-tokens-X.Y.Z.tokens.css (raw CSS variables)
44
+ cupped-tokens-X.Y.Z.components.css (component classes)
45
+ ```
46
+
47
+ You never bump `package.json` or write `CHANGELOG.md` by hand — merging the
48
+ Version Packages PR does it.
49
+
50
+ ## One-time first publish (manual)
51
+
52
+ The very first publish of a brand-new scoped package usually **cannot** be done
53
+ by the workflow's `GITHUB_TOKEN`, because creating a new package under the org
54
+ typically requires a personal access token. Do this once:
55
+
56
+ 1. Create a **fine-grained PAT** scoped to just `ybird-labs/cupped-design-system`
57
+ with **Packages: write** and a short expiry. (A classic PAT with
58
+ `write:packages` works too, but it is org-wide and long-lived — prefer
59
+ fine-grained.)
60
+ 2. Point npm at GitHub Packages for the scope, and pass the token via an env
61
+ var so it is never persisted to disk:
62
+ ```bash
63
+ echo "@cupped:registry=https://npm.pkg.github.com" >> ~/.npmrc
64
+ echo '//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}' >> ~/.npmrc
65
+ export NODE_AUTH_TOKEN=YOUR_PAT # this shell only — not written to ~/.npmrc
66
+ ```
67
+ 3. From a clean checkout on the released version:
68
+ ```bash
69
+ npm ci
70
+ npm run check
71
+ npm publish # prepublishOnly rebuilds dist/ first
72
+ ```
73
+
74
+ After the package exists, the workflow's `GITHUB_TOKEN` can publish every
75
+ subsequent release — no PAT needed in CI.
76
+
77
+ ## Granting consumer repos read access
78
+
79
+ Consuming repos in the same org need **read** access to the package:
80
+
81
+ - In the package settings on GitHub → *Package settings* → *Manage Actions
82
+ access* / *Manage access*, grant the consumer repos (e.g. the Phoenix and
83
+ Expo apps) read access.
84
+ - Consumers then authenticate with a token that has `read:packages`. (Consumer
85
+ `.npmrc` setup is documented per-app — see [expo.md](./expo.md). **TODO:**
86
+ finalize and document the consumer read-token model.)
87
+
88
+ ## Verifying a published release
89
+
90
+ - The package version appears under the repo's *Packages*.
91
+ - The GitHub Release `vX.Y.Z` exists with the three `cupped-tokens-X.Y.Z.*.css`
92
+ assets attached.
93
+ - `CHANGELOG.md` has an entry for the version.
package/docs/rules.md ADDED
@@ -0,0 +1,62 @@
1
+ # Cupped brand & accessibility invariants
2
+
3
+ The rules the tokens encode but cannot enforce by themselves. The contrast
4
+ rules marked ✓ are machine-checked in CI (`scripts/validate-contrast.mjs`);
5
+ the rest are review rules.
6
+
7
+ ## Color
8
+
9
+ - **Coral never carries text below 24px.** `--primary` (#E07A5F) is ~3.0:1
10
+ on white — accents, illustration, ≥24px display only. Small colored text,
11
+ links, and text-bearing fills use `--primary-strong` (4.6:1 on white ✓).
12
+ - **Coral = action; slate = chrome.** Flavor colors tag tasting notes
13
+ *only* — never UI chrome.
14
+ - **Every solid flavor fill carries white text at ≥4.5:1, zero exceptions** ✓
15
+ (the oklch band guarantees it; `--on-flavor` is always white).
16
+ - **Flavor-colored words on white use the `-accessible` text band** (≥5.5:1 ✓,
17
+ with a small hex-quantization tolerance — green lands at 5.496).
18
+ - **Feedback `-ink` variants** are the AA text/icon colors on their `-light`
19
+ backgrounds ✓; banner words stay ink/ink-secondary.
20
+ - **Gamification colors are reserved for reward moments** — never decoration.
21
+ They are aliases (`xp → sweet`, `badge → floral`, `streak → ember`):
22
+ change the source, never the alias.
23
+ - Known nuance (flagged upstream, not encoded): `--ink-inverse` (#F8FAFC) on
24
+ `--primary-strong` is 4.36:1 — the spec's 4.6:1 claim is for pure white.
25
+
26
+ ## Type
27
+
28
+ - Inter for all UI; six named sizes only — no ad-hoc sizes.
29
+ - **Serif display max once per screen**, often with an italic coral word;
30
+ tracking −1.5%.
31
+ - Mono is the technical register only (ratios, temps, grind, extraction).
32
+ - Never Lora, Fraunces, or Plus Jakarta. No emoji in product UI — ever.
33
+
34
+ ## Layout & interaction
35
+
36
+ - Strict 4pt grid; the seven `--space-*` stops are the only spacing values.
37
+ - **Hit targets ≥44pt, always** (`--hit-target-min`; `.btn` enforces it).
38
+ - **One primary action per screen.**
39
+ - Every interactive element ships all six states before it ships:
40
+ default · hover · pressed · focus · disabled · loading.
41
+ - **Focus is never removed without a replacement** — the dual ring
42
+ `--focus-ring` keeps ≥3:1 non-text contrast on any surface ✓.
43
+
44
+ ## Materials & motion
45
+
46
+ - Content surfaces are always opaque. **One chrome layer per screen**
47
+ (`.chrome` — tab bar or sticky header); scrim for sheets is `--scrim`.
48
+ - `prefers-reduced-transparency` collapses chrome to opaque card (built into
49
+ the generated CSS).
50
+ - One shared spring (`response 0.55s · damping 0.75`); tap pulse
51
+ `scale 0.95 · opacity 0.8`; stagger 0.08s; shimmer 1.5s.
52
+ - **Reduced motion keeps the state change and drops the travel** — springs
53
+ and staggers collapse to 120ms crossfades (built into the generated CSS;
54
+ app-side via `useReducedMotion` on native).
55
+ - Never gate content behind an animation. No bounces on content, no
56
+ infinite decorative loops.
57
+
58
+ ## The golden rule
59
+
60
+ > Never hard-code a hex or pixel value that exists as a token.
61
+
62
+ Lint recipes per platform: [phoenix.md](./phoenix.md), [expo.md](./expo.md).
@@ -0,0 +1,59 @@
1
+ # Versioning policy
2
+
3
+ `@cupped/tokens` follows [semantic versioning](https://semver.org/), but the
4
+ "public API" is the **token contract**, not just exported types. This page
5
+ defines what counts as a breaking change so consumers can upgrade with
6
+ confidence.
7
+
8
+ ## What is the public contract
9
+
10
+ - **`tokens/semantic/`** — the public API. Every semantic token aliases a
11
+ primitive and is what apps consume (via `@cupped/tokens`, `/css`,
12
+ `/tailwind`, …).
13
+ - **`tokens/primitive/`** — internal raw palettes and scales. **Never consumed
14
+ directly**; reshaping a primitive is *not* breaking as long as the semantic
15
+ tokens it feeds keep their resolved values and the contrast invariants hold.
16
+ - The package **subpath exports** (`.`, `./css`, `./css/components`,
17
+ `./tailwind`, `./json`, `./json/dtcg`) and the generated `.d.ts` shape are
18
+ also part of the contract.
19
+
20
+ ## Bump levels
21
+
22
+ ### MAJOR (breaking)
23
+ - Remove or **rename** a semantic token.
24
+ - Remove or reshape a subpath export, or a breaking change to the emitted
25
+ `.d.ts` types (e.g. narrowing a literal union, changing a shadow shape).
26
+ - A value change that **violates a guaranteed accessibility invariant** — the
27
+ `$extensions["app.cupped"].contrast` data is machine-checked in CI
28
+ (see [README](../README.md#architecture)); shipping a value that breaks a
29
+ documented contrast guarantee is breaking even though the key still exists.
30
+
31
+ ### MINOR (additive)
32
+ - Add a token, a new scale step, a new subpath export, or an optional
33
+ `$extensions` block.
34
+ - Add a deprecation alias (see below) ahead of a future removal.
35
+
36
+ ### PATCH
37
+ - A value tweak that keeps every invariant (contrast, collisions, parity).
38
+ - Documentation, descriptions, or `$description` fixes.
39
+
40
+ ## Softening breaking changes
41
+
42
+ Prefer **deprecate-then-remove** over a hard rename:
43
+
44
+ 1. Introduce the new token as a **minor**, keeping the old name as an alias to
45
+ the new one. Mark the old token deprecated in its `$description`.
46
+ 2. Remove the deprecated alias in the next **major**, after at least one minor
47
+ cycle, so consumers get a release where both names resolve.
48
+
49
+ ## Why Changesets (not fully inferred automation)
50
+
51
+ The bump level for a token change is **human judgment**: a rename is breaking,
52
+ a value tweak usually is not — but a value tweak that crosses a contrast
53
+ threshold *is*. Tools that infer the bump from commit messages
54
+ (semantic-release) or from a fixed convention can't make that call reliably.
55
+
56
+ With [Changesets](https://github.com/changesets/changesets) the author who
57
+ makes the change declares the intent in a reviewed `.changeset/*.md` file,
58
+ which doubles as the changelog entry. See [releasing.md](./releasing.md) for
59
+ the mechanics.
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@cupped/tokens",
3
+ "version": "0.1.0",
4
+ "description": "Cupped design tokens — canonical DTCG source with generated CSS custom properties, Tailwind v4 @theme, React Native theme object, and raw JSON.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ybird-labs/cupped-design-system.git"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "main": "./dist/native/index.cjs",
16
+ "types": "./dist/native/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/native/index.d.ts",
20
+ "react-native": "./dist/native/index.js",
21
+ "import": "./dist/native/index.js",
22
+ "require": "./dist/native/index.cjs",
23
+ "default": "./dist/native/index.cjs"
24
+ },
25
+ "./css": "./dist/css/tokens.css",
26
+ "./css/components": "./dist/css/components.css",
27
+ "./tailwind": "./dist/tailwind/theme.css",
28
+ "./json": "./dist/json/tokens.flat.json",
29
+ "./json/dtcg": "./dist/json/tokens.dtcg.json"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "tokens",
34
+ "docs",
35
+ "README.md",
36
+ "CHANGELOG.md"
37
+ ],
38
+ "scripts": {
39
+ "build": "node build/config.js",
40
+ "test": "vitest run",
41
+ "check": "npm run build && git diff --exit-code -- dist/ && test -z \"$(git status --porcelain --untracked-files=all -- dist/)\" && npm test",
42
+ "changeset": "changeset",
43
+ "version": "changeset version",
44
+ "release": "changeset publish",
45
+ "prepublishOnly": "npm run build"
46
+ },
47
+ "devDependencies": {
48
+ "@changesets/changelog-github": "^0.5.1",
49
+ "@changesets/cli": "^2.27.12",
50
+ "colorjs.io": "^0.6.1",
51
+ "postcss": "^8.5.15",
52
+ "style-dictionary": "^5.4.4",
53
+ "typescript": "^6.0.3",
54
+ "vitest": "^4.1.8"
55
+ }
56
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "color": {
3
+ "$type": "color",
4
+ "palette": {
5
+ "$description": "Raw palette values. Never consumed by apps — alias through the semantic layer.",
6
+ "white": { "$value": "#FFFFFF" },
7
+ "slate": {
8
+ "$description": "Cool slate neutrals — carry all UI chrome.",
9
+ "50": { "$value": "#F8FAFC" },
10
+ "100": { "$value": "#F1F5F9" },
11
+ "200": { "$value": "#E2E8F0" },
12
+ "400": { "$value": "#94A3B8" },
13
+ "600": { "$value": "#475569" },
14
+ "900": { "$value": "#0F172A" }
15
+ },
16
+ "coral": {
17
+ "$description": "Warm brand coral — carries action and brand. Coral = action; slate = chrome.",
18
+ "50": { "$value": "#FDF2F0" },
19
+ "500": { "$value": "#E07A5F" },
20
+ "550": { "$value": "#D16A4F" },
21
+ "600": { "$value": "#C05539" },
22
+ "650": { "$value": "#AD4A30" }
23
+ },
24
+ "ember": {
25
+ "$description": "Gamification source hue (streak fire). Promoted to a source token in the refreshed export; --streak aliases it.",
26
+ "fill": { "$value": "#F97316" },
27
+ "text": { "$value": "#C2410C" }
28
+ },
29
+ "green": {
30
+ "50": { "$value": "#F0FDF4" },
31
+ "500": { "$value": "#22C55E" },
32
+ "700": { "$value": "#15803D" }
33
+ },
34
+ "red": {
35
+ "50": { "$value": "#FEF2F2" },
36
+ "500": { "$value": "#EF4444" },
37
+ "700": { "$value": "#B91C1C" }
38
+ },
39
+ "amber": {
40
+ "50": { "$value": "#FFFBEB" },
41
+ "500": { "$value": "#F59E0B" },
42
+ "700": { "$value": "#B45309" }
43
+ },
44
+ "blue": {
45
+ "50": { "$value": "#EFF6FF" },
46
+ "500": { "$value": "#3B82F6" },
47
+ "700": { "$value": "#1D4ED8" }
48
+ },
49
+ "flavor": {
50
+ "$description": "SCA tasting vocabulary — parametric oklch band (\"Espresso\"), not hand-picked hexes. Fill band: L 0.555 · C 0.135 (bright) / 0.062 (earth) / earth ×0.7 (roasted). Text band: L 0.510 · C 0.110 (bright) / 0.060 (earth). Hue carries the flavor; lightness/chroma are shared, so every fill carries white text at ≥4.5:1 with zero exceptions.",
51
+ "fruity": {
52
+ "fill": { "$value": "#B44C54", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 18 }, "band": "fill" } } },
53
+ "text": { "$value": "#9B494E", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 18 }, "band": "text" } } }
54
+ },
55
+ "berry": {
56
+ "fill": { "$value": "#AC4D7B", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 352 }, "band": "fill" } } },
57
+ "text": { "$value": "#94496D", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 352 }, "band": "text" } } }
58
+ },
59
+ "citrus": {
60
+ "fill": { "$value": "#A65F02", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 62 }, "band": "fill" } } },
61
+ "text": { "$value": "#925512", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 62 }, "band": "text" } } }
62
+ },
63
+ "sweet": {
64
+ "fill": { "$value": "#8E6E09", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 88 }, "band": "fill" } } },
65
+ "text": { "$value": "#7E6207", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 88 }, "band": "text" } } }
66
+ },
67
+ "floral": {
68
+ "fill": { "$value": "#865CB2", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 305 }, "band": "fill" } } },
69
+ "text": { "$value": "#755498", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 305 }, "band": "text" } } }
70
+ },
71
+ "green": {
72
+ "fill": { "$value": "#4E8429", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 135 }, "band": "fill" } } },
73
+ "text": { "$value": "#49742E", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 135 }, "band": "text" } } }
74
+ },
75
+ "spice": {
76
+ "fill": { "$value": "#B4503A", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.135, "h": 34 }, "band": "fill" } } },
77
+ "text": { "$value": "#9B4C3A", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.11, "h": 34 }, "band": "text" } } }
78
+ },
79
+ "nutty": {
80
+ "fill": { "$value": "#876F49", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.062, "h": 78 }, "band": "fill" } } },
81
+ "text": { "$value": "#79623E", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.06, "h": 78 }, "band": "text" } } }
82
+ },
83
+ "chocolate": {
84
+ "fill": { "$value": "#916951", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.062, "h": 52 }, "band": "fill" } } },
85
+ "text": { "$value": "#825C46", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.06, "h": 52 }, "band": "text" } } }
86
+ },
87
+ "roasted": {
88
+ "fill": { "$value": "#896B5E", "$extensions": { "app.cupped": { "oklch": { "l": 0.555, "c": 0.0434, "h": 45 }, "band": "fill" } } },
89
+ "text": { "$value": "#7B5F52", "$extensions": { "app.cupped": { "oklch": { "l": 0.51, "c": 0.042, "h": 45 }, "band": "text" } } }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "dimension": {
3
+ "$type": "dimension",
4
+ "scale": {
5
+ "$description": "Raw 4pt-grid dimension stops (plus 2px ring width and the 9999px pill radius). Never consumed by apps — alias through the semantic layer.",
6
+ "2": { "$value": "2px" },
7
+ "4": { "$value": "4px" },
8
+ "8": { "$value": "8px" },
9
+ "12": { "$value": "12px" },
10
+ "16": { "$value": "16px" },
11
+ "20": { "$value": "20px" },
12
+ "24": { "$value": "24px" },
13
+ "32": { "$value": "32px" },
14
+ "44": { "$value": "44px" },
15
+ "full": { "$value": "9999px" }
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "duration": {
3
+ "$type": "duration",
4
+ "$description": "Raw durations. Kept in the spec's original units so generated CSS matches the canonical export character-for-character; the native pipeline normalizes to ms numbers.",
5
+ "spring": { "$value": "0.55s" },
6
+ "stagger": { "$value": "0.08s" },
7
+ "shimmer": { "$value": "1.5s" },
8
+ "reduced": { "$value": "120ms" }
9
+ }
10
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "font": {
3
+ "family": {
4
+ "$type": "fontFamily",
5
+ "$description": "Three families, each with a job: Inter for all UI, Instrument Serif for display (max one moment per screen), JetBrains Mono for the technical register. Never Lora, Fraunces, or Plus Jakarta.",
6
+ "sans": { "$value": ["Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "sans-serif"] },
7
+ "serif": { "$value": ["Instrument Serif", "Georgia", "serif"] },
8
+ "mono": { "$value": ["JetBrains Mono", "ui-monospace", "SFMono-Regular", "monospace"] }
9
+ },
10
+ "weight": {
11
+ "$type": "fontWeight",
12
+ "regular": { "$value": 400 },
13
+ "medium": { "$value": 500 },
14
+ "semibold": { "$value": 600 },
15
+ "bold": { "$value": 700 }
16
+ },
17
+ "size": {
18
+ "$type": "dimension",
19
+ "$description": "Raw size stops. Every size shipped in a component has a semantic token name — no ad-hoc sizes.",
20
+ "12": { "$value": "12px" },
21
+ "14": { "$value": "14px" },
22
+ "16": { "$value": "16px" },
23
+ "20": { "$value": "20px" },
24
+ "24": { "$value": "24px" },
25
+ "32": { "$value": "32px" },
26
+ "56": { "$value": "56px" }
27
+ }
28
+ }
29
+ }