astryx-dracula 0.5.1 → 0.6.2

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 (108) hide show
  1. package/BRAND.md +3 -3
  2. package/README.md +7 -4
  3. package/USAGE.md +49 -49
  4. package/astryx-dracula.d.ts +2 -2
  5. package/astryx-dracula.js +25 -9
  6. package/astryx-dracula.variants.d.ts +2 -2
  7. package/icons.mjs +4 -0
  8. package/icons.tsx +8 -0
  9. package/package.json +22 -17
  10. package/shared/chaptered-doc.tsx +1 -2
  11. package/shared/chart-hues.ts +21 -6
  12. package/shared/chart-legend.tsx +2 -2
  13. package/shared/data-bar.tsx +2 -2
  14. package/shared/settings-data.ts +0 -6
  15. package/shared/settings-rows.tsx +1 -23
  16. package/templates/ai-chat-landing.template.mjs +1 -0
  17. package/templates/ai-chat-landing.tsx +24 -2
  18. package/templates/ai-chat.template.mjs +1 -0
  19. package/templates/ai-chat.tsx +4 -2
  20. package/templates/blank.template.mjs +1 -0
  21. package/templates/blank.tsx +4 -2
  22. package/templates/centered-hero.template.mjs +1 -0
  23. package/templates/centered-hero.tsx +5 -3
  24. package/templates/classic-gallery.template.mjs +1 -0
  25. package/templates/classic-gallery.tsx +4 -2
  26. package/templates/contact-form.template.mjs +1 -0
  27. package/templates/contact-form.tsx +4 -2
  28. package/templates/dashboard-portfolio.template.mjs +1 -0
  29. package/templates/dashboard-portfolio.tsx +4 -2
  30. package/templates/dashboard.template.mjs +1 -0
  31. package/templates/dashboard.tsx +4 -2
  32. package/templates/detail-page.template.mjs +1 -0
  33. package/templates/detail-page.tsx +16 -16
  34. package/templates/documentation-design.template.mjs +1 -0
  35. package/templates/documentation-design.tsx +5 -3
  36. package/templates/documentation-technical.template.mjs +1 -0
  37. package/templates/documentation-technical.tsx +4 -2
  38. package/templates/documentation.template.mjs +1 -0
  39. package/templates/documentation.tsx +4 -2
  40. package/templates/editor.template.mjs +1 -0
  41. package/templates/editor.tsx +4 -2
  42. package/templates/file-explorer.template.mjs +1 -0
  43. package/templates/file-explorer.tsx +4 -2
  44. package/templates/form-two-column.template.mjs +1 -0
  45. package/templates/form-two-column.tsx +8 -2
  46. package/templates/gallery-hero.template.mjs +1 -0
  47. package/templates/gallery-hero.tsx +4 -2
  48. package/templates/ide.template.mjs +1 -0
  49. package/templates/ide.tsx +11 -6
  50. package/templates/incident-console.template.mjs +1 -0
  51. package/templates/incident-console.tsx +122 -80
  52. package/templates/kanban-board.template.mjs +1 -0
  53. package/templates/kanban-board.tsx +6 -4
  54. package/templates/library.template.mjs +1 -0
  55. package/templates/library.tsx +4 -2
  56. package/templates/login-card.template.mjs +1 -0
  57. package/templates/login-card.tsx +4 -2
  58. package/templates/login-split.template.mjs +1 -0
  59. package/templates/login-split.tsx +5 -2
  60. package/templates/login-sso.template.mjs +1 -0
  61. package/templates/login-sso.tsx +4 -2
  62. package/templates/login.template.mjs +1 -0
  63. package/templates/login.tsx +4 -2
  64. package/templates/messaging-shell.template.mjs +1 -0
  65. package/templates/messaging-shell.tsx +6 -2
  66. package/templates/mixed-gallery.template.mjs +1 -0
  67. package/templates/mixed-gallery.tsx +4 -2
  68. package/templates/payment-form.template.mjs +1 -0
  69. package/templates/payment-form.tsx +5 -3
  70. package/templates/product-detail.template.mjs +1 -0
  71. package/templates/product-detail.tsx +9 -2
  72. package/templates/product-gallery.template.mjs +1 -0
  73. package/templates/product-gallery.tsx +4 -2
  74. package/templates/product-tour.template.mjs +1 -0
  75. package/templates/product-tour.tsx +13 -10
  76. package/templates/settings-dialog.template.mjs +1 -0
  77. package/templates/settings-dialog.tsx +5 -3
  78. package/templates/settings-sidebar.template.mjs +1 -0
  79. package/templates/settings-sidebar.tsx +6 -4
  80. package/templates/settings.template.mjs +1 -0
  81. package/templates/settings.tsx +6 -10
  82. package/templates/shell-nav.template.mjs +1 -0
  83. package/templates/shell-nav.tsx +24 -26
  84. package/templates/shell-side-nav.template.mjs +1 -0
  85. package/templates/shell-side-nav.tsx +23 -18
  86. package/templates/shell-top-nav.template.mjs +1 -0
  87. package/templates/shell-top-nav.tsx +4 -2
  88. package/templates/side-gallery.template.mjs +1 -0
  89. package/templates/side-gallery.tsx +4 -2
  90. package/templates/table-grouped.template.mjs +1 -0
  91. package/templates/table-grouped.tsx +5 -3
  92. package/templates/table-page-chart.template.mjs +1 -0
  93. package/templates/table-page-chart.tsx +4 -2
  94. package/templates/table-page-heatmap-status.template.mjs +1 -0
  95. package/templates/table-page-heatmap-status.tsx +4 -2
  96. package/templates/table-page-shoe-store-heatmap.template.mjs +1 -0
  97. package/templates/table-page-shoe-store-heatmap.tsx +4 -2
  98. package/templates/table-page.template.mjs +1 -0
  99. package/templates/table-page.tsx +4 -2
  100. package/templates/table.template.mjs +1 -0
  101. package/templates/table.tsx +4 -2
  102. package/templates/tech-report.template.mjs +1 -0
  103. package/templates/tech-report.tsx +10 -8
  104. package/templates/theme-showcase.template.mjs +1 -0
  105. package/templates/theme-showcase.tsx +23 -17
  106. package/theme.css +35 -77
  107. package/theme.css.d.ts +9 -0
  108. package/tokens.css +38 -42
@@ -1,5 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
3
5
  // NOTE on `t=display-2`: the h1 asked for display-2 from the start, but
4
6
  // core 0.3.0's Heading emitted no `data-type`, and the 0.3.0 CLI emitted no
5
7
  // `.astryx-heading[data-type]` rule, so --text-display-2-size never reached a
@@ -8,7 +10,7 @@
8
10
  // pixel-diffing the pre-upgrade build against this one at 1440 and 390: the
9
11
  // only regression was this header detaching at 390, fixed by stacking the
10
12
  // title group under `isNarrow`.
11
- // L > (LH > H[g3 a=center] > (H[g2 a=center] > Hd"Night watch"[level=1 t=display-2] + Tx"3 investigating"[t=supporting]) + SegmentedControl"Filter by status" + B"Declare incident") + (LC[p0] > V[g0] > (H > PowerSearch"Search the night watch…") + (List > (ListItem)*7)) + (LayoutPanel[w380 p0] > V[g4] > (V[g2] > (H[g2 a=center] > StatusDot + Tx"INC-2417"[t=supporting] + Token"Investigating") + Hd"Checkout API elevated 5xx rate"[level=2]) + (H[g2] > B.primary"Mark mitigated" + B.secondary"Escalate") + Divider + MetadataList + Divider + (V[g2] > Hd"Timeline"[level=3] + List))
13
+ // L[h=fill] > (LH[divider p=6] > (H[g=3 a=center] > (SI[fill] > H[g=2 a=center wrap] > Hd"Night watch"[level=1 t=display-2] + Tx"3 investigating"[t=supporting]) + SG"Filter by status" + B"Declare incident")) + (LC[p=0 !scroll] > V[g=0] > (H > (SI[fill] > PS"Search the night watch…")) + (SI[fill] > (V[g=0] > (V[g=0] > (H[g=2 a=center] > Tx"Investigating"[t=label] + Tx"3"[t=supporting]) + (UL[density=compact hasDividers] > (LI > SB)*3)) + (V[g=0] > (H[g=2 a=center] > Tx"Mitigated"[t=label] + Tx"2"[t=supporting]) + (UL[density=compact hasDividers] > (LI > SB)*2)) + (V[g=0] > (H[g=2 a=center] > Tx"Resolved"[t=label] + Tx"2"[t=supporting]) + (UL[density=compact hasDividers] > (LI > SB)*2))))) + (LP[w=380 p=0] > V[g=4] > (V[g=2] > (H[g=1 a=center] > SB + Tx"INC-2417"[t=supporting] + Tk"Investigating") + Hd"Checkout API elevated 5xx rate"[level=2]) + (H[g=2 wrap] > B.primary"Mark mitigated" + B.secondary"Escalate") + D + ML + D + (V[g=2] > Hd"Timeline"[level=3] + (UL[density=compact] > LI*3)))) ;; Drawer"Incident details"[side=end w=380] > (DrawerHeader"Incident details" + (V[g=4] > (V[g2] > (H[g=1 a=center] > SB + Tx"INC-2417"[t=supporting] + Tk"Investigating") + Hd"Checkout API elevated 5xx rate"[level=2]) + (H[g=2 wrap] > B.primary"Mark mitigated" + B.secondary"Escalate") + D + ML + D + (V[g=2] > Hd"Timeline"[level=3] + (UL[density=compact] > LI*3))))
12
14
 
13
15
  /**
14
16
  * Incident Console — an on-call incident response tool for the night watch. Frame-first layout (see `bunx astryx docs layout`), distilled fro (Frame/responsive/container: see XLE header above.)
@@ -16,6 +18,7 @@
16
18
 
17
19
  import { Button } from "@astryxdesign/core/Button";
18
20
  import { Divider } from "@astryxdesign/core/Divider";
21
+ import { Drawer, DrawerHeader } from "@astryxdesign/core/Drawer";
19
22
  import { EmptyState } from "@astryxdesign/core/EmptyState";
20
23
  import { useMediaQuery } from "@astryxdesign/core/hooks";
21
24
  import { Icon } from "@astryxdesign/core/Icon";
@@ -373,6 +376,10 @@ function IncidentRows({
373
376
  key={incident.id}
374
377
  label={incident.title}
375
378
  description={`${incident.id} · ${incident.service} · ${incident.impact}`}
379
+ // Incident titles wrap to two lines. The severity badge
380
+ // and the status token belong on the TITLE's first
381
+ // line, not floating at the middle of the block.
382
+ align="first-line"
376
383
  startContent={
377
384
  <SeverityBadge
378
385
  severity={incident.severity}
@@ -476,6 +483,10 @@ export default function IncidentConsole() {
476
483
  const [statusFilter, setStatusFilter] = useState("all");
477
484
  const [filters, setFilters] = useState<PowerSearchFilter[]>([]);
478
485
  const [selectedId, setSelectedId] = useState<string | null>(INCIDENTS[0].id);
486
+ // Mobile inspector drawer. The inspector is a separate surface at <= 1024px
487
+ // (it is the `end` slot of the layout there), so it is a Drawer: a panel
488
+ // that slides in over the row list instead of a column that compresses it.
489
+ const [isInspectorOpen, setInspectorOpen] = useState(false);
479
490
  const { config, applyFilters } = usePowerSearchConfig(fieldDefs, "Incidents");
480
491
 
481
492
  // Responsive contract: the inspector is hidden at <= 1024px so the row
@@ -533,89 +544,120 @@ export default function IncidentConsole() {
533
544
  );
534
545
 
535
546
  return (
536
- <Layout
537
- height="fill"
538
- header={
539
- <LayoutHeader hasDivider padding={6}>
540
- {isNarrow ? (
541
- <VStack gap={2}>
542
- {titleGroup}
543
- <HStack
544
- gap={2}
545
- vAlign="center"
546
- style={styles.controlsRow}
547
- tabIndex={0}
548
- role="region"
549
- aria-label="Incident filter actions"
550
- >
547
+ <>
548
+ <Layout
549
+ height="fill"
550
+ header={
551
+ <LayoutHeader hasDivider padding={6}>
552
+ {isNarrow ? (
553
+ <VStack gap={2}>
554
+ {titleGroup}
555
+ <HStack
556
+ gap={2}
557
+ vAlign="center"
558
+ style={styles.controlsRow}
559
+ tabIndex={0}
560
+ role="region"
561
+ aria-label="Incident filter actions"
562
+ >
563
+ {statusFilterControl}
564
+ {declareButton}
565
+ </HStack>
566
+ </VStack>
567
+ ) : (
568
+ <HStack gap={3} vAlign="center">
569
+ <StackItem size="fill">{titleGroup}</StackItem>
551
570
  {statusFilterControl}
552
571
  {declareButton}
553
572
  </HStack>
554
- </VStack>
555
- ) : (
556
- <HStack gap={3} vAlign="center">
557
- <StackItem size="fill">{titleGroup}</StackItem>
558
- {statusFilterControl}
559
- {declareButton}
560
- </HStack>
561
- )}
562
- </LayoutHeader>
563
- }
564
- content={
565
- <LayoutContent padding={0} isScrollable={false}>
566
- <VStack gap={0} style={styles.contentFill}>
567
- <HStack style={styles.searchRow}>
568
- <StackItem size="fill">
569
- <PowerSearch
570
- config={config}
571
- filters={filters}
572
- onChange={(newFilters) => setFilters([...newFilters])}
573
- placeholder="Search the night watch…"
574
- resultCount={visible.length}
573
+ )}
574
+ </LayoutHeader>
575
+ }
576
+ content={
577
+ <LayoutContent padding={0} isScrollable={false}>
578
+ <VStack gap={0} style={styles.contentFill}>
579
+ <HStack style={styles.searchRow}>
580
+ <StackItem size="fill">
581
+ <PowerSearch
582
+ config={config}
583
+ filters={filters}
584
+ onChange={(newFilters) => setFilters([...newFilters])}
585
+ placeholder="Search the night watch…"
586
+ resultCount={visible.length}
587
+ />
588
+ </StackItem>
589
+ </HStack>
590
+ <StackItem
591
+ size="fill"
592
+ style={styles.rows}
593
+ tabIndex={0}
594
+ role="region"
595
+ aria-label="Incident rows"
596
+ >
597
+ <IncidentRows
598
+ incidents={visible}
599
+ selectedId={selected?.id ?? null}
600
+ onSelect={(id) => {
601
+ setSelectedId(id);
602
+ // On a narrow viewport the inspector is the Drawer, so
603
+ // picking a row is what opens it.
604
+ if (isNarrow) setInspectorOpen(true);
605
+ }}
575
606
  />
576
607
  </StackItem>
577
- </HStack>
578
- <StackItem
579
- size="fill"
580
- style={styles.rows}
581
- tabIndex={0}
582
- role="region"
583
- aria-label="Incident rows"
584
- >
585
- <IncidentRows
586
- incidents={visible}
587
- selectedId={selected?.id ?? null}
588
- onSelect={setSelectedId}
608
+ </VStack>
609
+ </LayoutContent>
610
+ }
611
+ end={
612
+ isNarrow ? undefined : (
613
+ <>
614
+ <ResizeHandle
615
+ direction="horizontal"
616
+ hasDivider
617
+ isAlwaysVisible={false}
618
+ resizable={inspectorPanel.props}
619
+ label="Resize inspector"
589
620
  />
590
- </StackItem>
591
- </VStack>
592
- </LayoutContent>
593
- }
594
- end={
595
- isNarrow ? undefined : (
596
- <>
597
- <ResizeHandle
598
- direction="horizontal"
599
- hasDivider
600
- isAlwaysVisible={false}
601
- resizable={inspectorPanel.props}
602
- label="Resize inspector"
603
- />
604
- <LayoutPanel width={inspectorPanel.size} padding={0} label="Incident details">
605
- {selected ? (
606
- <IncidentInspector incident={selected} />
607
- ) : (
608
- <EmptyState
609
- title="No incident under the lens"
610
- description="Select an incident from the rows to inspect its timeline."
611
- icon={<Icon icon={BellRing} size="lg" color="secondary" />}
612
- isCompact
613
- />
614
- )}
615
- </LayoutPanel>
616
- </>
617
- )
618
- }
619
- />
621
+ <LayoutPanel width={inspectorPanel.size} padding={0} label="Incident details">
622
+ {selected ? (
623
+ <IncidentInspector incident={selected} />
624
+ ) : (
625
+ <EmptyState
626
+ title="No incident under the lens"
627
+ description="Select an incident from the rows to inspect its timeline."
628
+ icon={<Icon icon={BellRing} size="lg" color="secondary" />}
629
+ isCompact
630
+ />
631
+ )}
632
+ </LayoutPanel>
633
+ </>
634
+ )
635
+ }
636
+ />
637
+ {/* Narrow viewport: the inspector is a Drawer, not a column. It is
638
+ non-modal (hasScrim={false}) so the row list stays visible behind it —
639
+ the point of a peek-at-the-detail surface. The close button comes from
640
+ DrawerHeader, which only renders it when it is given onOpenChange. */}
641
+ <Drawer
642
+ side="end"
643
+ label="Incident details"
644
+ isOpen={isNarrow && isInspectorOpen}
645
+ onOpenChange={setInspectorOpen}
646
+ hasScrim={false}
647
+ width={inspectorPanel.size}
648
+ >
649
+ <DrawerHeader title="Incident details" onOpenChange={setInspectorOpen} />
650
+ {selected ? (
651
+ <IncidentInspector incident={selected} />
652
+ ) : (
653
+ <EmptyState
654
+ title="No incident under the lens"
655
+ description="Select an incident from the rows to inspect its timeline."
656
+ icon={<Icon icon={BellRing} size="lg" color="secondary" />}
657
+ isCompact
658
+ />
659
+ )}
660
+ </Drawer>
661
+ </>
620
662
  );
621
663
  }
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Kanban Board",
4
4
  description: "Status columns with priority tags and metadata",
5
+ keywords: ["kanban", "board", "tasks", "workflow", "sprint", "columns"],
5
6
  category: "tooling",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // S > L > (LH[divider] > Tbar > (Hd"Sprint Board"[level=1] + Bd.neutral"8") + (H[g=2] > SE + D.strong + IB"Sort" + IB"Filter" + IB"Search" + B.primary"Add task")) + (LC[p=0] > G[c={min:280,max:4} g=4] > (C.muted[p=0] > L > (LH[divider] > H[j=between a=center] > (H[g=2 a=center] > SD + Hd"To-do"[level=2] + IB) + Tx) + (LC[p=3] > V[g=2] > (C[p=3] > V[g=2] > (H[j=between] > (H[g=2] > Tk + Bd) + MM) + (V[g=1] > Hd[level=3] + Tx) + Tx)*2))*4)
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // S[h=100dvh] > (L[h=fill] > (LH[divider p=6] > Tbar[g=2] > (Hd"Sprint Board"[level=1 t=display-2] + Bd.neutral"8") + (H[g=2 wrap] > SE"Sprint" + D.strong + (H[g=1 a=center wrap] > IB"Sort" + IB"Filter" + IB"Search") + B.primary"Add task")) + (LC[p=0 !scroll] > G[c={min:280,max:4} g=4] > (C.muted[p=0] > L[h=fill] > (LH[divider p=3] > H[j=between a=center] > (H[g=1 a=center] > SD + Hd"To-do"[level=2] + (Po > IB)) + Tx"2") + (LC[p=3] > V[g=2] > (C[p=3] > V[g=2] > (H[j=between] > (H[g=2] > Tk + Bd) + MM) + (V[g=1] > Hd[level=3] + Tx) + Tx)*2)) + (C.muted[p=0] > L[h=fill] > (LH[divider p=3] > H[j=between a=center] > (H[g=1 a=center] > SD + Hd"In progress"[level=2] + (Po > IB)) + Tx"2") + (LC[p=3] > V[g=2] > (C[p=3] > V[g=2] > (H[j=between] > (H[g=2] > Tk + Bd) + MM) + (V[g=1] > Hd[level=3] + Tx) + Tx)*2)) + (C.muted[p=0] > L[h=fill] > (LH[divider p=3] > H[j=between a=center] > (H[g=1 a=center] > SD + Hd"In review"[level=2] + (Po > IB)) + Tx"1") + (LC[p=3] > V[g=2] > (C[p=3] > V[g=2] > (H[j=between] > (H[g=2] > Tk + Bd) + MM) + (V[g=1] > Hd[level=3] + Tx) + Tx))) + (C.muted[p=0] > L[h=fill] > (LH[divider p=3] > H[j=between a=center] > (H[g=1 a=center] > SD + Hd"Done"[level=2] + (Po > IB)) + Tx"3") + (LC[p=3] > V[g=2] > (C[p=3] > V[g=2] > (H[j=between] > (H[g=2] > Tk + Bd) + MM) + (V[g=1] > Hd[level=3] + Tx) + Tx)*3))) + (C[p=3] > V[g=2] > (H[j=between] > (H[g=2] > Tk + Bd) + MM) + (V[g=1] > Hd[level=3] + Tx) + Tx)
4
6
 
5
7
  import { Badge } from "@astryxdesign/core/Badge";
6
8
  import { Button } from "@astryxdesign/core/Button";
@@ -595,7 +597,7 @@ export default function KanbanBoard() {
595
597
  const renderColumnCards = (colId: ColumnId): ReactNode => {
596
598
  const colItems = itemsByColumn[colId];
597
599
  const visible = drag ? colItems.filter((it) => it.id !== drag.id) : colItems;
598
- const ghostTarget = drag && drag.target && drag.target.column === colId ? drag : null;
600
+ const ghostTarget = drag?.target && drag.target.column === colId ? drag : null;
599
601
 
600
602
  if (visible.length === 0 && !ghostTarget) {
601
603
  return null;
@@ -611,7 +613,7 @@ export default function KanbanBoard() {
611
613
  />
612
614
  ));
613
615
 
614
- if (ghostTarget && ghostTarget.target) {
616
+ if (ghostTarget?.target) {
615
617
  const index = Math.min(ghostTarget.target.index, nodes.length);
616
618
  nodes.splice(
617
619
  index,
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Library",
4
4
  description: "Browsable grid with tabs and filters",
5
+ keywords: ["library", "catalog", "books", "content", "listing", "collection"],
5
6
  category: "docs",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > (LH[divider] > (V[g=1] > Hd"Library"[level=1] + Tx"30 components across 5 categories"[t=body])) + (LC[p=6] > V[g=8] > (V[g=4] > TI"Search the stacks…" + (H[a=center g=3] > (SI[fill] > TgG"Filter" > OFL > Tg"All"! + Tg"Layout" + Tg"Forms" + Tg"Navigation" + Tg"Feedback" + Tg"Data") + DM"Sort")) + D + (V[g=8] > (H[j=between a=center] > Hd"Layout"[level=2] + Tx"6 items"[t=body]) + (G[c={min:280} g=4] > (C[p=0] > AR + (S.transparent[p=4] > V[g=1] > Hd"Card"[level=3] + Tx"Description"[t=supporting]))*4)))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L[h=fill] > (LH[divider p=6] > (V[g=1] > Hd"Library"[level=1] + Tx"30 components across 5 categories"[t=body])) + (LC[p=6] > V[g=8] > (V[g=4] > TI"Search" + (H[g=3 a=center] > (SI[fill] > V > TgG"Filter" > OFL > Tg"All"! + Tg"Layout" + Tg"Forms" + Tg"Navigation" + Tg"Feedback" + Tg"Data") + DD"A-Z")) + (V[g=8] > (D + (V[g=8] > (H[j=between a=center] > Hd"Layout"[level=2] + Tx"6 items"[t=body]) + (G[c={min:280} g=4] > (C[p=0] > AR[ratio=16/9] > Scene{SceneTile}) + (S.transparent[p=4] > V[g=1] > Hd"Card"[level=3] + Tx"Description"[t=body]))*6))*5))
4
6
 
5
7
  /**
6
8
  * Library — a browsable grid of design-system entries grouped by category. Frame: page header (title) | content column (search, filter row, s (Frame/responsive/container: see XLE header above.)
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Login Card",
4
4
  description: "Centered auth card with email and password",
5
+ keywords: ["login", "sign in", "auth", "card", "credentials", "account"],
5
6
  category: "auth",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > V[g=4 a=center] > (V[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (C[p=4] > V[g=4] > (V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"Sign in to your crypt"[t=body]) + (V[g=2] > TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?")) + B.primary"Enter the night" + D"Or continue with" + (V[g=3] > B.secondary"Login with Apple" + B.secondary"Login with Google") + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // Ctr > V[g=4 a=center] > (V[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (C[p=4] > V[g=4] > (V[g=1 a=center] > Hd"Welcome back to the night"[level=1 t=display-2] + Tx"Sign in to your crypt"[t=body]) + (V[g=2] > TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?")) + B.primary"Sign in" + D"Or continue with" + (V[g=3] > B.secondary"Login with Apple" + B.secondary"Login with Google") + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
4
6
 
5
7
  import { Button } from "@astryxdesign/core/Button";
6
8
  import { Center } from "@astryxdesign/core/Center";
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Login Split",
4
4
  description: "Two-column auth with cover art panel",
5
+ keywords: ["login", "sign in", "auth", "split", "credentials", "account"],
5
6
  category: "auth",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > V[g=4] > (Ctr.horizontal > C[p=0] > G[c={min:240} g=8 a=stretch] > (S[p=0] > V[g=4] > (H[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (V[g=4] > (V[g=1] > Hd"Welcome back to the night"[level=1] + Tx"Sign in to your crypt"[t=body]) + (V[g=2] > TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?")) + B.primary"Enter the night" + D"Or continue with" + (G[c={min:200} g=3] > B.secondary"Login with Apple" + B.secondary"Login with Google")) + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (C[p=0] > AR)) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // Ctr > V[g=4] > (Ctr.horizontal > C[p=0] > G[c={min:240} g=8 a=stretch] > (S.transparent[p=0] > V[g=4] > (H[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (V[g=4] > (V[g=1] > Hd"Welcome back to the night"[level=1 t=display-2] + Tx"Sign in to your crypt"[t=body]) + (V[g=2] > TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?")) + B.primary"Sign in" + D"Or continue with" + (G[c={min:200} g=3] > B.secondary"Login with Apple" + B.secondary"Login with Google")) + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (C.transparent[p=0] > Scene{SceneCastle})) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
4
6
 
5
7
  import { Button } from "@astryxdesign/core/Button";
6
8
  import { Card } from "@astryxdesign/core/Card";
@@ -30,6 +32,7 @@ import {
30
32
  AUTH_TERMS_PRIVACY,
31
33
  AUTH_TERMS_SERVICE,
32
34
  } from "astryx-dracula/shared/auth-copy";
35
+
33
36
  import { demoLogin } from "astryx-dracula/shared/login-demo";
34
37
  import { SceneCastle } from "astryx-dracula/shared/scene-castle";
35
38
  import { AppleIcon, GoogleIcon } from "astryx-dracula/shared/sso-icons";
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Login SSO",
4
4
  description: "Email-first SSO with password fallback",
5
+ keywords: ["login", "sso", "saml", "oauth", "auth", "providers", "account"],
5
6
  category: "auth",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > C[p=4 mw=400] > V[g=4] > ((V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"Whisper your details to enter the night"[t=body]) + (V[g=1] > TI"Work email"[t=email] + Lk"Having trouble signing in?") + B.primary"Continue" + D"Or continue with" + B.secondary"Continue with SSO" + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Request access") + (V[g=2 a=center] > Av + Hd"Sign in with Google Workspace"[level=1] + Tx"You will be redirected back after signing in."[t=body]) + (C[p=0] > S[p=4 muted] > (H[g=2 a=center] > Ic + (V[g=0] > Tx"Google Workspace"[t=label] + Tx"vlad@castle.ro"[t=supporting]))) + (V[g=3] > B.primary"Continue with Google Workspace" + B.ghost"Use a different email") + (V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"vlad@castle.ro"[t=body]) + (V[g=4] > (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?") + (V[g=3] > B.primary"Enter the night" + B.ghost"Use a different email")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting]))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // Ctr > C[p=4 mw=400] > V[g=4] > ((V[g=1 a=center] > Hd"Welcome back to the night"[level=1 t=display-2] + Tx"Whisper your details to enter the night"[t=body]) + (V[g=1] > TI"Work email"[t=email] + Lk"Having trouble signing in?") + B.primary"Continue" + D"Or continue with" + B.secondary"Continue with SSO" + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Request access") + (V[g=2 a=center] > Av + Hd"Sign in with Google Workspace"[level=1 t=display-2] + Tx"You will be redirected back after signing in."[t=body]) + (C.muted[p=4] > (H[g=2 a=center] > Ic + (V[g=0] > Tx"Google Workspace"[t=label] + Tx"vlad@castle.ro"[t=supporting]))) + (V[g=3] > B.primary"Continue with Google Workspace" + B.ghost"Use a different email") + (V[g=1 a=center] > Hd"Welcome back to the night"[level=1 t=display-2] + Tx"vlad@castle.ro"[t=body]) + (V[g=4] > (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?") + (V[g=3] > B.primary"Sign in" + B.ghost"Use a different email")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting]))
4
6
 
5
7
  import { Avatar } from "@astryxdesign/core/Avatar";
6
8
  import { Button } from "@astryxdesign/core/Button";
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Basic Login",
4
4
  description: "Auth form with email and password inputs",
5
+ keywords: ["login", "sign in", "auth", "credentials", "account", "form"],
5
6
  category: "auth",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > V[g=4 a=center] > (V[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (C[p=4] > V[g=4] > (V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"Sign in to your crypt"[t=body]) + TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?") + B.primary"Enter the night" + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // Ctr > V[g=4 a=center] > (V[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (C[p=4] > V[g=4] > (V[g=1 a=center] > Hd"Welcome back to the night"[level=1 t=display-2] + Tx"Sign in to your crypt"[t=body]) + (V[g=2] > TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?")) + B.primary"Sign in" + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
4
6
 
5
7
  import { Center } from "@astryxdesign/core/Center";
6
8
  import { VStack } from "@astryxdesign/core/Layout";
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Messaging Shell",
4
4
  description: "Team messaging shell with channels and thread panel",
5
+ keywords: ["messaging", "chat", "inbox", "conversation", "threads", "shell"],
5
6
  category: "tooling",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > (LP[w=68] > V > Av + (V > IB*4) + IB) + (LP[w=260] > V > (H > Hd"Messages"[level=1] + IB) + TI"Jump to…" + (V > Tx"Channels"[t=label] + (List > (ListItem)*4)) + (V > Tx"Direct messages"[t=label] + (List > (ListItem)*3))) + (LC[p=0] > V > (H > Hd"design-systems"[level=2] + SD + IB) + (ChL > ChML > (ChM > ChB)*6 + ChC)) + (LP[w=340] > V > (H > Tx"Thread"[weight=semibold] + IB) + (ChL > ChML > (ChM > ChB)*3 + ChC))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L > (LP[w=68] > V[g=2] > Av + IB*4 + SI[fill] + IB"Settings") + (LP[w=260] > V > (H[g=2] > (SI[fill] > Hd"Messages"[level=1]) + IB"New message") + (V[g=0] > TI"Jump to…") + (SI[fill] > (UL[density=compact edge=flush !hasDividers] > (Tx"Channels"[t=label] + LI*4)) + (UL[density=compact edge=flush !hasDividers] > (Tx"Direct messages"[t=label] + LI*3)))) + (LC[p=0] > V > (H[g=3] > Ic + Hd"design-systems"[level=2] + (SI[fill] > Tx"Component APIs, tokens, and release coordination"[t=body]) + SD"12 online" + IB"Members") + D + (SI[fill] > ChL > (ChML[density=balanced] > ChSM"Tuesday, June 30" + ChSM"Sasha Ortiz joined #design-systems" + (ChM > ChBu) + (ChM > ChBu*2) + (ChM > ChBu*2) + (ChM > ChBu) + (ChM > ChBu)) + ChC"Message #design-systems" + EmptyState"No messages yet")) + (LP[w=340] > V > (H[g=2] > (SI[fill] > (H[g=2] > Tx"Thread"[weight=semibold] + Tx"#design-systems"[t=supporting])) + IB"Close thread") + D + (SI[fill] > ChML[density=compact] > (ChM > ChBu) + ChSM"2 replies" + (ChM > ChBu)*2) + (V[g=0] > ChC"Reply in thread…")))
4
6
 
5
7
  /**
6
8
  * Messaging Shell — Slack-style column frame for team messaging tools. Frame (desktop, left to right): (Frame/responsive/container: see XLE header above.)
@@ -497,6 +499,7 @@ export default function MessagingShell() {
497
499
  >
498
500
  <List
499
501
  density="compact"
502
+ edge="flush"
500
503
  hasDividers={false}
501
504
  header={
502
505
  <Text type="label" size="sm" color="secondary">
@@ -522,6 +525,7 @@ export default function MessagingShell() {
522
525
  container, so no wrapper element is needed. */}
523
526
  <List
524
527
  density="compact"
528
+ edge="flush"
525
529
  hasDividers={false}
526
530
  style={sectionGapStyle}
527
531
  header={
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Mixed Gallery",
4
4
  description: "Masonry image gallery with varying tile sizes",
5
+ keywords: ["gallery", "mixed", "grid", "collection", "showcase", "images"],
5
6
  category: "marketing",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > LC[p=6] > V[g=8] > ((V[g=2 a=center] > Hd"Every corner of the castle, caught after dark."[level=1] + Tx"Relics, sketches, and moonlit views from the coven archives, collected over one long Transylvanian night."[t=body]) + (V[g=3] > (V[g=2] > AR[ratio=3/1] + Tx"The castle at moonrise"[t=body]) + (G[c={min:280} g=4] > (V[g=2] > AR[ratio=3/2] + Tx"Coven gathering"[t=body]) + (V[g=2] > AR[ratio=3/2] + Tx"Belfry view"[t=body]) + (V[g=2] > AR[ratio=3/2] + Tx"Crypt archives"[t=body]) + (V[g=2] > AR[ratio=3/2] + Tx"Midnight garden"[t=body]))))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L[h=fill] > LC[p=6] > V[g=8] > ((V[g=2 a=center] > Hd"Every corner of the castle, caught after dark."[level=1] + Tx"Relics, sketches, and moonlit views from the coven archives, collected over one long Transylvanian night."[t=body]) + (V[g=3] > (V[g=2] > (AR[ratio=3/1] > Scene{SceneTile}) + Tx"The castle at moonrise"[t=body]) + (G[c={min:280} g=4] > (V[g=2] > (AR[ratio=3/2] > Scene{SceneTile}) + Tx"Coven gathering"[t=body]) + (V[g=2] > (AR[ratio=3/2] > Scene{SceneTile}) + Tx"Belfry view"[t=body]) + (V[g=2] > (AR[ratio=3/2] > Scene{SceneTile}) + Tx"Crypt archives"[t=body]) + (V[g=2] > (AR[ratio=3/2] > Scene{SceneTile}) + Tx"Midnight garden"[t=body]))))
4
6
 
5
7
  import { AspectRatio } from "@astryxdesign/core/AspectRatio";
6
8
  import { Grid } from "@astryxdesign/core/Grid";
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Checkout Form",
4
4
  description: "Billing info, card details, and order summary",
5
+ keywords: ["checkout", "payment", "billing", "cart", "buyer", "purchase", "order"],
5
6
  category: "commerce",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > LC[p=0] > Ctr > S.transparent[p=6 mw=1100] > V[g=5] > (V[g=6] > (V[g=2] > (Hd"Payment Request"[level=1 t=display-2] + Tx"Review your order"[t=body]) + D) + H[g=8] > (SI > V[g=8] > (V[g=1] > (H[j=between] > (Hd"Sign in to check out"[level=2] + B"Sign in") + Tx"Sign in to track your order"[t=body]) + V[g=3] > (Hd"Contact information"[level=2] + TI"Email" + CB"Email offers") + V[g=3] > (Hd"Shipping information"[level=2] + G[c=2 g=3] > (TI"First Name" + TI"Last Name") + TI"Address" + G[c=2 g=3] > (TI"City" + TI"ZIP Code") + SE"State" + TI"Phone Number" + CB"Save information") + V[g=3] > (V[g=1] > (Hd"Delivery"[level=2] + Tx"Processing time"[t=body]) + RL"Delivery method" > RLI*2) + V[g=3] > (V[g=1] > (Hd"Payment method"[level=2] + Tx"Encrypted"[t=body]) + G[c=2 g=3] > (B"PayPal" + B"Google Pay") + D"Or pay with card" + TI"Card Number" + G[c=3 g=3] > (SE"Expiry Month" + SE"Expiry Year" + TI"CVC") + TI"Name on Card" + CB"Billing address") + V[g=3] > (Hd"Promo code"[level=2] + H[g=2] > (TI"Promo code" + B"Apply")) + V[g=3] > (Hd"Gift options"[level=2] + CB"Gift message" + TA"Gift message") + V[g=4] > (H[g=5] > (H[g=1] > (Ic + Tx))*3 + V[g=2] > (B"Place order"[primary] + B"Continue shopping") + D + H[g=4] > (Lk"Refund policy" + Lk"Privacy policy" + Lk"Terms" + Lk"Cancellations"))) + SI > C[p=4] > Col"Order Summary" > V[g=4] > (V[g=3] > H[g=3] > (Tmb + V[g=1] > (Tx"Obsidian Ritual Chalice" + Tx"Hand-carved"[t=supporting])))*3 + V[g=3] > (Hd"Order Total"[level=3] + H[j=between] > (Tx"Subtotal" + Tx"$91.20") + D + H[j=between] > (Tx"Total"[t=large] + Tx"$91.20"[t=large]) + Bn"Free shipping over $300"))))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L[h=fill] > LC[p=0] > Ctr > S.transparent[p=6 mw=1100] > V[g=5] > (V[g=6] > (V[g=2] > (Hd"Payment Request"[level=1 t=display-2] + Tx"Review your order"[t=body]) + D) + H[g=8] > (SI > V[g=8] > (V[g=1] > (H[j=between] > (Hd"Sign in to check out"[level=2] + B.secondary"Sign in") + Tx"Sign in to track your order"[t=body]) + V[g=3] > (Hd"Contact information"[level=2] + TI"Email" + CB"Email me with news and offers") + V[g=3] > (Hd"Shipping information"[level=2] + G[c=2 g=3] > (TI"First Name" + TI"Last Name") + TI"Address" + G[c=2 g=3] > (TI"City" + TI"ZIP Code") + SE"State" + TI"Phone Number" + CB"Save my information for a faster checkout") + V[g=3] > (V[g=1] > (Hd"Delivery"[level=2] + Tx"Processing time"[t=body])) + (RL"Delivery method" > RLI*2) + V[g=3] > (V[g=1] > (Hd"Payment method"[level=2] + Tx"Encrypted"[t=body])) + (V[g=3] > G[c=2 g=3] > (B.secondary"PayPal" + B.secondary"Google Pay")) + D"Or pay with card" + (V[g=3] > TI"Card Number" + G[c=3 g=3] > (SE"Expiry Month" + SE"Expiry Year" + TI"CVC") + TI"Name on Card" + CB"Use shipping address as billing address" + (V[g=3] > TI"Address" + G[c=2 g=3] > (TI"City" + TI"ZIP Code") + SE"State")) + V[g=3] > (Hd"Promo code"[level=2] + H[g=2] > (TI"Promo code" + B.secondary"Apply")) + V[g=3] > (Hd"Gift options"[level=2] + CB"Add a gift message" + (V[g=3] > (G[c=2 g=3] > (TI"To" + TI"From")) + TA"Gift message")) + V[g=4] > (H[g=5] > (H[g=1] > (Ic + Tx))*3 + V[g=2] > (B.primary"Place order" + B.secondary"Continue shopping") + D + H[g=4] > (Lk"Refund policy" + Lk"Privacy policy" + Lk"Terms of service" + Lk"Cancellations"))) + SI > C[p=4] > Col"Order Summary" > V[g=4] > (H[g=3] > (C[p=0] > Scene{SceneTile}) + (SI > V[g=1] > (Tx"Obsidian Ritual Chalice" + Tx"Hand-carved · 12 oz"[t=supporting] + (H[g=2] > NI"Qty" + B.ghost"Remove" + B.ghost"Save"))))*3 + V[g=3] > (Hd"Order Total"[level=3] + V[g=2] > (H[j=between] > (Tx"Subtotal" + Tx"$230.00")) + (H[j=between] > (Tx"Shipping" + Tx"$4.95")) + (H[j=between] > (Tx"Tax" + Tx"$18.40")) + D + (H[j=between] > (Tx"Total"[t=large] + Tx"$253.35"[t=large])) + Bn"Free shipping on orders over $300")))
4
6
 
5
7
  import { Banner } from "@astryxdesign/core/Banner";
6
8
  import { Button } from "@astryxdesign/core/Button";
@@ -862,7 +864,7 @@ export default function PaymentForm() {
862
864
  placeholder="Enter promo code"
863
865
  value={promo}
864
866
  onChange={setPromo}
865
- style={fullWidth}
867
+ controlStyle={fullWidth}
866
868
  />
867
869
  </StackItem>
868
870
  <Button label="Apply" variant="secondary" size="lg" onClick={() => {}} />
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Product Detail",
4
4
  description: "Product page with gallery and collapsible sections",
5
+ keywords: ["product", "detail", "buy", "pdp", "shop", "specifications"],
5
6
  category: "commerce",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > LC[p=6] > G[c={min:280} g=8] > (V[g=3] > AR[ratio=4/5] + (G[c=3 g=2] > (AR[ratio=1] > SelectableCard)*6)) + (V[g=8] > (V[g=3] > Hd"Midnight Ceremony Mug & Plate Set"[level=1 t=display-2] + (H[g=1] > Ic*5 + Tx"4.3 (128)") + (H[g=2] > Tx"$89.00"[t=large] + Tx"$119.00"[t=body] + Tk"Sale")) + Tx"A hand-thrown mug and plate set"[t=large] + (V[g=3] > Tx"Glaze"[t=label] + SG) + (V[g=3] > Tx"Finish"[t=label] + SG) + (V[g=3] > Tx"Quantity"[t=label] + (H[g=1] > B.ghost"-" + TI"1" + B.ghost"+")) + (V[g=3] > B.primary"Add to cart" + B.secondary"Buy it now") + (ColG > Col"Composition" + Col"Delivery & Returns" + Col"Dimensions"))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L[h=fill] > LC[p=6] > G[c={min:280} g=8] > (V[g=3] > (AR[ratio=4/5] > Scene{SceneTile}) + (G[c=3 g=2] > (AR[ratio=1] > SC > Scene{SceneTile})*6)) + (V[g=0] > V[g=8] > (V[g=3] > Hd"Midnight Ceremony Mug & Plate Set"[level=1 t=display-2] + (H[g=1] > Ic*5 + Tx"4.3 (128)") + (H[g=2] > Tx"$89.00"[t=large] + Tx"$119.00"[t=body] + Tk"Sale")) + Tx"A hand-thrown mug and plate set"[t=large] + (V[g=3] > Tx"Glaze"[t=label] + SG"Glaze" > SGI*3) + (V[g=3] > Tx"Finish"[t=label] + SG"Finish" > SGI*3) + (V[g=3] > Tx"Quantity"[t=label] + (H[g=1] > B.ghost"-" + (Ctr[w=100] > NI"1") + B.ghost"+")) + (V[g=3] > Bn + B.primary"Add to cart" + B.secondary"Buy it now") + (ColG > (Col > (Hd"Composition"[level=3] + Tx"High-fire stoneware clay…"[t=body])) + (Col > (Hd"Delivery & Returns"[level=3] + Tx"Free shipping on all ceramics orders over $75…"[t=body])) + (Col > (Hd"Dimensions"[level=3] + Tx"Mug height: 9.5 cm / 3.75 in…"[t=body]))))
4
6
 
5
7
  import { AspectRatio } from "@astryxdesign/core/AspectRatio";
6
8
  import { Banner } from "@astryxdesign/core/Banner";
@@ -204,6 +206,11 @@ function ProductInfo() {
204
206
  {notice && (
205
207
  <Banner
206
208
  status={notice.status}
209
+ // Core 0.7.0 stopped deriving the live-region role from
210
+ // `status`, so this banner states its own. It mirrors the
211
+ // result of Add to cart / Buy it now, and a screen reader
212
+ // must hear it.
213
+ role="status"
207
214
  title={notice.title}
208
215
  container="card"
209
216
  isDismissable
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Product Gallery",
4
4
  description: "Card grid with images, prices, and descriptions",
5
+ keywords: ["product", "gallery", "images", "shop", "store", "pdp"],
5
6
  category: "commerce",
6
7
  };
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
- // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > LC[p=6] > V[g=8] > (G[c={min:280} g=4 a=start] > Hd"Small comforts for the midnight hours."[level=1 t=display-2] + (V[g=3 a=start] > Tx"Provisions from the castle workshops"[t=body] + B.primary"Browse the collection")) + (V[g=8] > (V[g=2] > Hd"The collection"[level=2] + Tx"Each piece is made in small batches"[t=body]) + (G[c={min:280} g=8] > (V[g=3] > (C[p=0] > AR[ratio=1]) + (V[g=1] > Hd"Nightfall Stoneware Mug"[level=3] + Tx"A hand-thrown mug that sits easy"[t=body color=secondary] + Tx.lg"$75.00"))*6))
2
+ // XLE (canonical structure of the tree below; the CLI had no XLE validator
3
+ // after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
4
+ // human drift check — update it with the file, never alone):
5
+ // L[h=fill] > LC[p=6] > V[g=8] > (G[c={min:280} g=4 a=start] > Hd"Small comforts for the midnight hours."[level=1 t=display-2] + (V[g=3 a=start] > Tx"Provisions from the castle workshops"[t=body] + B.primary"Browse the collection")) + (V[g=8] > (V[g=2] > Hd"The collection"[level=2] + Tx"Each piece is made in small batches"[t=body]) + (G[c={min:280} g=4] > (V[g=3] > (C[p=0] > AR[ratio=1] > Scene{SceneTile}) + (V[g=1] > Hd"Nightfall Stoneware Mug"[level=3] + Tx"A hand-thrown mug that sits easy"[t=body color=secondary] + Tx.lg"$75.00"))*6))
4
6
 
5
7
  import { AspectRatio } from "@astryxdesign/core/AspectRatio";
6
8
  import { Button } from "@astryxdesign/core/Button";
@@ -2,5 +2,6 @@ export default {
2
2
  type: "page",
3
3
  name: "Product Tour",
4
4
  description: "Chapter rail, walkthrough content, and outline",
5
+ keywords: ["tour", "walkthrough", "onboarding", "product", "guide", "tour steps"],
5
6
  category: "marketing",
6
7
  };