@putdotio/design 3.2.2 → 3.4.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/DESIGN.md +21 -27
- package/README.md +18 -31
- package/package.json +4 -8
- package/system/README.md +52 -88
package/DESIGN.md
CHANGED
|
@@ -70,13 +70,13 @@ The canonical source for tokens is DTCG-compatible JSON in `tokens/`. Generated
|
|
|
70
70
|
|
|
71
71
|
A web-rendered mock is never a native spec. Every surface binds to the system at one of five tiers, and every specimen card in `system/preview/` declares its tier in a strip above the artboard (`system/preview/_tier.css`; if the wording here changes, change that file in the same PR).
|
|
72
72
|
|
|
73
|
-
- **Tier 0
|
|
74
|
-
- **Tier 1
|
|
75
|
-
- **Tier 2
|
|
76
|
-
- **Tier 3
|
|
77
|
-
- **Tier 4
|
|
73
|
+
- **Tier 0: Foundations.** The token graph. Binds every tier, values only, never a component recipe.
|
|
74
|
+
- **Tier 1: www · app · auth.** The full design system: the component library and `system/components.css` recipes. The recipes are the contract here, and only here.
|
|
75
|
+
- **Tier 2: iOS · iPadOS · tvOS · watchOS · Android · Android TV.** Tokens only. Every component, composition, and page comes from the platform's human interface guidelines; the cards document how a stock control receives put.io tokens, never how to rebuild one.
|
|
76
|
+
- **Tier 3: Roku.** Tier-2 token inheritance, plus room for put.io conventions in custom SceneGraph components.
|
|
77
|
+
- **Tier 4: tv.put.io · Tizen · webOS.** The design system as on web, restrained to a generic, calm, list-first 10-foot interface.
|
|
78
78
|
|
|
79
|
-
A tier-1/3/4 card is a Component
|
|
79
|
+
A tier-1/3/4 card is a Component: put.io owns the recipe. A tier-2 card is an Element: the platform owns the control. Per-platform binding contracts live in `platforms/<platform>/DESIGN.md`. One rule holds on every tier: every file-type icon is brand yellow, Phosphor, never a text glyph.
|
|
80
80
|
|
|
81
81
|
## Colors
|
|
82
82
|
|
|
@@ -88,9 +88,9 @@ Dark app surfaces are the default product mode, but the token contract includes
|
|
|
88
88
|
|
|
89
89
|
Use GT America for product UI and display. Berkeley Mono is the single mono face: numerics, timestamps, file sizes, ETAs, counts, code, logs, identifiers, hashes, magnet URIs, and filenames. Its tabular figures keep metadata columns aligned, so pair it with `font-variant-numeric: tabular-nums` wherever numbers sit in a column.
|
|
90
90
|
|
|
91
|
-
The mono role is web and mobile only. TV surfaces have no mono face
|
|
91
|
+
The mono role is web and mobile only. TV surfaces have no mono face; 10-foot numerics render in GT America and should ask for its tabular figures wherever a number ticks or sits in a column. GT America Standard carries `tnum`, so any platform that can select OpenType features can honour this: web through `font-variant-numeric: tabular-nums`, React Native through the same font feature settings. Platforms that cannot select features get the default proportional figures, where `1` is 399/1000 em against `0` at 636, so a running timecode visibly reflows. Roku is the current example (its SceneGraph `Font` node accepts only a `uri` and a `size`), so budget 10-foot numeric layouts for proportional figures rather than assuming the columns line up.
|
|
92
92
|
|
|
93
|
-
GT America is a Latin face: 523 codepoints covering Western and Central European Latin plus Turkish, with no Cyrillic, Greek, CJK, Arabic, Hebrew, Thai, or Hangul, and no dingbats
|
|
93
|
+
GT America is a Latin face: 523 codepoints covering Western and Central European Latin plus Turkish, with no Cyrillic, Greek, CJK, Arabic, Hebrew, Thai, or Hangul, and no dingbats: no `U+2713 ✓`, `U+2714`, `U+25B6 ▶`, or `U+2605 ★`. Two rules follow. Symbols are always icons, never text glyphs, because a checkmark typed as text renders as nothing. And because put.io shows raw filenames, user content routinely arrives outside that coverage: keep a fallback family behind GT America for any text that can hold a filename. Web gets this for free, since `"GT America", sans-serif` falls back per glyph, so the gap is invisible there and shows up on platforms that resolve one font per label with no per-glyph fallback, where uncovered characters become `.notdef` boxes. Treat a platform's fallback story for content text as part of adopting the face, not an afterthought.
|
|
94
94
|
|
|
95
95
|
Body UI text should stay readable and dense. Use medium weight for labels and controls, bold weight only where the hierarchy earns it, and black weight for deliberate display moments.
|
|
96
96
|
|
|
@@ -102,7 +102,7 @@ put.io layouts are file-browser layouts first: lists, rows, columns, sidebars, t
|
|
|
102
102
|
|
|
103
103
|
The spacing scale is intentionally steep: `4, 8, 16, 32, 64, 128, 256, 512`. Use it consistently rather than tuning one-off gaps.
|
|
104
104
|
|
|
105
|
-
Stacking uses the token scale
|
|
105
|
+
Stacking uses the token scale, gapped for platform chrome: `--z-dropdown` (1000), `--z-sticky` (1100), `--z-modal` (1300), `--z-notification` (1400), `--z-tooltip` (1500). Never invent z-index numbers. Breakpoints (`--bp-sm`/`--bp-md`/`--bp-lg` = 640/768/1280px) mirror the product's responsive tiers; CSS media queries cannot read custom properties, so write the literal pixels in `@media` and keep them in sync with the tokens.
|
|
106
106
|
|
|
107
107
|
## Elevation & Depth
|
|
108
108
|
|
|
@@ -112,25 +112,19 @@ Elevation should clarify interaction, not decorate. Use borders and soft shadows
|
|
|
112
112
|
|
|
113
113
|
The default radius is `6px`. Use `4px` for tight controls, `8px` or `10px` for larger panels, and pill radius for segmented controls and badges.
|
|
114
114
|
|
|
115
|
-
TV is the exception: every 10-foot surface
|
|
115
|
+
TV is the exception: every 10-foot surface (list rows, buttons, sheets, cards, code tiles, keys) uses the single `--radius-tv` (`12px`). TV has one radius, not a scale; the only other radii on a TV surface are intentional pills and circles.
|
|
116
116
|
|
|
117
117
|
## Components
|
|
118
118
|
|
|
119
|
-
The component recipes below and in `system/components.css` are the
|
|
120
|
-
binding** of the token graph, and they bind the web surfaces in full.
|
|
121
|
-
tiers inherit
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
recipes (first adopter: `DESIGN.md` in
|
|
125
|
-
[putio-ios](https://github.com/putdotio/putio-ios)); Roku inherits the tokens
|
|
126
|
-
with more room for put.io conventions in custom SceneGraph components; the
|
|
127
|
-
web-based TV app applies this system restrained to the product's generic
|
|
128
|
-
list-first 10-foot interface, which every TV surface — including the native
|
|
129
|
-
ones, within their platform conventions — treats as the shared family look.
|
|
119
|
+
The component recipes below and in `system/components.css` are the
|
|
120
|
+
**web binding** of the token graph, and they bind the web surfaces in full.
|
|
121
|
+
Other tiers inherit as [Binding Tiers](#binding-tiers) describes; every TV
|
|
122
|
+
surface, native ones within their platform conventions, treats the web TV
|
|
123
|
+
list-first look as the shared family look.
|
|
130
124
|
|
|
131
|
-
Buttons have three tiers on one axis: emphasis. **Primary** is the brand fill
|
|
125
|
+
Buttons have three tiers on one axis: emphasis. **Primary** is the brand fill: `--button-primary-bg` at rest, `--button-primary-bg-hover` on hover so the CTA never feels inert, label `--button-primary-fg`. Yellow is reserved for primary commands, and an action group carries at most one. **Secondary** is a quiet fill: `--button-default-bg` (aliased as `--button-secondary-bg`) plus a border at `--border`, with hover stepping both (`--button-default-bg-hover`, `--border-hover`). Because it fills, it reads to a hard edge exactly like the primary, so the two compare safely side by side. **Ghost** is text only: no fill, no border. `--button-ghost-fg` at rest; hover paints `--button-ghost-bg-hover` behind the label and lifts it to `--button-ghost-fg-hover`. The label lift is mandatory: the faint fill is never the only hover cue. Disabled drops the colour to `--solid` at full opacity rather than fading the box. Use ghost for anything quieter than a filled secondary: a header `Sign in`, toolbar text actions, `Cancel` beside a filled confirm. An icon-only ghost is the quiet icon button, on the same tokens.
|
|
132
126
|
|
|
133
|
-
Outline is not a tier. The system ships no transparent-background bordered button, and consumers must not hand-roll one. The pairing rule behind that: never place a border-only button in the same action group as a solid fill. At identical box heights the outlined shape reads smaller, because a solid resolves to a hard edge while a low-contrast hairline resolves to a soft one, and the eye compares the two extents differently. It is worst on dark surfaces, where a 15%-white hairline is barely visible at all. If a bordered treatment must sit near a fill, it must itself fill
|
|
127
|
+
Outline is not a tier. The system ships no transparent-background bordered button, and consumers must not hand-roll one. The pairing rule behind that: never place a border-only button in the same action group as a solid fill. At identical box heights the outlined shape reads smaller, because a solid resolves to a hard edge while a low-contrast hairline resolves to a soft one, and the eye compares the two extents differently. It is worst on dark surfaces, where a 15%-white hairline is barely visible at all. If a bordered treatment must sit near a fill, it must itself fill (the secondary tier) and carry its border at `--border`-step contrast, never a sub-20% hairline on transparent.
|
|
134
128
|
|
|
135
129
|
Button geometry is tokenized so every tier and variant of a given size shares an identical box; tiers change colour only, which makes cross-variant comparison safe by construction. There are four sizes and no others. The default is `36px` tall with `12px` of horizontal padding, an `--fs-sm` uppercase label and `1px` of tracking; `md` is `32px` at `0.8px` tracking, `sm` is `28px` at `0.6px`, and `xs` is `24px` with `8px` padding, an `--fs-xs` label and `0.3px` tracking. Read them from `--button-height` (`-md`, `-sm`, `-xs`), `--button-padding-x` (`-xs`), `--button-label-size` (`-xs`), `--button-tracking` (`-md`, `-sm`, `-xs`), `--button-icon-size` and `--button-gap` rather than restating the numbers. Every button token aliases the neutral scale, which carries both modes, so a dark bar on a light page is a `.dark`-scoped region and never hand-picked dark literals. The sticky site top bar is `--nav-height` (`56px`), or `--nav-height-compact` (`52px`) on narrow viewports, and its CTA is the `md` box via `--nav-cta-height`.
|
|
136
130
|
|
|
@@ -181,9 +175,9 @@ to narrow Auth cards without horizontal scrolling.
|
|
|
181
175
|
|
|
182
176
|
TV components are list-first and focus-first. A TV app is still a file browser, not a poster wall. Roku, native Apple, Android, and web TV repos should consume the generic tokens and generate their own platform bindings.
|
|
183
177
|
|
|
184
|
-
TV surfaces are solid token colors
|
|
178
|
+
TV surfaces are solid token colors: no translucent materials and no blur. Focus is expressed as a fill, never a lift: rows go transparent to `--component-bg-active`, and buttons step both their fill and their border. Navigation is a Home hub that pushes full screens onto a stack; there is no tab bar. Menus are centred modals, not popovers anchored to a trigger. These focus and material rules bind the web TV and Roku surfaces; a native TV binding follows its platform's own control conventions where the platform owns focus. Apple tvOS buttons use the system focus treatment (adopted 2026-08) while non-control TV surfaces stay solid everywhere.
|
|
185
179
|
|
|
186
|
-
The 10-foot scale
|
|
180
|
+
The 10-foot scale (type steps, spacing ramp, overscan ratios, radius, and z-indices) lives in the `tv` token group, carried as data for platform adapters rather than emitted into web CSS. Overscan ratios declare their viewport axis: multiply `tv.overscan.x` by viewport width and `tv.overscan.y` by viewport height, then compose screen padding locally with the spacing tokens. Two rules do not cross over: TV has no mono face, and TV stacks `tv.z.overlay` above `tv.z.toast`, the reverse of the web `--z-*` order. Emit the two scales separately. Cite the `tv` group by its graph path, never as `--tv-*`: those names are read from the DTCG artifacts by platform adapters and are deliberately absent from `dist/css/tokens.css`, so CSS-variable syntax would promise a `var()` that resolves to nothing.
|
|
187
181
|
|
|
188
182
|
## Do's and Don'ts
|
|
189
183
|
|
|
@@ -193,6 +187,6 @@ Do use Phosphor-style inline SVG for forward-looking icon guidance and no emoji
|
|
|
193
187
|
|
|
194
188
|
Do keep `DESIGN.md`, `tokens/`, `dist/`, and `system/tokens.css` in agreement.
|
|
195
189
|
|
|
196
|
-
Don't publish private material in this public repo
|
|
190
|
+
Don't publish private material in this public repo; the enumerated list lives in the repo's `docs/DISTRIBUTION.md` (Public Safety).
|
|
197
191
|
|
|
198
|
-
Don't generate platform-native outputs (Swift, Kotlin, Android XML, Roku) from this repo
|
|
192
|
+
Don't generate platform-native outputs (Swift, Kotlin, Android XML, Roku) from this repo; platform repos own their adapters. Policy in the repo's `docs/DISTRIBUTION.md`.
|
package/README.md
CHANGED
|
@@ -24,13 +24,14 @@ static guidance.
|
|
|
24
24
|
|
|
25
25
|
## Install
|
|
26
26
|
|
|
27
|
-
Token sources live in [`tokens`](tokens). Generated package artifacts live in
|
|
28
|
-
[`dist`](dist). Package-safe brand assets live in [`system/assets`](system/assets).
|
|
29
|
-
|
|
30
27
|
```bash
|
|
31
28
|
npm install @putdotio/design
|
|
32
29
|
```
|
|
33
30
|
|
|
31
|
+
Token sources live in [`tokens`](tokens), generated artifacts in
|
|
32
|
+
[`dist`](dist), and package-safe brand assets in
|
|
33
|
+
[`system/assets`](system/assets).
|
|
34
|
+
|
|
34
35
|
## Use
|
|
35
36
|
|
|
36
37
|
Import the generated tokens first. Tier-1 web surfaces also import the
|
|
@@ -41,30 +42,21 @@ component recipes:
|
|
|
41
42
|
@import "@putdotio/design/components";
|
|
42
43
|
```
|
|
43
44
|
|
|
44
|
-
Component recipes
|
|
45
|
-
`--
|
|
46
|
-
into palette tokens directly.
|
|
47
|
-
|
|
48
|
-
Package entrypoints:
|
|
45
|
+
Component recipes consume semantic aliases such as `--field-*`, `--panel-*`,
|
|
46
|
+
`--primary`, `--success`, and `--destructive`, not palette tokens.
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
- Tier-1 component recipes: [`system/components.css`](system/components.css)
|
|
52
|
-
- DTCG token tree: [`dist/tokens.dtcg.json`](dist/tokens.dtcg.json)
|
|
53
|
-
- Flat token metadata: [`dist/tokens.flat.json`](dist/tokens.flat.json)
|
|
54
|
-
- Figma-safe subset: [`dist/figma/putio.tokens.json`](dist/figma/putio.tokens.json)
|
|
55
|
-
- Brand assets: [`system/assets`](system/assets) via `@putdotio/design/assets/<file>`
|
|
56
|
-
- Design contract: [`DESIGN.md`](DESIGN.md)
|
|
48
|
+
Package entrypoints, from `exports` in [package.json](package.json):
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
| Subpath | File |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `@putdotio/design/css` | [`dist/css/tokens.css`](dist/css/tokens.css) |
|
|
53
|
+
| `@putdotio/design/components` | [`system/components.css`](system/components.css) |
|
|
54
|
+
| `@putdotio/design/tokens` | [`dist/tokens.flat.json`](dist/tokens.flat.json) |
|
|
55
|
+
| `@putdotio/design/tokens/meta` | [`dist/tokens.js`](dist/tokens.js), typed by [`dist/tokens.d.ts`](dist/tokens.d.ts) |
|
|
56
|
+
| `@putdotio/design/tokens/dtcg` | [`dist/tokens.dtcg.json`](dist/tokens.dtcg.json) |
|
|
57
|
+
| `@putdotio/design/tokens/figma` | [`dist/figma/putio.tokens.json`](dist/figma/putio.tokens.json) |
|
|
58
|
+
| `@putdotio/design/assets/<file>` | [`system/assets`](system/assets) |
|
|
59
|
+
| `@putdotio/design/design.md` | [`DESIGN.md`](DESIGN.md) |
|
|
68
60
|
|
|
69
61
|
## Docs
|
|
70
62
|
|
|
@@ -72,14 +64,9 @@ Use `pnpm tokens:build` after token edits.
|
|
|
72
64
|
- [Design guide structure](system/README.md)
|
|
73
65
|
- [Distribution](docs/DISTRIBUTION.md)
|
|
74
66
|
- [Contributing](CONTRIBUTING.md)
|
|
75
|
-
- [Security](SECURITY.md)
|
|
67
|
+
- [Security](https://github.com/putdotio/.github/blob/main/SECURITY.md)
|
|
76
68
|
- [Agent guide](AGENTS.md)
|
|
77
69
|
|
|
78
|
-
## Contributing
|
|
79
|
-
|
|
80
|
-
See [Contributing](CONTRIBUTING.md) for local setup, validation, and pull
|
|
81
|
-
request expectations.
|
|
82
|
-
|
|
83
70
|
## License
|
|
84
71
|
|
|
85
72
|
[MIT](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@putdotio/design",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Public put.io design tokens, brand assets, and design-system guidance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -68,15 +68,12 @@
|
|
|
68
68
|
"dev": "node scripts/serve-system.ts",
|
|
69
69
|
"tokens:build": "node scripts/build-tokens.ts",
|
|
70
70
|
"tokens:check": "node scripts/build-tokens.ts && node scripts/check-component-css.ts && node scripts/verify-design-system.ts",
|
|
71
|
-
"design:mirror": "node scripts/export-design-mirror.ts",
|
|
72
71
|
"test:config": "node --test tests/playwright-config.test.mjs",
|
|
73
|
-
"test:mirror": "node --test tests/export-design-mirror.test.mjs",
|
|
74
72
|
"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",
|
|
75
73
|
"html:check": "html-validate \"system/**/*.html\"",
|
|
76
|
-
"skills:lint": "skillcheck lint",
|
|
77
74
|
"e2e": "playwright test",
|
|
78
|
-
"verify": "pnpm
|
|
79
|
-
"verify:full": "pnpm verify && pnpm test:
|
|
75
|
+
"verify": "pnpm typecheck:scripts && pnpm tokens:check && pnpm html:check",
|
|
76
|
+
"verify:full": "pnpm verify && pnpm test:config && pnpm e2e && npm pack --dry-run",
|
|
80
77
|
"check": "pnpm verify",
|
|
81
78
|
"deploy:production": "sst deploy --stage production"
|
|
82
79
|
},
|
|
@@ -84,12 +81,11 @@
|
|
|
84
81
|
"@axe-core/playwright": "^4.11.3",
|
|
85
82
|
"@playwright/test": "^1.60.0",
|
|
86
83
|
"@types/node": "^26.0.0",
|
|
87
|
-
"@uinaf/skillcheck": "0.4.0",
|
|
88
84
|
"html-validate": "^11.2.0",
|
|
89
85
|
"sst": "^4.14.3",
|
|
90
86
|
"style-dictionary": "^5.4.1",
|
|
91
87
|
"typescript": "^7.0.2",
|
|
92
88
|
"yaml": "^2.9.0"
|
|
93
89
|
},
|
|
94
|
-
"packageManager": "pnpm@12.
|
|
90
|
+
"packageManager": "pnpm@12.4.2"
|
|
95
91
|
}
|
package/system/README.md
CHANGED
|
@@ -4,42 +4,37 @@ This folder holds the generated site tokens, preview cards, and design-system in
|
|
|
4
4
|
|
|
5
5
|
## Layout
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Cards are named `<platform>-<section><n>-<subject>.html` — section letter `f`
|
|
23
|
-
foundations, `c` components, `e` elements, `p` patterns, `s` screens
|
|
7
|
+
- `index.html` redirects to `design-system.html`, the guide and preview-card
|
|
8
|
+
index. `design-system-light.html` is a legacy redirect to
|
|
9
|
+
`design-system.html?theme=light`.
|
|
10
|
+
- `tokens.css` is generated from `../tokens/**/*.tokens.json`; do not edit it.
|
|
11
|
+
- `components.css` is the Tier-1 component layer (the web contract).
|
|
12
|
+
- `tv.css` is the TV / 10-foot component CSS, scoped to `.tv` artboards.
|
|
13
|
+
- `assets/` holds logos, favicons, retro marks, and app icons.
|
|
14
|
+
- `preview/` holds specimen cards for the foundations and every platform tier.
|
|
15
|
+
Its `_`-prefixed files are card chrome only (frame, tier strip, HIG, M3, and
|
|
16
|
+
Roku shells, resize and password-toggle helpers).
|
|
17
|
+
|
|
18
|
+
Cards are named `<platform>-<section><n>-<subject>.html` with section letter `f`
|
|
19
|
+
foundations, `c` components, `e` elements, `p` patterns, or `s` screens
|
|
24
20
|
(`ios-e01-toggle.html`); the numbered `00-…13-…` files are the tier-0
|
|
25
21
|
foundations. Element cards are a fixed 1120×580 viewport: one control, its
|
|
26
22
|
states, a spec strip, a don't block.
|
|
27
23
|
|
|
28
24
|
## Fonts
|
|
29
25
|
|
|
30
|
-
Preview pages load the public put.io font CSS from `static.put.io
|
|
31
|
-
source as the product
|
|
32
|
-
|
|
33
|
-
`<head>`:
|
|
26
|
+
Preview pages load the public put.io font CSS from `static.put.io`, the same
|
|
27
|
+
source as the product. Do not commit font files here. Each HTML file links both
|
|
28
|
+
families in `<head>`:
|
|
34
29
|
|
|
35
30
|
```html
|
|
36
31
|
<link rel="stylesheet" href="https://static.put.io/fonts/gt-america/standard/font.css">
|
|
37
32
|
<link rel="stylesheet" href="https://static.put.io/fonts/berkeley-mono/variable/font.css">
|
|
38
33
|
```
|
|
39
34
|
|
|
40
|
-
GT America Mono is retired
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
GT America Mono is retired: all mono text reads `--font-mono` (Berkeley Mono),
|
|
36
|
+
whose tabular figures carry the numerics. Do not re-add the
|
|
37
|
+
`gt-america/mono/font.css` load.
|
|
43
38
|
|
|
44
39
|
Canonical weight mapping (matches the brand font host):
|
|
45
40
|
`100 ultra-light · 200 thin · 300 light · 400 regular · 500 medium · 700 bold · 900 black`
|
|
@@ -56,7 +51,7 @@ The canonical source is DTCG-compatible JSON in [`../tokens`](../tokens). This
|
|
|
56
51
|
site stylesheet (`system/tokens.css`) and the package stylesheet
|
|
57
52
|
(`dist/css/tokens.css`, exported as `@putdotio/design/css`) are generated from
|
|
58
53
|
the same source. Guide and component CSS should consume custom properties rather
|
|
59
|
-
than hard-coded palette values. Brand yellow is `var(--yellow-solid)
|
|
54
|
+
than hard-coded palette values. Brand yellow is `var(--yellow-solid)`,
|
|
60
55
|
canonical `#FDCE45`; the emitted hsl form is pinned in
|
|
61
56
|
[`../DESIGN.md`](../DESIGN.md) front-matter, and the `--yellow-solid-hover` →
|
|
62
57
|
`--button-primary-bg-hover` derivation is specified in the repo's
|
|
@@ -68,15 +63,9 @@ font-size is responsive; the canonical values live in
|
|
|
68
63
|
rule: CSS media queries can't read custom properties, so write the literal px
|
|
69
64
|
in `@media` and keep it in sync with the `--bp-*` token.
|
|
70
65
|
|
|
71
|
-
|
|
72
|
-
import the component recipes:
|
|
66
|
+
Package consumers import as in the [README](../README.md#use).
|
|
73
67
|
|
|
74
|
-
|
|
75
|
-
@import "@putdotio/design/css";
|
|
76
|
-
@import "@putdotio/design/components";
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
For **TV preview cards**, also load `tv.css` — it adds the `.tv`, `.scr`,
|
|
68
|
+
For **TV preview cards**, also load `tv.css`, which adds the `.tv`, `.scr`,
|
|
80
69
|
`.row`, solid surface tiers, and player chrome on top of the tokens, authored
|
|
81
70
|
1:1 at 1920×1080 (a px in the file is a px on the TV). Every component selector
|
|
82
71
|
is scoped under `.tv`, so the file is safe to bundle globally without bleeding
|
|
@@ -88,52 +77,30 @@ native UI stacks:
|
|
|
88
77
|
<link rel="stylesheet" href="./tv.css">
|
|
89
78
|
```
|
|
90
79
|
|
|
91
|
-
TV
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
The 10-foot scale itself is graph data, not CSS. The `tv` token group
|
|
97
|
-
(`--tv-text-*`, `--tv-space-*`, `--tv-overscan-*`,
|
|
98
|
-
`--tv-radius`, `--tv-z-*`) carries the 1:1 values for native emitters and is
|
|
99
|
-
`mode: "tv"`, so it is deliberately **not** emitted into `tokens.css` — read it
|
|
100
|
-
from `dist/tokens.flat.json`. Only `--radius-tv` (12px, the single radius for
|
|
101
|
-
every TV surface) is global and reachable in CSS. Two things must not be folded
|
|
102
|
-
into the web scales: TV has no mono face, and TV orders `--tv-z-overlay` (300)
|
|
103
|
-
*above* `--tv-z-toast` (200), the opposite of the web `--z-*` stack.
|
|
80
|
+
TV material, focus, and the `tv` token group rules live in
|
|
81
|
+
[`../DESIGN.md`](../DESIGN.md) (Components). Guide specifics: the `tv` group is
|
|
82
|
+
`mode: "tv"` and read from `dist/tokens.flat.json`; only `--radius-tv` is
|
|
83
|
+
global and reachable in CSS.
|
|
104
84
|
|
|
105
85
|
## Core rules
|
|
106
86
|
|
|
107
|
-
- **Yellow `#FDCE45` is the brand constant at rest
|
|
108
|
-
- **Yellow as text on light needs `--yellow-text-secondary`** (≈5:1, AA)
|
|
109
|
-
- **Labels on yellow** use `--primary-foreground` (`hsl(38, 65%, 10%)
|
|
87
|
+
- **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`).
|
|
88
|
+
- **Yellow as text on light needs `--yellow-text-secondary`** (≈5:1, AA); the brand `--yellow-solid` is fill-only on light backgrounds.
|
|
89
|
+
- **Labels on yellow** use `--primary-foreground` (`hsl(38, 65%, 10%)`, a warm dark, never pure black). Passes AA on the yellow fill without feeling "hard."
|
|
110
90
|
- **Icons: Phosphor-style inline SVG.** No emoji, ever.
|
|
111
91
|
- **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()`.
|
|
112
|
-
- **Fields and panels use the shared aliases** (`--field-*`, `--panel-*`)
|
|
92
|
+
- **Fields and panels use the shared aliases** (`--field-*`, `--panel-*`); full rules, including the `aria-invalid` red-border-only requirement, are in [`../DESIGN.md`](../DESIGN.md) (Components).
|
|
113
93
|
- **Wordmarks are optically pre-centred.** All three logo SVGs carry built-in top padding in the viewBox (retro pair `0 -16 376 112`, `logo.svg` `0 -24.44 621.79 176.055`). Flex-centre as-is; never add a `translateY` nudge. Height-set placements size boxes at 7/6 of the ink height.
|
|
114
94
|
- **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.
|
|
115
95
|
|
|
116
96
|
## Binding tiers
|
|
117
97
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| --- | --- | --- |
|
|
125
|
-
| 0 | Foundations | The token graph. Binds all tiers, values only |
|
|
126
|
-
| 1 | www · app · auth | The full system — `components.css` recipes are the contract, here and only here |
|
|
127
|
-
| 2 | iOS · iPadOS · tvOS · watchOS · Android · Android TV | Tokens only; every control from the platform HIG / Material 3 |
|
|
128
|
-
| 3 | Roku | Tier-2 token inheritance + put.io conventions in custom SceneGraph components |
|
|
129
|
-
| 4 | tv.put.io · Tizen · webOS | The web system, restrained to a list-first 10-foot interface |
|
|
130
|
-
|
|
131
|
-
A tier-1/3/4 card is a **Component** (put.io owns the recipe); a tier-2 card is
|
|
132
|
-
an **Element** (the platform owns the control; the card documents how it is
|
|
133
|
-
tokenized). Never read an Element card as a build-a-custom-control instruction.
|
|
134
|
-
|
|
135
|
-
Native cards are drawn 1:1 in their own unit — iPhone 393×852pt, iPad
|
|
136
|
-
1024×768pt, Apple Watch 176×215pt, Android 412×915dp, TV 1920×1080px — so a
|
|
98
|
+
Tier definitions, the Component/Element distinction, and the `preview/_tier.css`
|
|
99
|
+
pairing rule live in [`../DESIGN.md`](../DESIGN.md) (Binding Tiers). Never read
|
|
100
|
+
an Element card as a build-a-custom-control instruction.
|
|
101
|
+
|
|
102
|
+
Native cards are drawn 1:1 in their own unit (iPhone 393×852pt, iPad
|
|
103
|
+
1024×768pt, Apple Watch 176×215pt, Android 412×915dp, TV 1920×1080px), so a
|
|
137
104
|
number written on a card is the number in the HIG or the Material spec. Those
|
|
138
105
|
metrics are Apple's and Google's; they are **not** put.io spacing tokens and
|
|
139
106
|
must never be promoted into the graph. Device bezels and on-video chrome stay
|
|
@@ -147,25 +114,22 @@ The guide (`design-system.html`) owns the theme toggle. It writes
|
|
|
147
114
|
so embeds flip live without reloading. Each preview's inline bootstrap reads
|
|
148
115
|
the same storage key on standalone open, and `preview/_resize.js` listens for
|
|
149
116
|
the parent broadcast when embedded. A URL query (`?theme=light` or
|
|
150
|
-
`?theme=dark`) overrides both
|
|
117
|
+
`?theme=dark`) overrides both, which is useful for screenshots.
|
|
151
118
|
|
|
152
119
|
Fixed-mode previews opt out with `<html data-theme-lock="dark">`. This is only
|
|
153
|
-
for product mockups whose visual language is intrinsically one mode
|
|
120
|
+
for product mockups whose visual language is intrinsically one mode. A 10-foot
|
|
154
121
|
screen, a native app mockup, and on-video player chrome are fixed-mode
|
|
155
122
|
surfaces, so the `ios-*`, `android-*`, `androidtv-*`, `tvos-*`, `watchos-*`,
|
|
156
123
|
`roku-*`, and `tv-*` cards plus `web-p06-player.html` are locked. Locked
|
|
157
124
|
previews ignore both `localStorage` and parent broadcasts.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
both modes.
|
|
125
|
+
New component previews should not use the lock; the tokens render them
|
|
126
|
+
correctly in both modes.
|
|
161
127
|
|
|
162
128
|
## shadcn / Base UI interop
|
|
163
129
|
|
|
164
|
-
The token layer
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
resolve against our values via the aliases. Always alias, never duplicate, so
|
|
168
|
-
the two name systems can't drift.
|
|
130
|
+
The token layer aliases the canonical shadcn token names, so shadcn/ui blocks,
|
|
131
|
+
Base UI components, and Tailwind presets resolve against put.io values without
|
|
132
|
+
renaming. Always alias, never duplicate, so the two name systems cannot drift.
|
|
169
133
|
|
|
170
134
|
| shadcn name | put.io target |
|
|
171
135
|
| --- | --- |
|
|
@@ -187,23 +151,22 @@ renaming. **Author against `--field-*`;** the `--input-*` names exist only as th
|
|
|
187
151
|
alias bridge and must never carry their own value.
|
|
188
152
|
|
|
189
153
|
The foreground tokens (`--primary-foreground`, `--destructive-foreground`,
|
|
190
|
-
`--success-foreground`) encode the mode-safe inverse pair
|
|
154
|
+
`--success-foreground`) encode the mode-safe inverse pair; always read these
|
|
191
155
|
instead of writing literal `#000` / `#fff` on a solid surface.
|
|
192
156
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
157
|
+
put.io components prefer the long descriptive names (`--component-bg-hover`,
|
|
158
|
+
`--yellow-text-secondary`) because they name the role exactly; shadcn-imported
|
|
159
|
+
code keeps the short names.
|
|
196
160
|
|
|
197
161
|
### State via `data-state`, not class flags
|
|
198
162
|
|
|
199
163
|
Base UI / headless primitives set
|
|
200
164
|
`data-state="open|closed|checked|unchecked|active|completed|pending|loading"`
|
|
201
|
-
on the DOM. Our components target those same attribute values in CSS
|
|
165
|
+
on the DOM. Our components target those same attribute values in CSS; see the
|
|
202
166
|
wizard step bar in `web-p00b-form-flows.html`
|
|
203
167
|
(`.step[data-state="completed"]`) and the menu/table cards. Prefer `data-state`
|
|
204
168
|
over inventing class flags (`.is-active`, `.on`, `.current`) so the same DOM
|
|
205
|
-
contract works
|
|
206
|
-
hand-written HTML, and so devtools can read the component's state at a glance.
|
|
169
|
+
contract works with any controlling code and devtools show the state directly.
|
|
207
170
|
|
|
208
171
|
### Compound parts via `data-slot`
|
|
209
172
|
|
|
@@ -216,7 +179,7 @@ nested class chains.
|
|
|
216
179
|
|
|
217
180
|
## Design variants
|
|
218
181
|
|
|
219
|
-
Four documented type stacks
|
|
182
|
+
Four documented type stacks; pick one per surface:
|
|
220
183
|
|
|
221
184
|
| Variant | Stack | Use when |
|
|
222
185
|
|---------|-------|----------|
|
|
@@ -227,4 +190,5 @@ Four documented type stacks — pick one per surface:
|
|
|
227
190
|
|
|
228
191
|
## Preview cards
|
|
229
192
|
|
|
230
|
-
|
|
193
|
+
`design-system.html` is the full index. Each card renders one concept against
|
|
194
|
+
the real tokens; read it as a spec.
|