@putdotio/design 3.5.0 → 3.5.1
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 +57 -98
- package/README.md +11 -19
- package/package.json +7 -3
- package/system/README.md +91 -92
package/DESIGN.md
CHANGED
|
@@ -22,17 +22,17 @@ colors:
|
|
|
22
22
|
danger: "hsl(358, 75%, 59%)"
|
|
23
23
|
typography:
|
|
24
24
|
ui:
|
|
25
|
-
family: "
|
|
25
|
+
family: '"GT America", sans-serif'
|
|
26
26
|
weight: 400
|
|
27
27
|
size: "1rem"
|
|
28
28
|
lineHeight: 1.45
|
|
29
29
|
display:
|
|
30
|
-
family: "
|
|
30
|
+
family: '"GT America", sans-serif'
|
|
31
31
|
weight: 900
|
|
32
32
|
size: "96px"
|
|
33
33
|
lineHeight: 1.1
|
|
34
34
|
mono:
|
|
35
|
-
family: "
|
|
35
|
+
family: '"Berkeley Mono", monospace'
|
|
36
36
|
weight: 400
|
|
37
37
|
size: "0.875rem"
|
|
38
38
|
lineHeight: 1.45
|
|
@@ -62,131 +62,90 @@ components:
|
|
|
62
62
|
|
|
63
63
|
## Overview
|
|
64
64
|
|
|
65
|
-
put.io is a content-agnostic cloud file product.
|
|
66
|
-
|
|
67
|
-
The canonical source for tokens is DTCG-compatible JSON in `tokens/`. Generated artifacts in `dist/` and `system/tokens.css` are outputs. This file explains how to use those tokens.
|
|
65
|
+
put.io is a content-agnostic cloud file product. Its interface is a file browser: fast, quiet, and honest about the data it has. Design for raw filenames, folders, transfers, storage, playback, and settings; media metadata is usually absent.
|
|
68
66
|
|
|
69
67
|
## Binding Tiers
|
|
70
68
|
|
|
71
|
-
A web
|
|
69
|
+
A web mock is never a native spec. Every surface binds at one tier, and every card in `system/preview/` names its tier in a strip worded by `system/preview/_tier.css`.
|
|
72
70
|
|
|
73
|
-
- **Tier 0: Foundations.** The token graph.
|
|
74
|
-
- **Tier 1: www · app · auth.** The full
|
|
75
|
-
- **Tier 2: iOS · iPadOS · tvOS · watchOS · Android · Android TV.** Tokens only.
|
|
76
|
-
- **Tier 3: Roku.** Tier
|
|
77
|
-
- **Tier 4: tv.put.io · Tizen · webOS.** The
|
|
71
|
+
- **Tier 0: Foundations.** The token graph. Values only; binds every tier.
|
|
72
|
+
- **Tier 1: www · app · auth.** The full system: tokens plus the `system/components.css` recipes, which are the contract here only.
|
|
73
|
+
- **Tier 2: iOS · iPadOS · tvOS · watchOS · Android · Android TV.** Tokens only. Components and pages come from the platform's guidelines; cards show how a stock control takes put.io tokens.
|
|
74
|
+
- **Tier 3: Roku.** Tier 2, plus put.io conventions in custom SceneGraph components.
|
|
75
|
+
- **Tier 4: tv.put.io · Tizen · webOS.** The web system, restrained to a calm, list-first 10-foot interface.
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
Tier 1, 3, and 4 cards are Components (put.io owns the recipe); tier 2 cards are Elements (the platform owns the control). Per-platform contracts: `platforms/<platform>/DESIGN.md`. On every tier, file-type icons are brand-yellow Phosphor icons.
|
|
80
78
|
|
|
81
79
|
## Colors
|
|
82
80
|
|
|
83
|
-
Yellow `#FDCE45`
|
|
84
|
-
|
|
85
|
-
Dark
|
|
81
|
+
- Yellow `#FDCE45` (`--yellow-solid`) is the brand value in both modes: brand mark, primary actions, folder and file emphasis, progress, focus. Hover is `--yellow-solid-hover`.
|
|
82
|
+
- Every yellow is one of these tokens, flat. Yellow text on light surfaces is `--yellow-text-secondary`.
|
|
83
|
+
- Dark is the default product mode; light serves landing pages, docs, and states that read better light.
|
|
84
|
+
- Red, green, and neutral come from their token scales.
|
|
86
85
|
|
|
87
86
|
## Typography
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
The root font-size is responsive like the product: 14px, stepping to 15px at the `--bp-lg` breakpoint. Body, labels, and inputs sit at `--fs-base` (1rem, equal to the root); captions and button labels sit at `--fs-sm` (0.875rem). The display tiers (`--fs-2xl`, `--fs-3xl`, `--fs-display`) are fixed pixel sizes and do not scale with the root.
|
|
88
|
+
- GT America sets product UI and display. Labels and controls are medium, hierarchy is bold, display is black.
|
|
89
|
+
- Berkeley Mono is the one mono face: numerics, timestamps, sizes, ETAs, counts, code, logs, identifiers, hashes, magnet URIs, filenames. Columns of numbers add `font-variant-numeric: tabular-nums`.
|
|
90
|
+
- TV has no mono face: 10-foot numerics use GT America `tnum`. Platforms without OpenType feature selection (Roku: `Font` takes only `uri` and `size`) get proportional figures, so budget timecodes for reflow (`1` is 399/1000 em, `0` is 636).
|
|
91
|
+
- GT America is Latin only (523 codepoints, Western and Central European plus Turkish, no dingbats such as `✓` `▶` `★`). Symbols are icons. Text that can hold a filename keeps a fallback family; web falls back per glyph, while one-font-per-label platforms must ship a fallback or render `.notdef` boxes.
|
|
92
|
+
- Root font-size is 14px, 15px from `--bp-lg`. Body, labels, inputs: `--fs-base`. Captions, button labels: `--fs-sm`. `--fs-2xl`, `--fs-3xl`, `--fs-display` are fixed pixels.
|
|
98
93
|
|
|
99
94
|
## Layout & Spacing
|
|
100
95
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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.
|
|
96
|
+
- Layouts are file-browser layouts: lists, rows, columns, sidebars, toolbars, sheets. Whitespace and dividers come before cards.
|
|
97
|
+
- Spacing steps: `4, 8, 16, 32, 64, 128, 256, 512`. Every gap is one of them.
|
|
98
|
+
- z-index: `--z-dropdown` 1000, `--z-sticky` 1100, `--z-modal` 1300, `--z-notification` 1400, `--z-tooltip` 1500.
|
|
99
|
+
- Breakpoints `--bp-sm`/`--bp-md`/`--bp-lg` are 640/768/1280px; `@media` takes the literal pixels.
|
|
106
100
|
|
|
107
101
|
## Elevation & Depth
|
|
108
102
|
|
|
109
|
-
|
|
103
|
+
Borders and soft shadows mark interaction: popovers, modals, menus, TV focus, drag and drop targets. Glow belongs only to focused TV states.
|
|
110
104
|
|
|
111
105
|
## Shapes
|
|
112
106
|
|
|
113
|
-
|
|
114
|
-
|
|
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.
|
|
107
|
+
Default radius `6px`; `4px` for tight controls, `8px` or `10px` for panels, pill for segmented controls and badges. TV uses one radius, `--radius-tv` (`12px`), plus intentional pills and circles.
|
|
116
108
|
|
|
117
109
|
## Components
|
|
118
110
|
|
|
119
|
-
|
|
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.
|
|
124
|
-
|
|
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.
|
|
126
|
-
|
|
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.
|
|
128
|
-
|
|
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`.
|
|
130
|
-
|
|
131
|
-
TV needs no carve-out here: TV buttons keep the `tv` scale box and `--radius-tv`, and TV focus restores a visible box (`--component-bg-active` plus a border step), so the ghost tier stays legible in the 10-foot focus model.
|
|
132
|
-
|
|
133
|
-
File rows are the core component: preserve raw names, keep metadata compact, and make hover/focus states obvious without inflating row height.
|
|
134
|
-
|
|
135
|
-
Form fields use the shared `--field-*` aliases: `--field-bg` for the fill, `--field-border` for the resting border, and `--field-ring` for focus. Invalid fields opt in with `aria-invalid="true"` and use red border/text only; do not add red fill. Raised panels use the shared `--panel-*` aliases instead of auth- or screen-specific panel variables.
|
|
136
|
-
|
|
137
|
-
Browser Auth composition is fluid up to a `340px` column. It uses the standard
|
|
138
|
-
`36px` field recipe, a `30.33px` wordmark box (the `26px` ink height multiplied
|
|
139
|
-
by its `7/6` view-box ratio), and centered headings at `19px` / weight
|
|
140
|
-
`500`. The prompt and its action form a centered two-line footer. User-facing
|
|
141
|
-
actions say “Sign in” and “Sign up”, and an invalid credential response uses
|
|
142
|
-
“That username or password doesn't look right” with no terminal punctuation.
|
|
143
|
-
The shipped
|
|
144
|
-
`web-s06-auth` screen plus `web-p00c-auth-signin`, `web-p00d-auth-signup`, and
|
|
145
|
-
`web-p00e-auth-2fa` are the Tier-1 composition references for this contract.
|
|
146
|
-
|
|
147
|
-
Form feedback uses the exported `.form-callout` recipe. Its block form reads
|
|
148
|
-
the `--alert-*` families and selects `info`, `success`, or `error` with
|
|
149
|
-
`data-state`; `.inline` removes the surface and border while keeping the same
|
|
150
|
-
semantic text colour. Callouts remain messages, not containers for unrelated
|
|
151
|
-
form layout. Their parent uses `.form-group`, whose `6px` gap owns the spacing
|
|
152
|
-
between a control and its inline feedback.
|
|
111
|
+
`system/components.css` is the web binding and binds web surfaces in full. Every TV surface shares the web TV list-first look; native ones within their platform conventions.
|
|
153
112
|
|
|
154
|
-
|
|
155
|
-
inputs. Place it with two three-slot `.otp-group` elements, one
|
|
156
|
-
`.otp-separator`, and six visual `.otp-slot` elements. The input carries
|
|
157
|
-
`inputmode="numeric"`, `autocomplete="one-time-code"`, `maxlength="6"`, and
|
|
158
|
-
`aria-invalid`; script synchronizes slot text plus `filled` / `active` state.
|
|
159
|
-
Pointer interaction maps the clicked visual slot to the backing input's
|
|
160
|
-
selection; do not rely on the transparent input's native glyph hit testing.
|
|
161
|
-
Editing an invalid code clears its stale error and disables retry until all six
|
|
162
|
-
digits are present. Retrying enters the same locked verifying state as the
|
|
163
|
-
default form.
|
|
164
|
-
The root uses `data-state="verifying"` while submission is in flight and keeps
|
|
165
|
-
the entered digits visible; the backing input is disabled for the same period,
|
|
166
|
-
and the action label announces the transition through a polite live region.
|
|
167
|
-
Successful verification uses `data-state="success"`, retains the six digits in
|
|
168
|
-
a read-only input, announces automatic continuation with a `role="status"`
|
|
169
|
-
callout, and does not leave another action on screen. The consumer owns the
|
|
170
|
-
backend transition and navigation timing.
|
|
171
|
-
The reference omits recovery-code entry until its consumer owns a working
|
|
172
|
-
route. Do not expose a specimen-only recovery action.
|
|
173
|
-
Groups, not slots, own the responsive width, so the complete control contracts
|
|
174
|
-
to narrow Auth cards without horizontal scrolling.
|
|
113
|
+
### Buttons
|
|
175
114
|
|
|
176
|
-
|
|
115
|
+
- **Primary:** `--button-primary-bg`, hover `--button-primary-bg-hover`, label `--button-primary-fg`. At most one per action group.
|
|
116
|
+
- **Secondary:** `--button-default-bg` plus a `--border` border; hover steps both (`--button-default-bg-hover`, `--border-hover`).
|
|
117
|
+
- **Ghost:** text only, `--button-ghost-fg`; hover adds `--button-ghost-bg-hover` and lifts the label to `--button-ghost-fg-hover`. Use it for anything quieter than secondary: header `Sign in`, toolbar actions, `Cancel` beside a confirm, icon-only buttons.
|
|
118
|
+
- **Disabled:** colour drops to `--solid` at full opacity.
|
|
119
|
+
- A bordered button always fills (secondary). Border-only buttons read smaller beside solid fills and vanish on dark.
|
|
120
|
+
- Four sizes share one box per size across tiers: default 36px, `md` 32px, `sm` 28px, `xs` 24px. Read `--button-height*`, `--button-padding-x*`, `--button-label-size*`, `--button-tracking*`, `--button-icon-size`, `--button-gap`.
|
|
121
|
+
- A dark bar on a light page is a `.dark`-scoped region.
|
|
122
|
+
- The sticky top bar is `--nav-height` (`--nav-height-compact` on narrow viewports); its CTA is `md` via `--nav-cta-height`.
|
|
123
|
+
- TV buttons use the `tv` scale and `--radius-tv`; focus restores a visible box.
|
|
177
124
|
|
|
178
|
-
|
|
125
|
+
### Rows, fields, and panels
|
|
179
126
|
|
|
180
|
-
|
|
127
|
+
- File rows are the core component: raw names, compact metadata, obvious hover and focus at the same row height.
|
|
128
|
+
- Fields read `--field-bg`, `--field-border`, and `--field-ring` (focus). `aria-invalid="true"` turns border and text red; the fill stays.
|
|
129
|
+
- Raised panels read `--panel-*`.
|
|
130
|
+
- Feedback uses `.form-callout`: `--alert-*` colours, `data-state` `info`, `success`, or `error`; `.inline` drops surface and border. It holds a message only. `.form-group` (`6px` gap) spaces a control and its feedback.
|
|
181
131
|
|
|
182
|
-
|
|
132
|
+
### Auth
|
|
183
133
|
|
|
184
|
-
|
|
134
|
+
- A fluid column up to `340px`, the `36px` field recipe, a `30.33px` wordmark box (26px ink × 7/6), centred `19px`/500 headings, and a centred two-line prompt footer.
|
|
135
|
+
- Copy: “Sign in”, “Sign up”, and “That username or password doesn't look right” with no terminal punctuation.
|
|
136
|
+
- References: `web-s06-auth`, `web-p00c-auth-signin`, `web-p00d-auth-signup`, `web-p00e-auth-2fa`.
|
|
137
|
+
- One-time codes are one `.otp-input` (`inputmode="numeric"`, `autocomplete="one-time-code"`, `maxlength="6"`, `aria-invalid`) shown through two `.otp-group`s, one `.otp-separator`, and six `.otp-slot`s; script syncs slot text and `filled`/`active`, and maps a clicked slot to the input selection.
|
|
138
|
+
- Editing an invalid code clears the error; retry waits for six digits. `data-state="verifying"` disables the input, keeps the digits, and announces through a polite live region. `data-state="success"` keeps the digits read-only, announces continuation in a `role="status"` callout, and shows no further action; the consumer owns navigation.
|
|
139
|
+
- No recovery-code action until a consumer owns the route. Groups own the responsive width, so the control fits narrow cards without scrolling.
|
|
185
140
|
|
|
186
|
-
|
|
141
|
+
### TV
|
|
187
142
|
|
|
188
|
-
|
|
143
|
+
- TV is list-first and focus-first: a file browser, not a poster wall. TV repos consume the generic tokens and own their bindings.
|
|
144
|
+
- Surfaces are solid token colours, no blur or translucency. Focus is a fill: rows go to `--component-bg-active`; web TV buttons step fill and border at `tv.focus.borderWidth` (`3px`). Values live in `tv.focus.row.*` and `tv.focus.control.*`. Roku dialog buttons are borderless and follow their variant.
|
|
145
|
+
- Navigation is a Home hub pushing full screens; no tab bar. Menus are centred modals.
|
|
146
|
+
- These rules bind web TV and Roku. Native TV follows its platform where the platform owns focus; tvOS buttons use system focus, other surfaces stay solid.
|
|
147
|
+
- The `tv` token group (type, spacing, overscan, radius, focus, z) is adapter data, absent from web CSS: cite it by graph path, never `--tv-*`. Overscan multiplies `tv.overscan.x` by width and `tv.overscan.y` by height. `tv.z.overlay` sits above `tv.z.toast`, the reverse of web.
|
|
189
148
|
|
|
190
|
-
|
|
149
|
+
## Content and Icons
|
|
191
150
|
|
|
192
|
-
|
|
151
|
+
Show filenames exactly as stored, such as `The.Wire.S03E04.Back.Burners.1080p.BluRay.x264-DEMAND.mkv`. Icons are Phosphor-style inline SVG; product UI carries no emoji.
|
package/README.md
CHANGED
|
@@ -18,20 +18,12 @@
|
|
|
18
18
|
|
|
19
19
|
Live guide: [design.put.io](https://design.put.io)
|
|
20
20
|
|
|
21
|
-
This repo owns the public contract behind it: DTCG token sources, generated
|
|
22
|
-
CSS/JSON/TypeScript artifacts, brand assets, preview cards, and deployable
|
|
23
|
-
static guidance.
|
|
24
|
-
|
|
25
21
|
## Install
|
|
26
22
|
|
|
27
23
|
```bash
|
|
28
24
|
npm install @putdotio/design
|
|
29
25
|
```
|
|
30
26
|
|
|
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
|
-
|
|
35
27
|
## Use
|
|
36
28
|
|
|
37
29
|
Import the generated tokens first. Tier-1 web surfaces also import the
|
|
@@ -47,22 +39,22 @@ Component recipes consume semantic aliases such as `--field-*`, `--panel-*`,
|
|
|
47
39
|
|
|
48
40
|
Package entrypoints, from `exports` in [package.json](package.json):
|
|
49
41
|
|
|
50
|
-
| Subpath
|
|
51
|
-
|
|
|
52
|
-
| `@putdotio/design/css`
|
|
53
|
-
| `@putdotio/design/components`
|
|
54
|
-
| `@putdotio/design/tokens`
|
|
55
|
-
| `@putdotio/design/tokens/meta`
|
|
56
|
-
| `@putdotio/design/tokens/dtcg`
|
|
57
|
-
| `@putdotio/design/tokens/figma`
|
|
58
|
-
| `@putdotio/design/assets/<file>` | [`system/assets`](system/assets)
|
|
59
|
-
| `@putdotio/design/design.md`
|
|
42
|
+
| Subpath | File |
|
|
43
|
+
| -------------------------------- | ----------------------------------------------------------------------------------- |
|
|
44
|
+
| `@putdotio/design/css` | [`dist/css/tokens.css`](dist/css/tokens.css) |
|
|
45
|
+
| `@putdotio/design/components` | [`system/components.css`](system/components.css) |
|
|
46
|
+
| `@putdotio/design/tokens` | [`dist/tokens.flat.json`](dist/tokens.flat.json) |
|
|
47
|
+
| `@putdotio/design/tokens/meta` | [`dist/tokens.js`](dist/tokens.js), typed by [`dist/tokens.d.ts`](dist/tokens.d.ts) |
|
|
48
|
+
| `@putdotio/design/tokens/dtcg` | [`dist/tokens.dtcg.json`](dist/tokens.dtcg.json) |
|
|
49
|
+
| `@putdotio/design/tokens/figma` | [`dist/figma/putio.tokens.json`](dist/figma/putio.tokens.json) |
|
|
50
|
+
| `@putdotio/design/assets/<file>` | [`system/assets`](system/assets) |
|
|
51
|
+
| `@putdotio/design/design.md` | [`DESIGN.md`](DESIGN.md) |
|
|
60
52
|
|
|
61
53
|
## Docs
|
|
62
54
|
|
|
63
55
|
- [Design contract](DESIGN.md)
|
|
64
56
|
- [Design guide structure](system/README.md)
|
|
65
|
-
- [Distribution](docs/
|
|
57
|
+
- [Distribution](docs/distribution.md)
|
|
66
58
|
- [Contributing](CONTRIBUTING.md)
|
|
67
59
|
- [Security](https://github.com/putdotio/.github/blob/main/SECURITY.md)
|
|
68
60
|
- [Agent guide](AGENTS.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@putdotio/design",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"description": "Public put.io design tokens, brand assets, and design-system guidance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -68,24 +68,28 @@
|
|
|
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
|
+
"artifact": "node scripts/build-artifact.ts && node scripts/build-canvases.ts",
|
|
71
72
|
"test:config": "node --test tests/playwright-config.test.mjs",
|
|
72
73
|
"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",
|
|
73
74
|
"html:check": "html-validate \"system/**/*.html\"",
|
|
75
|
+
"markdown:check": "oxfmt --check \"**/*.md\"",
|
|
74
76
|
"e2e": "playwright test",
|
|
75
|
-
"verify": "pnpm typecheck:scripts && pnpm tokens:check && pnpm html:check",
|
|
77
|
+
"verify": "pnpm typecheck:scripts && pnpm tokens:check && pnpm artifact && pnpm html:check && pnpm markdown:check",
|
|
76
78
|
"verify:full": "pnpm verify && pnpm test:config && pnpm e2e && npm pack --dry-run",
|
|
77
79
|
"check": "pnpm verify",
|
|
78
80
|
"deploy:production": "sst deploy --stage production"
|
|
79
81
|
},
|
|
80
82
|
"devDependencies": {
|
|
81
83
|
"@axe-core/playwright": "^4.11.3",
|
|
84
|
+
"@phosphor-icons/web": "2.1.1",
|
|
82
85
|
"@playwright/test": "^1.60.0",
|
|
83
86
|
"@types/node": "^26.0.0",
|
|
84
87
|
"html-validate": "^11.2.0",
|
|
88
|
+
"oxfmt": "0.73.0",
|
|
85
89
|
"sst": "^4.14.3",
|
|
86
90
|
"style-dictionary": "^5.4.1",
|
|
87
91
|
"typescript": "^7.0.2",
|
|
88
92
|
"yaml": "^2.9.0"
|
|
89
93
|
},
|
|
90
|
-
"packageManager": "pnpm@12.
|
|
94
|
+
"packageManager": "pnpm@12.13.0"
|
|
91
95
|
}
|
package/system/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# put.io design system
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The guide site: generated site tokens, component CSS, preview cards, and the
|
|
4
|
+
design-system index.
|
|
4
5
|
|
|
5
6
|
## Layout
|
|
6
7
|
|
|
7
8
|
- `index.html` redirects to `design-system.html`, the guide and preview-card
|
|
8
|
-
index. `design-system-light.html`
|
|
9
|
+
index. `design-system-light.html` redirects to
|
|
9
10
|
`design-system.html?theme=light`.
|
|
10
|
-
- `tokens.css` is generated from `../tokens/**/*.tokens.json`;
|
|
11
|
+
- `tokens.css` is generated from `../tokens/**/*.tokens.json`; edit the token
|
|
12
|
+
sources and rebuild.
|
|
11
13
|
- `components.css` is the Tier-1 component layer (the web contract).
|
|
12
14
|
- `tv.css` is the TV / 10-foot component CSS, scoped to `.tv` artboards.
|
|
13
15
|
- `assets/` holds logos, favicons, retro marks, and app icons.
|
|
@@ -24,87 +26,84 @@ states, a spec strip, a don't block.
|
|
|
24
26
|
## Fonts
|
|
25
27
|
|
|
26
28
|
Preview pages load the public put.io font CSS from `static.put.io`, the same
|
|
27
|
-
source as the product
|
|
28
|
-
|
|
29
|
+
source as the product; font licensing is in
|
|
30
|
+
[Distribution](../docs/distribution.md#fonts-and-assets). Each preview links
|
|
31
|
+
both families in `<head>`:
|
|
29
32
|
|
|
30
33
|
```html
|
|
31
|
-
<link rel="stylesheet" href="https://static.put.io/fonts/gt-america/standard/font.css"
|
|
32
|
-
<link rel="stylesheet" href="https://static.put.io/fonts/berkeley-mono/variable/font.css"
|
|
34
|
+
<link rel="stylesheet" href="https://static.put.io/fonts/gt-america/standard/font.css" />
|
|
35
|
+
<link rel="stylesheet" href="https://static.put.io/fonts/berkeley-mono/variable/font.css" />
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
whose tabular figures carry the numerics.
|
|
37
|
-
`gt-america/mono/font.css` load.
|
|
38
|
+
Those two links are the full font set: mono text reads `--font-mono` (Berkeley
|
|
39
|
+
Mono), whose tabular figures carry the numerics.
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
`100 ultra-light · 200 thin · 300 light · 400 regular · 500 medium · 700 bold · 900 black
|
|
41
|
+
Weight names on the font host:
|
|
42
|
+
`100 ultra-light · 200 thin · 300 light · 400 regular · 500 medium · 700 bold · 900 black`.
|
|
43
|
+
The tokens use four of them through `--fw-regular`, `--fw-medium`, `--fw-bold`,
|
|
44
|
+
and `--fw-black`.
|
|
41
45
|
|
|
42
46
|
## Using tokens
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
Guide pages in `system/` load the generated site stylesheet:
|
|
45
49
|
|
|
46
50
|
```html
|
|
47
|
-
<link rel="stylesheet" href="./tokens.css"
|
|
51
|
+
<link rel="stylesheet" href="./tokens.css" />
|
|
48
52
|
```
|
|
49
53
|
|
|
50
54
|
The canonical source is DTCG-compatible JSON in [`../tokens`](../tokens). This
|
|
51
55
|
site stylesheet (`system/tokens.css`) and the package stylesheet
|
|
52
56
|
(`dist/css/tokens.css`, exported as `@putdotio/design/css`) are generated from
|
|
53
|
-
the same source.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
[
|
|
57
|
-
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
font-size is responsive; the canonical values live in
|
|
62
|
-
[`../DESIGN.md`](../DESIGN.md) (Layout & Spacing, Typography). Guide-specific
|
|
63
|
-
rule: CSS media queries can't read custom properties, so write the literal px
|
|
64
|
-
in `@media` and keep it in sync with the `--bp-*` token.
|
|
57
|
+
the same source. Brand yellow is `var(--yellow-solid)`; its emitted hsl form is
|
|
58
|
+
pinned in the [`../DESIGN.md`](../DESIGN.md) frontmatter, and the
|
|
59
|
+
`--button-primary-bg-hover` derivation is in
|
|
60
|
+
[Distribution](../docs/distribution.md#generated-files).
|
|
61
|
+
|
|
62
|
+
Stacking, breakpoints, the root font-size, and the `@media` literal rule live
|
|
63
|
+
in [Layout & Spacing](../DESIGN.md#layout--spacing) and
|
|
64
|
+
[Typography](../DESIGN.md#typography).
|
|
65
65
|
|
|
66
66
|
Package consumers import as in the [README](../README.md#use).
|
|
67
67
|
|
|
68
68
|
For **TV preview cards**, also load `tv.css`, which adds the `.tv`, `.scr`,
|
|
69
69
|
`.row`, solid surface tiers, and player chrome on top of the tokens, authored
|
|
70
70
|
1:1 at 1920×1080 (a px in the file is a px on the TV). Every component selector
|
|
71
|
-
is scoped under `.tv`, so the file
|
|
72
|
-
|
|
73
|
-
native UI stacks:
|
|
71
|
+
is scoped under `.tv`, so the file can be bundled globally alongside web
|
|
72
|
+
components. Platform repos implement TV components in their native UI stacks:
|
|
74
73
|
|
|
75
74
|
```html
|
|
76
|
-
<link rel="stylesheet" href="./tokens.css"
|
|
77
|
-
<link rel="stylesheet" href="./tv.css">
|
|
75
|
+
<link rel="stylesheet" href="./tokens.css" /> <link rel="stylesheet" href="./tv.css" />
|
|
78
76
|
```
|
|
79
77
|
|
|
80
78
|
TV material, focus, and the `tv` token group rules live in
|
|
81
|
-
[
|
|
82
|
-
`mode: "tv"` and read from `dist/tokens.flat.json`;
|
|
83
|
-
|
|
79
|
+
[Components](../DESIGN.md#components). Guide specifics: the `tv` group is
|
|
80
|
+
`mode: "tv"` and read from `dist/tokens.flat.json`; `--radius-tv` is the one
|
|
81
|
+
`tv` value the token CSS emits, so `tv.css` declares the `--tv-*` properties
|
|
82
|
+
its cards use itself.
|
|
84
83
|
|
|
85
84
|
## Core rules
|
|
86
85
|
|
|
87
|
-
- **Yellow
|
|
88
|
-
- **Yellow as text on light
|
|
89
|
-
- **Labels on yellow** use `--primary-foreground
|
|
90
|
-
- **
|
|
91
|
-
- **
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
- **Yellow** follows [Colors](../DESIGN.md#colors). Guide specifics: `--yellow-solid-hover` is the hover peer with one value in both modes, and button focus-visible rings use `--shadow-focus` (the yellow at 35% alpha, `--shadow-focus-color`). Form fields focus with the neutral `--field-ring` instead.
|
|
87
|
+
- **Yellow as text on light** uses `--yellow-text-secondary` (≈5:1, AA); `--yellow-solid` is fill-only on light backgrounds.
|
|
88
|
+
- **Labels on yellow** use `--primary-foreground`, a warm dark that passes AA on the yellow fill.
|
|
89
|
+
- **One design, two modes.** Light and dark are the same markup and components with different token values. Toggle `.dark` on `<html>`; one page serves both modes. Components consume semantic tokens (`--bg`, `--text`, `--border`, `--accent`, …) instead of raw `#hex` or `hsl()`.
|
|
90
|
+
- **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 them as-is, with no `translateY` nudge. Height-set placements size boxes at 7/6 of the ink height.
|
|
91
|
+
|
|
92
|
+
Icons, fields and panels, and raw filenames follow the root
|
|
93
|
+
[Components](../DESIGN.md#components) and
|
|
94
|
+
[Content and Icons](../DESIGN.md#content-and-icons).
|
|
95
95
|
|
|
96
96
|
## Binding tiers
|
|
97
97
|
|
|
98
98
|
Tier definitions, the Component/Element distinction, and the `preview/_tier.css`
|
|
99
|
-
pairing rule live in [
|
|
100
|
-
|
|
99
|
+
pairing rule live in [Binding Tiers](../DESIGN.md#binding-tiers). An Element
|
|
100
|
+
card shows how a stock control receives tokens.
|
|
101
101
|
|
|
102
102
|
Native cards are drawn 1:1 in their own unit (iPhone 393×852pt, iPad
|
|
103
103
|
1024×768pt, Apple Watch 176×215pt, Android 412×915dp, TV 1920×1080px), so a
|
|
104
104
|
number written on a card is the number in the HIG or the Material spec. Those
|
|
105
|
-
metrics are Apple's and Google's
|
|
106
|
-
|
|
107
|
-
deliberately un-tokenised.
|
|
105
|
+
metrics are Apple's and Google's and stay out of the put.io token graph. Device
|
|
106
|
+
bezels and on-video chrome stay untokenised.
|
|
108
107
|
|
|
109
108
|
## Theme System
|
|
110
109
|
|
|
@@ -114,47 +113,46 @@ The guide (`design-system.html`) owns the theme toggle. It writes
|
|
|
114
113
|
so embeds flip live without reloading. Each preview's inline bootstrap reads
|
|
115
114
|
the same storage key on standalone open, and `preview/_resize.js` listens for
|
|
116
115
|
the parent broadcast when embedded. A URL query (`?theme=light` or
|
|
117
|
-
`?theme=dark`) overrides both,
|
|
116
|
+
`?theme=dark`) overrides both, for screenshots.
|
|
118
117
|
|
|
119
|
-
Fixed-mode previews opt out with `<html data-theme-lock="dark"
|
|
120
|
-
|
|
118
|
+
Fixed-mode previews opt out with `<html data-theme-lock="dark">`, reserved for
|
|
119
|
+
product mockups whose visual language is intrinsically one mode. A 10-foot
|
|
121
120
|
screen, a native app mockup, and on-video player chrome are fixed-mode
|
|
122
121
|
surfaces, so the `ios-*`, `android-*`, `androidtv-*`, `tvos-*`, `watchos-*`,
|
|
123
|
-
`roku-*`, and `
|
|
124
|
-
previews ignore
|
|
125
|
-
New component previews
|
|
126
|
-
correctly in both modes.
|
|
122
|
+
`roku-*`, `tv-*`, and `mobile-*` cards plus `web-p06-player.html` are locked.
|
|
123
|
+
Locked previews ignore `localStorage`, parent broadcasts, and the URL query.
|
|
124
|
+
New component previews stay unlocked, so the tokens render them in both modes.
|
|
127
125
|
|
|
128
126
|
## shadcn / Base UI interop
|
|
129
127
|
|
|
130
128
|
The token layer aliases the canonical shadcn token names, so shadcn/ui blocks,
|
|
131
129
|
Base UI components, and Tailwind presets resolve against put.io values without
|
|
132
|
-
renaming.
|
|
130
|
+
renaming. Each shadcn name aliases a put.io token and carries no value of its
|
|
131
|
+
own, so the two name systems cannot drift.
|
|
133
132
|
|
|
134
|
-
| shadcn name
|
|
135
|
-
|
|
|
136
|
-
| `--primary` / `--primary-foreground`
|
|
133
|
+
| shadcn name | put.io target |
|
|
134
|
+
| -------------------------------------------- | ---------------------------------------------------------- |
|
|
135
|
+
| `--primary` / `--primary-foreground` | `--yellow-solid` / `var(--primary-foreground)` (warm dark) |
|
|
137
136
|
| `--destructive` / `--destructive-foreground` | `--red-solid` / `var(--destructive-foreground)` (= `#fff`) |
|
|
138
|
-
| `--success` / `--success-foreground`
|
|
139
|
-
| `--muted` / `--muted-foreground`
|
|
140
|
-
| `--accent` / `--accent-foreground`
|
|
141
|
-
| `--card` / `--card-foreground`
|
|
142
|
-
| `--popover` / `--popover-foreground`
|
|
143
|
-
| `--input`
|
|
144
|
-
| `--ring`
|
|
145
|
-
| `--background` / `--foreground`
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
`-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
put.io components prefer the long descriptive names (`--component-bg-hover`,
|
|
137
|
+
| `--success` / `--success-foreground` | `--green-solid` / `var(--success-foreground)` (= `#fff`) |
|
|
138
|
+
| `--muted` / `--muted-foreground` | `--bg-secondary` / `--text-secondary` |
|
|
139
|
+
| `--accent` / `--accent-foreground` | `--component-bg` / `--text` |
|
|
140
|
+
| `--card` / `--card-foreground` | `--component-bg` / `--text` |
|
|
141
|
+
| `--popover` / `--popover-foreground` | `--component-bg` / `--text` |
|
|
142
|
+
| `--input` | `--border` |
|
|
143
|
+
| `--ring` | `--shadow-focus-color` |
|
|
144
|
+
| `--background` / `--foreground` | `--bg` / `--text` |
|
|
145
|
+
|
|
146
|
+
A full `--input-*` set (`-bg`, `-border`, `-border-hover`, `-border-focus`,
|
|
147
|
+
`-ring`, `-text`, `-placeholder`, `-radius`) aliases the matching `--field-*`
|
|
148
|
+
tokens, so shadcn-shaped input code themes without renaming. **Author against
|
|
149
|
+
`--field-*`;** the `--input-*` names are only the alias bridge.
|
|
150
|
+
|
|
151
|
+
On a solid surface, read the foreground tokens (`--primary-foreground`,
|
|
152
|
+
`--destructive-foreground`, `--success-foreground`), which encode the mode-safe
|
|
153
|
+
inverse pair, instead of writing literal `#000` / `#fff`.
|
|
154
|
+
|
|
155
|
+
put.io components use the long descriptive names (`--component-bg-hover`,
|
|
158
156
|
`--yellow-text-secondary`) because they name the role exactly; shadcn-imported
|
|
159
157
|
code keeps the short names.
|
|
160
158
|
|
|
@@ -162,31 +160,32 @@ code keeps the short names.
|
|
|
162
160
|
|
|
163
161
|
Base UI / headless primitives set
|
|
164
162
|
`data-state="open|closed|checked|unchecked|active|completed|pending|loading"`
|
|
165
|
-
on the DOM.
|
|
166
|
-
wizard step bar in `web-p00b-form-flows.html`
|
|
167
|
-
(`.step[data-state="completed"]`) and the menu/table cards.
|
|
168
|
-
|
|
169
|
-
contract works with any controlling code and devtools show the state
|
|
163
|
+
on the DOM. put.io components target those same attribute values in CSS; see
|
|
164
|
+
the wizard step bar in `web-p00b-form-flows.html`
|
|
165
|
+
(`.step[data-state="completed"]`) and the menu/table cards. Express state with
|
|
166
|
+
`data-state` rather than class flags (`.is-active`, `.on`, `.current`), so the
|
|
167
|
+
same DOM contract works with any controlling code and devtools show the state
|
|
168
|
+
directly.
|
|
170
169
|
|
|
171
170
|
### Compound parts via `data-slot`
|
|
172
171
|
|
|
173
172
|
shadcn marks parts of compound components with `data-slot="card-header"`,
|
|
174
|
-
`data-slot="card-title"`,
|
|
173
|
+
`data-slot="card-title"`, and so on. put.io does the same on the folder picker
|
|
175
174
|
(`data-slot="modal" / "modal-header" / "modal-body" / "modal-footer"`) and the
|
|
176
175
|
auth cards in `web-p00c-auth-signin.html` and `web-p00d-auth-signup.html`
|
|
177
|
-
(`data-slot="auth" / "auth-header" / "auth-body"`)
|
|
178
|
-
nested class chains.
|
|
176
|
+
(`data-slot="auth" / "auth-header" / "auth-body"`), so external CSS can target
|
|
177
|
+
one part without nested class chains.
|
|
179
178
|
|
|
180
179
|
## Design variants
|
|
181
180
|
|
|
182
181
|
Four documented type stacks; pick one per surface:
|
|
183
182
|
|
|
184
|
-
| Variant
|
|
185
|
-
|
|
186
|
-
| Clean Modern | GT America
|
|
187
|
-
| Monospace
|
|
188
|
-
| Brutalist
|
|
189
|
-
| Editorial
|
|
183
|
+
| Variant | Stack | Use when |
|
|
184
|
+
| ------------ | -------------------------------------------------- | ------------------------------------ |
|
|
185
|
+
| Clean Modern | GT America | default app UI |
|
|
186
|
+
| Monospace | Berkeley Mono | terminal, logs, technical dashboards |
|
|
187
|
+
| Brutalist | GT America Black + Berkeley Mono | marketing punctuation |
|
|
188
|
+
| Editorial | GT America Black 900 (tight tracking) + GT America | landing hero, about |
|
|
190
189
|
|
|
191
190
|
## Preview cards
|
|
192
191
|
|