@putdotio/design 0.1.0-alpha.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 ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@putdotio/design",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Public put.io design tokens, generated token artifacts, and design-system guidance.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "homepage": "https://design.put.io",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/putdotio/putio-design.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/putdotio/putio-design/issues"
14
+ },
15
+ "keywords": [
16
+ "putio",
17
+ "design-system",
18
+ "design-tokens",
19
+ "dtcg"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "files": [
25
+ "DESIGN.md",
26
+ "dist",
27
+ "tokens",
28
+ "system/README.md"
29
+ ],
30
+ "exports": {
31
+ "./css": "./dist/css/tokens.css",
32
+ "./tokens": "./dist/tokens.flat.json",
33
+ "./tokens/meta": {
34
+ "types": "./dist/tokens.d.ts",
35
+ "default": "./dist/tokens.js"
36
+ },
37
+ "./tokens/dtcg": "./dist/tokens.dtcg.json",
38
+ "./tokens/figma": "./dist/figma/putio.tokens.json",
39
+ "./design.md": "./DESIGN.md"
40
+ },
41
+ "scripts": {
42
+ "dev": "node scripts/serve-system.ts",
43
+ "tokens:build": "node scripts/build-tokens.ts",
44
+ "tokens:check": "pnpm tokens:build && node scripts/verify-design-system.ts && git diff --exit-code -- DESIGN.md dist system/tokens.css",
45
+ "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",
46
+ "smoke": "node scripts/smoke-static.ts",
47
+ "html:check": "html-validate \"system/**/*.html\"",
48
+ "e2e": "playwright test",
49
+ "verify": "pnpm typecheck:scripts && pnpm tokens:check && pnpm html:check && pnpm smoke && sst version",
50
+ "verify:full": "pnpm verify && pnpm e2e && npm pack --dry-run",
51
+ "check": "pnpm verify",
52
+ "deploy:production": "sst deploy --stage production"
53
+ },
54
+ "devDependencies": {
55
+ "@axe-core/playwright": "^4.11.3",
56
+ "@playwright/test": "^1.60.0",
57
+ "@types/node": "^25.9.1",
58
+ "html-validate": "^11.2.0",
59
+ "sst": "^4.14.3",
60
+ "style-dictionary": "^5.4.1",
61
+ "typescript": "^6.0.2",
62
+ "yaml": "^2.9.0"
63
+ },
64
+ "packageManager": "pnpm@11.2.2"
65
+ }
@@ -0,0 +1,84 @@
1
+ # put.io design system
2
+
3
+ This is the `system/` folder inside [`putdotio/putio-design`](https://github.com/putdotio/putio-design). It holds the **tokens**, **preview cards**, and an **index** of the design system.
4
+
5
+ ## Layout
6
+
7
+ ```
8
+ project root/
9
+ └── system/
10
+ ├── index.html ← redirects to the design-system guide
11
+ ├── design-system.html ← guide and preview-card index
12
+ ├── design-system-light.html ← legacy redirect to design-system.html?theme=light
13
+ ├── tokens.css ← generated from ../tokens/**/*.tokens.json
14
+ ├── assets/ ← logos, favicons, retro marks, app icons
15
+ └── preview/ ← atomic review cards for foundations + components + brand + platforms
16
+ └── tv-shell.css ← TV / 10ft component CSS, scoped to .tv artboards (preview-only)
17
+ ```
18
+
19
+ ## Fonts
20
+
21
+ Fonts are loaded directly from `static.put.io`, so the design system uses the same public type families as put.io product surfaces. Each HTML file links three stylesheets in `<head>`:
22
+
23
+ ```html
24
+ <link rel="stylesheet" href="https://static.put.io/fonts/gt-america/standard/font.css">
25
+ <link rel="stylesheet" href="https://static.put.io/fonts/gt-america/mono/font.css">
26
+ <link rel="stylesheet" href="https://static.put.io/fonts/berkeley-mono/variable/font.css">
27
+ ```
28
+
29
+ Canonical weight mapping (matches `putdotio/putio-static`):
30
+ `100 ultra-light · 200 thin · 300 light · 400 regular · 500 medium · 700 bold · 900 black`
31
+
32
+ ## Using tokens
33
+
34
+ ```html
35
+ <link rel="stylesheet" href="system/tokens.css">
36
+ ```
37
+
38
+ The canonical source is DTCG-compatible JSON in [`../tokens`](../tokens). This
39
+ CSS file is generated for the design site and package consumers. Everything else
40
+ is a variable. The only hard-coded value you should ever write is `#FDCE45` —
41
+ and even that is exposed as `var(--yellow-solid)`.
42
+
43
+ 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:
44
+
45
+ ```html
46
+ <link rel="stylesheet" href="system/tokens.css">
47
+ <link rel="stylesheet" href="system/preview/tv-shell.css">
48
+ ```
49
+
50
+ ## Core rules
51
+
52
+ - **Yellow `#FDCE45` is sacred.** Never gradient, tint, or replace. Primary CTAs, folder icons, focus rings (at 35% alpha), progress bars. `--yellow-solid` is the brand value; for yellow text on light backgrounds use `--yellow-text-secondary` instead.
53
+ - **Icons: Phosphor-style inline SVG.** No emoji, ever.
54
+ - **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.
55
+ - **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.
56
+
57
+ ## Theme System
58
+
59
+ The guide owns the theme toggle. It stores `putio-ds-theme` in `localStorage`,
60
+ updates `html.dark`, and broadcasts the current mode to embedded preview iframes.
61
+ Standalone preview pages read the same storage key before paint. A URL query
62
+ (`?theme=light` or `?theme=dark`) can override the preview mode for screenshots.
63
+
64
+ Fixed-mode previews opt out with `<html data-theme-lock="dark">`. This is only
65
+ for product mockups whose visual language is intrinsically one mode, such as TV
66
+ and mobile shell specimens. Component previews should stay token-driven and work
67
+ in both modes.
68
+
69
+ ## Design variants
70
+
71
+ Four documented type stacks — pick one per surface:
72
+
73
+ | Variant | Stack | Use when |
74
+ |---------|-------|----------|
75
+ | Clean Modern | GT America | default app UI |
76
+ | Monospace | Berkeley Mono | terminal, logs, technical dashboards |
77
+ | Brutalist | GT America Black + Berkeley Mono | marketing punctuation |
78
+ | Editorial | GT America Black 900 (tight tracking) + GT America | landing hero, about |
79
+
80
+ ## Preview cards
81
+
82
+ Open `system/design-system.html` for the full index. Each card renders a single concept against the actual tokens — read it like a spec.
83
+
84
+ See the root [`AGENTS.md`](../AGENTS.md) for naming conventions and the rules agents must follow.
@@ -0,0 +1,229 @@
1
+ {
2
+ "$description": "Component-level aliases. Platform repos may map these to native controls, but this repo only emits generic tokens.",
3
+ "component": {
4
+ "button": {
5
+ "primary": {
6
+ "background": {
7
+ "$type": "color",
8
+ "$value": "{color.brand.yellow}",
9
+ "$description": "Primary command background.",
10
+ "$extensions": {
11
+ "putio": {
12
+ "cssName": "button-primary-bg",
13
+ "mode": "global"
14
+ }
15
+ }
16
+ },
17
+ "foreground": {
18
+ "$type": "color",
19
+ "$value": "#0A0A0A",
20
+ "$description": "Text or icon color on the yellow primary button.",
21
+ "$extensions": {
22
+ "putio": {
23
+ "cssName": "button-primary-fg",
24
+ "mode": "global"
25
+ }
26
+ }
27
+ },
28
+ "radius": {
29
+ "$type": "dimension",
30
+ "$value": "{radius.default}",
31
+ "$extensions": {
32
+ "putio": {
33
+ "cssName": "button-radius",
34
+ "mode": "global"
35
+ }
36
+ }
37
+ }
38
+ },
39
+ "secondary": {
40
+ "background": {
41
+ "$type": "color",
42
+ "$value": "{color.neutral.light.componentBg}",
43
+ "$extensions": {
44
+ "putio": {
45
+ "cssName": "button-secondary-bg",
46
+ "mode": "light"
47
+ }
48
+ }
49
+ },
50
+ "backgroundDark": {
51
+ "$type": "color",
52
+ "$value": "{color.neutral.dark.componentBg}",
53
+ "$extensions": {
54
+ "putio": {
55
+ "cssName": "button-secondary-bg",
56
+ "mode": "dark"
57
+ }
58
+ }
59
+ },
60
+ "foreground": {
61
+ "$type": "color",
62
+ "$value": "{color.neutral.light.text}",
63
+ "$extensions": {
64
+ "putio": {
65
+ "cssName": "button-secondary-fg",
66
+ "mode": "light"
67
+ }
68
+ }
69
+ },
70
+ "foregroundDark": {
71
+ "$type": "color",
72
+ "$value": "{color.neutral.dark.text}",
73
+ "$extensions": {
74
+ "putio": {
75
+ "cssName": "button-secondary-fg",
76
+ "mode": "dark"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ },
82
+ "input": {
83
+ "background": {
84
+ "$type": "color",
85
+ "$value": "{surface.light.appBg}",
86
+ "$extensions": {
87
+ "putio": {
88
+ "cssName": "input-bg",
89
+ "mode": "light"
90
+ }
91
+ }
92
+ },
93
+ "backgroundDark": {
94
+ "$type": "color",
95
+ "$value": "{color.neutral.dark.componentBg}",
96
+ "$extensions": {
97
+ "putio": {
98
+ "cssName": "input-bg",
99
+ "mode": "dark"
100
+ }
101
+ }
102
+ },
103
+ "border": {
104
+ "$type": "color",
105
+ "$value": "{color.neutral.light.border}",
106
+ "$extensions": {
107
+ "putio": {
108
+ "cssName": "input-border",
109
+ "mode": "light"
110
+ }
111
+ }
112
+ },
113
+ "borderDark": {
114
+ "$type": "color",
115
+ "$value": "{color.neutral.dark.border}",
116
+ "$extensions": {
117
+ "putio": {
118
+ "cssName": "input-border",
119
+ "mode": "dark"
120
+ }
121
+ }
122
+ },
123
+ "radius": {
124
+ "$type": "dimension",
125
+ "$value": "{radius.default}",
126
+ "$extensions": {
127
+ "putio": {
128
+ "cssName": "input-radius",
129
+ "mode": "global"
130
+ }
131
+ }
132
+ }
133
+ },
134
+ "fileRow": {
135
+ "background": {
136
+ "$type": "color",
137
+ "$value": "{surface.light.listItemBg}",
138
+ "$extensions": {
139
+ "putio": {
140
+ "cssName": "file-row-bg",
141
+ "mode": "light"
142
+ }
143
+ }
144
+ },
145
+ "backgroundDark": {
146
+ "$type": "color",
147
+ "$value": "{surface.dark.listItemBg}",
148
+ "$extensions": {
149
+ "putio": {
150
+ "cssName": "file-row-bg",
151
+ "mode": "dark"
152
+ }
153
+ }
154
+ },
155
+ "hoverBackground": {
156
+ "$type": "color",
157
+ "$value": "{surface.light.listItemBgHover}",
158
+ "$extensions": {
159
+ "putio": {
160
+ "cssName": "file-row-bg-hover",
161
+ "mode": "light"
162
+ }
163
+ }
164
+ },
165
+ "hoverBackgroundDark": {
166
+ "$type": "color",
167
+ "$value": "{surface.dark.listItemBgHover}",
168
+ "$extensions": {
169
+ "putio": {
170
+ "cssName": "file-row-bg-hover",
171
+ "mode": "dark"
172
+ }
173
+ }
174
+ },
175
+ "icon": {
176
+ "$type": "color",
177
+ "$value": "{color.brand.yellow}",
178
+ "$extensions": {
179
+ "putio": {
180
+ "cssName": "file-row-icon",
181
+ "mode": "global"
182
+ }
183
+ }
184
+ }
185
+ },
186
+ "notification": {
187
+ "infoBackground": {
188
+ "$type": "color",
189
+ "$value": "{color.neutral.light.bgSecondary}",
190
+ "$extensions": {
191
+ "putio": {
192
+ "cssName": "notification-info-bg",
193
+ "mode": "light"
194
+ }
195
+ }
196
+ },
197
+ "infoBackgroundDark": {
198
+ "$type": "color",
199
+ "$value": "{color.neutral.dark.componentBg}",
200
+ "$extensions": {
201
+ "putio": {
202
+ "cssName": "notification-info-bg",
203
+ "mode": "dark"
204
+ }
205
+ }
206
+ },
207
+ "dangerBackground": {
208
+ "$type": "color",
209
+ "$value": "{color.red.light.componentBg}",
210
+ "$extensions": {
211
+ "putio": {
212
+ "cssName": "notification-danger-bg",
213
+ "mode": "light"
214
+ }
215
+ }
216
+ },
217
+ "dangerBackgroundDark": {
218
+ "$type": "color",
219
+ "$value": "{color.red.dark.componentBg}",
220
+ "$extensions": {
221
+ "putio": {
222
+ "cssName": "notification-danger-bg",
223
+ "mode": "dark"
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
@@ -0,0 +1,155 @@
1
+ {
2
+ "$description": "Shared TV context tokens. Consuming TV repos generate their own platform artifacts from these values.",
3
+ "context": {
4
+ "tv": {
5
+ "surface": {
6
+ "0": {
7
+ "$type": "color",
8
+ "$value": "#000",
9
+ "$extensions": {
10
+ "putio": {
11
+ "cssName": "surf-0",
12
+ "mode": "tv"
13
+ }
14
+ }
15
+ },
16
+ "1": {
17
+ "$type": "color",
18
+ "$value": "#0a0a0a",
19
+ "$extensions": {
20
+ "putio": {
21
+ "cssName": "surf-1",
22
+ "mode": "tv"
23
+ }
24
+ }
25
+ },
26
+ "2": {
27
+ "$type": "color",
28
+ "$value": "#161616",
29
+ "$extensions": {
30
+ "putio": {
31
+ "cssName": "surf-2",
32
+ "mode": "tv"
33
+ }
34
+ }
35
+ },
36
+ "3": {
37
+ "$type": "color",
38
+ "$value": "#1f1f1f",
39
+ "$extensions": {
40
+ "putio": {
41
+ "cssName": "surf-3",
42
+ "mode": "tv"
43
+ }
44
+ }
45
+ }
46
+ },
47
+ "text": {
48
+ "primary": {
49
+ "$type": "color",
50
+ "$value": "#f4f4f4",
51
+ "$extensions": {
52
+ "putio": {
53
+ "cssName": "text-1",
54
+ "mode": "tv"
55
+ }
56
+ }
57
+ },
58
+ "secondary": {
59
+ "$type": "color",
60
+ "$value": "rgba(244,244,244,0.62)",
61
+ "$extensions": {
62
+ "putio": {
63
+ "cssName": "text-2",
64
+ "mode": "tv"
65
+ }
66
+ }
67
+ },
68
+ "tertiary": {
69
+ "$type": "color",
70
+ "$value": "rgba(244,244,244,0.38)",
71
+ "$extensions": {
72
+ "putio": {
73
+ "cssName": "text-3",
74
+ "mode": "tv"
75
+ }
76
+ }
77
+ }
78
+ },
79
+ "glass": {
80
+ "1": {
81
+ "$type": "color",
82
+ "$value": "rgba(255,255,255,0.06)",
83
+ "$extensions": {
84
+ "putio": {
85
+ "cssName": "glass-1-bg",
86
+ "mode": "tv"
87
+ }
88
+ }
89
+ },
90
+ "2": {
91
+ "$type": "color",
92
+ "$value": "rgba(255,255,255,0.10)",
93
+ "$extensions": {
94
+ "putio": {
95
+ "cssName": "glass-2-bg",
96
+ "mode": "tv"
97
+ }
98
+ }
99
+ },
100
+ "3": {
101
+ "$type": "color",
102
+ "$value": "rgba(255,255,255,0.14)",
103
+ "$extensions": {
104
+ "putio": {
105
+ "cssName": "glass-3-bg",
106
+ "mode": "tv"
107
+ }
108
+ }
109
+ },
110
+ "border": {
111
+ "$type": "color",
112
+ "$value": "rgba(255,255,255,0.12)",
113
+ "$extensions": {
114
+ "putio": {
115
+ "cssName": "glass-border",
116
+ "mode": "tv"
117
+ }
118
+ }
119
+ },
120
+ "borderStrong": {
121
+ "$type": "color",
122
+ "$value": "rgba(255,255,255,0.22)",
123
+ "$extensions": {
124
+ "putio": {
125
+ "cssName": "glass-border-strong",
126
+ "mode": "tv"
127
+ }
128
+ }
129
+ }
130
+ },
131
+ "focus": {
132
+ "ring": {
133
+ "$type": "color",
134
+ "$value": "rgba(253,206,69,0.18)",
135
+ "$extensions": {
136
+ "putio": {
137
+ "cssName": "focus-ring",
138
+ "mode": "tv"
139
+ }
140
+ }
141
+ },
142
+ "liftShadow": {
143
+ "$type": "string",
144
+ "$value": "0 14px 30px rgba(0,0,0,0.45)",
145
+ "$extensions": {
146
+ "putio": {
147
+ "cssName": "focus-shadow",
148
+ "mode": "tv"
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,133 @@
1
+ /* -- Document base ------------------------------------------------- */
2
+ html {
3
+ background: var(--html-bg);
4
+ color: var(--text);
5
+ font-family: var(--font-sans);
6
+ font-size: 15px; /* 14–15 responsive base, per app convention */
7
+ line-height: var(--lh-normal);
8
+ -webkit-font-smoothing: antialiased;
9
+ -moz-osx-font-smoothing: grayscale;
10
+ text-rendering: optimizeLegibility;
11
+ }
12
+ body {
13
+ background: var(--app-bg);
14
+ color: var(--text);
15
+ margin: 0;
16
+ }
17
+
18
+ /* -- Semantic type ------------------------------------------------- */
19
+
20
+ .h-display, h1.display {
21
+ font-family: var(--font-display);
22
+ font-size: var(--fs-display);
23
+ font-weight: var(--fw-black);
24
+ line-height: 0.95;
25
+ letter-spacing: var(--tracking-tight);
26
+ text-transform: uppercase;
27
+ margin: 0;
28
+ }
29
+
30
+ /* Numeric / tabular UI text — transfer speeds, storage sizes, timestamps */
31
+ .num, .tabular {
32
+ font-family: var(--font-ui-mono);
33
+ font-variant-numeric: tabular-nums;
34
+ font-feature-settings: "tnum";
35
+ }
36
+
37
+ h1, .h1 {
38
+ font-family: var(--font-sans);
39
+ font-size: var(--fs-3xl);
40
+ font-weight: var(--fw-bold);
41
+ line-height: var(--lh-tight);
42
+ letter-spacing: var(--tracking-tight);
43
+ margin: 0 0 var(--space-3);
44
+ }
45
+
46
+ h2, .h2 {
47
+ font-family: var(--font-sans);
48
+ font-size: var(--fs-2xl);
49
+ font-weight: var(--fw-bold);
50
+ line-height: var(--lh-tight);
51
+ margin: 0 0 var(--space-3);
52
+ }
53
+
54
+ h3, .h3 {
55
+ font-family: var(--font-sans);
56
+ font-size: var(--fs-xl);
57
+ font-weight: var(--fw-medium);
58
+ line-height: var(--lh-snug);
59
+ margin: 0 0 var(--space-2);
60
+ }
61
+
62
+ h4, .h4 {
63
+ font-family: var(--font-sans);
64
+ font-size: var(--fs-lg);
65
+ font-weight: var(--fw-medium);
66
+ line-height: var(--lh-snug);
67
+ margin: 0 0 var(--space-2);
68
+ }
69
+
70
+ .subheading {
71
+ font-size: var(--fs-md);
72
+ font-weight: var(--fw-medium);
73
+ color: var(--text);
74
+ }
75
+
76
+ p, .body {
77
+ font-family: var(--font-sans);
78
+ font-size: var(--fs-base);
79
+ font-weight: var(--fw-regular);
80
+ line-height: var(--lh-normal);
81
+ margin: 0 0 var(--space-3);
82
+ }
83
+
84
+ .lead {
85
+ font-size: var(--fs-lg);
86
+ font-weight: var(--fw-regular);
87
+ line-height: var(--lh-normal);
88
+ color: var(--text-secondary);
89
+ }
90
+
91
+ small, .caption {
92
+ font-size: var(--fs-xs);
93
+ color: var(--text-secondary);
94
+ }
95
+
96
+ .label {
97
+ font-size: var(--fs-sm);
98
+ font-weight: var(--fw-medium);
99
+ color: var(--text);
100
+ }
101
+
102
+ .button-text {
103
+ font-weight: var(--fw-medium);
104
+ font-size: var(--fs-base);
105
+ letter-spacing: 0;
106
+ }
107
+
108
+ code, kbd, .mono {
109
+ font-family: var(--font-mono);
110
+ font-size: 0.92em;
111
+ }
112
+
113
+ a {
114
+ color: inherit;
115
+ text-decoration: underline;
116
+ text-underline-offset: 2px;
117
+ text-decoration-thickness: 1px;
118
+ text-decoration-color: var(--border-hover);
119
+ transition: text-decoration-color var(--dur-fast) var(--ease-out);
120
+ }
121
+ a:hover { text-decoration-color: currentColor; }
122
+
123
+ /* -- Utility classes ---------------------------------------------- */
124
+ .fg-1 { color: var(--text); }
125
+ .fg-2 { color: var(--text-secondary); }
126
+ .fg-yellow { color: var(--yellow-text-secondary); }
127
+ .fg-green { color: var(--green-text-secondary); }
128
+ .fg-red { color: var(--red-text-secondary); }
129
+
130
+ .bg-1 { background: var(--bg); }
131
+ .bg-2 { background: var(--bg-secondary); }
132
+ .bg-component { background: var(--component-bg); }
133
+ .bg-yellow { background: var(--yellow-solid); color: #000; }