@putdotio/design 1.3.0 → 1.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putdotio/design",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Public put.io design tokens, brand assets, and design-system guidance.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -66,11 +66,13 @@
66
66
  "dev": "node scripts/serve-system.ts",
67
67
  "tokens:build": "node scripts/build-tokens.ts",
68
68
  "tokens:check": "node scripts/check-generated-tokens.ts && node scripts/verify-design-system.ts",
69
+ "design:mirror": "node scripts/export-design-mirror.ts",
70
+ "test:mirror": "node --test tests/export-design-mirror.test.mjs",
69
71
  "typecheck:scripts": "tsc --noEmit --ignoreConfig --skipLibCheck --module ESNext --moduleResolution Bundler --target ES2022 --strict --types node scripts/*.ts playwright.config.ts tests/e2e/*.ts sst.config.ts infra/*.ts",
70
72
  "smoke": "node scripts/smoke-static.ts",
71
73
  "html:check": "html-validate \"system/**/*.html\"",
72
74
  "e2e": "playwright test",
73
- "verify": "pnpm typecheck:scripts && pnpm tokens:check && pnpm html:check && pnpm smoke && sst version",
75
+ "verify": "pnpm typecheck:scripts && pnpm tokens:check && pnpm test:mirror && pnpm html:check && pnpm smoke && sst version",
74
76
  "verify:full": "pnpm verify && pnpm e2e && npm pack --dry-run",
75
77
  "check": "pnpm verify",
76
78
  "deploy:production": "sst deploy --stage production"
@@ -78,11 +80,11 @@
78
80
  "devDependencies": {
79
81
  "@axe-core/playwright": "^4.11.3",
80
82
  "@playwright/test": "^1.60.0",
81
- "@types/node": "^25.9.1",
83
+ "@types/node": "^26.0.0",
82
84
  "html-validate": "^11.2.0",
83
85
  "sst": "^4.14.3",
84
86
  "style-dictionary": "^5.4.1",
85
- "typescript": "^6.0.2",
87
+ "typescript": "^7.0.2",
86
88
  "yaml": "^2.9.0"
87
89
  },
88
90
  "packageManager": "pnpm@11.2.2"
package/system/README.md CHANGED
@@ -18,8 +18,22 @@ project root/
18
18
 
19
19
  ## Fonts
20
20
 
21
- Preview pages load the public put.io font CSS from `static.put.io`: GT America,
22
- GT America Mono, and Berkeley Mono. Do not commit font files here.
21
+ Preview pages load the public put.io font CSS from `static.put.io` (the same
22
+ source as the product), so the design system always matches production. Do not
23
+ commit font files here. Two families, so each HTML file links two stylesheets in
24
+ `<head>`:
25
+
26
+ ```html
27
+ <link rel="stylesheet" href="https://static.put.io/fonts/gt-america/standard/font.css">
28
+ <link rel="stylesheet" href="https://static.put.io/fonts/berkeley-mono/variable/font.css">
29
+ ```
30
+
31
+ GT America Mono is retired. `--font-ui-mono` no longer exists and everything mono
32
+ reads `--font-mono` (Berkeley Mono), whose tabular figures carry the numerics the
33
+ UI mono used to. Do not re-add the `gt-america/mono/font.css` load.
34
+
35
+ Canonical weight mapping (matches the brand font host):
36
+ `100 ultra-light · 200 thin · 300 light · 400 regular · 500 medium · 700 bold · 900 black`
23
37
 
24
38
  ## Using tokens
25
39
 
@@ -33,10 +47,17 @@ The canonical source is DTCG-compatible JSON in [`../tokens`](../tokens). This
33
47
  site stylesheet (`system/tokens.css`) and the package stylesheet
34
48
  (`dist/css/tokens.css`, exported as `@putdotio/design/css`) are generated from
35
49
  the same source. Guide and component CSS should consume custom properties rather
36
- than hard-coded palette values. Brand yellow is emitted as
37
- `var(--yellow-solid)`; its CSS token value is `hsl(44.7, 97.9%, 63.1%)`, the
38
- hsl form of canonical `#FDCE45`. Primary hover feedback uses
39
- `--yellow-solid-hover`, exposed to consumers as `--button-primary-bg-hover`.
50
+ than hard-coded palette values. Brand yellow is `var(--yellow-solid)` —
51
+ canonical `#FDCE45`; the emitted hsl form is pinned in
52
+ [`../DESIGN.md`](../DESIGN.md) front-matter, and the `--yellow-solid-hover` →
53
+ `--button-primary-bg-hover` derivation is specified in the repo's
54
+ `docs/DISTRIBUTION.md`.
55
+
56
+ Stacking and breakpoints are tokenized (`--z-*`, `--bp-*`) and the root
57
+ font-size is responsive; the canonical values live in
58
+ [`../DESIGN.md`](../DESIGN.md) (Layout & Spacing, Typography). Guide-specific
59
+ rule: CSS media queries can't read custom properties, so write the literal px
60
+ in `@media` and keep it in sync with the `--bp-*` token.
40
61
 
41
62
  Web package consumers import the package CSS export:
42
63
 
@@ -44,33 +65,113 @@ Web package consumers import the package CSS export:
44
65
  @import "@putdotio/design/css";
45
66
  ```
46
67
 
47
- For **TV preview cards**, also load `preview/tv-shell.css` — it adds the `.tv`, `.topnav`, `.scr`, glass tiers, and player chrome on top of the tokens. This is preview-only support CSS; platform repos implement TV components in their native UI stacks:
68
+ For **TV preview cards**, also load `preview/tv-shell.css` — it adds the `.tv`,
69
+ `.scr`, `.row`, solid surface tiers, and player chrome on top of the tokens.
70
+ Every component selector is scoped under `.tv`, so the file is safe to bundle
71
+ globally without bleeding into web components. This is preview-only support CSS;
72
+ platform repos implement TV components in their native UI stacks:
48
73
 
49
74
  ```html
50
75
  <link rel="stylesheet" href="./tokens.css">
51
76
  <link rel="stylesheet" href="./preview/tv-shell.css">
52
77
  ```
53
78
 
79
+ TV surfaces are **solid token colors** — no `backdrop-filter`, no translucent
80
+ white fills. Focus is a fill: rows go transparent → `--component-bg-active`,
81
+ buttons go `--component-bg` → `--component-bg-active` with the border stepping
82
+ to `--border-hover`. There is no scale, shadow, halo, or white invert.
83
+
84
+ The 10-foot scale itself is graph data, not CSS. The `tv` token group
85
+ (`--tv-text-*`, `--tv-space-*`, `--tv-overscan-*`,
86
+ `--tv-radius`, `--tv-z-*`) carries the 1:1 values for native emitters and is
87
+ `mode: "tv"`, so it is deliberately **not** emitted into `tokens.css` — read it
88
+ from `dist/tokens.flat.json`. Only `--radius-tv` (12px, the single radius for
89
+ every TV surface) is global and reachable in CSS. Two things must not be folded
90
+ into the web scales: TV has no mono face, and TV orders `--tv-z-overlay` (300)
91
+ *above* `--tv-z-toast` (200), the opposite of the web `--z-*` stack.
92
+
54
93
  ## Core rules
55
94
 
56
- - **Yellow `#FDCE45` is the brand fill in both modes.** Use it for primary CTAs, folder icons, button/nav/TV focus rings (at 35% alpha), and progress bars. Use `--yellow-solid-hover` / `--button-primary-bg-hover` for CTA hover feedback, `--primary-foreground` for labels on yellow, and `--yellow-text-secondary` for yellow text on light backgrounds.
95
+ - **Yellow `#FDCE45` is the brand constant at rest** — full rule and approved uses in [`../DESIGN.md`](../DESIGN.md) (Colors). Guide specifics: `--yellow-solid-hover` (`#F3C435`) is the hover peer with its own canonical hex in both modes, and focus rings use the yellow at 35% alpha (`--shadow-focus-color`).
96
+ - **Yellow as text on light needs `--yellow-text-secondary`** (≈5:1, AA) — the brand `--yellow-solid` is fill-only on light backgrounds.
97
+ - **Labels on yellow** use `--primary-foreground` (`hsl(38, 65%, 10%)` — warm dark, never pure black). Passes AA on the yellow fill without feeling "hard."
57
98
  - **Icons: Phosphor-style inline SVG.** No emoji, ever.
58
- - **One design, two modes.** Light and dark are the same markup and components with different token values. Toggle `.dark` on `<html>`; do not fork the page or rebuild a light-specific copy.
59
- - **Fields stay quiet.** Use `--field-bg`, `--field-border`, and `--field-ring` for text entry. Invalid fields use `aria-invalid="true"` with red border/text only, never a red fill.
60
- - **Panels are shared.** Use `--panel-bg`, `--panel-border`, `--panel-radius`, and `--panel-shadow` for raised cards and auth-style shells instead of screen-specific variables.
99
+ - **One design, two modes.** Light and dark are the same markup and components with different token values. Toggle `.dark` on `<html>`; do not fork the page or rebuild a light-specific copy. Components must consume semantic tokens (`--bg`, `--text`, `--border`, `--accent`, …), not raw `#hex` or `hsl()`.
100
+ - **Fields and panels use the shared aliases** (`--field-*`, `--panel-*`) — full rules, including the `aria-invalid` red-border-only requirement, in [`../DESIGN.md`](../DESIGN.md) (Components).
61
101
  - **Content-agnostic.** put.io doesn't know what the user's files are. Never parse `The.Wire.S03E04.1080p.mkv` into `The Wire`. Raw filenames only. See the root [`DESIGN.md`](../DESIGN.md) for the full principle.
62
102
 
63
103
  ## Theme System
64
104
 
65
- The guide owns the theme toggle. It stores `putio-ds-theme` in `localStorage`,
66
- updates `html.dark`, and broadcasts the current mode to embedded preview iframes.
67
- Standalone preview pages read the same storage key before paint. A URL query
68
- (`?theme=light` or `?theme=dark`) can override the preview mode for screenshots.
105
+ The guide (`design-system.html`) owns the theme toggle. It writes
106
+ `putio-ds-theme` to `localStorage`, updates `html.dark`, and broadcasts
107
+ `{type:'__theme', theme}` to every embedded preview iframe via `postMessage`,
108
+ so embeds flip live without reloading. Each preview's inline bootstrap reads
109
+ the same storage key on standalone open, and `preview/_resize.js` listens for
110
+ the parent broadcast when embedded. A URL query (`?theme=light` or
111
+ `?theme=dark`) overrides both — useful for screenshots.
69
112
 
70
113
  Fixed-mode previews opt out with `<html data-theme-lock="dark">`. This is only
71
- for product mockups whose visual language is intrinsically one mode, such as TV
72
- and mobile shell specimens. Component previews should stay token-driven and work
73
- in both modes.
114
+ for product mockups whose visual language is intrinsically one mode — a 10-foot
115
+ screen and on-video player chrome are dark-only surfaces, so `tv-*.html`,
116
+ `mobile-shell.html`, and `components-player-web.html` are locked. Locked previews ignore both `localStorage` and parent broadcasts.
117
+ If you write a new component preview, you almost certainly don't want the
118
+ lock — let the tokens do the work and your specimen will render correctly in
119
+ both modes.
120
+
121
+ ## shadcn / Base UI interop
122
+
123
+ The token layer ships a name-alias bridge so external component libraries that
124
+ read canonical shadcn token names theme correctly without renaming. Drop in a
125
+ shadcn/ui block, install a Base UI component, paste a Tailwind preset — they
126
+ resolve against our values via the aliases. Always alias, never duplicate, so
127
+ the two name systems can't drift.
128
+
129
+ | shadcn name | put.io target |
130
+ | --- | --- |
131
+ | `--primary` / `--primary-foreground` | `--yellow-solid` / `var(--primary-foreground)` (warm dark) |
132
+ | `--destructive` / `--destructive-foreground` | `--red-solid` / `var(--destructive-foreground)` (= `#fff`) |
133
+ | `--success` / `--success-foreground` | `--green-solid` / `var(--success-foreground)` (= `#fff`) |
134
+ | `--muted` / `--muted-foreground` | `--bg-secondary` / `--text-secondary` |
135
+ | `--accent` / `--accent-foreground` | `--component-bg` / `--text` |
136
+ | `--card` / `--card-foreground` | `--component-bg` / `--text` |
137
+ | `--popover` / `--popover-foreground` | `--component-bg` / `--text` |
138
+ | `--input` | `--border` |
139
+ | `--ring` | `--shadow-focus-color` |
140
+ | `--background` / `--foreground` | `--bg` / `--text` |
141
+
142
+ There is also a full `--input-*` set (`-bg`, `-border`, `-border-hover`,
143
+ `-border-focus`, `-ring`, `-text`, `-placeholder`, `-radius`) that aliases the
144
+ `--field-*` tokens one-for-one, so shadcn-shaped input code themes without
145
+ renaming. **Author against `--field-*`;** the `--input-*` names exist only as the
146
+ alias bridge and must never carry their own value.
147
+
148
+ The foreground tokens (`--primary-foreground`, `--destructive-foreground`,
149
+ `--success-foreground`) encode the mode-safe inverse pair — always read these
150
+ instead of writing literal `#000` / `#fff` on a solid surface.
151
+
152
+ Use whichever name reads cleaner in context. Our own components prefer the long
153
+ descriptive names (`--component-bg-hover`, `--yellow-text-secondary`) because
154
+ they describe the role exactly. shadcn-imported code prefers the short names.
155
+
156
+ ### State via `data-state`, not class flags
157
+
158
+ Base UI / headless primitives set
159
+ `data-state="open|closed|checked|unchecked|active|completed|pending|loading"`
160
+ on the DOM. Our components target those same attribute values in CSS — see the
161
+ wizard step bar in `components-form-layouts.html`
162
+ (`.step[data-state="completed"]`) and the menu/table cards. Prefer `data-state`
163
+ over inventing class flags (`.is-active`, `.on`, `.current`) so the same DOM
164
+ contract works whether the controlling code is React, Vue, vanilla JS, or
165
+ hand-written HTML, and so devtools can read the component's state at a glance.
166
+
167
+ ### Compound parts via `data-slot`
168
+
169
+ shadcn marks parts of compound components with `data-slot="card-header"`,
170
+ `data-slot="card-title"`, etc. We do the same on the folder picker
171
+ (`data-slot="modal" / "modal-header" / "modal-body" / "modal-footer"`) and the
172
+ auth card in `components-form-layouts.html` (`data-slot="auth" / "auth-header"
173
+ / "auth-body"`). This lets external CSS tweak one part without inventing
174
+ nested class chains.
74
175
 
75
176
  ## Design variants
76
177
 
@@ -85,4 +186,4 @@ Four documented type stacks — pick one per surface:
85
186
 
86
187
  ## Preview cards
87
188
 
88
- Open `system/design-system.html` for the full index. Each card renders a single concept against the actual tokens; treat it as the visual spec.
189
+ Open `system/design-system.html` for the full index. Each card renders a single concept against the actual tokens — read it like a spec.