Public: anyone can read this; the crew can change it.

Design: tokenise style/components.ts, studio/home.ts, pages/looks.ts, pages/site.ts #2705

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/forge:mason/2698-design-tokenise-style-components-ts-stud → main 6 commits fork

  • Merged. mason/2698-design-tokenise-style-components-ts-stud was merged into main as f5bd8d4.

Review

Sign in to review this change.
4 files changed +43 −164
docs/design/baseline.json +4 −144
@@ -1,172 +1,32 @@
11 {
22 "files": {
3 "src/web/chrome.ts": {
4 "colour": 2,
5 "size": 0
6 },
7 "src/web/layout.ts": {
8 "colour": 1,
9 "size": 0
10 },
11 "src/web/pages/account.ts": {
12 "colour": 1,
13 "size": 0
14 },
15 "src/web/pages/apps.ts": {
16 "colour": 1,
17 "size": 0
18 },
193 "src/web/pages/board.ts": {
20 "colour": 1,
4 "colour": 0,
215 "size": 6
226 },
23 "src/web/pages/claim.ts": {
24 "colour": 1,
25 "size": 0
26 },
277 "src/web/pages/conflicts.ts": {
288 "colour": 0,
299 "size": 1
3010 },
31 "src/web/pages/everyday.ts": {
32 "colour": 1,
33 "size": 0
34 },
35 "src/web/pages/family.ts": {
36 "colour": 1,
37 "size": 0
38 },
39 "src/web/pages/forges.ts": {
40 "colour": 1,
41 "size": 0
42 },
43 "src/web/pages/limb.ts": {
44 "colour": 1,
45 "size": 0
46 },
47 "src/web/pages/limb_replan.ts": {
48 "colour": 1,
49 "size": 0
50 },
51 "src/web/pages/looks.ts": {
52 "colour": 6,
53 "size": 0
54 },
5511 "src/web/pages/map.ts": {
5612 "colour": 3,
5713 "size": 12
5814 },
59 "src/web/pages/milestones.ts": {
60 "colour": 2,
61 "size": 0
62 },
63 "src/web/pages/new-forge.ts": {
64 "colour": 1,
65 "size": 0
66 },
67 "src/web/pages/next-steps.ts": {
68 "colour": 2,
69 "size": 0
70 },
71 "src/web/pages/settings.ts": {
72 "colour": 1,
73 "size": 0
74 },
75 "src/web/pages/site.ts": {
76 "colour": 3,
77 "size": 0
78 },
7915 "src/web/standards.ts": {
8016 "colour": 2,
8117 "size": 0
8218 },
83 "src/web/studio/app.ts": {
84 "colour": 2,
85 "size": 0
86 },
87 "src/web/studio/asks.ts": {
88 "colour": 1,
89 "size": 0
90 },
91 "src/web/studio/asks_page.ts": {
92 "colour": 2,
93 "size": 0
94 },
95 "src/web/studio/change.ts": {
96 "colour": 2,
97 "size": 0
98 },
99 "src/web/studio/cost_line.ts": {
100 "colour": 1,
101 "size": 0
102 },
103 "src/web/studio/crew.ts": {
104 "colour": 1,
105 "size": 0
106 },
107 "src/web/studio/explore.ts": {
108 "colour": 2,
109 "size": 0
110 },
111 "src/web/studio/feed.ts": {
112 "colour": 1,
113 "size": 0
114 },
115 "src/web/studio/home.ts": {
116 "colour": 7,
117 "size": 0
118 },
119 "src/web/studio/home_tabs.ts": {
120 "colour": 2,
121 "size": 0
122 },
123 "src/web/studio/make_it_yours.ts": {
124 "colour": 1,
125 "size": 0
126 },
127 "src/web/studio/nouns.ts": {
128 "colour": 1,
129 "size": 0
130 },
131 "src/web/studio/now.ts": {
132 "colour": 1,
133 "size": 0
134 },
135 "src/web/studio/previews.ts": {
136 "colour": 2,
137 "size": 0
138 },
139 "src/web/studio/shelf.ts": {
140 "colour": 2,
141 "size": 0
142 },
14319 "src/web/studio/style.ts": {
14420 "colour": 2,
14521 "size": 1
14622 },
147 "src/web/studio/tell.ts": {
148 "colour": 1,
149 "size": 0
150 },
151 "src/web/studio/toolbar.ts": {
152 "colour": 2,
153 "size": 0
154 },
155 "src/web/studio/words.ts": {
156 "colour": 1,
157 "size": 0
158 },
15923 "src/web/style/components.ts": {
160 "colour": 5,
161 "size": 16
24 "colour": 3,
25 "size": 2
16226 },
16327 "src/web/style/content.ts": {
164 "colour": 23,
28 "colour": 19,
16529 "size": 31
166 },
167 "src/web/ui.ts": {
168 "colour": 1,
169 "size": 0
17030 }
17131 },
17232 "classes": [
scripts/design-lint.ts +13 −3
@@ -69,11 +69,21 @@ function cssBlocks(text: string): string[] {
6969 return out;
7070 }
7171
72 /**
73 * The text without its comments, so an issue reference (`#599`) is not read
74 * as a three-digit hex. A line comment starts a line or follows whitespace,
75 * which keeps `https://` in a string.
76 */
77 export function uncommented(text: string): string {
78 return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
79 }
80
7281 export function countFile(text: string): Counts {
73 const mixes = (text.match(/color-mix\([^;{}]*\)/g) ?? []).filter((m) => !m.includes("var(")).length;
82 const code = uncommented(text);
83 const mixes = (code.match(/color-mix\([^;{}]*\)/g) ?? []).filter((m) => !m.includes("var(")).length;
7484 const colour =
75 (text.match(/#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{3}\b(?![0-9a-zA-Z_-])/g) ?? []).length +
76 (text.match(/\b(?:rgba?|hsla?)\(/g) ?? []).length +
85 (code.match(/#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{3}\b(?![0-9a-zA-Z_-])/g) ?? []).length +
86 (code.match(/\b(?:rgba?|hsla?)\(/g) ?? []).length +
7787 cssBlocks(text).reduce((n, css) => n + (css.match(NAMED_RE) ?? []).length, 0) +
7888 mixes;
7989 let size = 0;
src/web/style/components.ts +19 −17
@@ -208,17 +208,19 @@ kbd {
208208 min-width: 20px; height: 20px; padding: 0 5px;
209209 border: 1px solid var(--border); border-bottom-width: 2px;
210210 border-radius: var(--radius-sm); background: var(--muted);
211 font-family: var(--mono); font-size: 11px; font-weight: 500; color: var(--muted-fg);
211 font-family: var(--mono); font-size: 11.5px; font-weight: 500; color: var(--muted-fg);
212212 }
213213
214214 .sha {
215 font-family: var(--mono); font-size: 12px; padding: 1px 5px;
215 font-family: var(--mono); font-size: 12.5px; padding: 1px 5px;
216216 border-radius: 4px; background: var(--muted); color: var(--muted-fg);
217217 font-variant-numeric: tabular-nums;
218218 }
219219 a.sha:hover { color: var(--accent); }
220220
221 /* ---- avatar ---- */
221 /* ---- avatar ----
222 The hue is the person's (--hue), so it is mixed here, not a palette token;
223 the 20px and 16px circles' initials sit below the type scale on purpose. */
222224 .avatar {
223225 display: inline-flex; align-items: center; justify-content: center; flex: none;
224226 width: 20px; height: 20px; border-radius: 999px;
@@ -228,14 +230,14 @@ a.sha:hover { color: var(--accent); }
228230 font-size: 9.5px; font-weight: 650; letter-spacing: 0; text-transform: uppercase;
229231 user-select: none;
230232 }
231 .avatar[data-size="md"] { width: 28px; height: 28px; font-size: 12px; }
232 .avatar[data-size="lg"] { width: 56px; height: 56px; font-size: 22px; border-radius: var(--radius-lg); }
233 .avatar[data-size="md"] { width: 28px; height: 28px; font-size: 12.5px; }
234 .avatar[data-size="lg"] { width: 56px; height: 56px; font-size: 20px; border-radius: var(--radius-lg); }
233235
234236 .byline { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
235237 .byline a:hover { color: var(--accent); }
236238 .name-mark {
237239 display: inline-block; padding: 0 5px; border-radius: 4px;
238 font-size: 10px; font-weight: 600; line-height: 15px; letter-spacing: 0.04em;
240 font-size: 11.5px; font-weight: 600; line-height: 15px; letter-spacing: 0.04em;
239241 text-transform: uppercase; white-space: nowrap; vertical-align: 1px;
240242 color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
241243 }
@@ -350,7 +352,7 @@ a.row:hover .row-title { color: var(--accent); }
350352 /* ---- table ---- */
351353 .table { width: 100%; font-size: 13px; }
352354 .table th {
353 text-align: left; font-weight: 550; font-size: 12px; color: var(--muted-fg);
355 text-align: left; font-weight: 550; font-size: 12.5px; color: var(--muted-fg);
354356 padding: 8px 12px; border-bottom: 1px solid var(--border); white-space: nowrap;
355357 }
356358 .table td {
@@ -367,7 +369,7 @@ a.row:hover .row-title { color: var(--accent); }
367369 border-bottom: 1px solid var(--border);
368370 overflow-x: auto; scrollbar-width: none;
369371 scroll-snap-type: x proximity;
370 mask-image: linear-gradient(90deg, #000 0 calc(100% - 20px), transparent);
372 mask-image: linear-gradient(90deg, var(--fg) 0 calc(100% - 20px), transparent);
371373 }
372374 .tabs::-webkit-scrollbar { display: none; }
373375 .tabs > a {
@@ -429,7 +431,7 @@ a.row:hover .row-title { color: var(--accent); }
429431 @keyframes pop { from { opacity: 0; transform: translateY(-4px); } }
430432 .menu-title {
431433 display: block; padding: 6px 10px 4px;
432 font-size: 11px; font-weight: 600; letter-spacing: .08em;
434 font-size: 11.5px; font-weight: 600; letter-spacing: .08em;
433435 text-transform: uppercase; color: var(--faint);
434436 }
435437 .menu-item {
@@ -518,7 +520,7 @@ a.row:hover .row-title { color: var(--accent); }
518520
519521 /* ---- tell the crew, in place: fixed, so nothing shifts ---- */
520522 .tell-here { position: fixed; left: 0; right: 0; bottom: 0; z-index: 50; width: min(620px, 100%); margin: 0 auto; padding: 8px; }
521 .tell-here .comment-form { margin: 0; box-shadow: var(--shadow-lg, 0 8px 24px rgb(0 0 0 / .25)); }
523 .tell-here .comment-form { margin: 0; box-shadow: var(--shadow-2); }
522524
523525 /* ---- command palette ---- */
524526 /* A dialog centres itself with its own auto margins, which the reset zeroes
@@ -541,7 +543,7 @@ a.row:hover .row-title { color: var(--accent); }
541543 .palette-group + .palette-group { margin-top: 4px; }
542544 .palette-heading {
543545 padding: 8px 10px 4px;
544 font-size: 11px; font-weight: 600; letter-spacing: .08em;
546 font-size: 11.5px; font-weight: 600; letter-spacing: .08em;
545547 text-transform: uppercase; color: var(--faint);
546548 }
547549 .palette-item {
@@ -555,14 +557,14 @@ a.row:hover .row-title { color: var(--accent); }
555557 .palette-item .cat {
556558 flex: none; max-width: 45%; margin-left: auto;
557559 overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
558 font-size: 12px; color: var(--faint);
560 font-size: 12.5px; color: var(--faint);
559561 }
560562 /* One highlight. The pointer moves it (the script listens) rather than
561563 painting a second one under the cursor; the finder keeps plain hover. */
562564 .palette-item[aria-selected="true"], .finder .palette-item:hover { background: var(--muted); }
563565 .palette-foot {
564566 display: flex; align-items: center; gap: 10px; padding: 8px 14px;
565 border-top: 1px solid var(--border); font-size: 12px; color: var(--faint);
567 border-top: 1px solid var(--border); font-size: 12.5px; color: var(--faint);
566568 }
567569 .palette-empty {
568570 display: flex; align-items: center; justify-content: center; gap: 6px;
@@ -575,7 +577,7 @@ a.row:hover .row-title { color: var(--accent); }
575577 margin: 16px 0 0; padding: 10px 12px;
576578 border: 1px solid var(--border); border-radius: var(--radius);
577579 background: var(--card); box-shadow: var(--shadow-1);
578 font-size: 13.5px;
580 font-size: 14px;
579581 animation: pop 160ms var(--ease) both;
580582 }
581583 .toast .ico { margin-top: 1px; }
@@ -604,7 +606,7 @@ a.row:hover .row-title { color: var(--accent); }
604606 bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%);
605607 padding: 4px 8px; border-radius: var(--radius-sm);
606608 background: var(--primary); color: var(--primary-fg);
607 font-size: 12px; font-weight: 500; white-space: nowrap;
609 font-size: 12.5px; font-weight: 500; white-space: nowrap;
608610 opacity: 0; pointer-events: none; transition: opacity 120ms var(--ease);
609611 }
610612 [data-tip][data-tip-side="bottom"]::after { bottom: auto; top: calc(100% + 6px); }
@@ -683,7 +685,7 @@ a.row:hover .row-title { color: var(--accent); }
683685 .settings-nav a {
684686 display: inline-flex; align-items: center; gap: 6px;
685687 padding: 0 12px; min-height: 36px; white-space: nowrap;
686 font-size: 13.5px; color: var(--muted-fg); border-radius: var(--radius-sm);
688 font-size: 14px; color: var(--muted-fg); border-radius: var(--radius-sm);
687689 }
688690 .settings-nav a:hover { background: var(--muted); color: var(--fg); }
689691 .settings-nav a[aria-current="page"] { color: var(--fg); font-weight: 550; background: var(--muted); }
@@ -699,7 +701,7 @@ a.row:hover .row-title { color: var(--accent); }
699701 /* ---- clone strip ---- */
700702 .clone {
701703 display: flex; align-items: center; gap: 6px;
702 font-family: var(--mono); font-size: 12px;
704 font-family: var(--mono); font-size: 12.5px;
703705 padding: 6px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
704706 background: var(--muted); min-width: 0;
705707 }
test/design-lint.test.ts +7
@@ -16,6 +16,13 @@ describe("design check: nothing added outside the system (#2678)", () => {
1616 expect(countFile("a{color:var(--fg)} // issue #2678")).toEqual({ colour: 0, size: 0 });
1717 });
1818
19 test("an issue reference in a comment is not a colour; the same digits in code are", () => {
20 expect(countFile("// the feed (#599), whatever the mode (#625)\nx = 1;").colour).toBe(0);
21 expect(countFile("/**\n * Who may fetch a picture (#727, #738).\n */\nx = 1;").colour).toBe(0);
22 expect(countFile("a{color:#999}").colour).toBe(1);
23 expect(countFile('const u = "https://x.test/#abc";').colour).toBe(1);
24 });
25
1926 test("finds the classes a css template defines, and only those", () => {
2027 const text = "const x = a.ts; const css = /* css */ `.btn:hover, .card > .row-title { color: red }\n.new-thing { top: 0.5px }`; foo.bar";
2128 expect(classesIn(text).sort()).toEqual(["btn", "card", "new-thing", "row-title"]);

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs