@wtfalch/design 0.2.0 → 0.3.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/README.md CHANGED
@@ -27,38 +27,70 @@ Where a theme genuinely needs a layer that values cannot reach — a paper grain
27
27
  a vignette — the base CSS pre-declares the slot and the theme fills it. The rule
28
28
  is always ours.
29
29
 
30
- ## Writing a theme
30
+ ## Products
31
+
32
+ A site is one product, so it imports its product and everything it touches is
33
+ its own:
31
34
 
32
35
  ```ts
33
- import { applyTheme, defineTheme } from '@wtfalch/design'
34
-
35
- export const brand = defineTheme({
36
- name: 'Brand',
37
- note: 'Warm, roomy, and slower than the default',
38
- scheme: 'light',
39
- tokens: {
40
- '--bg': '#faf7f2',
41
- '--panel': '#ffffff',
42
- '--accent': '#7c3aed',
43
- '--on-accent': '#ffffff',
44
- '--density': '1.15', // every --space-* step follows
45
- '--font-size': '15px', // every --text-* step follows
46
- '--dur-md': '320ms',
47
- },
48
- })
49
-
50
- applyTheme(brand) // the object, straight from defineTheme
51
- applyTheme(brand, myEl) // or on a subtree
52
- applyTheme('paper') // or a built-in, by name
36
+ import '@wtfalch/design/valet.css' // the vocabulary, valet's identity, the components, valet's themes
37
+ import { Brand, Button, applyTheme } from '@wtfalch/design/valet'
38
+ // the same components; Brand is valet's badge, applyTheme knows valet's themes
53
39
  ```
54
40
 
55
- Name the tokens you change; the rest inherit from `tokens.css`. A theme naming
56
- three tokens is valid.
41
+ Three layers, each falling back to the one under it:
42
+
43
+ | | |
44
+ |---|---|
45
+ | **the system** | The components, the base values in `tokens.css`, the shared icons and illustrations. |
46
+ | **the product** | Its mark, and its identity: the tokens that make it itself under every theme — font, shape, density. On `:root` in the product's stylesheet, so a theme that is silent on them gets the product, not tf. `src/products/<name>.ts`. |
47
+ | **the theme** | A palette and a colour scheme, plus anything it deliberately changes. One `:root[data-theme='<id>']` rule each, generated from the object. |
48
+
49
+ The middle layer is what lets a theme be shared between products: it names its
50
+ colours and inherits the identity of whichever product wears it. Before it
51
+ existed, valet's two palettes each restated valet's font and corners, and a
52
+ palette written for two products would have shown tf's font on valet wherever
53
+ it kept quiet.
54
+
55
+ The product's default theme is also written on `:root` when no `data-theme` is
56
+ set, so the first paint is right with no attribute at all; set the attribute
57
+ before the bundle loads only to restore a theme somebody picked (see First
58
+ paint). tf's default is `system`, a `prefers-color-scheme` rule rather than a
59
+ palette, so tf still sets the attribute.
60
+
61
+ The main entry is the neutral view of all of it: `PRODUCTS` by name, `THEMES`
62
+ as the union every product's picker and the contrast test read,
63
+ `productTheme(product, id)` for a theme as a product wears it, `bindProduct`
64
+ for a product defined outside this package, and `productStylesheet` for its
65
+ CSS.
66
+
67
+ ```ts
68
+ import { PRODUCTS, applyTheme, productTheme } from '@wtfalch/design'
69
+
70
+ applyTheme(productTheme(PRODUCTS.valet, 'valet-night')) // valet night, on valet's identity
71
+ applyTheme('valet-night') // the palette alone, over the base
72
+ applyTheme(productTheme(PRODUCTS.valet), myEl) // valet's default, on a subtree
73
+ ```
74
+
75
+ ### Writing a theme
76
+
77
+ A theme is a `Partial<ThemeTokens>` with a name, a note and a scheme: name the
78
+ tokens you change, the rest inherit from the product's identity and then from
79
+ `tokens.css`. A theme naming three tokens is valid. A product's themes go in
80
+ `src/products/<name>.ts` beside its identity, keyed by the id `applyTheme`
81
+ derives from the name (lowercased, spaces to hyphens); `products.test.ts`
82
+ refuses a key that disagrees, a palette that restates its product's identity,
83
+ and a default that is not one of the product's themes. `build-products.mjs`
84
+ writes `dist/<name>.css` from the objects at build time.
57
85
 
58
86
  **A typo is a compile error.** `tokens` is a `Partial<ThemeTokens>`, so
59
87
  `'--densty'` fails to build rather than silently doing nothing — which is the
60
- failure a string-keyed map produces at run time, invisibly. This is the main
61
- reason the type is exported at all.
88
+ failure a string-keyed map produces at run time, invisibly.
89
+
90
+ **A product names its font and does not ship it.** valet's identity sets
91
+ `--font` to read a `--font-sans` variable the app defines with whatever loads
92
+ its fonts, and falls back to the family by name. The gallery vendors the two
93
+ families valet names so the specimens are photographed in them.
62
94
 
63
95
  ## The three kinds of token
64
96
 
@@ -101,9 +133,10 @@ share a value.
101
133
 
102
134
  ## First paint
103
135
 
104
- React mounts after the stylesheet, so a theme applied in an effect flashes the
105
- default. Cache the name and apply it from a blocking script before the bundle
106
- loads:
136
+ Your product's stylesheet paints its default theme with no attribute set, so
137
+ this is for restoring a choice. React mounts after the stylesheet, so a theme
138
+ applied in an effect flashes the default. Cache the name and apply it from a
139
+ blocking script before the bundle loads:
107
140
 
108
141
  ```html
109
142
  <script>
@@ -116,20 +149,33 @@ loads:
116
149
 
117
150
  Your server stays the source of truth. `localStorage` only beats the paint.
118
151
 
119
- ## Built-ins
152
+ ## Marks
153
+
154
+ Every product's mark, by name, in `brandMarks.ts`. tf's is one stroke. valet's
155
+ is a filled badge: the jacket with the shirt cut out of it and a bow tie in the
156
+ cut, one path under `evenodd` so the surface shows through the shirt. Both are
157
+ `currentColor`, so the stylesheet decides the colour and a theme can move it.
158
+
159
+ ```tsx
160
+ import { BRAND_MARKS, Brand } from '@wtfalch/design'
161
+
162
+ <Brand name="valet" /> // anywhere; a product entry's Brand defaults to its own
163
+ BRAND_MARKS.valet.d // the path, for a favicon or an app icon cut from the same drawing
164
+ ```
120
165
 
121
- `system`, `night` and `paper` ship as **examples, not as the menu** an app
122
- that installs this is expected to bring its own. `system` is a theme rather than
123
- a mode: it is the only one scoped to `prefers-color-scheme`, so choosing a dark
124
- theme on a light-mode laptop is not silently repainted.
166
+ Icons and illustrations are the system's, shared by every product the way
167
+ `Button` is. A product wanting its own inside the package's components is a
168
+ case nobody has had; when it comes, the product entry is where to bind it.
125
169
 
126
170
  ## Status
127
171
 
128
- `0.2.0`. Twenty-eight components, every one of the 70 gallery specimens
172
+ `0.3.0`. Twenty-eight components, every one of the 70 gallery specimens
129
173
  photographed in four themes, the open windows photographed too, and the
130
174
  contrast, reduced-motion and keyboard rules are tests rather than sentences.
131
175
  It came out of [tf](https://github.com/wtfalch/tf), which is its first consumer;
132
- `Brand` holds every product's mark by name, tf's first.
176
+ valet is the second. A product is a layer: tf and valet each ship as one
177
+ stylesheet and one entry, with their identity under their themes and their
178
+ mark in the table.
133
179
 
134
180
  Requires React 19. Behaviour comes from
135
181
  [React Aria Components](https://react-spectrum.adobe.com/react-aria/); every
@@ -17,6 +17,17 @@ import { type BrandName } from './brandMarks';
17
17
  * product that wants its mark to move does the same in its own code, and the
18
18
  * package stays the source of the still shape every product's icon is drawn
19
19
  * from.
20
+ *
21
+ * Stroked or filled, whichever the mark is. tf's is one line at a weight;
22
+ * valet's is a filled badge with the shirt cut out of it and the bow inside
23
+ * the cut, one path under `evenodd` so the surface shows through the shirt.
24
+ * Both are `currentColor`, so `.brand` still decides the colour and a theme
25
+ * can still move it -- a second colour would be the one thing a theme could
26
+ * not reach.
27
+ *
28
+ * A product's entry, `@wtfalch/design/<product>`, exports this with the
29
+ * product's name as the default, so a header there writes `<Brand />` and
30
+ * gets its own mark. Here the default is the first row.
20
31
  */
21
32
  export default function Brand({ name, title, className, }: {
22
33
  /** Which product's mark. */
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { BRAND_MARKS } from './brandMarks';
2
+ import { BRAND_MARKS, BRAND_NAMES } from './brandMarks';
3
3
  /**
4
4
  * A product's mark, at header size.
5
5
  *
@@ -18,8 +18,20 @@ import { BRAND_MARKS } from './brandMarks';
18
18
  * product that wants its mark to move does the same in its own code, and the
19
19
  * package stays the source of the still shape every product's icon is drawn
20
20
  * from.
21
+ *
22
+ * Stroked or filled, whichever the mark is. tf's is one line at a weight;
23
+ * valet's is a filled badge with the shirt cut out of it and the bow inside
24
+ * the cut, one path under `evenodd` so the surface shows through the shirt.
25
+ * Both are `currentColor`, so `.brand` still decides the colour and a theme
26
+ * can still move it -- a second colour would be the one thing a theme could
27
+ * not reach.
28
+ *
29
+ * A product's entry, `@wtfalch/design/<product>`, exports this with the
30
+ * product's name as the default, so a header there writes `<Brand />` and
31
+ * gets its own mark. Here the default is the first row.
21
32
  */
22
- export default function Brand({ name = 'tf', title = name, className, }) {
33
+ export default function Brand({ name = BRAND_NAMES[0], title, className, }) {
23
34
  const mark = BRAND_MARKS[name];
24
- return (_jsxs("svg", { className: `brand${className ? ` ${className}` : ''}`, viewBox: mark.view, fill: "none", role: "img", "aria-label": title, children: [_jsx("title", { children: title }), _jsx("path", { d: mark.d, stroke: "currentColor", strokeWidth: mark.stroke, strokeLinecap: "round", strokeLinejoin: "round" })] }));
35
+ const said = title ?? name;
36
+ return (_jsxs("svg", { className: `brand${className ? ` ${className}` : ''}`, viewBox: mark.view, fill: "none", role: "img", "aria-label": said, children: [_jsx("title", { children: said }), 'fill' in mark ? (_jsx("path", { d: mark.d, fill: "currentColor", fillRule: mark.fill })) : (_jsx("path", { d: mark.d, stroke: "currentColor", strokeWidth: mark.stroke, strokeLinecap: "round", strokeLinejoin: "round" }))] }));
25
37
  }
@@ -61,4 +61,4 @@ export default function Icon({ name, size, className, title, }: {
61
61
  /** Given only when the icon is the whole message. An icon beside a label it
62
62
  * repeats is decoration, and decoration announced twice is noise. */
63
63
  title?: string;
64
- }): import("react").JSX.Element;
64
+ }): import("react").JSX.Element | null;
@@ -1,233 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- const GLYPHS = {
3
- chat: {
4
- view: '-0.9 -0.9 21.79 21.79',
5
- d: [
6
- 'M1.5 12.102a2.5 2.5 0 0 0 2.5 2.5h.249c.307 1.238.741 1.898 1.455 1.898c.603 0 1.519-.62 2.94-1.898H16a2.5 2.5 0 0 0 2.5-2.5V6A2.5 2.5 0 0 0 16 3.5H4A2.5 2.5 0 0 0 1.5 6zm1 0V6A1.5 1.5 0 0 1 4 4.5h12A1.5 1.5 0 0 1 17.5 6v6.102a1.5 1.5 0 0 1-1.5 1.5H8.257l-.143.13C6.834 14.898 5.96 15.5 5.704 15.5c-.092 0-.35-.463-.567-1.5l-.083-.398H4a1.5 1.5 0 0 1-1.5-1.5',
7
- ],
8
- dots: [
9
- [7, 9, 1],
10
- [10, 9, 1],
11
- [13, 9, 1],
12
- ],
13
- },
14
- settings: {
15
- view: '0.06 0.06 19.87 19.87',
16
- d: [
17
- 'M8.232 11.768A2.5 2.5 0 0 0 10 12.5c.672 0 1.302-.267 1.768-.732A2.5 2.5 0 0 0 12.5 10c0-.672-.267-1.302-.732-1.768A2.5 2.5 0 0 0 10 7.5c-.672 0-1.302.267-1.768.732A2.5 2.5 0 0 0 7.5 10c0 .672.267 1.302.732 1.768m2.829-.707c-.28.28-.657.439-1.061.439s-.78-.16-1.06-.44s-.44-.656-.44-1.06s.16-.78.44-1.06s.656-.44 1.06-.44s.78.16 1.06.44s.44.656.44 1.06s-.16.78-.44 1.06',
18
- 'm14.216 3.773l-1.27.714a6.2 6.2 0 0 0-1.166-.48l-.47-1.414a.5.5 0 0 0-.474-.343H9.06a.5.5 0 0 0-.481.365l-.392 1.403a6.2 6.2 0 0 0-1.164.486L5.69 3.835a.5.5 0 0 0-.578.094L3.855 5.185a.5.5 0 0 0-.082.599l.714 1.27q-.3.556-.48 1.166l-1.414.47a.5.5 0 0 0-.343.474v1.777a.5.5 0 0 0 .365.481l1.403.392q.184.608.486 1.164l-.669 1.333a.5.5 0 0 0 .094.578l1.256 1.256a.5.5 0 0 0 .599.082l1.27-.714q.556.3 1.166.48l.47 1.414a.5.5 0 0 0 .474.343h1.777a.5.5 0 0 0 .481-.365l.392-1.403a6.2 6.2 0 0 0 1.164-.486l1.333.669a.5.5 0 0 0 .578-.093l1.256-1.257a.5.5 0 0 0 .082-.599l-.714-1.27q.3-.556.48-1.166l1.414-.47a.5.5 0 0 0 .343-.474V9.06a.5.5 0 0 0-.365-.481l-1.403-.392a6.2 6.2 0 0 0-.486-1.164l.669-1.333a.5.5 0 0 0-.093-.578l-1.257-1.256a.5.5 0 0 0-.599-.082m-1.024 1.724l1.184-.667l.733.733l-.627 1.25a.5.5 0 0 0 .019.482c.265.44.464.918.59 1.418a.5.5 0 0 0 .35.36l1.309.366v1.037l-1.327.44a.5.5 0 0 0-.328.354a5.2 5.2 0 0 1-.585 1.42a.5.5 0 0 0-.007.502l.667 1.184l-.733.733l-1.25-.627a.5.5 0 0 0-.482.019c-.44.265-.918.464-1.418.59a.5.5 0 0 0-.36.35l-.366 1.309H9.525l-.44-1.327a.5.5 0 0 0-.355-.328a5.2 5.2 0 0 1-1.42-.585a.5.5 0 0 0-.502-.007l-1.184.667l-.733-.733l.627-1.25a.5.5 0 0 0-.019-.482a5.2 5.2 0 0 1-.59-1.418a.5.5 0 0 0-.35-.36l-1.309-.366V9.525l1.327-.44a.5.5 0 0 0 .327-.355c.125-.5.323-.979.586-1.42a.5.5 0 0 0 .007-.502L4.83 5.624l.733-.733l1.25.627a.5.5 0 0 0 .482-.019c.44-.265.918-.464 1.418-.59a.5.5 0 0 0 .36-.35l.366-1.309h1.037l.44 1.327a.5.5 0 0 0 .354.327c.5.125.979.323 1.42.586a.5.5 0 0 0 .502.007',
19
- ],
20
- },
21
- refresh: {
22
- view: '-1.16 -1.07 21.93 21.93',
23
- d: [
24
- 'M5.254 14.596a.5.5 0 0 1 .707-.707A5.5 5.5 0 0 0 15.35 10a.5.5 0 0 1 .999.001a6.5 6.5 0 0 1-11.096 4.596',
25
- 'M13.131 12.416a.5.5 0 0 1-.555-.832l3-2a.5.5 0 1 1 .555.832z',
26
- 'M18.266 12.723a.5.5 0 1 1-.832.554l-2-3a.5.5 0 0 1 .832-.554zm-3.912-7.518a.5.5 0 0 1-.708.707a5.5 5.5 0 0 0-9.389 3.89a.5.5 0 0 1-1-.001a6.5 6.5 0 0 1 11.097-4.596',
27
- 'M6.476 7.385a.5.5 0 0 1 .555.832l-3 2a.5.5 0 1 1-.555-.832z',
28
- 'M1.341 7.078a.5.5 0 1 1 .832-.554l2 3a.5.5 0 0 1-.832.554z',
29
- ],
30
- },
31
- minimize: { view: '0 0 20 20', d: ['M5 10.5a.5.5 0 0 1 0-1h10a.5.5 0 0 1 0 1z'] },
32
- code: {
33
- view: '-0.47 -0.48 20.94 20.94',
34
- d: [
35
- 'M1.962 9.666a.5.5 0 0 1 .706-.038l3.333 3a.5.5 0 1 1-.669.744l-3.333-3a.5.5 0 0 1-.037-.706',
36
- 'M6.038 6.666a.5.5 0 0 1-.037.706l-3.333 3a.5.5 0 0 1-.67-.744l3.334-3a.5.5 0 0 1 .706.038m12 3a.5.5 0 0 1-.037.706l-3.333 3a.5.5 0 0 1-.67-.744l3.334-3a.5.5 0 0 1 .706.038',
37
- 'M13.962 6.666a.5.5 0 0 1 .706-.038l3.333 3a.5.5 0 0 1-.669.744l-3.333-3a.5.5 0 0 1-.037-.706m-2.33-2.648a.5.5 0 0 1 .35.614l-3 11a.5.5 0 0 1-.964-.264l3-11a.5.5 0 0 1 .614-.35',
38
- ],
39
- },
40
- wifi: {
41
- view: '-0.26 -0.06 20.51 20.51',
42
- d: [
43
- 'M13.348 11.641a.5.5 0 0 1-.696.718a2 2 0 0 0-.136-.12a3 3 0 0 0-.753-.424A4.8 4.8 0 0 0 10 11.5c-1.022 0-1.951.285-2.516.739q-.073.06-.136.12a.5.5 0 0 1-.696-.718q.097-.094.206-.182c.754-.606 1.905-.959 3.142-.959c.765 0 1.5.134 2.132.385c.385.153.726.346 1.01.574q.109.089.206.182m2.508-2.601a.5.5 0 1 1-.712.7a5 5 0 0 0-.35-.32a5 5 0 0 0-.399-.298a6.6 6.6 0 0 0-1.449-.731a8 8 0 0 0-1.126-.319a9 9 0 0 0-1.205-.162a9.3 9.3 0 0 0-1.839.061a9 9 0 0 0-1.172.242a8 8 0 0 0-1.071.39a6.6 6.6 0 0 0-.928.519q-.21.143-.399.298q-.186.155-.35.32a.5.5 0 0 1-.712-.7q.198-.202.424-.39q.225-.186.474-.355a7 7 0 0 1 1.076-.602a8 8 0 0 1 1.215-.443a9 9 0 0 1 1.31-.27A10 10 0 0 1 10 6.89a10 10 0 0 1 1.357.09a10 10 0 0 1 1.31.27a9 9 0 0 1 1.215.443a7.6 7.6 0 0 1 1.076.602q.249.17.474.355q.226.188.424.39',
44
- 'M17.873 6.555a.5.5 0 1 1-.746.667a7 7 0 0 0-.492-.496a8 8 0 0 0-.557-.46a9 9 0 0 0-1.29-.794a9.5 9.5 0 0 0-1.484-.594a10.6 10.6 0 0 0-1.617-.366A11.4 11.4 0 0 0 10 4.389a12 12 0 0 0-1.687.123a11 11 0 0 0-1.617.366a10 10 0 0 0-1.483.595a9 9 0 0 0-.673.374a8 8 0 0 0-.618.42q-.294.22-.557.46q-.263.239-.492.495a.5.5 0 1 1-.746-.667q.264-.294.564-.568t.632-.521q.332-.249.694-.471a10 10 0 0 1 1.545-.775a11 11 0 0 1 1.709-.53a12 12 0 0 1 1.808-.268a12.7 12.7 0 0 1 2.754.1a12 12 0 0 1 1.767.401a11 11 0 0 1 1.635.656q.387.194.749.416q.36.222.693.47q.333.249.632.522q.3.274.564.568M11.75 15.25a1.75 1.75 0 1 1-3.5 0a1.75 1.75 0 0 1 3.5 0',
45
- ],
46
- },
47
- 'wifi-off': {
48
- view: '-1.54 -1.54 23.08 23.08',
49
- d: [
50
- 'M13.348 11.641a.5.5 0 0 1-.696.718a2 2 0 0 0-.136-.12a3 3 0 0 0-.753-.424A4.8 4.8 0 0 0 10 11.5c-1.022 0-1.951.285-2.516.739q-.073.06-.136.12a.5.5 0 0 1-.696-.718q.097-.094.206-.182c.754-.606 1.905-.959 3.142-.959c.765 0 1.5.134 2.132.385c.385.153.726.346 1.01.574q.109.089.206.182m2.508-2.601a.5.5 0 1 1-.712.7a5 5 0 0 0-.35-.32a5 5 0 0 0-.399-.298a6.6 6.6 0 0 0-1.449-.731a8 8 0 0 0-1.126-.319a9 9 0 0 0-1.205-.162a9.3 9.3 0 0 0-1.839.061a9 9 0 0 0-1.172.242a8 8 0 0 0-1.071.39a6.6 6.6 0 0 0-.928.519q-.21.143-.399.298q-.186.155-.35.32a.5.5 0 0 1-.712-.7q.198-.202.424-.39q.225-.186.474-.355a7 7 0 0 1 1.076-.602a8 8 0 0 1 1.215-.443a9 9 0 0 1 1.31-.27A10 10 0 0 1 10 6.89a10 10 0 0 1 1.357.09a10 10 0 0 1 1.31.27a9 9 0 0 1 1.215.443a7.6 7.6 0 0 1 1.076.602q.249.17.474.355q.226.188.424.39',
51
- 'M17.873 6.555a.5.5 0 1 1-.746.667a7 7 0 0 0-.492-.496a8 8 0 0 0-.557-.46a9 9 0 0 0-1.29-.794a9.5 9.5 0 0 0-1.484-.594a10.6 10.6 0 0 0-1.617-.366A11.4 11.4 0 0 0 10 4.389a12 12 0 0 0-1.687.123a11 11 0 0 0-1.617.366a10 10 0 0 0-1.483.595a9 9 0 0 0-.673.374a8 8 0 0 0-.618.42q-.294.22-.557.46q-.263.239-.492.495a.5.5 0 1 1-.746-.667q.264-.294.564-.568t.632-.521q.332-.249.694-.471a10 10 0 0 1 1.545-.775a11 11 0 0 1 1.709-.53a12 12 0 0 1 1.808-.268a12.7 12.7 0 0 1 2.754.1a12 12 0 0 1 1.767.401a11 11 0 0 1 1.635.656q.387.194.749.416q.36.222.693.47q.333.249.632.522q.3.274.564.568M11.75 15.25a1.75 1.75 0 1 1-3.5 0a1.75 1.75 0 0 1 3.5 0',
52
- 'M1.15 1.878a.514.514 0 0 1 .728-.727l16.971 16.971a.514.514 0 0 1-.727.727z',
53
- ],
54
- },
55
- check: {
56
- view: '2.44 2.94 14.12 14.12',
57
- d: [
58
- 'm14.937 5.743l-5 9c-.324.583-1.198.097-.874-.486l5-9c.324-.583 1.198-.097.874.486',
59
- 'm4.812 10.11l5 4c.52.416-.104 1.197-.624.78l-5-4c-.52-.416.104-1.197.624-.78',
60
- ],
61
- },
62
- close: {
63
- view: '3.1 3.1 13.79 13.79',
64
- d: [
65
- 'M6.854 13.854a.5.5 0 0 1-.708-.708l7-7a.5.5 0 0 1 .708.708z',
66
- 'M6.146 6.854a.5.5 0 1 1 .708-.708l7 7a.5.5 0 0 1-.708.708z',
67
- ],
68
- },
69
- info: {
70
- view: '-2.29 -2.29 30.59 30.59',
71
- d: [
72
- 'M13.25 10.75a.75.75 0 0 1 .75.75v9a.75.75 0 0 1-1.5 0v-9a.75.75 0 0 1 .75-.75',
73
- 'M14.5 7.25a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0',
74
- 'M13 24.5c6.351 0 11.5-5.149 11.5-11.5S19.351 1.5 13 1.5S1.5 6.649 1.5 13S6.649 24.5 13 24.5m0 1c6.904 0 12.5-5.596 12.5-12.5S19.904.5 13 .5S.5 6.096.5 13S6.096 25.5 13 25.5',
75
- ],
76
- },
77
- warning: {
78
- view: '-2.29 -2.29 30.59 30.59',
79
- d: [
80
- 'M13.25 5.25A.75.75 0 0 1 14 6v9a.75.75 0 0 1-1.5 0V6a.75.75 0 0 1 .75-.75',
81
- 'M14.5 19.25a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0',
82
- 'M13 24.5c6.351 0 11.5-5.149 11.5-11.5S19.351 1.5 13 1.5S1.5 6.649 1.5 13S6.649 24.5 13 24.5m0 1c6.904 0 12.5-5.596 12.5-12.5S19.904.5 13 .5S.5 6.096.5 13S6.096 25.5 13 25.5',
83
- ],
84
- },
85
- error: {
86
- view: '0.38 0.38 19.23 19.23',
87
- d: [
88
- 'M10 3.5a6.5 6.5 0 1 0 0 13a6.5 6.5 0 0 0 0-13M2.5 10a7.5 7.5 0 1 1 15 0a7.5 7.5 0 0 1-15 0',
89
- 'M15.304 4.697a.5.5 0 0 1 0 .707l-9.9 9.9a.5.5 0 1 1-.707-.707l9.9-9.9a.5.5 0 0 1 .707 0',
90
- ],
91
- },
92
- expand: {
93
- view: '2.95 2.95 14.1 14.1',
94
- d: [
95
- 'M11.354 9.354a.5.5 0 0 1-.708-.708l4-4a.5.5 0 0 1 .708.708zm-6 6a.5.5 0 0 1-.708-.708l4-4a.5.5 0 0 1 .708.708z',
96
- 'M5 15.5a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1z',
97
- 'M5.5 15a.5.5 0 0 1-1 0v-4a.5.5 0 0 1 1 0zm10-6a.5.5 0 0 1-1 0V5a.5.5 0 0 1 1 0z',
98
- 'M11 5.5a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1z',
99
- ],
100
- },
101
- download: {
102
- view: '-0.26 0.25 20.52 20.52',
103
- d: [
104
- 'M11 3h-1a4 4 0 0 0-3.874 3H6a4 4 0 1 0 0 8h8a4 4 0 0 0 .899-7.899A4 4 0 0 0 11 3M6.901 7l.193-.75A3 3 0 0 1 10 4h1c1.405 0 2.614.975 2.924 2.325l.14.61l.61.141A3.001 3.001 0 0 1 14 13H6a3 3 0 1 1 0-6z',
105
- 'M10 10a.5.5 0 0 1 1 0v7.5a.5.5 0 0 1-1 0z',
106
- 'M12.688 15.11a.5.5 0 0 1 .624.78l-2.5 2a.5.5 0 1 1-.624-.78z',
107
- 'M7.688 15.89a.5.5 0 0 1 .624-.78l2.5 2a.5.5 0 1 1-.624.78z',
108
- ],
109
- },
110
- image: {
111
- view: '-0.9 -0.9 21.79 21.79',
112
- d: [
113
- 'M8 8a1 1 0 1 0 0-2a1 1 0 0 0 0 2m0 1a2 2 0 1 0 0-4a2 2 0 0 0 0 4',
114
- 'M7.5 10.678L5.59 13.25H7.5l1.91 1H5.59a1 1 0 0 1-.802-1.596l1.909-2.572a1 1 0 0 1 1.606 0l.955 1.286l-.803.596z',
115
- 'M14.257 13.25L11.25 9.778L8.243 13.25zm-2.251-4.127a1 1 0 0 0-1.512 0l-3.007 3.472a1 1 0 0 0 .756 1.655h6.014a1 1 0 0 0 .756-1.655z',
116
- 'M17 2.5H3a.5.5 0 0 0-.5.5v14a.5.5 0 0 0 .5.5h14a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5m-14-1A1.5 1.5 0 0 0 1.5 3v14A1.5 1.5 0 0 0 3 18.5h14a1.5 1.5 0 0 0 1.5-1.5V3A1.5 1.5 0 0 0 17 1.5z',
117
- ],
118
- },
119
- bolt: {
120
- view: '-0.26 0.24 20.52 20.52',
121
- d: [
122
- 'M15 8.5h-3.813l2.273-5.303A.5.5 0 0 0 13 2.5H8a.5.5 0 0 0-.46.303l-3 7A.5.5 0 0 0 5 10.5h2.474l-2.938 7.314c-.2.497.417.918.807.55l5.024-4.743l4.958-4.241A.5.5 0 0 0 15 8.5m-4.571 1h3.217l-3.948 3.378l-3.385 3.195l2.365-5.887a.5.5 0 0 0-.464-.686H5.758l2.572-6h3.912L9.969 8.803a.5.5 0 0 0 .46.697',
123
- ],
124
- },
125
- speaker: {
126
- view: '-0.76 -1.26 20.51 20.51',
127
- d: [
128
- 'M9.672 2.123L5.399 5.84H2a.5.5 0 0 0-.5.5v5.2a.5.5 0 0 0 .5.5h3.295l4.375 3.835a.5.5 0 0 0 .83-.376v-13a.5.5 0 0 0-.828-.377M5.884 6.745L9.5 3.598v10.799L6.014 11.34a.5.5 0 0 0-.458-.3H2.5v-4.2h2.894a.49.49 0 0 0 .49-.096',
129
- 'M13.326 11.88a.5.5 0 0 1-.652-.76q.06-.05.117-.11c.157-.162.295-.366.407-.602c.195-.409.302-.896.302-1.408c0-.817-.273-1.558-.709-2.01a2 2 0 0 0-.117-.11a.5.5 0 0 1 .652-.76q.096.083.185.176c.624.647.989 1.639.989 2.704c0 .66-.14 1.293-.399 1.838c-.157.33-.356.624-.59.866q-.09.093-.185.175',
130
- 'M14.274 14.918a.5.5 0 0 1-.548-.836a6.3 6.3 0 0 0 1.359-1.208a6.1 6.1 0 0 0 .942-1.537A5.9 5.9 0 0 0 16.5 9a6 6 0 0 0-.267-1.773a6 6 0 0 0-.783-1.62a6.1 6.1 0 0 0-1.232-1.33a6 6 0 0 0-.492-.359a.5.5 0 0 1 .548-.836a7.3 7.3 0 0 1 2.002 1.961a7.1 7.1 0 0 1 .913 1.889A7 7 0 0 1 17.5 9a7 7 0 0 1-.311 2.068a7 7 0 0 1-1.338 2.45a7.2 7.2 0 0 1-1.577 1.4',
131
- ],
132
- },
133
- mic: {
134
- view: '0.39 -0.12 19.23 19.23',
135
- d: [
136
- 'M7.75 4.25a2.25 2.25 0 0 1 4.5 0v3.5a2.25 2.25 0 0 1-4.5 0z',
137
- 'M10 17c-2.48 0-4-.217-4-1s1.52-1 4-1s4 .217 4 1s-1.52 1-4 1',
138
- 'M9.5 12.5h1V16h-1z',
139
- 'M14 7.5a.5.5 0 0 1 1 0v1.65c0 2.421-2.254 4.35-5 4.35s-5-1.929-5-4.35V7.5a.5.5 0 0 1 1 0v1.65c0 1.831 1.775 3.35 4 3.35s4-1.519 4-3.35z',
140
- ],
141
- },
142
- back: {
143
- view: '2.96 2.95 14.09 14.09',
144
- d: [
145
- 'M8.653 14.384a.5.5 0 0 1-.704-.064l-3.333-4a.5.5 0 0 1 .768-.64l3.333 4a.5.5 0 0 1-.064.704',
146
- 'M8.653 5.616a.5.5 0 0 1 .064.704l-3.333 4a.5.5 0 0 1-.768-.64l3.333-4a.5.5 0 0 1 .704-.064',
147
- 'M5 10a.5.5 0 0 1 .5-.5H15a.5.5 0 0 1 0 1H5.5A.5.5 0 0 1 5 10',
148
- ],
149
- },
150
- spinner: {
151
- view: '2.03 1.03 17.93 17.93',
152
- d: [
153
- 'M13.375 5.038a.5.5 0 0 1-.564.827A5 5 0 1 0 15 10a.5.5 0 0 1 1 0a6 6 0 1 1-2.626-4.962',
154
- 'M12.769 11.585a.5.5 0 1 1-.539-.842l3.482-2.227a.5.5 0 1 1 .539.842z',
155
- 'M17.947 12.114a.5.5 0 0 1-.913.407l-1.509-3.38a.5.5 0 1 1 .914-.408z',
156
- ],
157
- },
158
- folder: {
159
- view: '-0.26 -0.01 20.51 20.51',
160
- d: [
161
- 'M15.5 5.5h-5.281L9.51 4.26A2.5 2.5 0 0 0 7.34 3H4.5A2.5 2.5 0 0 0 2 5.5V15a2.5 2.5 0 0 0 2.5 2.5h11A2.5 2.5 0 0 0 18 15V8a2.5 2.5 0 0 0-2.5-2.5m-11 11A1.5 1.5 0 0 1 3 15V5.5A1.5 1.5 0 0 1 4.5 4h2.84a1.5 1.5 0 0 1 1.302.756l.852 1.492a.5.5 0 0 0 .435.252H15.5A1.5 1.5 0 0 1 17 8v7a1.5 1.5 0 0 1-1.5 1.5z',
162
- ],
163
- },
164
- book: {
165
- view: '-1.76 -1.76 23.53 23.53',
166
- d: [
167
- 'M10.08 4.304L2.244 3.019A1.5 1.5 0 0 0 .5 4.5v9.738a1.5 1.5 0 0 0 1.268 1.482l8.155 1.275a.5.5 0 0 0 .577-.494V4.797a.5.5 0 0 0-.42-.493m-8-.298L9.5 5.222v10.694L1.923 14.73a.5.5 0 0 1-.423-.493V4.5a.5.5 0 0 1 .58-.494',
168
- 'M18 3a1.5 1.5 0 0 0-.243.02L9.92 4.303a.5.5 0 0 0-.419.493V16.5a.5.5 0 0 0 .577.494l8.155-1.275a1.5 1.5 0 0 0 1.268-1.482V4.5A1.5 1.5 0 0 0 18 3m.077 11.73L10.5 15.917V5.222l7.42-1.216a.5.5 0 0 1 .58.494v9.737a.5.5 0 0 1-.423.493',
169
- ],
170
- },
171
- eye: {
172
- view: '-0.9 -1.4 21.8 21.8',
173
- d: [
174
- 'M10 16c4.658 0 8.5-2.161 8.5-5S14.658 6 10 6s-8.5 2.161-8.5 5s3.842 5 8.5 5m0-9c4.179 0 7.5 1.868 7.5 4s-3.321 4-7.5 4s-7.5-1.868-7.5-4S5.821 7 10 7',
175
- 'M9.5 3.5a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0zm4.01.402a.5.5 0 0 1 .98.196l-.5 2.5a.5.5 0 0 1-.98-.196zm-7.02 0a.5.5 0 0 0-.98.196l.5 2.5a.5.5 0 0 0 .98-.196zM2.429 5.243a.5.5 0 0 0-.858.514l1.5 2.5a.5.5 0 0 0 .858-.514zm15.142 0a.5.5 0 1 1 .858.514l-1.5 2.5a.5.5 0 1 1-.858-.514zM13 10.5a3 3 0 1 1-6 0a3 3 0 0 1 6 0',
176
- ],
177
- },
178
- /* The same eye with a line through it. Pepicons' `eye-off`, but drawn from
179
- THIS file's `eye` plus the slash rather than copied whole: the set has
180
- redrawn its lashes since the open eye was taken, and a pair that toggles
181
- in one button must differ by the slash alone or the eye appears to jump.
182
- For the same reason it shares `eye`'s `view` instead of a measured one --
183
- the slash sits inside that box (0.8..18.5 on both axes), so nothing is
184
- clipped, and the body of the eye does not move when the state does. */
185
- 'eye-off': {
186
- view: '-0.9 -1.4 21.8 21.8',
187
- d: [
188
- 'M10 16c4.658 0 8.5-2.161 8.5-5S14.658 6 10 6s-8.5 2.161-8.5 5s3.842 5 8.5 5m0-9c4.179 0 7.5 1.868 7.5 4s-3.321 4-7.5 4s-7.5-1.868-7.5-4S5.821 7 10 7',
189
- 'M9.5 3.5a.5.5 0 0 1 1 0v3a.5.5 0 0 1-1 0zm4.01.402a.5.5 0 0 1 .98.196l-.5 2.5a.5.5 0 0 1-.98-.196zm-7.02 0a.5.5 0 0 0-.98.196l.5 2.5a.5.5 0 0 0 .98-.196zM2.429 5.243a.5.5 0 0 0-.858.514l1.5 2.5a.5.5 0 0 0 .858-.514zm15.142 0a.5.5 0 1 1 .858.514l-1.5 2.5a.5.5 0 1 1-.858-.514zM13 10.5a3 3 0 1 1-6 0a3 3 0 0 1 6 0',
190
- 'M1.15 1.878a.514.514 0 0 1 .728-.727l16.971 16.971a.514.514 0 0 1-.727.727z',
191
- ],
192
- },
193
- palette: {
194
- view: '-2.13 -2.1 23.53 23.53',
195
- d: [
196
- 'M14.725 13.334c.97-3.623-1.336-7.377-5.135-8.395C5.323 3.795-.725 6.762.544 10.35c.383 1.084 1.134 1.341 2.682 1.363l.108.002c1.1.013 1.471.1 1.678.436c.214.348.216.756.054 1.788c-.067.422-.088.569-.113.794c-.145 1.3.067 2.274.89 3.16c2.584 2.784 7.788-.48 8.882-4.56M1.486 10.017c-.908-2.57 4.217-5.084 7.845-4.112c3.277.878 5.252 4.093 4.428 7.17c-.924 3.446-5.287 6.182-7.183 4.14c-.594-.64-.745-1.333-.63-2.37c.024-.205.044-.344.108-.75c.2-1.275.197-1.836-.19-2.467c-.465-.756-1.059-.894-2.517-.912l-.107-.002c-1.159-.016-1.562-.154-1.754-.697',
197
- 'M4.75 9.5a1.25 1.25 0 1 1 0-2.5a1.25 1.25 0 0 1 0 2.5m4 0a1.25 1.25 0 1 1 0-2.5a1.25 1.25 0 0 1 0 2.5m2.5 3a1.25 1.25 0 1 1 0-2.5a1.25 1.25 0 0 1 0 2.5M9.75 16a1.25 1.25 0 1 1 0-2.5a1.25 1.25 0 0 1 0 2.5m3.62-12.395a1 1 0 0 1 1.371.443l4.093 8.4a.652.652 0 0 1-1.149.611l-4.708-8.07a1 1 0 0 1 .394-1.384',
198
- 'M11.538 3.484c.486.915 1.305 1.171 2.097.75c.791-.42 1.038-1.243.551-2.158C13.63 1.028 12.438.078 11.648.497s-.668 1.939-.11 2.987m.882-.47a3.2 3.2 0 0 1-.32-1.137a2 2 0 0 1 .018-.496l.018.009c.05.024.205.096.403.254c.302.241.602.596.764.901c.229.43.164.646-.138.807c-.3.16-.516.092-.745-.337',
199
- ],
200
- },
201
- stars: {
202
- view: '-1.95 -1.76 23.53 23.53',
203
- d: [
204
- 'M9 .5a.5.5 0 0 1 .479.354l1.928 6.333l5.765 2.107a.5.5 0 0 1 .013.934l-5.727 2.275l-1.979 6.64a.5.5 0 0 1-.958 0l-1.979-6.64l-5.727-2.275a.5.5 0 0 1 .013-.934l5.765-2.107L8.522.854A.5.5 0 0 1 9 .5m0 2.216l-1.523 5a.5.5 0 0 1-.307.324L2.403 9.784l4.734 1.88a.5.5 0 0 1 .294.322l1.57 5.264l1.568-5.264a.5.5 0 0 1 .294-.322l4.734-1.88l-4.767-1.742a.5.5 0 0 1-.307-.324z',
205
- 'M16.75 19.12c-.1 0-.19-.08-.2-.18c-.18-1.82-.32-2.4-1.99-2.56c-.1-.01-.18-.1-.18-.2s.08-.19.18-.2c1.71-.16 1.81-.6 1.99-2.42c0-.1.1-.18.2-.18s.19.08.2.18c.18 1.82.29 2.26 1.99 2.42c.1.01.18.1.18.2s-.08.19-.18.2c-1.68.16-1.81.74-1.99 2.57c0 .1-.09.18-.2.18z',
206
- ],
207
- },
208
- wrench: {
209
- view: '-0.17 -0.74 21.07 21.07',
210
- d: [
211
- 'M11.876 7.574a3 3 0 0 0-.384-.382c.295-1.338-.066-2.802-1.014-3.93c-1.182-1.41-3.03-2-4.668-1.523a.5.5 0 0 0-.243.801l1.741 2.076a.5.5 0 0 1-.061.704l-.581.488a.5.5 0 0 1-.705-.062L4.22 3.671a.5.5 0 0 0-.832.1c-.755 1.531-.494 3.453.689 4.862c.935 1.114 2.29 1.724 3.645 1.683q.138.251.324.471l5.302 6.32a2.5 2.5 0 1 0 3.83-3.214zm-7.033.416c-.735-.875-1.016-1.99-.82-2.997l1.172 1.396a1.5 1.5 0 0 0 2.113.185l.582-.488a1.5 1.5 0 0 0 .184-2.113L6.903 2.577c1.025-.02 2.075.452 2.81 1.327c.813.97 1.077 2.24.732 3.34a.5.5 0 0 0 .208.571a2 2 0 0 1 .457.401l5.303 6.32a1.5 1.5 0 1 1-2.299 1.928l-5.302-6.32a2 2 0 0 1-.323-.538a.5.5 0 0 0-.522-.31c-1.133.133-2.32-.348-3.124-1.306',
212
- ],
213
- },
214
- file: {
215
- view: '-1.54 -1.54 23.08 23.08',
216
- d: [
217
- 'M6.5 12a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1zm0 3a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1z',
218
- 'M11.185 1H4.5A1.5 1.5 0 0 0 3 2.5v15A1.5 1.5 0 0 0 4.5 19h11a1.5 1.5 0 0 0 1.5-1.5V7.202a1.5 1.5 0 0 0-.395-1.014l-4.314-4.702A1.5 1.5 0 0 0 11.185 1M4 2.5a.5.5 0 0 1 .5-.5h6.685a.5.5 0 0 1 .369.162l4.314 4.702a.5.5 0 0 1 .132.338V17.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5z',
219
- 'M11 7h5.5a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5v-6a.5.5 0 0 1 1 0z',
220
- ],
221
- },
222
- cloud: {
223
- view: '-0.26 -1.76 20.52 20.52',
224
- d: [
225
- 'M11 3h-1a4 4 0 0 0-3.874 3H6a4 4 0 1 0 0 8h8a4 4 0 0 0 .899-7.899A4 4 0 0 0 11 3M6.901 7l.193-.75A3 3 0 0 1 10 4h1c1.405 0 2.614.975 2.924 2.325l.14.61l.61.141A3.001 3.001 0 0 1 14 13H6a3 3 0 1 1 0-6z',
226
- ],
227
- },
228
- };
2
+ import { ICONS } from './icons';
229
3
  export default function Icon({ name, size = 18, className, title, }) {
230
- const g = GLYPHS[name];
4
+ /* The table with its measured views is `icons.ts`, beside this file. */
5
+ const g = ICONS[name];
6
+ if (!g)
7
+ return null;
231
8
  return (_jsxs("svg", { className: className, width: size, height: size, viewBox: g.view, fill: "currentColor",
232
9
  /* Pepicons paths are self-intersecting outlines; without evenodd the
233
10
  counters fill in and a gear becomes a blob. */
@@ -34,9 +34,8 @@ import { ILLUSTRATION_SVG } from '../illustrations';
34
34
  not exist. `scripts/build-illustrations.mjs` generates a plain module
35
35
  instead, which removes the dependency on a bundler rather than documenting
36
36
  it. */
37
- const BY_NAME = ILLUSTRATION_SVG;
38
37
  export default function Illustration({ name, size = 160, alt, className, }) {
39
- const svg = BY_NAME[name];
38
+ const svg = ILLUSTRATION_SVG[name];
40
39
  if (!svg)
41
40
  return null;
42
41
  return (_jsx("span", { className: `illo${className ? ` ${className}` : ''}`, style: { height: size }, role: alt ? 'img' : undefined, "aria-label": alt, "aria-hidden": alt ? undefined : true,
@@ -5,7 +5,9 @@
5
5
  * product's logo -- it is the place every product's mark lives, and adding a
6
6
  * brand to the system is adding a row here. A mark is a path in its own ink
7
7
  * box, drawn with `currentColor` at the stroke weight it was designed at, so
8
- * the stylesheet decides the colour and a theme can move it.
8
+ * the stylesheet decides the colour and a theme can move it. Or filled: a
9
+ * mark that is a figure rather than a line carries a fill rule instead of a
10
+ * weight, and `Brand` draws whichever it is handed.
9
11
  *
10
12
  * A product that animates its mark does that in its own code: tf's lowercase
11
13
  * tf becomes a cog under the pointer, and that morph is
@@ -17,6 +19,29 @@
17
19
  * A sibling module rather than part of `Brand.tsx`, so that file keeps its
18
20
  * Fast Refresh boundary (`fastRefresh.test.ts`).
19
21
  */
22
+ /**
23
+ * A mark: one path in its own ink box, stroked or filled.
24
+ *
25
+ * tf's is a stroke, one line at the weight it was drawn at. valet's is a
26
+ * figure: a filled badge with the shirt cut out of it and the bow drawn back
27
+ * inside the cut, which is one path under `evenodd` -- the jacket, the shirt
28
+ * and the bow are subpaths, and the rule makes the shirt a hole the surface
29
+ * shows through. Two shapes of the same thing, so `Brand` draws either in
30
+ * `currentColor` and a product supplies the one its mark is.
31
+ */
32
+ export type Mark = StrokeMark | FillMark;
33
+ /** One line, at the weight it was drawn at. */
34
+ export interface StrokeMark {
35
+ view: string;
36
+ d: string;
37
+ stroke: number;
38
+ }
39
+ /** One filled path; `fill` is the rule that decides which subpaths are holes. */
40
+ export interface FillMark {
41
+ view: string;
42
+ d: string;
43
+ fill: 'evenodd' | 'nonzero';
44
+ }
20
45
  export declare const BRAND_MARKS: {
21
46
  /** A lowercase tf in one unbroken stroke -- down the t, round the foot, up
22
47
  * the f, over its head, and back along the crossbar through both stems.
@@ -27,6 +52,20 @@ export declare const BRAND_MARKS: {
27
52
  readonly d: "M372 262V620A100 100 0 0 0 572 620V330A92 92 0 0 1 756 330A100 100 0 0 1 656 430H296";
28
53
  readonly stroke: 96;
29
54
  };
55
+ /** valet's badge. The rounded square is the jacket, the shirt is cut out of
56
+ * it in an open collar that runs to the top edge, and the bow tie floats in
57
+ * the shirt just below the collar. One path under `evenodd` with five
58
+ * subpaths -- the jacket, the shirt, two wings and the knot -- so the shirt
59
+ * is a hole the surface shows through and the bow is drawn back inside it.
60
+ * Drawn 24-first, which is the header size: the 60 units of jacket over the
61
+ * bow are the pixel that keeps it a bow rather than a notch in the edge, and
62
+ * the collar rolls outward at the top so the shirt reads as lapels rather
63
+ * than a V. The view is the canvas, because the jacket fills it. */
64
+ readonly valet: {
65
+ readonly view: "0 0 1024 1024";
66
+ readonly d: "M236 0 H788 A236 236 0 0 1 1024 236 V788 A236 236 0 0 1 788 1024 H236 A236 236 0 0 1 0 788 V236 A236 236 0 0 1 236 0 Z M234.2 0 L789.8 0 A16 16 0 0 1 805 21.1 C843.3 285.6 625.5 464.7 578.4 700.8 A70 70 0 0 1 445.6 700.8 C398.5 464.7 180.7 285.6 219 21.1 A16 16 0 0 1 234.2 0 Z M444 164.5 C376 162.7 327.4 127.5 322 127.5 A40 40 0 0 0 282 167.5 L282 272.5 A40 40 0 0 0 322 312.5 C327.4 312.5 376 277.4 444 275.5 Z M580 164.5 C648 162.7 696.6 127.5 702 127.5 A40 40 0 0 1 742 167.5 L742 272.5 A40 40 0 0 1 702 312.5 C696.6 312.5 648 277.4 580 275.5 Z M512 60 A68 68 0 0 1 580 128 V232 A68 68 0 0 1 512 300 A68 68 0 0 1 444 232 V128 A68 68 0 0 1 512 60 Z";
67
+ readonly fill: "evenodd";
68
+ };
30
69
  };
31
70
  export type BrandName = keyof typeof BRAND_MARKS;
32
71
  export declare const BRAND_NAMES: BrandName[];
@@ -1,22 +1,3 @@
1
- /**
2
- * The marks, one per wtfalch product.
3
- *
4
- * `@wtfalch/design` serves more than one product, so `Brand` cannot be one
5
- * product's logo -- it is the place every product's mark lives, and adding a
6
- * brand to the system is adding a row here. A mark is a path in its own ink
7
- * box, drawn with `currentColor` at the stroke weight it was designed at, so
8
- * the stylesheet decides the colour and a theme can move it.
9
- *
10
- * A product that animates its mark does that in its own code: tf's lowercase
11
- * tf becomes a cog under the pointer, and that morph is
12
- * `tf/dashboard/src/components/Brand.tsx`, not this package's business. What
13
- * ships here is the still mark, which is also what the product's app icon is
14
- * rendered from -- tf's `brandMark.test.ts` holds the icon, this row and its
15
- * own animated copy to one path.
16
- *
17
- * A sibling module rather than part of `Brand.tsx`, so that file keeps its
18
- * Fast Refresh boundary (`fastRefresh.test.ts`).
19
- */
20
1
  export const BRAND_MARKS = {
21
2
  /** A lowercase tf in one unbroken stroke -- down the t, round the foot, up
22
3
  * the f, over its head, and back along the crossbar through both stems.
@@ -27,5 +8,19 @@ export const BRAND_MARKS = {
27
8
  d: 'M372 262V620A100 100 0 0 0 572 620V330A92 92 0 0 1 756 330A100 100 0 0 1 656 430H296',
28
9
  stroke: 96,
29
10
  },
11
+ /** valet's badge. The rounded square is the jacket, the shirt is cut out of
12
+ * it in an open collar that runs to the top edge, and the bow tie floats in
13
+ * the shirt just below the collar. One path under `evenodd` with five
14
+ * subpaths -- the jacket, the shirt, two wings and the knot -- so the shirt
15
+ * is a hole the surface shows through and the bow is drawn back inside it.
16
+ * Drawn 24-first, which is the header size: the 60 units of jacket over the
17
+ * bow are the pixel that keeps it a bow rather than a notch in the edge, and
18
+ * the collar rolls outward at the top so the shirt reads as lapels rather
19
+ * than a V. The view is the canvas, because the jacket fills it. */
20
+ valet: {
21
+ view: '0 0 1024 1024',
22
+ d: 'M236 0 H788 A236 236 0 0 1 1024 236 V788 A236 236 0 0 1 788 1024 H236 A236 236 0 0 1 0 788 V236 A236 236 0 0 1 236 0 Z M234.2 0 L789.8 0 A16 16 0 0 1 805 21.1 C843.3 285.6 625.5 464.7 578.4 700.8 A70 70 0 0 1 445.6 700.8 C398.5 464.7 180.7 285.6 219 21.1 A16 16 0 0 1 234.2 0 Z M444 164.5 C376 162.7 327.4 127.5 322 127.5 A40 40 0 0 0 282 167.5 L282 272.5 A40 40 0 0 0 322 312.5 C327.4 312.5 376 277.4 444 275.5 Z M580 164.5 C648 162.7 696.6 127.5 702 127.5 A40 40 0 0 1 742 167.5 L742 272.5 A40 40 0 0 1 702 312.5 C696.6 312.5 648 277.4 580 275.5 Z M512 60 A68 68 0 0 1 580 128 V232 A68 68 0 0 1 512 300 A68 68 0 0 1 444 232 V128 A68 68 0 0 1 512 60 Z',
23
+ fill: 'evenodd',
24
+ },
30
25
  };
31
26
  export const BRAND_NAMES = Object.keys(BRAND_MARKS);
@@ -0,0 +1,37 @@
1
+ import type { IconName } from './iconNames';
2
+ /** A glyph, with the viewBox it is drawn *at* rather than the one it shipped in. */
3
+ export interface Glyph {
4
+ /** `x y w h`, measured. Not the source viewBox. */
5
+ view: string;
6
+ /** Filled outline paths. */
7
+ d: string[];
8
+ /** Dots drawn as circles, for a glyph that has them. */
9
+ dots?: [number, number, number][];
10
+ }
11
+ /**
12
+ * The icon set, as data. The system's, shared by every product the way
13
+ * Button is; `Icon.tsx` keeps the reasons for the set and its weight.
14
+ *
15
+ * A glyph, with the viewBox it is drawn *at* rather than the one it shipped in.
16
+ *
17
+ * **This is the whole reason there is a `view` per icon.** Pepicons does not
18
+ * draw to a consistent optical size: measured against its own grid, `times`
19
+ * fills 40% of its box while `wifi-off` fills 90% and the circled alert glyphs
20
+ * fill 96%. Rendered at one `size` that is a 2.4x spread -- a close button half
21
+ * the size of the gear beside it, which reads as broken rather than as
22
+ * hand-drawn.
23
+ *
24
+ * So each `view` is the glyph's measured bounding box, re-centred and grown
25
+ * until the drawing fills 78% of it. Two deliberate exceptions:
26
+ *
27
+ * - `minimize` targets 55%. It is a bar, not a shape, and a bar stretched to
28
+ * 78% of the box stops reading as "minimise" and starts reading as a divider.
29
+ * - The scale is clamped to 0.85x-1.45x. Unclamped, `close` came out at 2x and
30
+ * its strokes went visibly heavier than every neighbour -- trading one
31
+ * inconsistency for another. Clamped, the set sits together at 14px.
32
+ *
33
+ * Numbers came from `getBBox()` on each glyph, not from eyeballing. If you add
34
+ * an icon, measure it the same way; a viewBox copied from the source file will
35
+ * be the wrong size and only slightly, which is the hardest kind to notice.
36
+ */
37
+ export declare const ICONS: Record<IconName, Glyph>;