@ggui-ai/ui-gen 0.24.0 → 0.25.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.
Files changed (98) hide show
  1. package/dist/{adapter-Du7P_5Fx.d.ts → adapter-Cg_rkpGq.d.ts} +1 -1
  2. package/dist/adapters/generation-dispatch.d.ts +27 -5
  3. package/dist/adapters/generation-dispatch.js +909 -53
  4. package/dist/adapters/generation-dispatch.js.map +1 -1
  5. package/dist/adapters/index.js +3 -1
  6. package/dist/adapters/index.js.map +1 -1
  7. package/dist/advanced/index.d.ts +12 -10
  8. package/dist/advanced/index.js +917 -98
  9. package/dist/advanced/index.js.map +1 -1
  10. package/dist/{axes-CzLEMDeB.d.ts → axes-CKVCzR_x.d.ts} +1 -1
  11. package/dist/bank-CT4eEkta.d.ts +118 -0
  12. package/dist/boilerplate.js +24 -12
  13. package/dist/boilerplate.js.map +1 -1
  14. package/dist/check/index.d.ts +11 -4
  15. package/dist/check/index.js +47 -1
  16. package/dist/check/index.js.map +1 -1
  17. package/dist/classifier/index.d.ts +2 -2
  18. package/dist/compose.d.ts +2 -2
  19. package/dist/{create-ui-generator-GIhzx08x.d.ts → create-ui-generator-DFnnArrv.d.ts} +11 -3
  20. package/dist/{dispatch-BnAH56p4.d.ts → dispatch-CStl9eAw.d.ts} +5 -3
  21. package/dist/evaluation/axis-checks/index.d.ts +5 -5
  22. package/dist/evaluation/axis-checks/index.js +90 -2
  23. package/dist/evaluation/axis-checks/index.js.map +1 -1
  24. package/dist/evaluation/axis-checks/registry.d.ts +3 -3
  25. package/dist/evaluation/axis-checks/registry.js +88 -1
  26. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  27. package/dist/evaluation/index.d.ts +300 -23
  28. package/dist/evaluation/index.js +746 -30
  29. package/dist/evaluation/index.js.map +1 -1
  30. package/dist/evaluation/types.d.ts +9 -1
  31. package/dist/evaluation/types.js.map +1 -1
  32. package/dist/fragments/index.d.ts +3 -3
  33. package/dist/harness/check/runtime-render/index.d.ts +5 -5
  34. package/dist/harness/index.d.ts +4 -4
  35. package/dist/harness/index.js +33 -15
  36. package/dist/harness/index.js.map +1 -1
  37. package/dist/harness/result-types.d.ts +3 -3
  38. package/dist/harness/runtime.d.ts +10 -4
  39. package/dist/harness/runtime.js +26 -14
  40. package/dist/harness/runtime.js.map +1 -1
  41. package/dist/harness/types-public.d.ts +3 -3
  42. package/dist/harness/types-public.js +33 -15
  43. package/dist/harness/types-public.js.map +1 -1
  44. package/dist/harness/validator-trace-sink.d.ts +3 -3
  45. package/dist/hash.d.ts +1 -1
  46. package/dist/index.d.ts +25 -9
  47. package/dist/index.js +926 -101
  48. package/dist/index.js.map +1 -1
  49. package/dist/{llm-router-DwwUK1bz.d.ts → llm-router-nfO7wPec.d.ts} +3 -1
  50. package/dist/patch.d.ts +3 -3
  51. package/dist/policy.d.ts +1 -1
  52. package/dist/tools/get-primitives-ts.d.ts +1 -1
  53. package/dist/tools/get-primitives-ts.js +1 -1
  54. package/dist/tools/get-primitives-ts.js.map +1 -1
  55. package/dist/{types-BOvHNG7K.d.ts → types-Dh1s4K9j.d.ts} +1 -1
  56. package/dist/{types-public-Bk6d1WLN.d.ts → types-public-BJTLpzWn.d.ts} +134 -7
  57. package/dist/validation/index.d.ts +1 -1
  58. package/dist/validation/index.js +1 -1
  59. package/dist/validation/index.js.map +1 -1
  60. package/dist/workflows.d.ts +3 -3
  61. package/package.json +12 -12
  62. package/src/adapters/generation-dispatch.ts +36 -2
  63. package/src/adapters/tools.ts +2 -1
  64. package/src/boilerplate/free-design-prompt.ts +1 -1
  65. package/src/boilerplate/generate.ts +26 -1
  66. package/src/boilerplate/hard-sections.ts +13 -13
  67. package/src/boilerplate/system-prompt.ts +1 -1
  68. package/src/check/index.ts +1 -0
  69. package/src/check/sample-props.ts +63 -0
  70. package/src/coding-agent/tools.ts +4 -1
  71. package/src/create-harness.ts +3 -3
  72. package/src/create-ui-generator.ts +20 -1
  73. package/src/evaluation/__fixtures__/png.ts +100 -0
  74. package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
  75. package/src/evaluation/axis-checks/checks/universal.ts +111 -0
  76. package/src/evaluation/axis-checks/dispatch.ts +4 -1
  77. package/src/evaluation/criteria/bank.ts +85 -0
  78. package/src/evaluation/criteria/context.ts +52 -0
  79. package/src/evaluation/criteria/resolve.ts +159 -0
  80. package/src/evaluation/criteria/select.ts +79 -0
  81. package/src/evaluation/index.ts +23 -0
  82. package/src/evaluation/ink-extent.ts +200 -0
  83. package/src/evaluation/theme-css.ts +12 -5
  84. package/src/evaluation/types-public.ts +130 -3
  85. package/src/evaluation/types.ts +9 -0
  86. package/src/evaluation/visual-evaluator.ts +636 -30
  87. package/src/harness/coding/generate-task-runner.ts +3 -0
  88. package/src/harness/coding/run-coding-turn.ts +7 -3
  89. package/src/harness/coding/run-eval-round.ts +19 -0
  90. package/src/harness/runtime.ts +5 -0
  91. package/src/harness-tool-names.ts +38 -0
  92. package/src/index.ts +12 -0
  93. package/src/run-check.ts +2 -0
  94. package/src/tools/get-primitives-ts.ts +1 -1
  95. package/src/tools/get-wire.ts +1 -1
  96. package/src/tools/render-check.ts +0 -60
  97. package/src/tools.ts +11 -1
  98. package/src/validation/primitives.ts +1 -1
@@ -37,4 +37,4 @@ interface Classification {
37
37
  riskTier: RiskTier;
38
38
  }
39
39
 
40
- export type { AxisVector as A, Classification as C, FetchShape as F, LayoutShape as L, RiskTier as R, StreamEventKind as S, ToolingShape as T, WriteTrigger as W, AxisSource as a, RealtimeShape as b, RenderShape as c, StateShape as d, WriteShape as e, AxisProvenance as f };
40
+ export type { AxisVector as A, Classification as C, FetchShape as F, LayoutShape as L, RiskTier as R, StateShape as S, ToolingShape as T, WriteShape as W, RenderShape as a, StreamEventKind as b, AxisSource as c, RealtimeShape as d, WriteTrigger as e, AxisProvenance as f };
@@ -0,0 +1,118 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * The visual criteria bank — ggui#1436 (step 1 of the founder's visual-assessment steer).
5
+ *
6
+ * The bank is DATA, never code (#1437 authors it; #1060's bar-and-judge policy): a versioned set of
7
+ * criteria the judge, an instrument or a person answers off the frame, split as the founder ruled —
8
+ * `severity` is importance, `evaluation` the method, `status` the method's readiness (the only source
9
+ * of an `n/a`), `scope` where a row applies ({} = every card; {kind} / {canvas} / {preset} = one). The
10
+ * general rows are `criteria`; a preset's own expression is a `recipes` row carrying `scope.preset`.
11
+ * This module is the ONE loader: a malformed bank refuses at launch with the offending path named,
12
+ * never mid-run. Fields the loader does not read (`examples`, `fields`, `groups`, `seed`) are page
13
+ * matter and are dropped here; an additive field tomorrow does not break today's loader.
14
+ */
15
+
16
+ /** Where a row applies; every key named must match the card's context. */
17
+ declare const criteriaScopeSchema: z.ZodObject<{
18
+ kind: z.ZodOptional<z.ZodString>;
19
+ canvas: z.ZodOptional<z.ZodString>;
20
+ preset: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strip>;
22
+ declare const bankRowSchema: z.ZodObject<{
23
+ id: z.ZodString;
24
+ group: z.ZodOptional<z.ZodString>;
25
+ scope: z.ZodDefault<z.ZodObject<{
26
+ kind: z.ZodOptional<z.ZodString>;
27
+ canvas: z.ZodOptional<z.ZodString>;
28
+ preset: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strip>>;
30
+ severity: z.ZodEnum<{
31
+ must: "must";
32
+ should: "should";
33
+ }>;
34
+ evaluation: z.ZodEnum<{
35
+ instrument: "instrument";
36
+ judge: "judge";
37
+ human: "human";
38
+ }>;
39
+ status: z.ZodEnum<{
40
+ live: "live";
41
+ reference: "reference";
42
+ planned: "planned";
43
+ manual: "manual";
44
+ }>;
45
+ text: z.ZodString;
46
+ evidence: z.ZodString;
47
+ fix: z.ZodOptional<z.ZodString>;
48
+ }, z.core.$strip>;
49
+ declare const criteriaBankSchema: z.ZodObject<{
50
+ version: z.ZodString;
51
+ applies: z.ZodOptional<z.ZodObject<{
52
+ kind: z.ZodOptional<z.ZodArray<z.ZodString>>;
53
+ canvases: z.ZodOptional<z.ZodArray<z.ZodString>>;
54
+ }, z.core.$strip>>;
55
+ criteria: z.ZodArray<z.ZodObject<{
56
+ id: z.ZodString;
57
+ group: z.ZodOptional<z.ZodString>;
58
+ scope: z.ZodDefault<z.ZodObject<{
59
+ kind: z.ZodOptional<z.ZodString>;
60
+ canvas: z.ZodOptional<z.ZodString>;
61
+ preset: z.ZodOptional<z.ZodString>;
62
+ }, z.core.$strip>>;
63
+ severity: z.ZodEnum<{
64
+ must: "must";
65
+ should: "should";
66
+ }>;
67
+ evaluation: z.ZodEnum<{
68
+ instrument: "instrument";
69
+ judge: "judge";
70
+ human: "human";
71
+ }>;
72
+ status: z.ZodEnum<{
73
+ live: "live";
74
+ reference: "reference";
75
+ planned: "planned";
76
+ manual: "manual";
77
+ }>;
78
+ text: z.ZodString;
79
+ evidence: z.ZodString;
80
+ fix: z.ZodOptional<z.ZodString>;
81
+ }, z.core.$strip>>;
82
+ recipes: z.ZodOptional<z.ZodArray<z.ZodObject<{
83
+ id: z.ZodString;
84
+ group: z.ZodOptional<z.ZodString>;
85
+ scope: z.ZodDefault<z.ZodObject<{
86
+ kind: z.ZodOptional<z.ZodString>;
87
+ canvas: z.ZodOptional<z.ZodString>;
88
+ preset: z.ZodOptional<z.ZodString>;
89
+ }, z.core.$strip>>;
90
+ severity: z.ZodEnum<{
91
+ must: "must";
92
+ should: "should";
93
+ }>;
94
+ evaluation: z.ZodEnum<{
95
+ instrument: "instrument";
96
+ judge: "judge";
97
+ human: "human";
98
+ }>;
99
+ status: z.ZodEnum<{
100
+ live: "live";
101
+ reference: "reference";
102
+ planned: "planned";
103
+ manual: "manual";
104
+ }>;
105
+ text: z.ZodString;
106
+ evidence: z.ZodString;
107
+ fix: z.ZodOptional<z.ZodString>;
108
+ }, z.core.$strip>>>;
109
+ }, z.core.$strip>;
110
+ type CriteriaBank = z.infer<typeof criteriaBankSchema>;
111
+ type BankRow = z.infer<typeof bankRowSchema>;
112
+ type CriteriaScope = z.infer<typeof criteriaScopeSchema>;
113
+ /** Every row of the bank — the general criteria, then the recipes. */
114
+ declare function bankRows(bank: CriteriaBank): readonly BankRow[];
115
+ /** The one door for bank data: refuses loudly, naming the path, before anything is judged. */
116
+ declare function parseCriteriaBank(raw: unknown): CriteriaBank;
117
+
118
+ export { type BankRow as B, type CriteriaBank as C, type CriteriaScope as a, bankRows as b, criteriaBankSchema as c, parseCriteriaBank as p };
@@ -315,6 +315,7 @@ function generateBoilerplate(_userPrompt, contract, shellType, screen, composedS
315
315
  }
316
316
  const actionTypeAliases = [];
317
317
  const actionHookCalls = [];
318
+ let hasOneShotAction = false;
318
319
  const actionsMap = contract?.actionSpec ?? {};
319
320
  for (const [key, entry] of Object.entries(actionsMap)) {
320
321
  const label = entry.label ?? key;
@@ -335,7 +336,20 @@ type ${typeName} = ${tsType};`
335
336
  const callSig = tsType === "void" ? "() => void \u2014 fire and forget" : `(data: ${tsType}) => void`;
336
337
  const toolHint = tool ? ` \u2192 nextStep: ${tool}` : "";
337
338
  const onceHint = " \u2014 if this is meant ONCE, disable its control after it fires";
338
- actionHookCalls.push(` const ${key} = useAction<${typeName}>('${key}'); // ${callSig}${toolHint}${onceHint}`);
339
+ if (entry.oneShot === true) {
340
+ hasOneShotAction = true;
341
+ actionHookCalls.push(
342
+ ` const ${key} = useAction<${typeName}>('${key}'); // ${callSig}${toolHint} \u2014 declared oneShot: the runtime drops a second dispatch`
343
+ );
344
+ actionHookCalls.push(
345
+ ` const ${key}Spent = useActionSpent('${key}'); // true once fired, also after a reload \u2014 render the control from THIS: disabled={${key}Spent} and a spent label`
346
+ );
347
+ } else {
348
+ actionHookCalls.push(` const ${key} = useAction<${typeName}>('${key}'); // ${callSig}${toolHint}${onceHint}`);
349
+ }
350
+ actionHookCalls.push(
351
+ ` const ${key}Pending = useActionPending('${key}'); // true from the tap until the agent's answer repaints the card \u2014 render EVERY control that dispatches '${key}' (a Button, a Select's onChange, an Input's Enter) from THIS: disabled={${key}Pending} aria-busy={${key}Pending}, and a working label on a button; no other control carries its aria-busy`
352
+ );
339
353
  }
340
354
  const streamChannels = contract?.streamSpec ?? {};
341
355
  const streamChannelEntries = Object.entries(streamChannels);
@@ -433,6 +447,8 @@ ${hookLines.join("\n")}
433
447
  const hasAnyWireFromWire = hasActions || hasStream || hasContext;
434
448
  const wireHooks = [];
435
449
  if (hasActions) wireHooks.push("useAction");
450
+ if (hasOneShotAction) wireHooks.push("useActionSpent");
451
+ if (hasActions) wireHooks.push("useActionPending");
436
452
  if (hasStream) wireHooks.push("useStream");
437
453
  if (hasContext) wireHooks.push("useGguiContext");
438
454
  const wireImport = hasAnyWireFromWire ? `import { ${wireHooks.join(", ")} } from '@ggui-ai/wire';
@@ -962,21 +978,17 @@ Rules:
962
978
  2. Layout and styling are universal. Colors, spacing, structure \u2014 these are the reusable part.
963
979
  3. Default values come from the current request \u2014 so the component renders correctly standalone.
964
980
  4. Copy comes from the props. NEVER invent display text the contract does not carry \u2014 no eyebrow / kicker / overline (a \`<Text caps>\` label), no helper line, no status text, no tagline. **Do NOT add a caps label above the heading** \u2014 not \`<Text caps>LET'S BEGIN</Text>\`, not \`WELCOME\`, not a section kicker like \`GETTING STARTED\` or \`OVERVIEW\` \u2014 UNLESS a prop supplies that exact text (\`<Text caps>{props.section}</Text>\`). When the request asks for a heading and a line, render the heading and the line and NOTHING above the heading. A heading's fallback is the request's own words, never a slogan of your own. Text the agent cannot change is chatter on every render, and a caps eyebrow you wrote is the most common form of it. An action's control shows the contract's \`label\` VERBATIM \u2014 every character, including \`&\`, \`/\`, \`\u2013\` and quotes \u2014 never paraphrased, shortened or re-cased (\`"Confirm & Schedule"\` is not \`"Confirm Schedule"\`).
965
- 5. Props interface must be typed and exported.`;
981
+ 5. Props interface must be typed and exported.
982
+ 6. Keys on a mapped list come from the item's own identity: \`key={item.id}\` when the contract gives an id; when the items carry none (KPI tiles \`{label, value}\`, forecast days \`{day, high}\`), key by the field that names each one \u2014 \`key={item.label}\`, \`key={day.day}\` \u2014 never by the map index: a reorder or a stream merge re-keys by index and re-mounts the wrong rows, and the self-check refuses an index key.`;
966
983
  var TERMINAL_ACTIONS = `## Actions the user means once
967
984
 
968
985
  A second press is a mistake only when it would make a SECOND THING HAPPEN IN THE WORLD \u2014 a second booking, a second charge, a second order, a second application sent. If pressing again would only write the SAME STATE AGAIN \u2014 saving, updating, renaming, setting a preference, toggling \u2014 nothing is duplicated and the control stays armed.
969
986
 
970
- Guard a control only where you have evidence, not a feeling about the verb: the request itself names a one-time commitment \u2014 "submit the application", "place the order", "confirm the booking".
971
-
972
- **When in doubt, leave it armed.** A control the user cannot press a second time has lost them something that worked \u2014 save, edit, save again is ordinary. A control left armed is only what every interface does by default.
987
+ **The contract says which actions are once.** An action the contract declares \`oneShot: true\` (in the Contract block above) is guarded by the runtime \u2014 a second dispatch is dropped \u2014 and its spent state is DATA you read, never state you hold: render its control from \`const spent = useActionSpent('<name>')\`, \`disabled={spent}\`, and say so in the label or beside it ("Submitted", "Request sent") \u2014 a control that is disabled without explanation reads as broken. The hook's answer survives a reload and a second tab; \`useState(false)\` does not, so a reloaded card built on local state re-arms a control that already fired.
973
988
 
974
- When you do guard one:
975
- - Hold a small piece of state for it and set it in the handler: \`const [submitted, setSubmitted] = useState(false)\`.
976
- - \`disabled={submitted}\` on the control that fires it, and say so in the label or beside it ("Submitted", "Request sent") \u2014 a control that is disabled without explanation reads as broken.
977
- - A card re-rendered with new props starts fresh; the state above lives with the component, not with the request.
989
+ **An action the contract does not declare \`oneShot\` stays armed.** Never guard one from a feeling about the verb \u2014 "confirm", "submit", "place" alone are not evidence. Guard it only where the request itself names a one-time commitment the contract failed to declare ("submit the application", "place the order"), and then with local state as the fallback: \`const [submitted, setSubmitted] = useState(false)\` set in the handler, \`disabled={submitted}\` with a label \u2014 knowing that this state lives with the component, not with the request, and starts fresh on a re-render with new props.
978
990
 
979
- Nothing in the wire DECLARES one-shot-ness yet, which is why the test is what a second press would DO \u2014 not how final the word sounds.`;
991
+ **When in doubt, leave it armed.** A control the user cannot press a second time has lost them something that worked \u2014 save, edit, save again is ordinary. A control left armed is only what every interface does by default.`;
980
992
  var FRAME_SIZING = `## Sizing \u2014 the frame owns the height and the width
981
993
 
982
994
  The host sizes the iframe; the component never sizes itself to the viewport. NEVER write \`100vh\` / \`100dvh\` / \`100svh\` (or an \`h-screen\` / \`min-h-screen\` class) as a height on any element:
@@ -1132,7 +1144,7 @@ With raw elements YOU own the semantics \u2014 nothing is accessible by construc
1132
1144
  3. **Images have \`alt\`** (\`alt=""\` for decorative). Icon-only buttons need \`aria-label\`; decorative icons and emoji are \`aria-hidden="true"\`.
1133
1145
  4. **Live & streaming data** (a \`useStream\` \`.latest\` value, counters, clocks, flashing prices) sits in an \`aria-live="polite"\` (or \`role="status"\`) region so screen readers announce the change.
1134
1146
  5. **Headings nest**: one \`<h1>\` per screen, \`<h2>\` for sections, \`<h3>\` below \u2014 never skipped or inverted.
1135
- 6. **Busy state is announced**: \`<button disabled={isLoading} aria-busy={isLoading}>{isLoading ? 'Submitting\u2026' : 'Submit'}</button>\`.
1147
+ 6. **Busy state is announced**: every control that dispatches an action (a button, a select whose change dispatches, an input that sends on Enter) stays pending from \`useActionPending('<action>')\` until the agent's answer repaints the card \u2014 \`<button disabled={sendPending} aria-busy={sendPending}>{sendPending ? 'Sending\u2026' : 'Send'}</button>\`. Only a control that dispatches the action carries its busy look; while it is pending, other controls of the same record may be disabled \u2014 nothing more (controls of other records, creation controls and the action's own cancel stay enabled); busy state the card owns (\`isLoading\`) the same way.
1136
1148
  7. **Stateful controls announce state**: \`role="checkbox"\` + \`aria-checked\` on a toggleable row, \`aria-pressed\` on a toggle button, \`aria-selected\` on the chosen item, \`aria-expanded\` on an open/close affordance. Styling (strikethrough, color, a check mark) is never the only carrier of state.
1137
1149
  8. **Focus is visible** \u2014 style \`:focus-visible\` with a token color; never \`outline: none\` without a replacement.`;
1138
1150
  var FREE_QUALITY_CHECKLIST = `## Quality Checklist (verify before returning)
@@ -1676,7 +1688,7 @@ The design-system primitives are accessible by construction \u2014 they emit the
1676
1688
  3. **Icons are decorative by default** \u2014 \`<Icon name="check" />\` is hidden from screen readers, which is correct for an icon sitting next to text. Add \`aria-label\` ONLY for a standalone, meaning-bearing icon with no adjacent text. Icon-only \`Button\`s still need \`aria-label\` on the **Button** itself.
1677
1689
  4. **Live & streaming data** \u2014 wrap any region whose content updates on its own (a \`useStream\` \`.latest\` value, a live clock, an "N new" counter, a flashing price) in an element with \`aria-live="polite"\` so screen readers announce the change.
1678
1690
  5. **Headings nest** \u2014 one \`<Heading level={1}>\` per screen, \`level={2}\` for sections, \`level={3}\` for subsections. Never skip or invert levels.
1679
- 6. **Buttons** \u2014 descriptive text content; icon-only buttons need \`aria-label\`. Announce busy state: \`<Button disabled={isLoading} aria-busy={isLoading}>{isLoading ? 'Submitting\u2026' : 'Submit'}</Button>\`.
1691
+ 6. **Buttons** \u2014 descriptive text content; icon-only buttons need \`aria-label\`. Announce busy state. EVERY control that dispatches an action \u2014 a Button, a Select whose change dispatches, an Input that sends on Enter \u2014 stays pending from that action's \`useActionPending\` until the agent's answer repaints the card: with \`const sendPending = useActionPending('send');\`, write \`<Button disabled={sendPending} aria-busy={sendPending} onClick={() => send({ text })}>{sendPending ? 'Sending\u2026' : 'Send'}</Button>\`, and give a dispatching Select or Input the same \`disabled={sendPending} aria-busy={sendPending}\`. Only a control that dispatches the action carries its \`aria-busy\` and a working label. While it is pending, other controls of the SAME record may be \`disabled\` \u2014 nothing more; controls of other records, creation controls and the action's own cancel stay enabled. Busy state the card owns (a local \`isLoading\`) is announced the same way.
1680
1692
  7. **Stateful controls announce their state.** Toggleable / selectable / expandable things use the primitive that carries the semantics (\`Checkbox\`, \`Toggle\`, \`Tabs\`, \`Accordion\`) \u2014 never a styled \`Box\`/\`Icon\` that only *looks* checked. When you hand-roll one with \`as={Clickable}\`, \`Clickable\` adds button + keyboard semantics but NOT state \u2014 add it yourself: \`role="checkbox"\` + \`aria-checked={done}\` on a toggleable row, \`aria-pressed\` on a toggle button, \`aria-selected\` on the chosen item, \`aria-expanded\` on an open/close affordance. Styling (strikethrough, color, a check icon) must never be the only carrier of state.
1681
1693
 
1682
1694
  ## Design System Usage (CRITICAL)