@vectoriox/iox-ui 5.37.0 → 5.38.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 (38) hide show
  1. package/actions-core/index.d.ts +6 -1
  2. package/composite-core/index.d.ts +294 -14
  3. package/design-core/index.d.ts +130 -3
  4. package/fesm2022/vectoriox-iox-ui-actions-core.mjs +9 -3
  5. package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
  6. package/fesm2022/vectoriox-iox-ui-composite-core.mjs +694 -59
  7. package/fesm2022/vectoriox-iox-ui-composite-core.mjs.map +1 -1
  8. package/fesm2022/vectoriox-iox-ui-design-core.mjs +172 -3
  9. package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
  10. package/fesm2022/vectoriox-iox-ui-generation-core.mjs +410 -10
  11. package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
  12. package/fesm2022/vectoriox-iox-ui-media-core.mjs +571 -0
  13. package/fesm2022/vectoriox-iox-ui-media-core.mjs.map +1 -0
  14. package/fesm2022/vectoriox-iox-ui.mjs +478 -118
  15. package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
  16. package/generation-core/index.d.ts +167 -4
  17. package/index.d.ts +206 -24
  18. package/media-core/index.d.ts +277 -0
  19. package/media-core/package.json +3 -0
  20. package/package.json +5 -1
  21. package/src/actions-core/element-actions.ts +9 -2
  22. package/src/composite-core/composite-node.ts +4 -0
  23. package/src/composite-core/lightbox.ts +192 -0
  24. package/src/composite-core/public-api.ts +3 -0
  25. package/src/composite-core/slider-runtime.ts +180 -0
  26. package/src/composite-core/slider.ts +378 -0
  27. package/src/composite-core/structure.ts +17 -4
  28. package/src/design-core/element-styles.ts +27 -0
  29. package/src/design-core/flavor-spec.generated.ts +146 -2
  30. package/src/generation-core/baseline-roles.ts +2 -0
  31. package/src/generation-core/compose-site.ts +80 -4
  32. package/src/generation-core/node-baselines.generated.ts +148 -4
  33. package/src/generation-core/patterns.ts +17 -0
  34. package/src/generation-core/site-spec.generated.ts +164 -0
  35. package/src/media-core/public-api.ts +9 -0
  36. package/src/media-core/video-runtime.ts +239 -0
  37. package/src/media-core/video.ts +443 -0
  38. package/styles/iox-design.css +96 -0
@@ -1108,6 +1108,21 @@ export const SPEC_TIERS = {
1108
1108
  "name": "media-scrim-ink",
1109
1109
  "type": "color",
1110
1110
  "default": "#FFFFFF"
1111
+ },
1112
+ {
1113
+ "name": "video-play-bg",
1114
+ "type": "color",
1115
+ "default": "var(--iox-button-primary-bg)"
1116
+ },
1117
+ {
1118
+ "name": "video-play-ink",
1119
+ "type": "color",
1120
+ "default": "var(--iox-button-primary-ink)"
1121
+ },
1122
+ {
1123
+ "name": "video-play-size",
1124
+ "type": "size",
1125
+ "default": "64px"
1111
1126
  }
1112
1127
  ]
1113
1128
  } as const;
@@ -1785,7 +1800,8 @@ export const SPEC_ELEMENT_STYLES = [
1785
1800
  "appliesTo": [
1786
1801
  "Container",
1787
1802
  "LinkedContainer",
1788
- "Card"
1803
+ "Card",
1804
+ "SliderSlide"
1789
1805
  ],
1790
1806
  "base": {
1791
1807
  "color": "inherit",
@@ -1940,7 +1956,8 @@ export const SPEC_ELEMENT_STYLES = [
1940
1956
  "appliesTo": [
1941
1957
  "Image",
1942
1958
  "SliderSlide",
1943
- "Gallery"
1959
+ "Gallery",
1960
+ "Video"
1944
1961
  ],
1945
1962
  "base": {
1946
1963
  "borderRadius": "var(--iox-image-radius)",
@@ -2186,6 +2203,7 @@ export const SPEC_DEFAULT_ELEMENT_STYLE = {
2186
2203
  "Link": "link",
2187
2204
  "Image": "media",
2188
2205
  "SliderSlide": "media",
2206
+ "Video": "media",
2189
2207
  "Divider": "rule",
2190
2208
  "Icon": "icon",
2191
2209
  "Dialog": "dialog",
@@ -4288,6 +4306,27 @@ export const SPEC_FLAVOR_PACKS = [
4288
4306
  "type": "color",
4289
4307
  "group": "components",
4290
4308
  "value": "#FFFFFF"
4309
+ },
4310
+ {
4311
+ "name": "video-play-bg",
4312
+ "tier": "component",
4313
+ "type": "color",
4314
+ "group": "components",
4315
+ "value": "var(--iox-button-primary-bg)"
4316
+ },
4317
+ {
4318
+ "name": "video-play-ink",
4319
+ "tier": "component",
4320
+ "type": "color",
4321
+ "group": "components",
4322
+ "value": "var(--iox-button-primary-ink)"
4323
+ },
4324
+ {
4325
+ "name": "video-play-size",
4326
+ "tier": "component",
4327
+ "type": "size",
4328
+ "group": "components",
4329
+ "value": "64px"
4291
4330
  }
4292
4331
  ]
4293
4332
  },
@@ -5910,6 +5949,27 @@ export const SPEC_FLAVOR_PACKS = [
5910
5949
  "type": "color",
5911
5950
  "group": "components",
5912
5951
  "value": "#FFFFFF"
5952
+ },
5953
+ {
5954
+ "name": "video-play-bg",
5955
+ "tier": "component",
5956
+ "type": "color",
5957
+ "group": "components",
5958
+ "value": "var(--iox-button-primary-bg)"
5959
+ },
5960
+ {
5961
+ "name": "video-play-ink",
5962
+ "tier": "component",
5963
+ "type": "color",
5964
+ "group": "components",
5965
+ "value": "var(--iox-button-primary-ink)"
5966
+ },
5967
+ {
5968
+ "name": "video-play-size",
5969
+ "tier": "component",
5970
+ "type": "size",
5971
+ "group": "components",
5972
+ "value": "64px"
5913
5973
  }
5914
5974
  ]
5915
5975
  },
@@ -7524,6 +7584,27 @@ export const SPEC_FLAVOR_PACKS = [
7524
7584
  "type": "color",
7525
7585
  "group": "components",
7526
7586
  "value": "#FFFFFF"
7587
+ },
7588
+ {
7589
+ "name": "video-play-bg",
7590
+ "tier": "component",
7591
+ "type": "color",
7592
+ "group": "components",
7593
+ "value": "var(--iox-button-primary-bg)"
7594
+ },
7595
+ {
7596
+ "name": "video-play-ink",
7597
+ "tier": "component",
7598
+ "type": "color",
7599
+ "group": "components",
7600
+ "value": "var(--iox-button-primary-ink)"
7601
+ },
7602
+ {
7603
+ "name": "video-play-size",
7604
+ "tier": "component",
7605
+ "type": "size",
7606
+ "group": "components",
7607
+ "value": "64px"
7527
7608
  }
7528
7609
  ]
7529
7610
  },
@@ -9180,6 +9261,27 @@ export const SPEC_FLAVOR_PACKS = [
9180
9261
  "type": "color",
9181
9262
  "group": "components",
9182
9263
  "value": "#FFFFFF"
9264
+ },
9265
+ {
9266
+ "name": "video-play-bg",
9267
+ "tier": "component",
9268
+ "type": "color",
9269
+ "group": "components",
9270
+ "value": "var(--iox-button-primary-bg)"
9271
+ },
9272
+ {
9273
+ "name": "video-play-ink",
9274
+ "tier": "component",
9275
+ "type": "color",
9276
+ "group": "components",
9277
+ "value": "var(--iox-button-primary-ink)"
9278
+ },
9279
+ {
9280
+ "name": "video-play-size",
9281
+ "tier": "component",
9282
+ "type": "size",
9283
+ "group": "components",
9284
+ "value": "64px"
9183
9285
  }
9184
9286
  ]
9185
9287
  },
@@ -10798,6 +10900,27 @@ export const SPEC_FLAVOR_PACKS = [
10798
10900
  "type": "color",
10799
10901
  "group": "components",
10800
10902
  "value": "#FFFFFF"
10903
+ },
10904
+ {
10905
+ "name": "video-play-bg",
10906
+ "tier": "component",
10907
+ "type": "color",
10908
+ "group": "components",
10909
+ "value": "var(--iox-button-primary-bg)"
10910
+ },
10911
+ {
10912
+ "name": "video-play-ink",
10913
+ "tier": "component",
10914
+ "type": "color",
10915
+ "group": "components",
10916
+ "value": "var(--iox-button-primary-ink)"
10917
+ },
10918
+ {
10919
+ "name": "video-play-size",
10920
+ "tier": "component",
10921
+ "type": "size",
10922
+ "group": "components",
10923
+ "value": "64px"
10801
10924
  }
10802
10925
  ]
10803
10926
  },
@@ -12423,6 +12546,27 @@ export const SPEC_FLAVOR_PACKS = [
12423
12546
  "type": "color",
12424
12547
  "group": "components",
12425
12548
  "value": "#FFFFFF"
12549
+ },
12550
+ {
12551
+ "name": "video-play-bg",
12552
+ "tier": "component",
12553
+ "type": "color",
12554
+ "group": "components",
12555
+ "value": "var(--iox-button-primary-bg)"
12556
+ },
12557
+ {
12558
+ "name": "video-play-ink",
12559
+ "tier": "component",
12560
+ "type": "color",
12561
+ "group": "components",
12562
+ "value": "var(--iox-button-primary-ink)"
12563
+ },
12564
+ {
12565
+ "name": "video-play-size",
12566
+ "tier": "component",
12567
+ "type": "size",
12568
+ "group": "components",
12569
+ "value": "64px"
12426
12570
  }
12427
12571
  ]
12428
12572
  }
@@ -61,6 +61,8 @@ export const BASELINE_ROLES: Readonly<Record<string, Readonly<Record<string, Bas
61
61
  FormSuccess: { width: Behaviour, color: Look, padding: Look, borderRadius: Look, backgroundColor: Look },
62
62
  FormError: { width: Behaviour, color: Look, padding: Look, borderRadius: Look, backgroundColor: Look },
63
63
  Dialog: { width: Behaviour, padding: Look, borderRadius: Look, backgroundColor: Look, boxShadow: Look },
64
+ // CC3: a Video fills its column at 16 / 9 — the shape the poster and the player take.
65
+ Video: { width: Behaviour, aspectRatio: Behaviour },
64
66
  };
65
67
 
66
68
  /** The style values a type's constructor sets for an empty palette drop, each mapped to the value the
@@ -2,12 +2,12 @@ import { DesignChoices, LinkType, resolveDesignChoices } from '@vectoriox/iox-ui
2
2
  import { FormPresetDef, FormPresetField, formPresetById } from '@vectoriox/iox-ui/forms-core';
3
3
  import {
4
4
  ACCORDION_BIND_PROP, ACCORDION_CONTENT_LIMIT, ACCORDION_WORDS, accordionAnswerOf, accordionItems, accordionQuestionOf, AccordionRole,
5
- buildAccordionRecipe, buildCardRecipe, CardLayout, CardPart, CardRole, CARD_LAYOUTS, CARD_PARTS, CompositeNode, CompositeNodeSpec, findByRole,
6
- pickAccordionAutoBinds,
5
+ buildAccordionRecipe, buildCardRecipe, buildCarouselRecipe, buildLightboxRecipe, CardLayout, CardPart, CardRole, CARD_LAYOUTS, CARD_PARTS, CompositeNode,
6
+ CompositeNodeSpec, findByRole, LIGHTBOX_LABEL, pickAccordionAutoBinds, SLIDE_TO_ROW, SLIDE_TYPE,
7
7
  } from '@vectoriox/iox-ui/composite-core';
8
8
 
9
9
  import { NODE_BASELINES } from './node-baselines.generated';
10
- import { BindScope, CardBindTarget, PatternDirective, PatternNode, PatternPlaceholder, PatternRecipe, SitePattern, patternById } from './patterns';
10
+ import { BindScope, CardBindTarget, PatternDirective, PatternNode, PatternOpens, PatternPlaceholder, PatternRecipe, SitePattern, patternById } from './patterns';
11
11
  import { assertStyleSlots, ComposeError, createSerializedNode, SerializedBinding, SerializedNode, setStyleProps } from './serialized-node';
12
12
  import {
13
13
  ContentTypePlan, DataSourcePlan, FactKey, FACT_KEYS, GlobalSlot, PagePlan, PageSectionPlan, PlanDataSourceMode, PlanDataSourceType,
@@ -37,6 +37,8 @@ export enum PlaceholderKind {
37
37
  Copy = 'copy',
38
38
  /** An image slot with no image: the empty-image placeholder (DS3). */
39
39
  Image = 'image',
40
+ /** CC3: a video slot with no clip — the studio uploads its own (the poster blank meanwhile). */
41
+ Video = 'video',
40
42
  }
41
43
 
42
44
  export interface ComposedPlaceholder {
@@ -174,6 +176,10 @@ const BUTTON_BLOCK = 'ButtonBlock';
174
176
  const FORM = 'Form';
175
177
  const FIELD = 'Field';
176
178
  const IMAGE = 'Image';
179
+ const VIDEO = 'Video';
180
+ /** A Video's source prop (media-core `VideoTrait.Source` — not `source`, which names a data source). */
181
+ const VIDEO_SOURCE_PROP = 'videoSource';
182
+ const DIALOG = 'Dialog';
177
183
  const SUBMIT_BUTTON = 'SubmitButton';
178
184
  const FORM_SUCCESS = 'FormSuccess';
179
185
  const FORM_ERROR = 'FormError';
@@ -231,6 +237,8 @@ interface Ctx {
231
237
  placeholders: ComposedPlaceholder[];
232
238
  issues: ComposeIssue[];
233
239
  routes: ReadonlySet<string>;
240
+ /** CC3: nodes whose click opens the pattern's lightbox (`opens`), wired once the pattern is stamped. */
241
+ openers?: string[];
234
242
  }
235
243
 
236
244
  function nonEmpty(v: unknown): v is string {
@@ -480,6 +488,22 @@ function composeNode(pn: PatternNode, id: string, repeater: RepeaterScope | null
480
488
  }
481
489
  }
482
490
 
491
+ // CC3: a Video in background mode is an uploaded file's; its clip is the studio's own (a placeholder until then).
492
+ if (pn.video) {
493
+ if (type !== VIDEO) throw new ComposeError(`${id}: "video" belongs on a Video`);
494
+ setProp(node, VIDEO_SOURCE_PROP, 'file');
495
+ setProp(node, 'mode', pn.video);
496
+ }
497
+ if (pn.ph === PatternPlaceholder.Video) {
498
+ setProp(node, 'src', '');
499
+ ctx.placeholders.push({ nodeId: id, owner: ctx.owner, kind: PlaceholderKind.Video, text: '' });
500
+ }
501
+ if (pn.name) setProp(node, 'ariaLabel', pn.name);
502
+ if (pn.opens) {
503
+ if (pn.opens !== PatternOpens.Lightbox) throw new ComposeError(`${id}: "opens" names the pattern's lightbox`);
504
+ (ctx.openers ??= []).push(id);
505
+ }
506
+
483
507
  // Images: an empty slot, or a bound one — both fall back to the DS3 empty image, never a stock URL.
484
508
  if (pn.ph === PatternPlaceholder.Image) {
485
509
  setProp(node, 'src', '');
@@ -562,6 +586,8 @@ const CARD_BINDS: Readonly<Record<CardBindTarget, { role: CardRole; prop: string
562
586
  */
563
587
  function composeRecipe(pn: PatternNode, id: string, repeater: RepeaterScope | null, ctx: Ctx): SerializedNode {
564
588
  if (pn.recipe === PatternRecipe.Accordion) return composeAccordion(pn, id, ctx);
589
+ if (pn.recipe === PatternRecipe.Carousel) return composeCarousel(pn, id, ctx);
590
+ if (pn.recipe === PatternRecipe.Lightbox) return composeLightbox(pn, id, repeater, ctx);
565
591
  if (pn.recipe !== PatternRecipe.Card) throw new ComposeError(`${id}: no recipe "${pn.recipe}" in composite-core`);
566
592
  const layout = (pn.layout ?? CardLayout.Top) as CardLayout;
567
593
  if (!CARD_LAYOUTS.includes(layout)) throw new ComposeError(`${id}: a card has no layout "${pn.layout}"`);
@@ -641,10 +667,60 @@ function composeAccordion(pn: PatternNode, id: string, ctx: Ctx): SerializedNode
641
667
  return root;
642
668
  }
643
669
 
670
+ /** A recipe's node factory for this composer: ids derive from the recipe's keys (`<id>-<key>`, the root `<id>`). */
671
+ function recipeFactory(id: string, rootKey: string): (spec: CompositeNodeSpec) => CompositeNode {
672
+ return spec => create(spec.key === rootKey ? id : `${id}-${spec.key}`, spec.type, { ...(spec.props ?? {}) }, spec.elementStyle ?? null) as CompositeNode;
673
+ }
674
+
675
+ /**
676
+ * CC3: the testimonial carousel — composite-core's recipe (the Slider with its control parts), stamped with this
677
+ * composer's factory. Every quote and every "client · project" line is a placeholder the studio rewrites (`ph: copy`):
678
+ * testimonials are facts, never invented.
679
+ */
680
+ function composeCarousel(pn: PatternNode, id: string, ctx: Ctx): SerializedNode {
681
+ const slider = buildCarouselRecipe(recipeFactory(id, 'carousel')) as SerializedNode;
682
+ if (pn.ph === PatternPlaceholder.Copy) {
683
+ for (const slide of (slider.children ?? []).filter(c => c.type === SLIDE_TYPE)) {
684
+ for (const text of slide.children ?? []) ctx.placeholders.push({ nodeId: text.id, owner: ctx.owner, kind: PlaceholderKind.Copy, text: String(text.props?.['content'] ?? '') });
685
+ }
686
+ }
687
+ return slider;
688
+ }
689
+
690
+ /**
691
+ * CC3: the Lightbox — a Dialog holding a Slider bound to the node's `src` (the gallery's own source): one template
692
+ * slide whose image reads the row (a bare URL row: decorative alt), its arrows hidden on a phone.
693
+ */
694
+ function composeLightbox(pn: PatternNode, id: string, repeater: RepeaterScope | null, ctx: Ctx): SerializedNode {
695
+ if (!pn.src) throw new ComposeError(`${id}: a lightbox needs the gallery's source (src)`);
696
+ const scoped = resolveScoped(pn.src, repeater, ctx, id);
697
+ const sourcePath = scoped.path === ITEM_VALUE_PATH ? '' : scoped.path;
698
+ return buildLightboxRecipe(recipeFactory(id, 'lightbox'), { source: scoped.source, sourcePath, imageField: 'item' }) as SerializedNode;
699
+ }
700
+
701
+ /** CC3: every `opens: lightbox` node of a stamped pattern opens the pattern's lightbox at its own row. */
702
+ function wireOpeners(root: SerializedNode, openers: ReadonlyArray<string>): void {
703
+ if (!openers.length) return;
704
+ let lightbox: SerializedNode | null = null;
705
+ const find = (n: SerializedNode) => { if (!lightbox && n.type === DIALOG && n.label === LIGHTBOX_LABEL) lightbox = n; (n.children ?? []).forEach(find); };
706
+ find(root);
707
+ if (!lightbox) throw new ComposeError(`${root.id}: "opens: lightbox" with no lightbox in the pattern`);
708
+ const target = (lightbox as SerializedNode).id;
709
+ const visit = (n: SerializedNode) => {
710
+ if (openers.includes(n.id)) {
711
+ n.interactions = [{ id: `${n.id}-opens`, trigger: 'click', actions: [{ type: 'openDialog', target, params: { slideTo: SLIDE_TO_ROW }, duration: 0, delay: 0, easing: 'ease' }] }];
712
+ }
713
+ (n.children ?? []).forEach(visit);
714
+ };
715
+ visit(root);
716
+ }
717
+
644
718
  function composePattern(pattern: SitePattern, id: string, ctx: Ctx): SerializedNode {
645
719
  if (pattern.node.each) throw new ComposeError(`${id}: a pattern's root cannot be stamped per page`);
646
- const node = composeNode(pattern.node, id, null, ctx);
720
+ const own: Ctx = { ...ctx, openers: [] };
721
+ const node = composeNode(pattern.node, id, null, own);
647
722
  if (!node) throw new ComposeError(`${id}: a pattern's root cannot be optional or a link`);
723
+ wireOpeners(node, own.openers ?? []);
648
724
  node.label = pattern.label;
649
725
  return node;
650
726
  }
@@ -3032,7 +3032,8 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3032
3032
  "enableParallax": false,
3033
3033
  "enableGrid": false,
3034
3034
  "gridRows": 2,
3035
- "breakpoints": ""
3035
+ "breakpoints": "",
3036
+ "ariaLabel": ""
3036
3037
  },
3037
3038
  "styleProps": {
3038
3039
  "position": "static",
@@ -3142,7 +3143,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3142
3143
  "enableGrid",
3143
3144
  "gridRows",
3144
3145
  "__responsiveLabel",
3145
- "breakpoints"
3146
+ "breakpoints",
3147
+ "__accessibilityLabel",
3148
+ "ariaLabel"
3146
3149
  ],
3147
3150
  "styleTraits": [
3148
3151
  "position",
@@ -4660,7 +4663,8 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4660
4663
  "showCloseButton": true,
4661
4664
  "consentRole": false,
4662
4665
  "closeAbove": "",
4663
- "ariaLabel": ""
4666
+ "ariaLabel": "",
4667
+ "colorScheme": "default"
4664
4668
  },
4665
4669
  "styleProps": {
4666
4670
  "position": "static",
@@ -4753,7 +4757,8 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4753
4757
  "__responsiveLabel",
4754
4758
  "closeAbove",
4755
4759
  "__accessibilityLabel",
4756
- "ariaLabel"
4760
+ "ariaLabel",
4761
+ "colorScheme"
4757
4762
  ],
4758
4763
  "styleTraits": [
4759
4764
  "position",
@@ -4847,6 +4852,145 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4847
4852
  "transitionTimingFunction"
4848
4853
  ],
4849
4854
  "container": true
4855
+ },
4856
+ "Video": {
4857
+ "props": {
4858
+ "videoSource": "youtube",
4859
+ "src": "",
4860
+ "poster": "",
4861
+ "mode": "inline",
4862
+ "autoplay": false,
4863
+ "title": "",
4864
+ "captions": "",
4865
+ "captionsLang": "en",
4866
+ "consentCategory": "marketing"
4867
+ },
4868
+ "styleProps": {
4869
+ "position": "static",
4870
+ "top": "auto",
4871
+ "right": "auto",
4872
+ "bottom": "auto",
4873
+ "left": "auto",
4874
+ "zIndex": "0",
4875
+ "width": "100%",
4876
+ "height": "auto",
4877
+ "minWidth": "auto",
4878
+ "minHeight": "auto",
4879
+ "maxWidth": "auto",
4880
+ "maxHeight": "auto",
4881
+ "aspectRatio": "16 / 9",
4882
+ "objectFit": "cover",
4883
+ "overflow": "visible",
4884
+ "margin": "0px 0px 0px 0px",
4885
+ "padding": "0px 0px 0px 0px",
4886
+ "borderWidth": "0px",
4887
+ "borderStyle": "solid",
4888
+ "borderColor": "#CCCCCC",
4889
+ "borderRadius": "0px",
4890
+ "opacity": "1",
4891
+ "mixBlendMode": "normal",
4892
+ "cursor": "auto",
4893
+ "boxShadow": "",
4894
+ "textShadow": "",
4895
+ "translateX": "0px",
4896
+ "translateY": "0px",
4897
+ "scaleX": "1",
4898
+ "scaleY": "1",
4899
+ "rotate": "0deg",
4900
+ "skewX": "0deg",
4901
+ "skewY": "0deg",
4902
+ "filterBlur": "0px",
4903
+ "filterHueRotate": "0deg",
4904
+ "filterBrightness": "1",
4905
+ "filterContrast": "1",
4906
+ "filterSaturate": "1",
4907
+ "filterGrayscale": "0",
4908
+ "filterSepia": "0",
4909
+ "filterInvert": "0",
4910
+ "backdropFilter": "none",
4911
+ "backdropBlur": "0px",
4912
+ "backdropBrightness": "1",
4913
+ "backdropContrast": "1",
4914
+ "backdropSaturate": "1",
4915
+ "clipPath": "none",
4916
+ "transitionDuration": "0ms",
4917
+ "transitionDelay": "0ms",
4918
+ "transitionTimingFunction": "ease"
4919
+ },
4920
+ "traits": [
4921
+ "__mediaLabel",
4922
+ "videoSource",
4923
+ "src",
4924
+ "poster",
4925
+ "__behaviourLabel",
4926
+ "mode",
4927
+ "autoplay",
4928
+ "__accessibilityLabel",
4929
+ "title",
4930
+ "captions",
4931
+ "captionsLang",
4932
+ "__privacyLabel",
4933
+ "consentCategory"
4934
+ ],
4935
+ "styleTraits": [
4936
+ "position",
4937
+ "top",
4938
+ "right",
4939
+ "bottom",
4940
+ "left",
4941
+ "zIndex",
4942
+ "width",
4943
+ "height",
4944
+ "minWidth",
4945
+ "minHeight",
4946
+ "maxWidth",
4947
+ "maxHeight",
4948
+ "aspectRatio",
4949
+ "objectFit",
4950
+ "overflow",
4951
+ "margin",
4952
+ "padding",
4953
+ "borderWidth",
4954
+ "borderStyle",
4955
+ "borderColor",
4956
+ "borderRadius",
4957
+ "opacity",
4958
+ "mixBlendMode",
4959
+ "cursor",
4960
+ "_sep_shadow",
4961
+ "boxShadow",
4962
+ "textShadow",
4963
+ "_sep_transform",
4964
+ "translateX",
4965
+ "translateY",
4966
+ "scaleX",
4967
+ "scaleY",
4968
+ "rotate",
4969
+ "skewX",
4970
+ "skewY",
4971
+ "_sep_filter",
4972
+ "filterBlur",
4973
+ "filterHueRotate",
4974
+ "filterBrightness",
4975
+ "filterContrast",
4976
+ "filterSaturate",
4977
+ "filterGrayscale",
4978
+ "filterSepia",
4979
+ "filterInvert",
4980
+ "_sep_backdrop",
4981
+ "backdropFilter",
4982
+ "backdropBlur",
4983
+ "backdropBrightness",
4984
+ "backdropContrast",
4985
+ "backdropSaturate",
4986
+ "_sep_clip",
4987
+ "clipPath",
4988
+ "_sep_transition",
4989
+ "transitionDuration",
4990
+ "transitionDelay",
4991
+ "transitionTimingFunction"
4992
+ ],
4993
+ "container": false
4850
4994
  }
4851
4995
  };
4852
4996
 
@@ -53,6 +53,12 @@ export interface PatternNode {
53
53
  readonly layout?: string;
54
54
  readonly parts?: Readonly<Record<string, boolean>>;
55
55
  readonly binds?: Readonly<Record<string, string>>;
56
+ /** CC3: a Video's mode — `background` (an uploaded file, a muted loop). */
57
+ readonly video?: string;
58
+ /** CC3: a click on this node opens the pattern's lightbox at the clicked row (`lightbox`). */
59
+ readonly opens?: string;
60
+ /** CC3: a control's accessible name (`ariaLabel`). */
61
+ readonly name?: string;
56
62
  }
57
63
 
58
64
  /** The composite-core recipes a pattern may stamp (`recipe`). */
@@ -61,6 +67,10 @@ export enum PatternRecipe {
61
67
  /** CC2: the Accordion — Manual (three placeholder items), or From content when the section names a list
62
68
  * `binding` (`src: '$binding?'`): one template item bound to the type's question / answer fields. */
63
69
  Accordion = 'accordion',
70
+ /** CC3: the accessible carousel — a named Slider whose controls are parts, one quote per slide (placeholders). */
71
+ Carousel = 'carousel',
72
+ /** CC3: the Lightbox — a Dialog holding a Slider over the node's `src` (the gallery's own source). */
73
+ Lightbox = 'lightbox',
64
74
  }
65
75
 
66
76
  /** A recipe node's `binds` keys → the part each binds (Card v2). */
@@ -97,6 +107,13 @@ export const SITE_PATTERNS: Readonly<Record<string, SitePattern>> = SPEC_SITE_PA
97
107
  export enum PatternPlaceholder {
98
108
  Copy = 'copy',
99
109
  Image = 'image',
110
+ /** CC3: a clip the studio uploads (a Video's file): the poster blank until then, publish-blocking. */
111
+ Video = 'video',
112
+ }
113
+
114
+ /** CC3: what an `opens` node opens. */
115
+ export enum PatternOpens {
116
+ Lightbox = 'lightbox',
100
117
  }
101
118
 
102
119
  /** The composer directives a pattern writes where a plan supplies the value. */