@wtfalch/design 0.10.1 → 0.11.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
@@ -17,6 +17,12 @@ draws them changes. 0.9.0 did exactly that to several of them, and 0.10.0
17
17
  deleted 41 more: the components that drew them draw themselves now, so the
18
18
  rules had no reader left.
19
19
 
20
+ 0.11.0 went the other way and removed the rules that reached *through* a
21
+ class, which is the same mistake seen from the other side. `.row > .switch-row`
22
+ styled a Toggle by the container a caller had written round it, so the caller
23
+ had to know to write `.row` -- a class no component here renders. Those are
24
+ props now: `inRow` on `Toggle` and on `Field`, `tile` on `Checkbox`.
25
+
20
26
  If you need a card, render `<Card>`; if you need a row of your own, write it
21
27
  in the token vocabulary:
22
28
 
@@ -1,4 +1,4 @@
1
- export default function Checkbox({ label, hint, meta, checked, defaultChecked, onChange, name, value, disabled, className, }: {
1
+ export default function Checkbox({ label, hint, meta, checked, defaultChecked, onChange, name, value, disabled, tile, className, }: {
2
2
  label: React.ReactNode;
3
3
  /** What choosing it means, or what it costs. Under the name. `hint`, the
4
4
  * word `Toggle`, `Slider` and `Field` use for the same line; it was `why`. */
@@ -17,5 +17,15 @@ export default function Checkbox({ label, hint, meta, checked, defaultChecked, o
17
17
  * which is rarely the word a Server Action wants to read. */
18
18
  value?: string;
19
19
  disabled?: boolean;
20
+ /** In a grid of tiles rather than a run of full-width rows.
21
+ *
22
+ * The tick is positioned at the top right, and in a full-width row the
23
+ * label never reaches it. In a tile it runs underneath, so the body needs
24
+ * the room reserved. This was a `.choice-tiles .choice-body` rule, which
25
+ * meant the package styling its own component through a class the caller
26
+ * had to know to write on the container -- and there is no such container
27
+ * component, so the class was only ever a private name in a consumer's
28
+ * markup. A prop says the same thing and belongs to the component. */
29
+ tile?: boolean;
20
30
  className?: string;
21
31
  }): import("react").JSX.Element;
@@ -38,14 +38,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
38
38
  * neither) for one the form reads at submit.
39
39
  */
40
40
  import { Checkbox as AriaCheckbox } from 'react-aria-components';
41
- export default function Checkbox({ label, hint, meta, checked, defaultChecked, onChange, name, value, disabled, className, }) {
41
+ export default function Checkbox({ label, hint, meta, checked, defaultChecked, onChange, name, value, disabled, tile, className, }) {
42
42
  /* React Aria's `Checkbox` is the `<label>`. It keeps the real input in the
43
43
  markup and visually hidden -- exactly the arrangement this component
44
44
  already had by hand -- and stamps `data-selected`, `data-focus-visible`
45
45
  and `data-disabled` on the row. The ring and the tick are still drawn by
46
46
  the row's `::after` and `::before`; `checkbox.css` now reads the state off
47
47
  those attributes instead of `:has(input:checked)`. */
48
- return (_jsx(AriaCheckbox, { className: `choice${className ? ` ${className}` : ''}`,
48
+ return (_jsx(AriaCheckbox, { className: `choice${tile ? ' choice-tile' : ''}${className ? ` ${className}` : ''}`,
49
49
  /* `undefined` is what makes React Aria leave the box uncontrolled, so
50
50
  the controlled and uncontrolled cases are the same call: pass both and
51
51
  let whichever was given decide. Passing `isSelected={false}` here
@@ -53,5 +53,5 @@ match, label, busyLabel, busy = false, disabled = false, onConfirm,
53
53
  unavailable, }) {
54
54
  const [asking, setAsking] = useState(false);
55
55
  const [typed, setTyped] = useState('');
56
- return (_jsxs("div", { className: `danger-act${kind === 'plain' ? ' plain' : ''}`, children: [_jsx("h4", { children: title }), _jsx("div", { className: "set-hint", children: description }), unavailable ? (_jsx("div", { className: "set-hint mt-2", children: unavailable })) : confirm === 'type' ? (_jsx(_Fragment, { children: _jsxs("div", { className: "row danger-row field-row", children: [_jsx(Field, { label: "Confirm", hint: _jsxs(_Fragment, { children: ["Type ", _jsx("code", { className: "mono danger-name", children: match }), " to confirm."] }), children: (f) => (_jsx(Input, { ...f, mono: true, value: typed, disabled: disabled || busy, autoComplete: "off", spellCheck: false, onChange: (e) => setTyped(e.target.value) })) }), _jsx(Button, { kind: "danger", isDisabled: disabled || busy || typed.trim() !== match, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label })] }) })) : confirm === 'none' ? (_jsx("div", { className: "row danger-row", children: _jsx(Button, { kind: kind === 'plain' ? 'primary' : 'danger', isDisabled: disabled || busy, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label }) })) : asking ? (_jsxs("div", { className: "row danger-row", children: [_jsx(Button, { kind: "danger", isDisabled: disabled || busy, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label }), _jsx(Button, { isDisabled: disabled || busy, onPress: () => setAsking(false), children: "Cancel" })] })) : (_jsx("div", { className: "row danger-row", children: _jsx(Button, { kind: "danger", isDisabled: disabled || busy, onPress: () => setAsking(true), children: label }) }))] }));
56
+ return (_jsxs("div", { className: `danger-act${kind === 'plain' ? ' plain' : ''}`, children: [_jsx("h4", { children: title }), _jsx("div", { className: "set-hint", children: description }), unavailable ? (_jsx("div", { className: "set-hint mt-2", children: unavailable })) : confirm === 'type' ? (_jsx(_Fragment, { children: _jsxs("div", { className: "danger-row danger-field-row", children: [_jsx(Field, { inRow: true, label: "Confirm", hint: _jsxs(_Fragment, { children: ["Type ", _jsx("code", { className: "mono danger-name", children: match }), " to confirm."] }), children: (f) => (_jsx(Input, { ...f, mono: true, value: typed, disabled: disabled || busy, autoComplete: "off", spellCheck: false, onChange: (e) => setTyped(e.target.value) })) }), _jsx(Button, { kind: "danger", isDisabled: disabled || busy || typed.trim() !== match, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label })] }) })) : confirm === 'none' ? (_jsx("div", { className: "danger-row", children: _jsx(Button, { kind: kind === 'plain' ? 'primary' : 'danger', isDisabled: disabled || busy, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label }) })) : asking ? (_jsxs("div", { className: "danger-row", children: [_jsx(Button, { kind: "danger", isDisabled: disabled || busy, onPress: onConfirm, children: busy ? (busyLabel ?? '…') : label }), _jsx(Button, { isDisabled: disabled || busy, onPress: () => setAsking(false), children: "Cancel" })] })) : (_jsx("div", { className: "danger-row", children: _jsx(Button, { kind: "danger", isDisabled: disabled || busy, onPress: () => setAsking(true), children: label }) }))] }));
57
57
  }
@@ -1,6 +1,6 @@
1
1
  import { type FieldWiring } from './fieldWiring.js';
2
2
  export type { FieldWiring } from './fieldWiring.js';
3
- export default function Field({ label, hint, error, required, children, labelHidden, layout, className, }: {
3
+ export default function Field({ label, hint, error, required, children, labelHidden, layout, inRow, className, }: {
4
4
  /** What the field is. Always given -- there is no unlabelled case, only
5
5
  * fields whose label is hidden. */
6
6
  label: string;
@@ -39,5 +39,12 @@ export default function Field({ label, hint, error, required, children, labelHid
39
39
  * rendered somewhere the provider does not reach.
40
40
  */
41
41
  children: React.ReactNode | ((field: FieldWiring) => React.ReactNode);
42
+ /** Sitting in a row of controls rather than in a column of settings.
43
+ *
44
+ * This was a rule keyed on the container -- `.field-row > .field` -- which
45
+ * meant the package reaching through a class the caller had to know to
46
+ * write, and `.field-row` is not a class this package hands out. A prop says the
47
+ * same thing and travels with the component. */
48
+ inRow?: boolean;
42
49
  className?: string;
43
50
  }): import("react").JSX.Element;
@@ -39,7 +39,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
39
39
  */
40
40
  import { useId } from 'react';
41
41
  import { FieldWiringContext } from './fieldWiring.js';
42
- export default function Field({ label, hint, error, required, children, labelHidden, layout = 'stack', className, }) {
42
+ export default function Field({ label, hint, error, required, children, labelHidden, layout = 'stack', inRow, className, }) {
43
43
  const id = useId();
44
44
  const hintId = `${id}-hint`;
45
45
  const errorId = `${id}-error`;
@@ -53,5 +53,5 @@ export default function Field({ label, hint, error, required, children, labelHid
53
53
  'aria-describedby': [hint && hintId, error && errorId].filter(Boolean).join(' ') || undefined,
54
54
  'aria-invalid': error ? true : undefined,
55
55
  };
56
- return (_jsxs("div", { className: `field field-${layout}-layout${error ? ' field-bad' : ''}${className ? ` ${className}` : ''}`, children: [_jsxs("label", { id: labelId, className: labelHidden ? 'sr-only' : 'field-label', htmlFor: id, children: [label, required && (_jsxs("span", { className: "text-muted", "aria-label": "required", children: [' ', "*"] }))] }), hint && (_jsx("p", { className: "field-hint", id: hintId, children: hint })), typeof children === 'function' ? (children(wiring)) : (_jsx(FieldWiringContext.Provider, { value: wiring, children: children })), error && (_jsx("p", { className: "field-error", id: errorId, role: "alert", children: error }))] }));
56
+ return (_jsxs("div", { className: `field field-${layout}-layout${error ? ' field-bad' : ''}${inRow ? ' field-in-row' : ''}${className ? ` ${className}` : ''}`, children: [_jsxs("label", { id: labelId, className: labelHidden ? 'sr-only' : 'field-label', htmlFor: id, children: [label, required && (_jsxs("span", { className: "text-muted", "aria-label": "required", children: [' ', "*"] }))] }), hint && (_jsx("p", { className: "field-hint", id: hintId, children: hint })), typeof children === 'function' ? (children(wiring)) : (_jsx(FieldWiringContext.Provider, { value: wiring, children: children })), error && (_jsx("p", { className: "field-error", id: errorId, role: "alert", children: error }))] }));
57
57
  }
@@ -1,4 +1,4 @@
1
- export default function Toggle({ label, hint, checked, onChange, disabled, said, labelHidden, size, className, }: {
1
+ export default function Toggle({ label, hint, checked, onChange, disabled, said, labelHidden, size, inRow, className, }: {
2
2
  label: React.ReactNode;
3
3
  /** What it does, or what turning it off costs. Under the label, in the same
4
4
  * column, so a long explanation does not push the switch off its row. */
@@ -22,5 +22,12 @@ export default function Toggle({ label, hint, checked, onChange, disabled, said,
22
22
  /** The same three the rest of the controls take. `sm` for a switch in a
23
23
  * toolbar or a dense row; `lg` where it is the only thing on the screen. */
24
24
  size?: 'sm' | 'md' | 'lg';
25
+ /** Sitting in a row of controls rather than in a column of settings.
26
+ *
27
+ * This was a rule keyed on the container -- `.row > .switch-row` -- which
28
+ * meant the package reaching through a class the caller had to know to
29
+ * write, and `.row` is not a class this package hands out. A prop says the
30
+ * same thing and travels with the component. */
31
+ inRow?: boolean;
25
32
  className?: string;
26
33
  }): import("react").JSX.Element;
@@ -49,7 +49,7 @@ const KNOB = { sm: 10, md: 12, lg: 16 };
49
49
  * than pressed. Six is over the jitter and still a third of the way across
50
50
  * the shortest track. */
51
51
  const SLOP = 6;
52
- export default function Toggle({ label, hint, checked, onChange, disabled, said, labelHidden, size = 'md', className, }) {
52
+ export default function Toggle({ label, hint, checked, onChange, disabled, said, labelHidden, size = 'md', inRow, className, }) {
53
53
  /* React Aria's `Switch` is the `<label>`: it owns a visually-hidden
54
54
  `<input type="checkbox" role="switch">` and stamps `data-selected`,
55
55
  `data-disabled`, `data-focus-visible` and `data-pressed` on the label. So
@@ -154,7 +154,7 @@ export default function Toggle({ label, hint, checked, onChange, disabled, said,
154
154
  return 0;
155
155
  return Math.min(1, Math.max(0, (x - box.left - 2 - knobWidth / 2) / travel));
156
156
  };
157
- return (_jsxs(Switch, { ref: rowRef, className: `switch-row switch-${size}${className ? ` ${className}` : ''}`, isSelected: shown, onChange: commit, isDisabled: disabled,
157
+ return (_jsxs(Switch, { ref: rowRef, className: `switch-row switch-${size}${inRow ? ' switch-in-row' : ''}${className ? ` ${className}` : ''}`, isSelected: shown, onChange: commit, isDisabled: disabled,
158
158
  /* Read-only, not disabled, while a request is out: focus stays where it
159
159
  is and the row does not dim, it just refuses a second answer until
160
160
  the first has been taken. */
@@ -1243,13 +1243,6 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
1243
1243
  to { transform: scaleX(0); }
1244
1244
  }
1245
1245
 
1246
- /* (The bordered head bar that used to live here is gone. It was a second
1247
- `.card-head` -- same name, different thing -- and the only place it was ever
1248
- used was the gallery page documenting it. A card that holds rows now takes
1249
- `title` like any other, and the header the component draws is the one
1250
- header.) */
1251
- .row { display: flex; align-items: center; gap: var(--space-3); }
1252
-
1253
1246
  .ctl-grow { flex: 1; min-width: 0; }
1254
1247
 
1255
1248
  .mono { font-family: var(--font-mono); font-size: var(--text-sm); }
@@ -1510,13 +1503,6 @@ input:active, textarea:active, select:active { transform: none; }
1510
1503
  template are allowed to diverge, and this is only saying that they have. */
1511
1504
  .set-hint.drifted { color: var(--warn); }
1512
1505
 
1513
- /* One applet, alone on the page, at `/applet/<id>`. Fills the window: there is
1514
- no grid here to decide a height, and a screenshot of an applet letterboxed
1515
- inside a page is mostly not the applet. */
1516
- .bare { height: 100vh; display: flex; flex-direction: column; background: var(--bg); }
1517
-
1518
- .bare > * { flex: 1; min-height: 0; }
1519
-
1520
1506
  /* A round is in flight. On the button that asks for one, because that is where
1521
1507
  you are looking when you have just pressed it -- and not in the foot, which
1522
1508
  would flicker every thirty seconds on a tile that is working perfectly. */
@@ -1552,10 +1538,6 @@ input:active, textarea:active, select:active { transform: none; }
1552
1538
  40% { opacity: 1; transform: translateY(-3px); }
1553
1539
  }
1554
1540
 
1555
- /* The top bar carries the title, the views and the way into settings. Nothing
1556
- that belongs to a conversation lives here any more, so it can breathe. */
1557
- .topbar .ctl-grow { flex: 1; min-width: 0; }
1558
-
1559
1541
  @keyframes listening-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
1560
1542
 
1561
1543
  /* table ---------------------------------------------------------------- */
@@ -1824,23 +1806,6 @@ input:active, textarea:active, select:active { transform: none; }
1824
1806
  border-color: var(--bad);
1825
1807
  }
1826
1808
 
1827
- /* Three fields on one line, and their inputs on one line too.
1828
-
1829
- `Field` puts the description *above* the control, which is the house rule and
1830
- right -- but only `Arguments` has one here, so as a flex row the other two
1831
- inputs floated a description's height above it and the row read as broken.
1832
- Aligned to the end instead: the labels sit where their text needs them and
1833
- the three boxes share a baseline, which is the edge the eye actually follows.
1834
-
1835
- A grid rather than flex, so `Arguments` gets the room it needs by declaration
1836
- instead of by an inline pixel width on each of its neighbours. */
1837
- .mcp-local .field-row {
1838
- display: grid;
1839
- grid-template-columns: 1fr 1fr 2fr;
1840
- align-items: end;
1841
- gap: var(--space-3);
1842
- }
1843
-
1844
1809
  /* How an applet's polling is going, said the same way on every tile. Muted
1845
1810
  when it is going fine -- this is a fact you glance at, not one you are being
1846
1811
  told. */
@@ -1885,7 +1850,15 @@ input:active, textarea:active, select:active { transform: none; }
1885
1850
 
1886
1851
  .danger-act > .set-hint + .set-hint { margin-top: var(--space-1); }
1887
1852
 
1888
- .danger-row { margin-top: var(--space-2); gap: var(--space-2); align-items: center; }
1853
+ /* `display: flex` is here now. These rows carried `.row` as well, which is a
1854
+ class no component of this package renders -- it only ever reached them
1855
+ because a consumer's stylesheet happened to define it too. */
1856
+ .danger-row {
1857
+ display: flex;
1858
+ margin-top: var(--space-2);
1859
+ gap: var(--space-2);
1860
+ align-items: center;
1861
+ }
1889
1862
 
1890
1863
  .danger-row > input { min-width: 0; }
1891
1864
 
@@ -1907,15 +1880,15 @@ input:active, textarea:active, select:active { transform: none; }
1907
1880
  column-gap: var(--space-3);
1908
1881
  }
1909
1882
 
1910
- .field-row { align-items: flex-end; gap: var(--space-2); }
1883
+ .danger-field-row { align-items: flex-end; gap: var(--space-2); }
1911
1884
 
1912
- .field-row > button { flex: none; }
1885
+ .danger-field-row > button { flex: none; }
1913
1886
 
1914
1887
  /* Two forms one under the other, whose buttons say different words -- "Add"
1915
1888
  and "Block". Sized to their text they came out different widths, and two
1916
1889
  controls in the same column at the same indent that do not line up read as
1917
1890
  an accident rather than as a pair. */
1918
- .field-row > button { min-width: 9ch; }
1891
+ .danger-field-row > button { min-width: 9ch; }
1919
1892
  }
1920
1893
 
1921
1894
  /* ---- ./callout.css ---- */
@@ -2246,9 +2219,12 @@ input:active, textarea:active, select:active { transform: none; }
2246
2219
  /* Sized to its words, for a switch that sits in a row of other controls rather
2247
2220
  than owning a row of its own. `flex: 1` there would push the label away from
2248
2221
  the switch by the whole width of the bar. */
2249
- .row > .switch-row { flex: none; padding-block: 0; }
2222
+ /* `inRow`: a toggle beside other controls rather than in a column of
2223
+ settings. Was `.row > .switch-row`, which reached through `.row` -- a class
2224
+ no component here renders. */
2225
+ .switch-in-row { flex: none; padding-block: 0; }
2250
2226
 
2251
- .row > .switch-row .switch-body { flex: none; }
2227
+ .switch-in-row .switch-body { flex: none; }
2252
2228
 
2253
2229
  /* The opposite case: a switch that *is* the row, with something small beside it
2254
2230
  -- a tooltip, a state word. `.set-row` wraps, so a switch that does not grow
@@ -3023,11 +2999,6 @@ input[type='radio']:active:not(:disabled) {
3023
2999
  width: min(760px, 100%);
3024
3000
  }
3025
3001
 
3026
- /* Settings is wider than the others: the rail carries group headings with a
3027
- hint each and indented tabs under them, and a pane beside it that draws
3028
- cards with a control on the right -- 760 gave one or the other room. */
3029
- .modal.modal-settings { width: min(880px, 100%); }
3030
-
3031
3002
  .modal {
3032
3003
  /* The window, less the backdrop's padding at each end -- `--space-15` is 60px
3033
3004
  and there are two of them, above and below.
@@ -3462,25 +3433,12 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
3462
3433
  /* A run of checkboxes is one choice, not several settings that happen to be
3463
3434
  adjacent. Tight enough that the group reads as a block -- at the spacing a
3464
3435
  form field wants, four tools look like four unrelated questions. */
3465
- /* The same choice, laid out as tiles instead of a column.
3466
-
3467
- Four short names down the full width of a card is four wide bands for four
3468
- words -- the row is sized by the card, not by anything in it, so `Read` gets
3469
- the same 1000px as a sentence would. As tiles they are sized by their
3470
- content and the group reads as one set of four rather than a stack of four
3471
- settings.
3472
-
3473
- `auto-fit` with a floor rather than a fixed four: the pane is a modal that
3474
- narrows with the window, and four columns of 40px is worse than two of 160.
3475
- The floor is the width at which a two-word name still fits on one line. */
3476
- .choice-tiles {
3477
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
3478
- gap: var(--space-2);
3479
- }
3480
-
3481
3436
  /* Room for the tick, which is absolutely positioned at the top right. In a
3482
- full-width row the text never reached it; in a tile it would run underneath. */
3483
- .choice-tiles .choice-body { padding-right: var(--space-6); }
3437
+ full-width row the text never reaches it; in a tile it would run underneath.
3438
+ `tile` is a prop on the component now: this was keyed on a `.choice-tiles`
3439
+ container that no component renders, so the class only ever existed in a
3440
+ consumer's markup and the package was styling itself through it. */
3441
+ .choice-tile .choice-body { padding-right: var(--space-6); }
3484
3442
 
3485
3443
  /* The floor is 220px and not 150px because of what four items do at the widths
3486
3444
  in between. The settings pane gives this group 514px: at a 150px floor that
@@ -3855,7 +3813,10 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
3855
3813
  /* Fields stacked down a form. Wider than the gap inside one field, so the
3856
3814
  label of the next belongs to the input under it rather than to the error
3857
3815
  above it. */
3858
- .field-row > .field { flex: 1; min-width: 0; }
3816
+ /* `inRow`: a field sharing a line with other controls rather than stacked in a
3817
+ column. Was `.field-row > .field`, which reached through a container class
3818
+ the caller had to write and this package does not hand out. */
3819
+ .field-in-row { flex: 1; min-width: 0; }
3859
3820
 
3860
3821
  /* A password with an eye -- `Input type="password"`.
3861
3822
  A grid rather than `position: absolute`: both children take the one cell, the
package/dist/tf.css CHANGED
@@ -1513,13 +1513,6 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
1513
1513
  to { transform: scaleX(0); }
1514
1514
  }
1515
1515
 
1516
- /* (The bordered head bar that used to live here is gone. It was a second
1517
- `.card-head` -- same name, different thing -- and the only place it was ever
1518
- used was the gallery page documenting it. A card that holds rows now takes
1519
- `title` like any other, and the header the component draws is the one
1520
- header.) */
1521
- .row { display: flex; align-items: center; gap: var(--space-3); }
1522
-
1523
1516
  .ctl-grow { flex: 1; min-width: 0; }
1524
1517
 
1525
1518
  .mono { font-family: var(--font-mono); font-size: var(--text-sm); }
@@ -1780,13 +1773,6 @@ input:active, textarea:active, select:active { transform: none; }
1780
1773
  template are allowed to diverge, and this is only saying that they have. */
1781
1774
  .set-hint.drifted { color: var(--warn); }
1782
1775
 
1783
- /* One applet, alone on the page, at `/applet/<id>`. Fills the window: there is
1784
- no grid here to decide a height, and a screenshot of an applet letterboxed
1785
- inside a page is mostly not the applet. */
1786
- .bare { height: 100vh; display: flex; flex-direction: column; background: var(--bg); }
1787
-
1788
- .bare > * { flex: 1; min-height: 0; }
1789
-
1790
1776
  /* A round is in flight. On the button that asks for one, because that is where
1791
1777
  you are looking when you have just pressed it -- and not in the foot, which
1792
1778
  would flicker every thirty seconds on a tile that is working perfectly. */
@@ -1822,10 +1808,6 @@ input:active, textarea:active, select:active { transform: none; }
1822
1808
  40% { opacity: 1; transform: translateY(-3px); }
1823
1809
  }
1824
1810
 
1825
- /* The top bar carries the title, the views and the way into settings. Nothing
1826
- that belongs to a conversation lives here any more, so it can breathe. */
1827
- .topbar .ctl-grow { flex: 1; min-width: 0; }
1828
-
1829
1811
  @keyframes listening-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
1830
1812
 
1831
1813
  /* table ---------------------------------------------------------------- */
@@ -2094,23 +2076,6 @@ input:active, textarea:active, select:active { transform: none; }
2094
2076
  border-color: var(--bad);
2095
2077
  }
2096
2078
 
2097
- /* Three fields on one line, and their inputs on one line too.
2098
-
2099
- `Field` puts the description *above* the control, which is the house rule and
2100
- right -- but only `Arguments` has one here, so as a flex row the other two
2101
- inputs floated a description's height above it and the row read as broken.
2102
- Aligned to the end instead: the labels sit where their text needs them and
2103
- the three boxes share a baseline, which is the edge the eye actually follows.
2104
-
2105
- A grid rather than flex, so `Arguments` gets the room it needs by declaration
2106
- instead of by an inline pixel width on each of its neighbours. */
2107
- .mcp-local .field-row {
2108
- display: grid;
2109
- grid-template-columns: 1fr 1fr 2fr;
2110
- align-items: end;
2111
- gap: var(--space-3);
2112
- }
2113
-
2114
2079
  /* How an applet's polling is going, said the same way on every tile. Muted
2115
2080
  when it is going fine -- this is a fact you glance at, not one you are being
2116
2081
  told. */
@@ -2155,7 +2120,15 @@ input:active, textarea:active, select:active { transform: none; }
2155
2120
 
2156
2121
  .danger-act > .set-hint + .set-hint { margin-top: var(--space-1); }
2157
2122
 
2158
- .danger-row { margin-top: var(--space-2); gap: var(--space-2); align-items: center; }
2123
+ /* `display: flex` is here now. These rows carried `.row` as well, which is a
2124
+ class no component of this package renders -- it only ever reached them
2125
+ because a consumer's stylesheet happened to define it too. */
2126
+ .danger-row {
2127
+ display: flex;
2128
+ margin-top: var(--space-2);
2129
+ gap: var(--space-2);
2130
+ align-items: center;
2131
+ }
2159
2132
 
2160
2133
  .danger-row > input { min-width: 0; }
2161
2134
 
@@ -2177,15 +2150,15 @@ input:active, textarea:active, select:active { transform: none; }
2177
2150
  column-gap: var(--space-3);
2178
2151
  }
2179
2152
 
2180
- .field-row { align-items: flex-end; gap: var(--space-2); }
2153
+ .danger-field-row { align-items: flex-end; gap: var(--space-2); }
2181
2154
 
2182
- .field-row > button { flex: none; }
2155
+ .danger-field-row > button { flex: none; }
2183
2156
 
2184
2157
  /* Two forms one under the other, whose buttons say different words -- "Add"
2185
2158
  and "Block". Sized to their text they came out different widths, and two
2186
2159
  controls in the same column at the same indent that do not line up read as
2187
2160
  an accident rather than as a pair. */
2188
- .field-row > button { min-width: 9ch; }
2161
+ .danger-field-row > button { min-width: 9ch; }
2189
2162
  }
2190
2163
 
2191
2164
  /* ---- ./callout.css ---- */
@@ -2516,9 +2489,12 @@ input:active, textarea:active, select:active { transform: none; }
2516
2489
  /* Sized to its words, for a switch that sits in a row of other controls rather
2517
2490
  than owning a row of its own. `flex: 1` there would push the label away from
2518
2491
  the switch by the whole width of the bar. */
2519
- .row > .switch-row { flex: none; padding-block: 0; }
2492
+ /* `inRow`: a toggle beside other controls rather than in a column of
2493
+ settings. Was `.row > .switch-row`, which reached through `.row` -- a class
2494
+ no component here renders. */
2495
+ .switch-in-row { flex: none; padding-block: 0; }
2520
2496
 
2521
- .row > .switch-row .switch-body { flex: none; }
2497
+ .switch-in-row .switch-body { flex: none; }
2522
2498
 
2523
2499
  /* The opposite case: a switch that *is* the row, with something small beside it
2524
2500
  -- a tooltip, a state word. `.set-row` wraps, so a switch that does not grow
@@ -3293,11 +3269,6 @@ input[type='radio']:active:not(:disabled) {
3293
3269
  width: min(760px, 100%);
3294
3270
  }
3295
3271
 
3296
- /* Settings is wider than the others: the rail carries group headings with a
3297
- hint each and indented tabs under them, and a pane beside it that draws
3298
- cards with a control on the right -- 760 gave one or the other room. */
3299
- .modal.modal-settings { width: min(880px, 100%); }
3300
-
3301
3272
  .modal {
3302
3273
  /* The window, less the backdrop's padding at each end -- `--space-15` is 60px
3303
3274
  and there are two of them, above and below.
@@ -3732,25 +3703,12 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
3732
3703
  /* A run of checkboxes is one choice, not several settings that happen to be
3733
3704
  adjacent. Tight enough that the group reads as a block -- at the spacing a
3734
3705
  form field wants, four tools look like four unrelated questions. */
3735
- /* The same choice, laid out as tiles instead of a column.
3736
-
3737
- Four short names down the full width of a card is four wide bands for four
3738
- words -- the row is sized by the card, not by anything in it, so `Read` gets
3739
- the same 1000px as a sentence would. As tiles they are sized by their
3740
- content and the group reads as one set of four rather than a stack of four
3741
- settings.
3742
-
3743
- `auto-fit` with a floor rather than a fixed four: the pane is a modal that
3744
- narrows with the window, and four columns of 40px is worse than two of 160.
3745
- The floor is the width at which a two-word name still fits on one line. */
3746
- .choice-tiles {
3747
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
3748
- gap: var(--space-2);
3749
- }
3750
-
3751
3706
  /* Room for the tick, which is absolutely positioned at the top right. In a
3752
- full-width row the text never reached it; in a tile it would run underneath. */
3753
- .choice-tiles .choice-body { padding-right: var(--space-6); }
3707
+ full-width row the text never reaches it; in a tile it would run underneath.
3708
+ `tile` is a prop on the component now: this was keyed on a `.choice-tiles`
3709
+ container that no component renders, so the class only ever existed in a
3710
+ consumer's markup and the package was styling itself through it. */
3711
+ .choice-tile .choice-body { padding-right: var(--space-6); }
3754
3712
 
3755
3713
  /* The floor is 220px and not 150px because of what four items do at the widths
3756
3714
  in between. The settings pane gives this group 514px: at a 150px floor that
@@ -4125,7 +4083,10 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
4125
4083
  /* Fields stacked down a form. Wider than the gap inside one field, so the
4126
4084
  label of the next belongs to the input under it rather than to the error
4127
4085
  above it. */
4128
- .field-row > .field { flex: 1; min-width: 0; }
4086
+ /* `inRow`: a field sharing a line with other controls rather than stacked in a
4087
+ column. Was `.field-row > .field`, which reached through a container class
4088
+ the caller had to write and this package does not hand out. */
4089
+ .field-in-row { flex: 1; min-width: 0; }
4129
4090
 
4130
4091
  /* A password with an eye -- `Input type="password"`.
4131
4092
  A grid rather than `position: absolute`: both children take the one cell, the
package/dist/valet.css CHANGED
@@ -1517,13 +1517,6 @@ select:disabled { opacity: 0.45; cursor: not-allowed; }
1517
1517
  to { transform: scaleX(0); }
1518
1518
  }
1519
1519
 
1520
- /* (The bordered head bar that used to live here is gone. It was a second
1521
- `.card-head` -- same name, different thing -- and the only place it was ever
1522
- used was the gallery page documenting it. A card that holds rows now takes
1523
- `title` like any other, and the header the component draws is the one
1524
- header.) */
1525
- .row { display: flex; align-items: center; gap: var(--space-3); }
1526
-
1527
1520
  .ctl-grow { flex: 1; min-width: 0; }
1528
1521
 
1529
1522
  .mono { font-family: var(--font-mono); font-size: var(--text-sm); }
@@ -1784,13 +1777,6 @@ input:active, textarea:active, select:active { transform: none; }
1784
1777
  template are allowed to diverge, and this is only saying that they have. */
1785
1778
  .set-hint.drifted { color: var(--warn); }
1786
1779
 
1787
- /* One applet, alone on the page, at `/applet/<id>`. Fills the window: there is
1788
- no grid here to decide a height, and a screenshot of an applet letterboxed
1789
- inside a page is mostly not the applet. */
1790
- .bare { height: 100vh; display: flex; flex-direction: column; background: var(--bg); }
1791
-
1792
- .bare > * { flex: 1; min-height: 0; }
1793
-
1794
1780
  /* A round is in flight. On the button that asks for one, because that is where
1795
1781
  you are looking when you have just pressed it -- and not in the foot, which
1796
1782
  would flicker every thirty seconds on a tile that is working perfectly. */
@@ -1826,10 +1812,6 @@ input:active, textarea:active, select:active { transform: none; }
1826
1812
  40% { opacity: 1; transform: translateY(-3px); }
1827
1813
  }
1828
1814
 
1829
- /* The top bar carries the title, the views and the way into settings. Nothing
1830
- that belongs to a conversation lives here any more, so it can breathe. */
1831
- .topbar .ctl-grow { flex: 1; min-width: 0; }
1832
-
1833
1815
  @keyframes listening-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
1834
1816
 
1835
1817
  /* table ---------------------------------------------------------------- */
@@ -2098,23 +2080,6 @@ input:active, textarea:active, select:active { transform: none; }
2098
2080
  border-color: var(--bad);
2099
2081
  }
2100
2082
 
2101
- /* Three fields on one line, and their inputs on one line too.
2102
-
2103
- `Field` puts the description *above* the control, which is the house rule and
2104
- right -- but only `Arguments` has one here, so as a flex row the other two
2105
- inputs floated a description's height above it and the row read as broken.
2106
- Aligned to the end instead: the labels sit where their text needs them and
2107
- the three boxes share a baseline, which is the edge the eye actually follows.
2108
-
2109
- A grid rather than flex, so `Arguments` gets the room it needs by declaration
2110
- instead of by an inline pixel width on each of its neighbours. */
2111
- .mcp-local .field-row {
2112
- display: grid;
2113
- grid-template-columns: 1fr 1fr 2fr;
2114
- align-items: end;
2115
- gap: var(--space-3);
2116
- }
2117
-
2118
2083
  /* How an applet's polling is going, said the same way on every tile. Muted
2119
2084
  when it is going fine -- this is a fact you glance at, not one you are being
2120
2085
  told. */
@@ -2159,7 +2124,15 @@ input:active, textarea:active, select:active { transform: none; }
2159
2124
 
2160
2125
  .danger-act > .set-hint + .set-hint { margin-top: var(--space-1); }
2161
2126
 
2162
- .danger-row { margin-top: var(--space-2); gap: var(--space-2); align-items: center; }
2127
+ /* `display: flex` is here now. These rows carried `.row` as well, which is a
2128
+ class no component of this package renders -- it only ever reached them
2129
+ because a consumer's stylesheet happened to define it too. */
2130
+ .danger-row {
2131
+ display: flex;
2132
+ margin-top: var(--space-2);
2133
+ gap: var(--space-2);
2134
+ align-items: center;
2135
+ }
2163
2136
 
2164
2137
  .danger-row > input { min-width: 0; }
2165
2138
 
@@ -2181,15 +2154,15 @@ input:active, textarea:active, select:active { transform: none; }
2181
2154
  column-gap: var(--space-3);
2182
2155
  }
2183
2156
 
2184
- .field-row { align-items: flex-end; gap: var(--space-2); }
2157
+ .danger-field-row { align-items: flex-end; gap: var(--space-2); }
2185
2158
 
2186
- .field-row > button { flex: none; }
2159
+ .danger-field-row > button { flex: none; }
2187
2160
 
2188
2161
  /* Two forms one under the other, whose buttons say different words -- "Add"
2189
2162
  and "Block". Sized to their text they came out different widths, and two
2190
2163
  controls in the same column at the same indent that do not line up read as
2191
2164
  an accident rather than as a pair. */
2192
- .field-row > button { min-width: 9ch; }
2165
+ .danger-field-row > button { min-width: 9ch; }
2193
2166
  }
2194
2167
 
2195
2168
  /* ---- ./callout.css ---- */
@@ -2520,9 +2493,12 @@ input:active, textarea:active, select:active { transform: none; }
2520
2493
  /* Sized to its words, for a switch that sits in a row of other controls rather
2521
2494
  than owning a row of its own. `flex: 1` there would push the label away from
2522
2495
  the switch by the whole width of the bar. */
2523
- .row > .switch-row { flex: none; padding-block: 0; }
2496
+ /* `inRow`: a toggle beside other controls rather than in a column of
2497
+ settings. Was `.row > .switch-row`, which reached through `.row` -- a class
2498
+ no component here renders. */
2499
+ .switch-in-row { flex: none; padding-block: 0; }
2524
2500
 
2525
- .row > .switch-row .switch-body { flex: none; }
2501
+ .switch-in-row .switch-body { flex: none; }
2526
2502
 
2527
2503
  /* The opposite case: a switch that *is* the row, with something small beside it
2528
2504
  -- a tooltip, a state word. `.set-row` wraps, so a switch that does not grow
@@ -3297,11 +3273,6 @@ input[type='radio']:active:not(:disabled) {
3297
3273
  width: min(760px, 100%);
3298
3274
  }
3299
3275
 
3300
- /* Settings is wider than the others: the rail carries group headings with a
3301
- hint each and indented tabs under them, and a pane beside it that draws
3302
- cards with a control on the right -- 760 gave one or the other room. */
3303
- .modal.modal-settings { width: min(880px, 100%); }
3304
-
3305
3276
  .modal {
3306
3277
  /* The window, less the backdrop's padding at each end -- `--space-15` is 60px
3307
3278
  and there are two of them, above and below.
@@ -3736,25 +3707,12 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
3736
3707
  /* A run of checkboxes is one choice, not several settings that happen to be
3737
3708
  adjacent. Tight enough that the group reads as a block -- at the spacing a
3738
3709
  form field wants, four tools look like four unrelated questions. */
3739
- /* The same choice, laid out as tiles instead of a column.
3740
-
3741
- Four short names down the full width of a card is four wide bands for four
3742
- words -- the row is sized by the card, not by anything in it, so `Read` gets
3743
- the same 1000px as a sentence would. As tiles they are sized by their
3744
- content and the group reads as one set of four rather than a stack of four
3745
- settings.
3746
-
3747
- `auto-fit` with a floor rather than a fixed four: the pane is a modal that
3748
- narrows with the window, and four columns of 40px is worse than two of 160.
3749
- The floor is the width at which a two-word name still fits on one line. */
3750
- .choice-tiles {
3751
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
3752
- gap: var(--space-2);
3753
- }
3754
-
3755
3710
  /* Room for the tick, which is absolutely positioned at the top right. In a
3756
- full-width row the text never reached it; in a tile it would run underneath. */
3757
- .choice-tiles .choice-body { padding-right: var(--space-6); }
3711
+ full-width row the text never reaches it; in a tile it would run underneath.
3712
+ `tile` is a prop on the component now: this was keyed on a `.choice-tiles`
3713
+ container that no component renders, so the class only ever existed in a
3714
+ consumer's markup and the package was styling itself through it. */
3715
+ .choice-tile .choice-body { padding-right: var(--space-6); }
3758
3716
 
3759
3717
  /* The floor is 220px and not 150px because of what four items do at the widths
3760
3718
  in between. The settings pane gives this group 514px: at a 150px floor that
@@ -4129,7 +4087,10 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
4129
4087
  /* Fields stacked down a form. Wider than the gap inside one field, so the
4130
4088
  label of the next belongs to the input under it rather than to the error
4131
4089
  above it. */
4132
- .field-row > .field { flex: 1; min-width: 0; }
4090
+ /* `inRow`: a field sharing a line with other controls rather than stacked in a
4091
+ column. Was `.field-row > .field`, which reached through a container class
4092
+ the caller had to write and this package does not hand out. */
4093
+ .field-in-row { flex: 1; min-width: 0; }
4133
4094
 
4134
4095
  /* A password with an eye -- `Input type="password"`.
4135
4096
  A grid rather than `position: absolute`: both children take the one cell, the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/design",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "wtfalch's design system: themeable components on a fixed token vocabulary.",
5
5
  "keywords": [
6
6
  "design-system",