@vectoriox/iox-ui 5.34.0 → 5.36.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.
@@ -52,7 +52,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
52
52
  "lineHeight": "1.5",
53
53
  "letterSpacing": "0px",
54
54
  "textTransform": "none",
55
- "textAlign": "left",
55
+ "textAlign": "start",
56
56
  "fontStyle": "normal",
57
57
  "textDecoration": "none",
58
58
  "WebkitTextStrokeWidth": "0px",
@@ -243,7 +243,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
243
243
  "lineHeight": "1.5",
244
244
  "letterSpacing": "0px",
245
245
  "textTransform": "none",
246
- "textAlign": "left",
246
+ "textAlign": "start",
247
247
  "fontStyle": "normal",
248
248
  "textDecoration": "none",
249
249
  "WebkitTextStrokeWidth": "0px",
@@ -435,7 +435,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
435
435
  "lineHeight": "1.5",
436
436
  "letterSpacing": "0px",
437
437
  "textTransform": "none",
438
- "textAlign": "left",
438
+ "textAlign": "start",
439
439
  "fontStyle": "normal",
440
440
  "textDecoration": "none",
441
441
  "WebkitTextStrokeWidth": "0px",
@@ -621,7 +621,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
621
621
  "lineHeight": "1.5",
622
622
  "letterSpacing": "0px",
623
623
  "textTransform": "none",
624
- "textAlign": "left",
624
+ "textAlign": "start",
625
625
  "fontStyle": "normal",
626
626
  "textDecoration": "none",
627
627
  "WebkitTextStrokeWidth": "0px",
@@ -665,7 +665,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
665
665
  },
666
666
  "traits": [
667
667
  "htmlTag",
668
- "colorScheme"
668
+ "colorScheme",
669
+ "multiple",
670
+ "defaultOn"
669
671
  ],
670
672
  "styleTraits": [
671
673
  "position",
@@ -810,7 +812,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
810
812
  "lineHeight": "1.5",
811
813
  "letterSpacing": "0px",
812
814
  "textTransform": "none",
813
- "textAlign": "left",
815
+ "textAlign": "start",
814
816
  "fontStyle": "normal",
815
817
  "textDecoration": "none",
816
818
  "WebkitTextStrokeWidth": "0px",
@@ -1001,7 +1003,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1001
1003
  "lineHeight": "1.5",
1002
1004
  "letterSpacing": "0px",
1003
1005
  "textTransform": "none",
1004
- "textAlign": "left",
1006
+ "textAlign": "start",
1005
1007
  "fontStyle": "normal",
1006
1008
  "textDecoration": "none",
1007
1009
  "WebkitTextStrokeWidth": "0px",
@@ -1188,7 +1190,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1188
1190
  "lineHeight": "1.5",
1189
1191
  "letterSpacing": "0px",
1190
1192
  "textTransform": "none",
1191
- "textAlign": "left",
1193
+ "textAlign": "start",
1192
1194
  "fontStyle": "normal",
1193
1195
  "textDecoration": "none",
1194
1196
  "WebkitTextStrokeWidth": "0px",
@@ -1377,7 +1379,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1377
1379
  "lineHeight": "1.5",
1378
1380
  "letterSpacing": "0px",
1379
1381
  "textTransform": "none",
1380
- "textAlign": "left",
1382
+ "textAlign": "start",
1381
1383
  "fontStyle": "normal",
1382
1384
  "textDecoration": "none",
1383
1385
  "WebkitTextStrokeWidth": "0px",
@@ -1571,7 +1573,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1571
1573
  "lineHeight": "1.5",
1572
1574
  "letterSpacing": "0px",
1573
1575
  "textTransform": "none",
1574
- "textAlign": "left",
1576
+ "textAlign": "start",
1575
1577
  "fontStyle": "normal",
1576
1578
  "textDecoration": "none",
1577
1579
  "WebkitTextStrokeWidth": "0px",
@@ -1760,7 +1762,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1760
1762
  "lineHeight": "1.5",
1761
1763
  "letterSpacing": "0px",
1762
1764
  "textTransform": "none",
1763
- "textAlign": "left",
1765
+ "textAlign": "start",
1764
1766
  "fontStyle": "normal",
1765
1767
  "textDecoration": "none",
1766
1768
  "WebkitTextStrokeWidth": "0px",
@@ -1943,7 +1945,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1943
1945
  "lineHeight": "0",
1944
1946
  "letterSpacing": "0px",
1945
1947
  "textTransform": "none",
1946
- "textAlign": "left",
1948
+ "textAlign": "start",
1947
1949
  "fontStyle": "normal",
1948
1950
  "textDecoration": "none",
1949
1951
  "WebkitTextStrokeWidth": "0px",
@@ -2124,7 +2126,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2124
2126
  "lineHeight": "1.5",
2125
2127
  "letterSpacing": "0px",
2126
2128
  "textTransform": "none",
2127
- "textAlign": "left",
2129
+ "textAlign": "start",
2128
2130
  "fontStyle": "normal",
2129
2131
  "textDecoration": "none",
2130
2132
  "WebkitTextStrokeWidth": "0px",
@@ -2303,7 +2305,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2303
2305
  "lineHeight": "1.5",
2304
2306
  "letterSpacing": "0px",
2305
2307
  "textTransform": "none",
2306
- "textAlign": "left",
2308
+ "textAlign": "start",
2307
2309
  "fontStyle": "normal",
2308
2310
  "textDecoration": "none",
2309
2311
  "WebkitTextStrokeWidth": "0px",
@@ -2491,7 +2493,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2491
2493
  "lineHeight": "1.5",
2492
2494
  "letterSpacing": "0px",
2493
2495
  "textTransform": "none",
2494
- "textAlign": "left",
2496
+ "textAlign": "start",
2495
2497
  "fontStyle": "normal",
2496
2498
  "textDecoration": "none",
2497
2499
  "WebkitTextStrokeWidth": "0px",
@@ -2668,7 +2670,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2668
2670
  "lineHeight": "1.5",
2669
2671
  "letterSpacing": "0px",
2670
2672
  "textTransform": "none",
2671
- "textAlign": "left",
2673
+ "textAlign": "start",
2672
2674
  "fontStyle": "normal",
2673
2675
  "textDecoration": "none",
2674
2676
  "WebkitTextStrokeWidth": "0px",
@@ -2852,7 +2854,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2852
2854
  "lineHeight": "1.5",
2853
2855
  "letterSpacing": "0px",
2854
2856
  "textTransform": "none",
2855
- "textAlign": "left",
2857
+ "textAlign": "start",
2856
2858
  "fontStyle": "normal",
2857
2859
  "textDecoration": "none",
2858
2860
  "WebkitTextStrokeWidth": "0px",
@@ -2896,7 +2898,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2896
2898
  },
2897
2899
  "traits": [
2898
2900
  "source",
2899
- "limit"
2901
+ "limit",
2902
+ "multiple",
2903
+ "defaultOn"
2900
2904
  ],
2901
2905
  "styleTraits": [
2902
2906
  "position",
@@ -3254,7 +3258,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3254
3258
  "lineHeight": "1.5",
3255
3259
  "letterSpacing": "0px",
3256
3260
  "textTransform": "none",
3257
- "textAlign": "left",
3261
+ "textAlign": "start",
3258
3262
  "fontStyle": "normal",
3259
3263
  "textDecoration": "none",
3260
3264
  "WebkitTextStrokeWidth": "0px",
@@ -3564,7 +3568,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3564
3568
  "lineHeight": "1.5",
3565
3569
  "letterSpacing": "0px",
3566
3570
  "textTransform": "none",
3567
- "textAlign": "left",
3571
+ "textAlign": "start",
3568
3572
  "fontStyle": "normal",
3569
3573
  "textDecoration": "none",
3570
3574
  "WebkitTextStrokeWidth": "0px",
@@ -3759,7 +3763,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3759
3763
  "lineHeight": "1.5",
3760
3764
  "letterSpacing": "0px",
3761
3765
  "textTransform": "none",
3762
- "textAlign": "left",
3766
+ "textAlign": "start",
3763
3767
  "fontStyle": "normal",
3764
3768
  "textDecoration": "none",
3765
3769
  "WebkitTextStrokeWidth": "0px",
@@ -3958,7 +3962,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3958
3962
  "lineHeight": "1.5",
3959
3963
  "letterSpacing": "0px",
3960
3964
  "textTransform": "none",
3961
- "textAlign": "left",
3965
+ "textAlign": "start",
3962
3966
  "fontStyle": "normal",
3963
3967
  "textDecoration": "none",
3964
3968
  "WebkitTextStrokeWidth": "0px",
@@ -4143,7 +4147,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4143
4147
  "lineHeight": "1.5",
4144
4148
  "letterSpacing": "0px",
4145
4149
  "textTransform": "none",
4146
- "textAlign": "left",
4150
+ "textAlign": "start",
4147
4151
  "fontStyle": "normal",
4148
4152
  "textDecoration": "none",
4149
4153
  "WebkitTextStrokeWidth": "0px",
@@ -4328,7 +4332,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4328
4332
  "lineHeight": "1.5",
4329
4333
  "letterSpacing": "0px",
4330
4334
  "textTransform": "none",
4331
- "textAlign": "left",
4335
+ "textAlign": "start",
4332
4336
  "fontStyle": "normal",
4333
4337
  "textDecoration": "none",
4334
4338
  "WebkitTextStrokeWidth": "0px",
@@ -4511,7 +4515,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4511
4515
  "lineHeight": "1.5",
4512
4516
  "letterSpacing": "0px",
4513
4517
  "textTransform": "none",
4514
- "textAlign": "left",
4518
+ "textAlign": "start",
4515
4519
  "fontStyle": "normal",
4516
4520
  "textDecoration": "none",
4517
4521
  "WebkitTextStrokeWidth": "0px",
@@ -4699,7 +4703,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4699
4703
  "lineHeight": "1.5",
4700
4704
  "letterSpacing": "0px",
4701
4705
  "textTransform": "none",
4702
- "textAlign": "left",
4706
+ "textAlign": "start",
4703
4707
  "fontStyle": "normal",
4704
4708
  "textDecoration": "none",
4705
4709
  "WebkitTextStrokeWidth": "0px",
@@ -4888,7 +4892,7 @@ export const NEUTRAL_STYLE_PROPS: Readonly<Record<string, unknown>> = {
4888
4892
  "lineHeight": "1.5",
4889
4893
  "letterSpacing": "0px",
4890
4894
  "textTransform": "none",
4891
- "textAlign": "left",
4895
+ "textAlign": "start",
4892
4896
  "fontStyle": "normal",
4893
4897
  "textDecoration": "none",
4894
4898
  "WebkitTextStrokeWidth": "0px",
@@ -58,6 +58,9 @@ export interface PatternNode {
58
58
  /** The composite-core recipes a pattern may stamp (`recipe`). */
59
59
  export enum PatternRecipe {
60
60
  Card = 'card',
61
+ /** CC2: the Accordion — Manual (three placeholder items), or From content when the section names a list
62
+ * `binding` (`src: '$binding?'`): one template item bound to the type's question / answer fields. */
63
+ Accordion = 'accordion',
61
64
  }
62
65
 
63
66
  /** A recipe node's `binds` keys → the part each binds (Card v2). */
@@ -100,6 +103,8 @@ export enum PatternPlaceholder {
100
103
  export enum PatternDirective {
101
104
  /** A Repeater's source: the section's `binding`. */
102
105
  Binding = '$binding',
106
+ /** The section MAY name a list `binding` (faq.accordion): with one, the recipe's items come from it. */
107
+ OptionalBinding = '$binding?',
103
108
  /** `each`: one plain node per page of the plan that is in the navigation (`inNav`), in plan order —
104
109
  * words and routes written in, editable on the canvas (W1.1). */
105
110
  NavPages = '$navPages',
@@ -150,6 +155,11 @@ export function patternNeedsBinding(pattern: SitePattern): boolean {
150
155
  return patternNodes(pattern).some(n => n.src === PatternDirective.Binding);
151
156
  }
152
157
 
158
+ /** True when the pattern may take a section `binding` — it needs one, or names `$binding?` (CC2). */
159
+ export function patternTakesBinding(pattern: SitePattern): boolean {
160
+ return patternNeedsBinding(pattern) || patternNodes(pattern).some(n => n.src === PatternDirective.OptionalBinding);
161
+ }
162
+
153
163
  const IMAGE_TYPE = 'Image';
154
164
 
155
165
  /** The scopes whose ITEM a pattern's bound Images take their alt text from (an Image that names no `alt`
@@ -30,6 +30,10 @@ export interface SerializedNode {
30
30
  /** The part this node plays in a composite (composite-core: `card.title`; a navigation's `nav.drawer`,
31
31
  * written by `planNavShape`). Additive; a node without one is a plain node. */
32
32
  role?: string;
33
+ /** An item's identity inside its composite (CC2: an Accordion item's `k1`…). Additive. */
34
+ itemKey?: string;
35
+ /** A targeted state's driving node (CC2: an icon's `trigger-on` → its item). */
36
+ stateTriggers?: Partial<Record<string, string>>;
33
37
  bindings: SerializedBinding[];
34
38
  interactions: unknown[];
35
39
  isGlobal?: boolean;
@@ -89,7 +89,7 @@ export const SPEC_SITE_PLAN_SHAPE = {
89
89
  "section": {
90
90
  "pattern": "string — a non-global pattern id",
91
91
  "slots?": "{ [slot]: string } — the pattern's declared slots",
92
- "binding?": "string — the list source a pattern's $binding repeater iterates (exactly when it has one)",
92
+ "binding?": "string — the list source a pattern's $binding repeater iterates (exactly when it has one); a $binding? pattern (faq.accordion) MAY take one — then its items come from that content",
93
93
  "without?": "string[] — the pattern's optional parts to leave out"
94
94
  },
95
95
  "globals": {
@@ -933,6 +933,51 @@ export const SPEC_SITE_PATTERNS = {
933
933
  }
934
934
  ]
935
935
  }
936
+ },
937
+ "faq.accordion": {
938
+ "family": "faq",
939
+ "label": "FAQ — questions that open",
940
+ "node": {
941
+ "t": "Section",
942
+ "es": "section",
943
+ "c": [
944
+ {
945
+ "t": "Container",
946
+ "es": "box",
947
+ "lay": {
948
+ "display": "flex",
949
+ "flexDirection": "column",
950
+ "gap": "14px",
951
+ "maxWidth": "860px",
952
+ "width": "100%",
953
+ "margin": "0px auto 0px auto"
954
+ },
955
+ "c": [
956
+ {
957
+ "t": "Text",
958
+ "es": "eyebrow",
959
+ "slot": "eyebrow",
960
+ "ph": "copy",
961
+ "text": "FAQ"
962
+ },
963
+ {
964
+ "t": "Heading",
965
+ "es": "h2",
966
+ "level": "h2",
967
+ "slot": "title",
968
+ "ph": "copy",
969
+ "text": "Questions people ask"
970
+ },
971
+ {
972
+ "t": "Container",
973
+ "recipe": "accordion",
974
+ "src": "$binding?",
975
+ "ph": "copy"
976
+ }
977
+ ]
978
+ }
979
+ ]
980
+ }
936
981
  }
937
982
  } as const;
938
983
 
@@ -1,7 +1,7 @@
1
1
  import { flavorPackById, parseHexColor, SPEC_AXES } from '@vectoriox/iox-ui/design-core';
2
2
 
3
3
  import {
4
- altSourceScopes, BindScope, patternById, patternNeedsBinding, patternOptionalParts, patternScopes, patternSlots, PatternGlobalPosition,
4
+ altSourceScopes, BindScope, patternById, patternNeedsBinding, patternOptionalParts, patternTakesBinding, patternScopes, patternSlots, PatternGlobalPosition,
5
5
  } from './patterns';
6
6
  import {
7
7
  FACT_KEYS, GlobalSlot, PlanDataSourceMode, PlanDataSourceType, RelativeOp, SitePlanSourceKind, SITE_PLAN_VERSION,
@@ -367,9 +367,10 @@ function validatePages(c: Collector, list: unknown[], types: Map<string, Content
367
367
  });
368
368
  }
369
369
  const binding = c.string(s, 'binding', sp, false);
370
- if (patternNeedsBinding(pattern)) {
371
- if (!binding) c.add(SitePlanIssueCode.Binding, `${sp}.binding`, `"${patternId}" repeats the section's binding — name a list source`);
372
- else {
370
+ if (patternTakesBinding(pattern)) {
371
+ if (!binding) {
372
+ if (patternNeedsBinding(pattern)) c.add(SitePlanIssueCode.Binding, `${sp}.binding`, `"${patternId}" repeats the section's binding — name a list source`);
373
+ } else {
373
374
  const src = sources.get(binding);
374
375
  if (!src) c.add(SitePlanIssueCode.UnknownDataSource, `${sp}.binding`, `no data source "${binding}"`);
375
376
  else if (src.type !== PlanDataSourceType.Cms || src.mode !== PlanDataSourceMode.Query) c.add(SitePlanIssueCode.Binding, `${sp}.binding`, `"${binding}" is not a list source`);
@@ -207,3 +207,33 @@ dialog[data-iox-dialog]:not([open]) {
207
207
  --iox-heading-color: currentColor;
208
208
  --iox-text-muted: currentColor;
209
209
  }
210
+
211
+ /* ── CC2: the Accordion (complex-components boards 3–4; composite K1) ──────────────────────────────
212
+ The renderers write ONE markup (composite-core accordion.ts): the header is a <button> inside a heading of
213
+ its question's level; the panel sits in a region inside a TRACK. These rules are structural only — the look
214
+ is the parts' element styles (accordion-item / -trigger / -panel), on their own node rules. */
215
+
216
+ /* The heading wrapper carries the outline level and nothing of its own. */
217
+ .iox-accordion-heading { margin: 0; padding: 0; font: inherit; color: inherit; }
218
+ /* The header is a real button: its UA look is reset at zero specificity, so the node's own rule (the
219
+ accordion-trigger style and the recipe's row layout) always wins. */
220
+ :where(.iox-accordion-button) {
221
+ appearance: none; -webkit-appearance: none; background: none; border: 0; border-radius: 0; margin: 0;
222
+ font: inherit; color: inherit; text-align: start; cursor: pointer; box-sizing: border-box;
223
+ /* A <button> sizes to its content even as a block: the header is the whole row (its width, an OUTER
224
+ prop, lands on the canvas twin's host — the button fills it). */
225
+ width: 100%;
226
+ }
227
+ /* The ONE hiding rule (board 4, Q11): a closed item's panel track collapses to a 0 row. The region inside it
228
+ is `hidden="until-found"` while closed — content-visibility, not display:none — so find-in-page still
229
+ reaches the answer; it carries no padding (a hidden box keeps its own padding), the panel node inside it
230
+ does. Opening animates the row over dur-base (0ms under reduced motion: the motion tokens collapse). */
231
+ .iox-accordion-track {
232
+ display: grid; grid-template-rows: 0fr; min-width: 0;
233
+ transition: grid-template-rows var(--iox-dur-base) var(--iox-ease-standard);
234
+ }
235
+ .iox-accordion-track[data-iox-on] { grid-template-rows: 1fr; }
236
+ .iox-accordion-region { min-height: 0; min-width: 0; overflow: hidden; padding: 0; margin: 0; }
237
+ @media (prefers-reduced-motion: reduce) {
238
+ .iox-accordion-track { transition: none; }
239
+ }