@wtfalch/design 0.12.0 → 0.14.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 +6 -6
- package/dist/components/Brand.d.ts +4 -4
- package/dist/components/Brand.js +4 -4
- package/dist/components/Input.d.ts +1 -1
- package/dist/components/Rows.d.ts +8 -1
- package/dist/components/Rows.js +3 -2
- package/dist/components/Slider.d.ts +1 -1
- package/dist/components/Slider.js +1 -1
- package/dist/components/Textarea.d.ts +1 -1
- package/dist/components/brandMarks.d.ts +14 -11
- package/dist/components/brandMarks.js +10 -7
- package/dist/components/tourMarker.d.ts +2 -2
- package/dist/components/tourMarker.js +2 -2
- package/dist/{tf.css → otf.css} +22 -13
- package/dist/{tf.d.ts → otf.d.ts} +3 -3
- package/dist/{tf.js → otf.js} +5 -5
- package/dist/products/index.d.ts +3 -3
- package/dist/products/index.js +3 -3
- package/dist/products/{tf.d.ts → otf.d.ts} +7 -7
- package/dist/products/{tf.js → otf.js} +9 -9
- package/dist/products/valet.js +2 -2
- package/dist/styles/index.css +21 -12
- package/dist/themes/choice.d.ts +1 -1
- package/dist/themes/choice.js +1 -1
- package/dist/themes/css.d.ts +1 -1
- package/dist/themes/css.js +1 -1
- package/dist/themes/index.d.ts +3 -3
- package/dist/themes/index.js +6 -6
- package/dist/valet.css +21 -12
- package/package.json +17 -14
package/README.md
CHANGED
|
@@ -70,20 +70,20 @@ Three layers, each falling back to the one under it:
|
|
|
70
70
|
| | |
|
|
71
71
|
|---|---|
|
|
72
72
|
| **the system** | The components, the base values in `tokens.css`, the shared icons and illustrations. |
|
|
73
|
-
| **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
|
|
73
|
+
| **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 otf. `src/products/<name>.ts`. |
|
|
74
74
|
| **the theme** | A palette and a colour scheme, plus anything it deliberately changes. One `:root[data-theme='<id>']` rule each, generated from the object. |
|
|
75
75
|
|
|
76
76
|
The middle layer is what lets a theme be shared between products: it names its
|
|
77
77
|
colours and inherits the identity of whichever product wears it. Before it
|
|
78
78
|
existed, valet's two palettes each restated valet's font and corners, and a
|
|
79
|
-
palette written for two products would have shown
|
|
79
|
+
palette written for two products would have shown otf's font on valet wherever
|
|
80
80
|
it kept quiet.
|
|
81
81
|
|
|
82
82
|
The product's default theme is also written on `:root` when no `data-theme` is
|
|
83
83
|
set, so the first paint is right with no attribute at all; set the attribute
|
|
84
84
|
before the bundle loads only to restore a theme somebody picked (see First
|
|
85
|
-
paint).
|
|
86
|
-
palette, so
|
|
85
|
+
paint). otf's default is `system`, a `prefers-color-scheme` rule rather than a
|
|
86
|
+
palette, so otf still sets the attribute.
|
|
87
87
|
|
|
88
88
|
The main entry is the neutral view of all of it: `PRODUCTS` by name, `THEMES`
|
|
89
89
|
as the union every product's picker and the contrast test read,
|
|
@@ -178,7 +178,7 @@ Your server stays the source of truth. `localStorage` only beats the paint.
|
|
|
178
178
|
|
|
179
179
|
## Marks
|
|
180
180
|
|
|
181
|
-
Every product's mark, by name, in `brandMarks.ts`.
|
|
181
|
+
Every product's mark, by name, in `brandMarks.ts`. otf's is one stroke. valet's
|
|
182
182
|
is a filled badge: the jacket with the shirt cut out of it and a bow tie in the
|
|
183
183
|
cut, one path under `evenodd` so the surface shows through the shirt. Both are
|
|
184
184
|
`currentColor`, so the stylesheet decides the colour and a theme can move it.
|
|
@@ -230,7 +230,7 @@ is the difference from `Markdown`, which parses and must sanitise.
|
|
|
230
230
|
photographed in four themes, the open windows photographed too, and the
|
|
231
231
|
contrast, reduced-motion and keyboard rules are tests rather than sentences.
|
|
232
232
|
It came out of [tf](https://github.com/wtfalch/tf), which is its first consumer;
|
|
233
|
-
valet is the second. A product is a layer:
|
|
233
|
+
valet is the second. A product is a layer: otf and valet each ship as one
|
|
234
234
|
stylesheet and one entry, with their identity under their themes and their
|
|
235
235
|
mark in the table.
|
|
236
236
|
|
|
@@ -4,21 +4,21 @@ import { type BrandName } from './brandMarks.js';
|
|
|
4
4
|
*
|
|
5
5
|
* One component for every wtfalch product: `name` picks the mark out of
|
|
6
6
|
* `brandMarks.ts`, and adding a product is adding a row there. The default is
|
|
7
|
-
* `
|
|
7
|
+
* `otf` because it is the first, not because it is special.
|
|
8
8
|
*
|
|
9
9
|
* `currentColor`, so the stylesheet decides the colour and a theme can move
|
|
10
10
|
* it; the mark itself knows nothing about green. No `width` or `height`
|
|
11
11
|
* attributes either: the viewBox is the ink box, measured, and `.brand` sets
|
|
12
12
|
* a height off the type scale so the width follows the aspect.
|
|
13
13
|
*
|
|
14
|
-
* Still, on purpose. Until 0.2.0 this component also morphed
|
|
14
|
+
* Still, on purpose. Until 0.2.0 this component also morphed otf's mark into a
|
|
15
15
|
* cog under the pointer, which made a shared package carry one product's
|
|
16
|
-
* animation. That morph is
|
|
16
|
+
* animation. That morph is otf's own `Brand` now, layered over this path; a
|
|
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
20
|
*
|
|
21
|
-
* Stroked or filled, whichever the mark is.
|
|
21
|
+
* Stroked or filled, whichever the mark is. otf's is one line at a weight;
|
|
22
22
|
* valet's is a filled badge with the shirt cut out of it and the bow inside
|
|
23
23
|
* the cut, one path under `evenodd` so the surface shows through the shirt.
|
|
24
24
|
* Both are `currentColor`, so `.brand` still decides the colour and a theme
|
package/dist/components/Brand.js
CHANGED
|
@@ -5,21 +5,21 @@ import { BRAND_MARKS, BRAND_NAMES } from './brandMarks.js';
|
|
|
5
5
|
*
|
|
6
6
|
* One component for every wtfalch product: `name` picks the mark out of
|
|
7
7
|
* `brandMarks.ts`, and adding a product is adding a row there. The default is
|
|
8
|
-
* `
|
|
8
|
+
* `otf` because it is the first, not because it is special.
|
|
9
9
|
*
|
|
10
10
|
* `currentColor`, so the stylesheet decides the colour and a theme can move
|
|
11
11
|
* it; the mark itself knows nothing about green. No `width` or `height`
|
|
12
12
|
* attributes either: the viewBox is the ink box, measured, and `.brand` sets
|
|
13
13
|
* a height off the type scale so the width follows the aspect.
|
|
14
14
|
*
|
|
15
|
-
* Still, on purpose. Until 0.2.0 this component also morphed
|
|
15
|
+
* Still, on purpose. Until 0.2.0 this component also morphed otf's mark into a
|
|
16
16
|
* cog under the pointer, which made a shared package carry one product's
|
|
17
|
-
* animation. That morph is
|
|
17
|
+
* animation. That morph is otf's own `Brand` now, layered over this path; a
|
|
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
21
|
*
|
|
22
|
-
* Stroked or filled, whichever the mark is.
|
|
22
|
+
* Stroked or filled, whichever the mark is. otf's is one line at a weight;
|
|
23
23
|
* valet's is a filled badge with the shirt cut out of it and the bow inside
|
|
24
24
|
* the cut, one path under `evenodd` so the surface shows through the shirt.
|
|
25
25
|
* Both are `currentColor`, so `.brand` still decides the colour and a theme
|
|
@@ -27,7 +27,7 @@ export interface Props extends Omit<React.InputHTMLAttributes<HTMLInputElement>,
|
|
|
27
27
|
* `Field`'s `<label for>` already names it. Taking it out of the props is
|
|
28
28
|
* what keeps it off the DOM node: React passes through anything it does
|
|
29
29
|
* not recognise, and the browser got `labelid="…"` with a console warning
|
|
30
|
-
* on every field in valet and
|
|
30
|
+
* on every field in valet and otf. */
|
|
31
31
|
labelId?: string;
|
|
32
32
|
}
|
|
33
33
|
declare const Input: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -60,7 +60,7 @@ export declare function Rows({ children, empty, label, look, className, }: {
|
|
|
60
60
|
look?: 'list' | 'pick';
|
|
61
61
|
className?: string;
|
|
62
62
|
}): import("react").JSX.Element | null;
|
|
63
|
-
export declare function Row({ name, pills, hint, trail, actions, below, onClick, tone, picked, loading, waiting, align, className, }: {
|
|
63
|
+
export declare function Row({ name, pills, hint, trail, actions, below, onClick, tone, picked, loading, waiting, disabled, align, className, }: {
|
|
64
64
|
/** The subject. Truncates -- it is the only part that may. */
|
|
65
65
|
name: React.ReactNode;
|
|
66
66
|
/** Qualifiers, on the name's line. Never truncated: a badge that is
|
|
@@ -93,6 +93,13 @@ export declare function Row({ name, pills, hint, trail, actions, below, onClick,
|
|
|
93
93
|
loading?: boolean;
|
|
94
94
|
/** Something else is working, so this one cannot be chosen yet. */
|
|
95
95
|
waiting?: boolean;
|
|
96
|
+
/** This one cannot be chosen, and nothing on the page will change that: the
|
|
97
|
+
* build lacks something, or a setting elsewhere rules it out. Say why in
|
|
98
|
+
* `hint`. Drawn as `waiting` is -- dimmed, its hit area a disabled button,
|
|
99
|
+
* `picked` still showing through -- but a separate word, because otf's
|
|
100
|
+
* About pane borrowed `waiting` for "this build cannot apply updates", and
|
|
101
|
+
* a row that says it is waiting is a row somebody waits on. */
|
|
102
|
+
disabled?: boolean;
|
|
96
103
|
/** `start` when the row has enough text that vertically centred buttons drift
|
|
97
104
|
* away from the name they belong to. */
|
|
98
105
|
align?: 'center' | 'start';
|
package/dist/components/Rows.js
CHANGED
|
@@ -51,12 +51,13 @@ export function Rows({ children, empty, label, look = 'list', className, }) {
|
|
|
51
51
|
// biome-ignore lint/a11y/useSemanticElements: valid ARIA on a div; a <ul> brings the browser's list reset and the stylesheet and 204 baselines key on `div.rows`.
|
|
52
52
|
role: "list", "aria-label": label, children: items }));
|
|
53
53
|
}
|
|
54
|
-
export function Row({ name, pills, hint, trail, actions, below, onClick, tone, picked, loading, waiting, align = 'center', className, }) {
|
|
54
|
+
export function Row({ name, pills, hint, trail, actions, below, onClick, tone, picked, loading, waiting, disabled, align = 'center', className, }) {
|
|
55
55
|
const body = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "row-subject", children: [_jsxs("span", { className: "named", children: [_jsx("span", { className: "truncate", children: name }), pills] }), hint && _jsx("div", { className: "set-hint", children: hint })] }), trail && _jsx("span", { className: "flex-none ml-auto whitespace-nowrap set-hint mono", children: trail })] }));
|
|
56
56
|
const rowClass = `set-row rows-row${tone ? ` rows-${tone}` : ''}` +
|
|
57
57
|
`${align === 'start' ? ' rows-top' : ''}${picked ? ' is-picked' : ''}` +
|
|
58
58
|
`${loading ? ' is-loading' : ''}${waiting ? ' is-waiting' : ''}` +
|
|
59
|
+
`${disabled ? ' is-disabled' : ''}` +
|
|
59
60
|
`${className ? ` ${className}` : ''}`;
|
|
60
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: rowClass, role: "listitem", children: [onClick ? (_jsx("button", { type: "button", className: "row-hit", disabled: waiting, onClick: onClick, children: body })) : (body), actions] }), below] }));
|
|
61
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: rowClass, role: "listitem", children: [onClick ? (_jsx("button", { type: "button", className: "row-hit", disabled: waiting || disabled, onClick: onClick, children: body })) : (body), actions] }), below] }));
|
|
61
62
|
}
|
|
62
63
|
export default Rows;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* A number chosen from a range, by dragging.
|
|
3
3
|
*
|
|
4
4
|
* For a setting whose answer is "about this much" rather than one of a list:
|
|
5
|
-
* how much memory
|
|
5
|
+
* how much memory otf may take, say. A `Select` with eight sizes on it makes
|
|
6
6
|
* the person pick the nearest wrong one; a box you type a number into asks
|
|
7
7
|
* them to know the ceiling. A slider shows the ceiling, the floor, and where
|
|
8
8
|
* between them they are.
|
|
@@ -11,7 +11,7 @@ import { useEffect, useId, useRef, useState } from 'react';
|
|
|
11
11
|
* A number chosen from a range, by dragging.
|
|
12
12
|
*
|
|
13
13
|
* For a setting whose answer is "about this much" rather than one of a list:
|
|
14
|
-
* how much memory
|
|
14
|
+
* how much memory otf may take, say. A `Select` with eight sizes on it makes
|
|
15
15
|
* the person pick the nearest wrong one; a box you type a number into asks
|
|
16
16
|
* them to know the ceiling. A slider shows the ceiling, the floor, and where
|
|
17
17
|
* between them they are.
|
|
@@ -12,7 +12,7 @@ export interface Props extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaEle
|
|
|
12
12
|
* `Field`'s `<label for>` already names it. Taking it out of the props is
|
|
13
13
|
* what keeps it off the DOM node: React passes through anything it does
|
|
14
14
|
* not recognise, and the browser got `labelid="…"` with a console warning
|
|
15
|
-
* on every field in valet and
|
|
15
|
+
* on every field in valet and otf. */
|
|
16
16
|
labelId?: string;
|
|
17
17
|
}
|
|
18
18
|
declare const Textarea: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
* mark that is a figure rather than a line carries a fill rule instead of a
|
|
10
10
|
* weight, and `Brand` draws whichever it is handed.
|
|
11
11
|
*
|
|
12
|
-
* A product that animates its mark does that in its own code:
|
|
13
|
-
*
|
|
12
|
+
* A product that animates its mark does that in its own code: otf's
|
|
13
|
+
* lowercase otf becomes a cog under the pointer, and that morph is
|
|
14
14
|
* `tf/dashboard/src/components/Brand.tsx`, not this package's business. What
|
|
15
15
|
* ships here is the still mark, which is also what the product's app icon is
|
|
16
|
-
* rendered from --
|
|
16
|
+
* rendered from -- otf's `brandMark.test.ts` holds the icon, this row and its
|
|
17
17
|
* own animated copy to one path.
|
|
18
18
|
*
|
|
19
19
|
* A sibling module rather than part of `Brand.tsx`, so that file keeps its
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
/**
|
|
23
23
|
* A mark: one path in its own ink box, stroked or filled.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
25
|
+
* otf's is a stroke, one line at the weight it was drawn at. valet's is a
|
|
26
26
|
* figure: a filled badge with the shirt cut out of it and the bow drawn back
|
|
27
27
|
* inside the cut, which is one path under `evenodd` -- the jacket, the shirt
|
|
28
28
|
* and the bow are subpaths, and the rule makes the shirt a hole the surface
|
|
@@ -43,13 +43,16 @@ export interface FillMark {
|
|
|
43
43
|
fill: 'evenodd' | 'nonzero';
|
|
44
44
|
}
|
|
45
45
|
export declare const BRAND_MARKS: {
|
|
46
|
-
/** A lowercase tf in one unbroken stroke
|
|
47
|
-
* the
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
/** A lowercase tf merged into an O, in one unbroken stroke. Both ends of
|
|
47
|
+
* the tf start on the O -- the crossbar runs left and folds down onto it,
|
|
48
|
+
* the stem runs up and turns right onto it -- and the 95 degrees of ring
|
|
49
|
+
* between those two points is left out, so the O reads as open across the
|
|
50
|
+
* top left rather than closed. One stroke, no loose ends. The view is the
|
|
51
|
+
* stroke's outer bounds, not the canvas: the stroke is 96 wide, so 48 of
|
|
52
|
+
* cap and arc on every side is already in these. */
|
|
53
|
+
readonly otf: {
|
|
54
|
+
readonly view: "69.3 -12.2 958.4 958.4";
|
|
55
|
+
readonly d: "M470.2 43.0A120 120 0 0 0 372 161.0V620A100 100 0 0 0 572 620V330A92 92 0 0 1 756 330A100 100 0 0 1 656 430H248.6A120 120 0 0 0 132.9 582.0A431.2 431.2 0 1 0 470.2 43.0";
|
|
53
56
|
readonly stroke: 96;
|
|
54
57
|
};
|
|
55
58
|
/** valet's badge. The rounded square is the jacket, the shirt is cut out of
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
export const BRAND_MARKS = {
|
|
2
|
-
/** A lowercase tf in one unbroken stroke
|
|
3
|
-
* the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
/** A lowercase tf merged into an O, in one unbroken stroke. Both ends of
|
|
3
|
+
* the tf start on the O -- the crossbar runs left and folds down onto it,
|
|
4
|
+
* the stem runs up and turns right onto it -- and the 95 degrees of ring
|
|
5
|
+
* between those two points is left out, so the O reads as open across the
|
|
6
|
+
* top left rather than closed. One stroke, no loose ends. The view is the
|
|
7
|
+
* stroke's outer bounds, not the canvas: the stroke is 96 wide, so 48 of
|
|
8
|
+
* cap and arc on every side is already in these. */
|
|
9
|
+
otf: {
|
|
10
|
+
view: '69.3 -12.2 958.4 958.4',
|
|
11
|
+
d: 'M470.2 43.0A120 120 0 0 0 372 161.0V620A100 100 0 0 0 572 620V330A92 92 0 0 1 756 330A100 100 0 0 1 656 430H248.6A120 120 0 0 0 132.9 582.0A431.2 431.2 0 1 0 470.2 43.0',
|
|
9
12
|
stroke: 96,
|
|
10
13
|
},
|
|
11
14
|
/** valet's badge. The rounded square is the jacket, the shirt is cut out of
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* Settings and is said here rather than left implied. */
|
|
20
20
|
/** Where a product records that the tour was seen. Every product that installs
|
|
21
21
|
* the package gets its own key by passing one; the default is the package's,
|
|
22
|
-
* so two products on one origin do not share a memory.
|
|
23
|
-
* `
|
|
22
|
+
* so two products on one origin do not share a memory. otf passes
|
|
23
|
+
* `otf-tour-seen`, the key its users already hold. */
|
|
24
24
|
export declare const DEFAULT_TOUR_KEY = "design-tour-seen";
|
|
25
25
|
export declare function tourSeen(key?: string): boolean;
|
|
26
26
|
/**
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* Settings and is said here rather than left implied. */
|
|
20
20
|
/** Where a product records that the tour was seen. Every product that installs
|
|
21
21
|
* the package gets its own key by passing one; the default is the package's,
|
|
22
|
-
* so two products on one origin do not share a memory.
|
|
23
|
-
* `
|
|
22
|
+
* so two products on one origin do not share a memory. otf passes
|
|
23
|
+
* `otf-tour-seen`, the key its users already hold. */
|
|
24
24
|
export const DEFAULT_TOUR_KEY = 'design-tour-seen';
|
|
25
25
|
export function tourSeen(key = DEFAULT_TOUR_KEY) {
|
|
26
26
|
try {
|
package/dist/{tf.css → otf.css}
RENAMED
|
@@ -1279,7 +1279,7 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
1279
1279
|
|
|
1280
1280
|
/* The checkbox, drawn rather than inherited.
|
|
1281
1281
|
The native one is the platform's, which means it is a different shape, a
|
|
1282
|
-
different blue and a different size on every machine
|
|
1282
|
+
different blue and a different size on every machine otf runs on -- the one
|
|
1283
1283
|
control in the app that refused to join the design system. It is also the
|
|
1284
1284
|
only form control that can be replaced in pure CSS: unlike a select, whose
|
|
1285
1285
|
dropdown belongs to the OS, a checkbox is a box and a tick.
|
|
@@ -1503,7 +1503,7 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
|
1503
1503
|
whatever next needs to know. */
|
|
1504
1504
|
/* The wordmark, and the same mark the app icon carries: `Brand.tsx` draws the
|
|
1505
1505
|
path `desktop/build/icon.html` draws, and `test/brandMark.test.ts` holds
|
|
1506
|
-
them to one string. It was "
|
|
1506
|
+
them to one string. It was "otf" set in the system font at -0.14em, matching
|
|
1507
1507
|
an icon that did the same; both are now one drawn line, and the tracking
|
|
1508
1508
|
arithmetic that used to live here went with the letters.
|
|
1509
1509
|
The height comes off the type scale rather than being a pixel: 1.5 times
|
|
@@ -1521,7 +1521,7 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
|
1521
1521
|
width: auto;
|
|
1522
1522
|
color: var(--good);
|
|
1523
1523
|
/* A product that animates its mark turns it about its own centre, which is
|
|
1524
|
-
the ink box's;
|
|
1524
|
+
the ink box's; otf's cog does. */
|
|
1525
1525
|
transform-origin: center;
|
|
1526
1526
|
}
|
|
1527
1527
|
|
|
@@ -1820,7 +1820,7 @@ input:active, textarea:active, select:active { transform: none; }
|
|
|
1820
1820
|
.spin { animation: none; opacity: 0.6; }
|
|
1821
1821
|
}
|
|
1822
1822
|
|
|
1823
|
-
@keyframes
|
|
1823
|
+
@keyframes otf-bounce {
|
|
1824
1824
|
0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
|
|
1825
1825
|
40% { opacity: 1; transform: translateY(-3px); }
|
|
1826
1826
|
}
|
|
@@ -2827,8 +2827,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2827
2827
|
}
|
|
2828
2828
|
|
|
2829
2829
|
/* Only where there is something to pick. A row without `row-hit` is not a
|
|
2830
|
-
choice, and lighting it up on hover promises something that does not happen.
|
|
2831
|
-
|
|
2830
|
+
choice, and lighting it up on hover promises something that does not happen.
|
|
2831
|
+
Nor is a row whose hit is disabled: a waiting row lit up under its own
|
|
2832
|
+
`not-allowed` cursor, and `disabled` would have made that permanent. */
|
|
2833
|
+
.rows-pick > .rows-row:has(.row-hit:enabled):hover {
|
|
2832
2834
|
border-color: var(--accent);
|
|
2833
2835
|
background: var(--panel-2);
|
|
2834
2836
|
}
|
|
@@ -2880,9 +2882,16 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2880
2882
|
/* And nothing else can be chosen while it happens: picking a second model
|
|
2881
2883
|
mid-load queues a second load, which is how a machine ends up swapping two
|
|
2882
2884
|
sets of weights it cannot hold at once. */
|
|
2883
|
-
.rows-pick > .rows-row.is-waiting
|
|
2885
|
+
.rows-pick > .rows-row.is-waiting,
|
|
2886
|
+
.rows-pick > .rows-row.is-disabled { opacity: 0.5; }
|
|
2884
2887
|
|
|
2885
|
-
.rows-pick > .rows-row.is-waiting .row-hit
|
|
2888
|
+
.rows-pick > .rows-row.is-waiting .row-hit,
|
|
2889
|
+
.rows-pick > .rows-row.is-disabled .row-hit { cursor: not-allowed; }
|
|
2890
|
+
|
|
2891
|
+
/* Dimmed once. `.row-hit:disabled` dims the button as well, and under the
|
|
2892
|
+
row's own opacity the hint of a disabled row, the line saying why it cannot
|
|
2893
|
+
be chosen, came out at a quarter strength. */
|
|
2894
|
+
.rows-pick > .rows-row.is-disabled > .row-hit:disabled { opacity: 1; }
|
|
2886
2895
|
|
|
2887
2896
|
@media (prefers-reduced-motion: reduce) {
|
|
2888
2897
|
/* The border still says which one, without the breathing. */
|
|
@@ -2934,7 +2943,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2934
2943
|
margin-bottom: calc(var(--space-2) * -1);
|
|
2935
2944
|
}
|
|
2936
2945
|
|
|
2937
|
-
.rows-pick > .rows-row.is-picked:has(.row-hit):hover {
|
|
2946
|
+
.rows-pick > .rows-row.is-picked:has(.row-hit:enabled):hover {
|
|
2938
2947
|
background: color-mix(in srgb, var(--accent) 13%, var(--panel));
|
|
2939
2948
|
}
|
|
2940
2949
|
|
|
@@ -2993,7 +3002,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2993
3002
|
border-radius: var(--radius-sm);
|
|
2994
3003
|
}
|
|
2995
3004
|
|
|
2996
|
-
.row-hit:hover .truncate { color: var(--accent); }
|
|
3005
|
+
.row-hit:enabled:hover .truncate { color: var(--accent); }
|
|
2997
3006
|
|
|
2998
3007
|
.row-hit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
2999
3008
|
}
|
|
@@ -3328,7 +3337,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
3328
3337
|
as part of the heading rather than as the first line of content. */
|
|
3329
3338
|
.modal-body { padding: var(--space-2) var(--space-4) var(--space-3); overflow-y: auto; }
|
|
3330
3339
|
|
|
3331
|
-
/* The close cross. Its rule stayed behind in
|
|
3340
|
+
/* The close cross. Its rule stayed behind in otf's stylesheet when the split
|
|
3332
3341
|
ran -- a one-letter class name, and the scan that finds classes in source
|
|
3333
3342
|
dropped it -- so in the package the cross had only `.icon-btn`, which is the
|
|
3334
3343
|
picker's 34px square tile with a panel behind it. The head is laid out for
|
|
@@ -3405,7 +3414,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
3405
3414
|
/* The tone is on the box, not only on the button. A destructive question that
|
|
3406
3415
|
looks like every other question is one the reader skims.
|
|
3407
3416
|
`Dialog` writes this as `dialog-${tone}`, which a literal scan of the source
|
|
3408
|
-
never sees -- so the rule was left behind in
|
|
3417
|
+
never sees -- so the rule was left behind in otf's stylesheet by the split and
|
|
3409
3418
|
the package shipped a `tone="bad"` prop that changed nothing. */
|
|
3410
3419
|
.dialog-bad { border-color: color-mix(in oklab, var(--bad) 45%, var(--border)); }
|
|
3411
3420
|
|
|
@@ -5072,6 +5081,6 @@ body.splitting * { cursor: inherit !important; }
|
|
|
5072
5081
|
pointer-events: none;
|
|
5073
5082
|
}
|
|
5074
5083
|
}
|
|
5075
|
-
/* ----
|
|
5084
|
+
/* ---- otf: the themes ---- */
|
|
5076
5085
|
:root[data-theme='night']{--bg:#0f1115;--panel:#161a21;--panel-2:#1c222b;--border:#262d38;--text:#e6e9ef;--muted:#8b94a4;--accent:#5b9dff;--accent-dim:#2a4877;--app-bg:#0f1115;color-scheme:dark}
|
|
5077
5086
|
:root[data-theme='paper']{--bg:#f6f7f9;--panel:#ffffff;--panel-2:#f0f2f5;--border:#e4e8ec;--border-strong:#8792a1;--text:#191d23;--muted:#5d6773;--accent:#0e7872;--accent-dim:#7fbdb8;--on-accent:#ffffff;--good:#1c7a4a;--warn:#8a6216;--bad:#b3312c;--info:#216bc9;--shadow-1:0 4px 14px rgba(16, 24, 40, 0.08);--shadow-2:0 8px 24px rgba(16, 24, 40, 0.10);--shadow-3:0 12px 32px rgba(16, 24, 40, 0.12);--scrim:rgba(16, 24, 40, 0.32);color-scheme:light}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* otf's entry: the package, as otf.
|
|
3
3
|
*
|
|
4
4
|
* Everything the main entry exports, and on top of it `Brand`, `THEMES`,
|
|
5
|
-
* `DEFAULT_THEME` and `applyTheme` bound to
|
|
5
|
+
* `DEFAULT_THEME` and `applyTheme` bound to otf -- a local export shadows
|
|
6
6
|
* the same name from `export *`. A site is one product, so it imports this
|
|
7
|
-
* and `@wtfalch/design/
|
|
7
|
+
* and `@wtfalch/design/otf.css` and nothing of anyone else's.
|
|
8
8
|
*/
|
|
9
9
|
export * from './index.js';
|
|
10
10
|
export declare const product: import("./products/index.js").Product;
|
package/dist/{tf.js → otf.js}
RENAMED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* otf's entry: the package, as otf.
|
|
3
3
|
*
|
|
4
4
|
* Everything the main entry exports, and on top of it `Brand`, `THEMES`,
|
|
5
|
-
* `DEFAULT_THEME` and `applyTheme` bound to
|
|
5
|
+
* `DEFAULT_THEME` and `applyTheme` bound to otf -- a local export shadows
|
|
6
6
|
* the same name from `export *`. A site is one product, so it imports this
|
|
7
|
-
* and `@wtfalch/design/
|
|
7
|
+
* and `@wtfalch/design/otf.css` and nothing of anyone else's.
|
|
8
8
|
*/
|
|
9
9
|
export * from './index.js';
|
|
10
10
|
import { bindProduct } from './products/index.js';
|
|
11
|
-
import {
|
|
12
|
-
const bound = bindProduct(
|
|
11
|
+
import { otf } from './products/otf.js';
|
|
12
|
+
const bound = bindProduct(otf);
|
|
13
13
|
export const product = bound.product;
|
|
14
14
|
export const Brand = bound.Brand;
|
|
15
15
|
export const THEMES = bound.THEMES;
|
package/dist/products/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { type Theme, type ThemeTokens } from '../themes/index.js';
|
|
|
14
14
|
*
|
|
15
15
|
* The middle layer is what lets a theme be shared between products. A theme
|
|
16
16
|
* is a sparse map, and what it is sparse *over* decides what a silent token
|
|
17
|
-
* shows: over the base it shows
|
|
17
|
+
* shows: over the base it shows otf, over the product it shows the product.
|
|
18
18
|
* Before this layer valet's two palettes each restated valet's font and
|
|
19
19
|
* corners, because there was nowhere else to put them.
|
|
20
20
|
*
|
|
@@ -36,7 +36,7 @@ export interface Product {
|
|
|
36
36
|
}
|
|
37
37
|
/** Every product, by the name of its mark. */
|
|
38
38
|
export declare const PRODUCTS: {
|
|
39
|
-
readonly
|
|
39
|
+
readonly otf: Product;
|
|
40
40
|
readonly valet: Product;
|
|
41
41
|
};
|
|
42
42
|
/** Declare a product, so a default that is not a theme or a key `applyTheme`
|
|
@@ -47,7 +47,7 @@ export declare function defineProduct(product: Product): Product;
|
|
|
47
47
|
*
|
|
48
48
|
* An id the product does not offer resolves to the product's default rather
|
|
49
49
|
* than the package's, so a valet setting that remembers a theme valet has
|
|
50
|
-
* since dropped comes up as valet, not as
|
|
50
|
+
* since dropped comes up as valet, not as otf.
|
|
51
51
|
*/
|
|
52
52
|
export declare function productTheme(product: Product, theme?: string | Theme): Theme;
|
|
53
53
|
type BrandProps = ComponentProps<typeof BrandDefault>;
|
package/dist/products/index.js
CHANGED
|
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import BrandDefault from '../components/Brand.js';
|
|
3
3
|
import { applyTheme } from '../themes/index.js';
|
|
4
4
|
import { themeId } from '../themes/css.js';
|
|
5
|
-
import {
|
|
5
|
+
import { otf } from './otf.js';
|
|
6
6
|
import { valet } from './valet.js';
|
|
7
7
|
/** Every product, by the name of its mark. */
|
|
8
|
-
export const PRODUCTS = {
|
|
8
|
+
export const PRODUCTS = { otf, valet };
|
|
9
9
|
/** Declare a product, so a default that is not a theme or a key `applyTheme`
|
|
10
10
|
* would not use is an error where it is written. */
|
|
11
11
|
export function defineProduct(product) {
|
|
@@ -24,7 +24,7 @@ export function defineProduct(product) {
|
|
|
24
24
|
*
|
|
25
25
|
* An id the product does not offer resolves to the product's default rather
|
|
26
26
|
* than the package's, so a valet setting that remembers a theme valet has
|
|
27
|
-
* since dropped comes up as valet, not as
|
|
27
|
+
* since dropped comes up as valet, not as otf.
|
|
28
28
|
*/
|
|
29
29
|
export function productTheme(product, theme = product.defaultTheme) {
|
|
30
30
|
const resolved = typeof theme === 'string'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Theme } from '../themes/index.js';
|
|
2
2
|
import type { Product } from './index.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* otf's themes: the three the package shipped with, and the ones the gallery
|
|
5
5
|
* was photographed in first.
|
|
6
6
|
*
|
|
7
7
|
* `system` is a theme, not a mode. A `prefers-color-scheme` block that
|
|
@@ -14,19 +14,19 @@ export declare const system: Theme;
|
|
|
14
14
|
export declare const night: Theme;
|
|
15
15
|
export declare const paper: Theme;
|
|
16
16
|
/** Keyed by the name `applyTheme` takes, which is `name` lowercased. */
|
|
17
|
-
export declare const
|
|
17
|
+
export declare const OTF_THEMES: {
|
|
18
18
|
readonly system: Theme;
|
|
19
19
|
readonly night: Theme;
|
|
20
20
|
readonly paper: Theme;
|
|
21
21
|
};
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* otf, the product.
|
|
24
24
|
*
|
|
25
|
-
* Its identity is empty on purpose: `tokens.css` carries
|
|
25
|
+
* Its identity is empty on purpose: `tokens.css` carries otf's font, shape and
|
|
26
26
|
* density as the base values, so there is nothing to lay over them. `system`
|
|
27
27
|
* is the default because following the OS is the choice a tool open all day
|
|
28
28
|
* should make for you -- and because it is a `prefers-color-scheme` rule
|
|
29
|
-
* rather than a palette,
|
|
30
|
-
* `data-theme` before the bundle loads, as it always has.
|
|
29
|
+
* rather than a palette, otf's stylesheet writes no default on `:root`; otf
|
|
30
|
+
* sets `data-theme` before the bundle loads, as it always has.
|
|
31
31
|
*/
|
|
32
|
-
export declare const
|
|
32
|
+
export declare const otf: Product;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* otf's themes: the three the package shipped with, and the ones the gallery
|
|
3
3
|
* was photographed in first.
|
|
4
4
|
*
|
|
5
5
|
* `system` is a theme, not a mode. A `prefers-color-scheme` block that
|
|
@@ -85,20 +85,20 @@ export const paper = {
|
|
|
85
85
|
},
|
|
86
86
|
};
|
|
87
87
|
/** Keyed by the name `applyTheme` takes, which is `name` lowercased. */
|
|
88
|
-
export const
|
|
88
|
+
export const OTF_THEMES = { system, night, paper };
|
|
89
89
|
/**
|
|
90
|
-
*
|
|
90
|
+
* otf, the product.
|
|
91
91
|
*
|
|
92
|
-
* Its identity is empty on purpose: `tokens.css` carries
|
|
92
|
+
* Its identity is empty on purpose: `tokens.css` carries otf's font, shape and
|
|
93
93
|
* density as the base values, so there is nothing to lay over them. `system`
|
|
94
94
|
* is the default because following the OS is the choice a tool open all day
|
|
95
95
|
* should make for you -- and because it is a `prefers-color-scheme` rule
|
|
96
|
-
* rather than a palette,
|
|
97
|
-
* `data-theme` before the bundle loads, as it always has.
|
|
96
|
+
* rather than a palette, otf's stylesheet writes no default on `:root`; otf
|
|
97
|
+
* sets `data-theme` before the bundle loads, as it always has.
|
|
98
98
|
*/
|
|
99
|
-
export const
|
|
100
|
-
name: '
|
|
99
|
+
export const otf = {
|
|
100
|
+
name: 'otf',
|
|
101
101
|
identity: {},
|
|
102
|
-
themes:
|
|
102
|
+
themes: OTF_THEMES,
|
|
103
103
|
defaultTheme: 'system',
|
|
104
104
|
};
|
package/dist/products/valet.js
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
* tones are what the system says. They never share a colour.
|
|
9
9
|
*
|
|
10
10
|
* Every colour is measured, not judged: `contrast.test.ts` holds both palettes
|
|
11
|
-
* to the same pairs as
|
|
11
|
+
* to the same pairs as otf's.
|
|
12
12
|
*
|
|
13
13
|
* The palettes name colours and nothing else. What makes valet valet under
|
|
14
14
|
* either of them -- the type and the corners -- is the identity below, on
|
|
15
15
|
* `:root` in valet's stylesheet and under every palette `applyTheme` lays on.
|
|
16
16
|
* Both palettes used to restate it, which is the shape that breaks the moment
|
|
17
17
|
* a theme is shared between products: silent on the font, it would have fallen
|
|
18
|
-
* back to
|
|
18
|
+
* back to otf's.
|
|
19
19
|
*/
|
|
20
20
|
const FONT = 'var(--font-sans, "IBM Plex Sans"), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
|
21
21
|
const FONT_MONO = 'var(--font-mono, "JetBrains Mono"), ui-monospace, SFMono-Regular, Menlo, monospace';
|
package/dist/styles/index.css
CHANGED
|
@@ -1009,7 +1009,7 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
1009
1009
|
|
|
1010
1010
|
/* The checkbox, drawn rather than inherited.
|
|
1011
1011
|
The native one is the platform's, which means it is a different shape, a
|
|
1012
|
-
different blue and a different size on every machine
|
|
1012
|
+
different blue and a different size on every machine otf runs on -- the one
|
|
1013
1013
|
control in the app that refused to join the design system. It is also the
|
|
1014
1014
|
only form control that can be replaced in pure CSS: unlike a select, whose
|
|
1015
1015
|
dropdown belongs to the OS, a checkbox is a box and a tick.
|
|
@@ -1233,7 +1233,7 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
|
1233
1233
|
whatever next needs to know. */
|
|
1234
1234
|
/* The wordmark, and the same mark the app icon carries: `Brand.tsx` draws the
|
|
1235
1235
|
path `desktop/build/icon.html` draws, and `test/brandMark.test.ts` holds
|
|
1236
|
-
them to one string. It was "
|
|
1236
|
+
them to one string. It was "otf" set in the system font at -0.14em, matching
|
|
1237
1237
|
an icon that did the same; both are now one drawn line, and the tracking
|
|
1238
1238
|
arithmetic that used to live here went with the letters.
|
|
1239
1239
|
The height comes off the type scale rather than being a pixel: 1.5 times
|
|
@@ -1251,7 +1251,7 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
|
1251
1251
|
width: auto;
|
|
1252
1252
|
color: var(--good);
|
|
1253
1253
|
/* A product that animates its mark turns it about its own centre, which is
|
|
1254
|
-
the ink box's;
|
|
1254
|
+
the ink box's; otf's cog does. */
|
|
1255
1255
|
transform-origin: center;
|
|
1256
1256
|
}
|
|
1257
1257
|
|
|
@@ -1550,7 +1550,7 @@ input:active, textarea:active, select:active { transform: none; }
|
|
|
1550
1550
|
.spin { animation: none; opacity: 0.6; }
|
|
1551
1551
|
}
|
|
1552
1552
|
|
|
1553
|
-
@keyframes
|
|
1553
|
+
@keyframes otf-bounce {
|
|
1554
1554
|
0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
|
|
1555
1555
|
40% { opacity: 1; transform: translateY(-3px); }
|
|
1556
1556
|
}
|
|
@@ -2557,8 +2557,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2557
2557
|
}
|
|
2558
2558
|
|
|
2559
2559
|
/* Only where there is something to pick. A row without `row-hit` is not a
|
|
2560
|
-
choice, and lighting it up on hover promises something that does not happen.
|
|
2561
|
-
|
|
2560
|
+
choice, and lighting it up on hover promises something that does not happen.
|
|
2561
|
+
Nor is a row whose hit is disabled: a waiting row lit up under its own
|
|
2562
|
+
`not-allowed` cursor, and `disabled` would have made that permanent. */
|
|
2563
|
+
.rows-pick > .rows-row:has(.row-hit:enabled):hover {
|
|
2562
2564
|
border-color: var(--accent);
|
|
2563
2565
|
background: var(--panel-2);
|
|
2564
2566
|
}
|
|
@@ -2610,9 +2612,16 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2610
2612
|
/* And nothing else can be chosen while it happens: picking a second model
|
|
2611
2613
|
mid-load queues a second load, which is how a machine ends up swapping two
|
|
2612
2614
|
sets of weights it cannot hold at once. */
|
|
2613
|
-
.rows-pick > .rows-row.is-waiting
|
|
2615
|
+
.rows-pick > .rows-row.is-waiting,
|
|
2616
|
+
.rows-pick > .rows-row.is-disabled { opacity: 0.5; }
|
|
2614
2617
|
|
|
2615
|
-
.rows-pick > .rows-row.is-waiting .row-hit
|
|
2618
|
+
.rows-pick > .rows-row.is-waiting .row-hit,
|
|
2619
|
+
.rows-pick > .rows-row.is-disabled .row-hit { cursor: not-allowed; }
|
|
2620
|
+
|
|
2621
|
+
/* Dimmed once. `.row-hit:disabled` dims the button as well, and under the
|
|
2622
|
+
row's own opacity the hint of a disabled row, the line saying why it cannot
|
|
2623
|
+
be chosen, came out at a quarter strength. */
|
|
2624
|
+
.rows-pick > .rows-row.is-disabled > .row-hit:disabled { opacity: 1; }
|
|
2616
2625
|
|
|
2617
2626
|
@media (prefers-reduced-motion: reduce) {
|
|
2618
2627
|
/* The border still says which one, without the breathing. */
|
|
@@ -2664,7 +2673,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2664
2673
|
margin-bottom: calc(var(--space-2) * -1);
|
|
2665
2674
|
}
|
|
2666
2675
|
|
|
2667
|
-
.rows-pick > .rows-row.is-picked:has(.row-hit):hover {
|
|
2676
|
+
.rows-pick > .rows-row.is-picked:has(.row-hit:enabled):hover {
|
|
2668
2677
|
background: color-mix(in srgb, var(--accent) 13%, var(--panel));
|
|
2669
2678
|
}
|
|
2670
2679
|
|
|
@@ -2723,7 +2732,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2723
2732
|
border-radius: var(--radius-sm);
|
|
2724
2733
|
}
|
|
2725
2734
|
|
|
2726
|
-
.row-hit:hover .truncate { color: var(--accent); }
|
|
2735
|
+
.row-hit:enabled:hover .truncate { color: var(--accent); }
|
|
2727
2736
|
|
|
2728
2737
|
.row-hit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
2729
2738
|
}
|
|
@@ -3058,7 +3067,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
3058
3067
|
as part of the heading rather than as the first line of content. */
|
|
3059
3068
|
.modal-body { padding: var(--space-2) var(--space-4) var(--space-3); overflow-y: auto; }
|
|
3060
3069
|
|
|
3061
|
-
/* The close cross. Its rule stayed behind in
|
|
3070
|
+
/* The close cross. Its rule stayed behind in otf's stylesheet when the split
|
|
3062
3071
|
ran -- a one-letter class name, and the scan that finds classes in source
|
|
3063
3072
|
dropped it -- so in the package the cross had only `.icon-btn`, which is the
|
|
3064
3073
|
picker's 34px square tile with a panel behind it. The head is laid out for
|
|
@@ -3135,7 +3144,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
3135
3144
|
/* The tone is on the box, not only on the button. A destructive question that
|
|
3136
3145
|
looks like every other question is one the reader skims.
|
|
3137
3146
|
`Dialog` writes this as `dialog-${tone}`, which a literal scan of the source
|
|
3138
|
-
never sees -- so the rule was left behind in
|
|
3147
|
+
never sees -- so the rule was left behind in otf's stylesheet by the split and
|
|
3139
3148
|
the package shipped a `tone="bad"` prop that changed nothing. */
|
|
3140
3149
|
.dialog-bad { border-color: color-mix(in oklab, var(--bad) 45%, var(--border)); }
|
|
3141
3150
|
|
package/dist/themes/choice.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare function themeChoices(product: BrandName | Product, only?: readon
|
|
|
50
50
|
/**
|
|
51
51
|
* The blocking script, as a string to put in a `<script>` in `<head>`.
|
|
52
52
|
*
|
|
53
|
-
* `dangerouslySetInnerHTML={{ __html: themeChoiceScript('
|
|
53
|
+
* `dangerouslySetInnerHTML={{ __html: themeChoiceScript('otf') }}` in a Next
|
|
54
54
|
* root layout, above everything. It reads the stored id, checks it against
|
|
55
55
|
* what this app offers, and writes `data-theme` on `<html>`.
|
|
56
56
|
*
|
package/dist/themes/choice.js
CHANGED
|
@@ -49,7 +49,7 @@ export function themeChoices(product, only) {
|
|
|
49
49
|
/**
|
|
50
50
|
* The blocking script, as a string to put in a `<script>` in `<head>`.
|
|
51
51
|
*
|
|
52
|
-
* `dangerouslySetInnerHTML={{ __html: themeChoiceScript('
|
|
52
|
+
* `dangerouslySetInnerHTML={{ __html: themeChoiceScript('otf') }}` in a Next
|
|
53
53
|
* root layout, above everything. It reads the stored id, checks it against
|
|
54
54
|
* what this app offers, and writes `data-theme` on `<html>`.
|
|
55
55
|
*
|
package/dist/themes/css.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export declare function productCss(themes: Record<string, Theme>): string;
|
|
|
31
31
|
* wins over the base values by coming later at the same specificity; the
|
|
32
32
|
* default theme on `:root:not([data-theme])`, so the first paint is right with
|
|
33
33
|
* no attribute at all; the components; and one rule per theme. A default with
|
|
34
|
-
* no tokens --
|
|
34
|
+
* no tokens -- otf's `system`, which is a media query rather than a palette --
|
|
35
35
|
* writes no default rule, and neither does an empty identity.
|
|
36
36
|
*
|
|
37
37
|
* Structural, not typed to `Product`: that type lives beside the React
|
package/dist/themes/css.js
CHANGED
|
@@ -53,7 +53,7 @@ export function productCss(themes) {
|
|
|
53
53
|
* wins over the base values by coming later at the same specificity; the
|
|
54
54
|
* default theme on `:root:not([data-theme])`, so the first paint is right with
|
|
55
55
|
* no attribute at all; the components; and one rule per theme. A default with
|
|
56
|
-
* no tokens --
|
|
56
|
+
* no tokens -- otf's `system`, which is a media query rather than a palette --
|
|
57
57
|
* writes no default rule, and neither does an empty identity.
|
|
58
58
|
*
|
|
59
59
|
* Structural, not typed to `Product`: that type lives beside the React
|
package/dist/themes/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* than a property that silently does nothing — which is exactly the failure a
|
|
10
10
|
* string-keyed map would produce, at run time, invisibly. That property is the
|
|
11
11
|
* reason this file is exported from the package at all: a consumer writing a
|
|
12
|
-
* theme gets the same compile error
|
|
12
|
+
* theme gets the same compile error otf gets, and a theme that ships as JSON or
|
|
13
13
|
* as a hand-written CSS file does not.
|
|
14
14
|
*
|
|
15
15
|
* ---
|
|
@@ -173,7 +173,7 @@ export declare function defineTheme(theme: Theme): Theme;
|
|
|
173
173
|
* mark is, and 0.2.0 already made `Brand` the home of every product's mark by
|
|
174
174
|
* name; keeping each product's palette in its own repo meant each repo
|
|
175
175
|
* re-deriving first paint, the contrast measurement and a page to look at it
|
|
176
|
-
* on. So `products/
|
|
176
|
+
* on. So `products/otf.ts` holds otf's three and `products/valet.ts` valet's two,
|
|
177
177
|
* this is the union, and the contrast test and the gallery read the union.
|
|
178
178
|
*
|
|
179
179
|
* A consumer that wants only its own imports the product module, or the CSS
|
|
@@ -186,7 +186,7 @@ export declare function isTheme(name: unknown): name is string;
|
|
|
186
186
|
* Put a theme on an element.
|
|
187
187
|
*
|
|
188
188
|
* **The element is an argument, and that is the whole difference between this
|
|
189
|
-
* and the version that lived in the app.**
|
|
189
|
+
* and the version that lived in the app.** otf owns its document, so writing to
|
|
190
190
|
* `documentElement` was free. A package does not: mounted inside a page it did
|
|
191
191
|
* not build, it has to be able to theme a subtree and leave the rest alone.
|
|
192
192
|
* Defaulting to `documentElement` keeps the app case a no-op.
|
package/dist/themes/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* than a property that silently does nothing — which is exactly the failure a
|
|
10
10
|
* string-keyed map would produce, at run time, invisibly. That property is the
|
|
11
11
|
* reason this file is exported from the package at all: a consumer writing a
|
|
12
|
-
* theme gets the same compile error
|
|
12
|
+
* theme gets the same compile error otf gets, and a theme that ships as JSON or
|
|
13
13
|
* as a hand-written CSS file does not.
|
|
14
14
|
*
|
|
15
15
|
* ---
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* token added to the stylesheet and to no list fails the suite rather than
|
|
38
38
|
* becoming a fourth, undocumented category.
|
|
39
39
|
*/
|
|
40
|
-
import {
|
|
40
|
+
import { OTF_THEMES } from '../products/otf.js';
|
|
41
41
|
import { VALET_THEMES } from '../products/valet.js';
|
|
42
42
|
/**
|
|
43
43
|
* Computed from a themeable token, and not settable.
|
|
@@ -164,13 +164,13 @@ export function defineTheme(theme) {
|
|
|
164
164
|
* mark is, and 0.2.0 already made `Brand` the home of every product's mark by
|
|
165
165
|
* name; keeping each product's palette in its own repo meant each repo
|
|
166
166
|
* re-deriving first paint, the contrast measurement and a page to look at it
|
|
167
|
-
* on. So `products/
|
|
167
|
+
* on. So `products/otf.ts` holds otf's three and `products/valet.ts` valet's two,
|
|
168
168
|
* this is the union, and the contrast test and the gallery read the union.
|
|
169
169
|
*
|
|
170
170
|
* A consumer that wants only its own imports the product module, or the CSS
|
|
171
171
|
* `build-products.mjs` generates from it, and bundles nobody else's.
|
|
172
172
|
*/
|
|
173
|
-
export const THEMES = { ...
|
|
173
|
+
export const THEMES = { ...OTF_THEMES, ...VALET_THEMES };
|
|
174
174
|
export const DEFAULT_THEME = 'system';
|
|
175
175
|
export function isTheme(name) {
|
|
176
176
|
return typeof name === 'string' && name in THEMES;
|
|
@@ -179,7 +179,7 @@ export function isTheme(name) {
|
|
|
179
179
|
* Put a theme on an element.
|
|
180
180
|
*
|
|
181
181
|
* **The element is an argument, and that is the whole difference between this
|
|
182
|
-
* and the version that lived in the app.**
|
|
182
|
+
* and the version that lived in the app.** otf owns its document, so writing to
|
|
183
183
|
* `documentElement` was free. A package does not: mounted inside a page it did
|
|
184
184
|
* not build, it has to be able to theme a subtree and leave the rest alone.
|
|
185
185
|
* Defaulting to `documentElement` keeps the app case a no-op.
|
|
@@ -191,7 +191,7 @@ export function isTheme(name) {
|
|
|
191
191
|
*/
|
|
192
192
|
export function applyTheme(theme, el = document.documentElement) {
|
|
193
193
|
/* A registered name, or a `Theme` object straight from `defineTheme`.
|
|
194
|
-
The second is how an app that is not
|
|
194
|
+
The second is how an app that is not otf applies its own palette without
|
|
195
195
|
first pushing it into a registry it does not own -- `THEMES` is the
|
|
196
196
|
built-ins, and a consumer's theme is theirs. */
|
|
197
197
|
const resolved = typeof theme === 'string' ? (THEMES[theme] ?? THEMES[DEFAULT_THEME]) : theme;
|
package/dist/valet.css
CHANGED
|
@@ -1283,7 +1283,7 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
1283
1283
|
|
|
1284
1284
|
/* The checkbox, drawn rather than inherited.
|
|
1285
1285
|
The native one is the platform's, which means it is a different shape, a
|
|
1286
|
-
different blue and a different size on every machine
|
|
1286
|
+
different blue and a different size on every machine otf runs on -- the one
|
|
1287
1287
|
control in the app that refused to join the design system. It is also the
|
|
1288
1288
|
only form control that can be replaced in pure CSS: unlike a select, whose
|
|
1289
1289
|
dropdown belongs to the OS, a checkbox is a box and a tick.
|
|
@@ -1507,7 +1507,7 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
|
1507
1507
|
whatever next needs to know. */
|
|
1508
1508
|
/* The wordmark, and the same mark the app icon carries: `Brand.tsx` draws the
|
|
1509
1509
|
path `desktop/build/icon.html` draws, and `test/brandMark.test.ts` holds
|
|
1510
|
-
them to one string. It was "
|
|
1510
|
+
them to one string. It was "otf" set in the system font at -0.14em, matching
|
|
1511
1511
|
an icon that did the same; both are now one drawn line, and the tracking
|
|
1512
1512
|
arithmetic that used to live here went with the letters.
|
|
1513
1513
|
The height comes off the type scale rather than being a pixel: 1.5 times
|
|
@@ -1525,7 +1525,7 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
|
1525
1525
|
width: auto;
|
|
1526
1526
|
color: var(--good);
|
|
1527
1527
|
/* A product that animates its mark turns it about its own centre, which is
|
|
1528
|
-
the ink box's;
|
|
1528
|
+
the ink box's; otf's cog does. */
|
|
1529
1529
|
transform-origin: center;
|
|
1530
1530
|
}
|
|
1531
1531
|
|
|
@@ -1824,7 +1824,7 @@ input:active, textarea:active, select:active { transform: none; }
|
|
|
1824
1824
|
.spin { animation: none; opacity: 0.6; }
|
|
1825
1825
|
}
|
|
1826
1826
|
|
|
1827
|
-
@keyframes
|
|
1827
|
+
@keyframes otf-bounce {
|
|
1828
1828
|
0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
|
|
1829
1829
|
40% { opacity: 1; transform: translateY(-3px); }
|
|
1830
1830
|
}
|
|
@@ -2831,8 +2831,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2831
2831
|
}
|
|
2832
2832
|
|
|
2833
2833
|
/* Only where there is something to pick. A row without `row-hit` is not a
|
|
2834
|
-
choice, and lighting it up on hover promises something that does not happen.
|
|
2835
|
-
|
|
2834
|
+
choice, and lighting it up on hover promises something that does not happen.
|
|
2835
|
+
Nor is a row whose hit is disabled: a waiting row lit up under its own
|
|
2836
|
+
`not-allowed` cursor, and `disabled` would have made that permanent. */
|
|
2837
|
+
.rows-pick > .rows-row:has(.row-hit:enabled):hover {
|
|
2836
2838
|
border-color: var(--accent);
|
|
2837
2839
|
background: var(--panel-2);
|
|
2838
2840
|
}
|
|
@@ -2884,9 +2886,16 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2884
2886
|
/* And nothing else can be chosen while it happens: picking a second model
|
|
2885
2887
|
mid-load queues a second load, which is how a machine ends up swapping two
|
|
2886
2888
|
sets of weights it cannot hold at once. */
|
|
2887
|
-
.rows-pick > .rows-row.is-waiting
|
|
2889
|
+
.rows-pick > .rows-row.is-waiting,
|
|
2890
|
+
.rows-pick > .rows-row.is-disabled { opacity: 0.5; }
|
|
2888
2891
|
|
|
2889
|
-
.rows-pick > .rows-row.is-waiting .row-hit
|
|
2892
|
+
.rows-pick > .rows-row.is-waiting .row-hit,
|
|
2893
|
+
.rows-pick > .rows-row.is-disabled .row-hit { cursor: not-allowed; }
|
|
2894
|
+
|
|
2895
|
+
/* Dimmed once. `.row-hit:disabled` dims the button as well, and under the
|
|
2896
|
+
row's own opacity the hint of a disabled row, the line saying why it cannot
|
|
2897
|
+
be chosen, came out at a quarter strength. */
|
|
2898
|
+
.rows-pick > .rows-row.is-disabled > .row-hit:disabled { opacity: 1; }
|
|
2890
2899
|
|
|
2891
2900
|
@media (prefers-reduced-motion: reduce) {
|
|
2892
2901
|
/* The border still says which one, without the breathing. */
|
|
@@ -2938,7 +2947,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2938
2947
|
margin-bottom: calc(var(--space-2) * -1);
|
|
2939
2948
|
}
|
|
2940
2949
|
|
|
2941
|
-
.rows-pick > .rows-row.is-picked:has(.row-hit):hover {
|
|
2950
|
+
.rows-pick > .rows-row.is-picked:has(.row-hit:enabled):hover {
|
|
2942
2951
|
background: color-mix(in srgb, var(--accent) 13%, var(--panel));
|
|
2943
2952
|
}
|
|
2944
2953
|
|
|
@@ -2997,7 +3006,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2997
3006
|
border-radius: var(--radius-sm);
|
|
2998
3007
|
}
|
|
2999
3008
|
|
|
3000
|
-
.row-hit:hover .truncate { color: var(--accent); }
|
|
3009
|
+
.row-hit:enabled:hover .truncate { color: var(--accent); }
|
|
3001
3010
|
|
|
3002
3011
|
.row-hit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
3003
3012
|
}
|
|
@@ -3332,7 +3341,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
3332
3341
|
as part of the heading rather than as the first line of content. */
|
|
3333
3342
|
.modal-body { padding: var(--space-2) var(--space-4) var(--space-3); overflow-y: auto; }
|
|
3334
3343
|
|
|
3335
|
-
/* The close cross. Its rule stayed behind in
|
|
3344
|
+
/* The close cross. Its rule stayed behind in otf's stylesheet when the split
|
|
3336
3345
|
ran -- a one-letter class name, and the scan that finds classes in source
|
|
3337
3346
|
dropped it -- so in the package the cross had only `.icon-btn`, which is the
|
|
3338
3347
|
picker's 34px square tile with a panel behind it. The head is laid out for
|
|
@@ -3409,7 +3418,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
3409
3418
|
/* The tone is on the box, not only on the button. A destructive question that
|
|
3410
3419
|
looks like every other question is one the reader skims.
|
|
3411
3420
|
`Dialog` writes this as `dialog-${tone}`, which a literal scan of the source
|
|
3412
|
-
never sees -- so the rule was left behind in
|
|
3421
|
+
never sees -- so the rule was left behind in otf's stylesheet by the split and
|
|
3413
3422
|
the package shipped a `tone="bad"` prop that changed nothing. */
|
|
3414
3423
|
.dialog-bad { border-color: color-mix(in oklab, var(--bad) 45%, var(--border)); }
|
|
3415
3424
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtfalch/design",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "wtfalch's design system: themeable components on a fixed token vocabulary.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
},
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"type": "module",
|
|
27
|
-
"files": [
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
28
30
|
"publishConfig": {
|
|
29
31
|
"access": "public"
|
|
30
32
|
},
|
|
@@ -33,15 +35,15 @@
|
|
|
33
35
|
"types": "./dist/index.d.ts",
|
|
34
36
|
"default": "./dist/index.js"
|
|
35
37
|
},
|
|
36
|
-
"./
|
|
37
|
-
"types": "./dist/
|
|
38
|
-
"default": "./dist/
|
|
38
|
+
"./otf": {
|
|
39
|
+
"types": "./dist/otf.d.ts",
|
|
40
|
+
"default": "./dist/otf.js"
|
|
39
41
|
},
|
|
40
42
|
"./valet": {
|
|
41
43
|
"types": "./dist/valet.d.ts",
|
|
42
44
|
"default": "./dist/valet.js"
|
|
43
45
|
},
|
|
44
|
-
"./
|
|
46
|
+
"./otf.css": "./dist/otf.css",
|
|
45
47
|
"./valet.css": "./dist/valet.css",
|
|
46
48
|
"./themes": {
|
|
47
49
|
"types": "./dist/themes/index.d.ts",
|
|
@@ -58,16 +60,12 @@
|
|
|
58
60
|
"default": "./dist/rich-text.js"
|
|
59
61
|
}
|
|
60
62
|
},
|
|
61
|
-
"sideEffects": [
|
|
63
|
+
"sideEffects": [
|
|
64
|
+
"*.css"
|
|
65
|
+
],
|
|
62
66
|
"engines": {
|
|
63
67
|
"node": ">=22.0.0"
|
|
64
68
|
},
|
|
65
|
-
"scripts": {
|
|
66
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/fix-extensions.mjs && node scripts/copy-css.mjs && node scripts/build-products.mjs",
|
|
67
|
-
"prepack": "pnpm build",
|
|
68
|
-
"typecheck": "tsc --noEmit",
|
|
69
|
-
"test": "vitest run"
|
|
70
|
-
},
|
|
71
69
|
"peerDependencies": {
|
|
72
70
|
"@tiptap/extension-link": "^3.31.0",
|
|
73
71
|
"@tiptap/extensions": "^3.31.0",
|
|
@@ -120,5 +118,10 @@
|
|
|
120
118
|
"zod": {
|
|
121
119
|
"optional": true
|
|
122
120
|
}
|
|
121
|
+
},
|
|
122
|
+
"scripts": {
|
|
123
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/fix-extensions.mjs && node scripts/copy-css.mjs && node scripts/build-products.mjs",
|
|
124
|
+
"typecheck": "tsc --noEmit",
|
|
125
|
+
"test": "vitest run"
|
|
123
126
|
}
|
|
124
|
-
}
|
|
127
|
+
}
|