astryx-dracula 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BRAND.md +54 -3
- package/README.md +11 -4
- package/USAGE.md +1 -1
- package/astryx-dracula.d.ts +2 -2
- package/astryx-dracula.js +26 -4
- package/astryx-dracula.variants.d.ts +2 -2
- package/package.json +11 -11
- package/shared/auth-chrome-config.ts +0 -4
- package/shared/auth-chrome.tsx +2 -3
- package/shared/chaptered-doc-config.ts +3 -2
- package/shared/order-desk.tsx +52 -54
- package/shared/revenue-chart.tsx +1 -1
- package/shared/scene-castle.tsx +21 -13
- package/templates/ai-chat-landing.tsx +6 -9
- package/templates/ai-chat.tsx +5 -1
- package/templates/centered-hero.tsx +2 -2
- package/templates/classic-gallery.tsx +5 -0
- package/templates/contact-form.tsx +9 -0
- package/templates/dashboard-portfolio.tsx +17 -4
- package/templates/dashboard.tsx +5 -5
- package/templates/detail-page.tsx +20 -28
- package/templates/documentation-design.tsx +3 -0
- package/templates/documentation-technical.tsx +8 -5
- package/templates/documentation.tsx +13 -6
- package/templates/form-two-column.tsx +11 -2
- package/templates/form-wizard.template.mjs +15 -0
- package/templates/form-wizard.tsx +878 -0
- package/templates/gallery-hero.tsx +10 -4
- package/templates/ide.tsx +177 -96
- package/templates/incident-console.tsx +15 -5
- package/templates/kanban-board.tsx +46 -4
- package/templates/library.tsx +12 -3
- package/templates/login-split.tsx +3 -3
- package/templates/login-sso.tsx +9 -17
- package/templates/messaging-shell.tsx +109 -46
- package/templates/mixed-gallery.tsx +6 -0
- package/templates/payment-form.tsx +29 -28
- package/templates/product-detail.tsx +12 -3
- package/templates/product-gallery.tsx +8 -3
- package/templates/product-tour.tsx +11 -7
- package/templates/settings-dialog.tsx +5 -0
- package/templates/settings-sidebar.tsx +603 -584
- package/templates/settings.tsx +5 -2
- package/templates/shell-nav.tsx +23 -3
- package/templates/shell-side-nav.tsx +94 -81
- package/templates/shell-top-nav.tsx +98 -86
- package/templates/table-grouped.tsx +146 -132
- package/templates/{table-page-shoe-store-heatmap.template.mjs → table-page-shoe-store.template.mjs} +1 -1
- package/templates/{table-page-shoe-store-heatmap.tsx → table-page-shoe-store.tsx} +1 -1
- package/templates/table-page.tsx +8 -0
- package/templates/table-tree.template.mjs +16 -0
- package/templates/table-tree.tsx +707 -0
- package/templates/tech-report.tsx +8 -5
- package/templates/theme-showcase.tsx +18 -11
- package/templates/work-item-detail.template.mjs +8 -0
- package/templates/work-item-detail.tsx +1049 -0
- package/theme.css +38 -3
- package/theme.css.d.ts +2 -2
- package/tokens.css +41 -4
|
@@ -31,6 +31,10 @@ const frameClip: CSSProperties = {
|
|
|
31
31
|
const textCenter: CSSProperties = {
|
|
32
32
|
textAlign: "center",
|
|
33
33
|
};
|
|
34
|
+
// Definite ancestor for `Layout height="fill"`: 100dvh, not minHeight: '100%'
|
|
35
|
+
// (a percentage min-height against an indefinite block computes to 0 — CSS 2.1
|
|
36
|
+
// 10.5). Same shape as side-gallery.tsx.
|
|
37
|
+
const pageStyle: CSSProperties = { height: "100dvh" };
|
|
34
38
|
|
|
35
39
|
// ─── Gallery Data ───────────────────────────────────────────────────────────
|
|
36
40
|
|
|
@@ -80,6 +84,7 @@ export default function ClassicGallery() {
|
|
|
80
84
|
|
|
81
85
|
return (
|
|
82
86
|
<Layout
|
|
87
|
+
style={pageStyle}
|
|
83
88
|
height="fill"
|
|
84
89
|
content={
|
|
85
90
|
<LayoutContent padding={0}>
|
|
@@ -146,6 +146,7 @@ export default function ContactForm() {
|
|
|
146
146
|
label="Full name"
|
|
147
147
|
isRequired
|
|
148
148
|
placeholder="Full name"
|
|
149
|
+
autoComplete="name"
|
|
149
150
|
value={fullName}
|
|
150
151
|
onChange={setFullName}
|
|
151
152
|
status={errors.fullName ? { type: "error", message: errors.fullName } : undefined}
|
|
@@ -153,7 +154,9 @@ export default function ContactForm() {
|
|
|
153
154
|
<TextInput
|
|
154
155
|
label="Email"
|
|
155
156
|
isRequired
|
|
157
|
+
type="email"
|
|
156
158
|
placeholder="you@company.com"
|
|
159
|
+
autoComplete="email"
|
|
157
160
|
value={email}
|
|
158
161
|
onChange={setEmail}
|
|
159
162
|
status={errors.email ? { type: "error", message: errors.email } : undefined}
|
|
@@ -164,6 +167,7 @@ export default function ContactForm() {
|
|
|
164
167
|
label="Company"
|
|
165
168
|
isRequired
|
|
166
169
|
placeholder="Company"
|
|
170
|
+
autoComplete="organization"
|
|
167
171
|
value={company}
|
|
168
172
|
onChange={setCompany}
|
|
169
173
|
status={errors.company ? { type: "error", message: errors.company } : undefined}
|
|
@@ -171,7 +175,9 @@ export default function ContactForm() {
|
|
|
171
175
|
<TextInput
|
|
172
176
|
label="Phone"
|
|
173
177
|
isRequired
|
|
178
|
+
type="tel"
|
|
174
179
|
placeholder="Phone number"
|
|
180
|
+
autoComplete="tel"
|
|
175
181
|
value={phone}
|
|
176
182
|
onChange={setPhone}
|
|
177
183
|
status={errors.phone ? { type: "error", message: errors.phone } : undefined}
|
|
@@ -190,6 +196,9 @@ export default function ContactForm() {
|
|
|
190
196
|
labelID="campaign-goals-label"
|
|
191
197
|
isGroupLabel
|
|
192
198
|
isRequired
|
|
199
|
+
// The token row is a custom, non-bordered control, so the
|
|
200
|
+
// message box must sit below it (attached overlaps the input).
|
|
201
|
+
statusVariant="detached"
|
|
193
202
|
status={errors.goals ? { type: "error", message: errors.goals } : undefined}
|
|
194
203
|
>
|
|
195
204
|
<HStack gap={2} wrap="wrap" role="group" aria-labelledby="campaign-goals-label">
|
|
@@ -18,11 +18,13 @@ import { List, ListItem } from "@astryxdesign/core/List";
|
|
|
18
18
|
import type { PixelWidth, TableColumn } from "@astryxdesign/core/Table";
|
|
19
19
|
import { pixel, proportional } from "@astryxdesign/core/Table";
|
|
20
20
|
import { Heading, Text } from "@astryxdesign/core/Text";
|
|
21
|
+
import { CHART_HUES } from "astryx-dracula/shared/chart-hues";
|
|
21
22
|
import { ChartLabel } from "astryx-dracula/shared/chart-labels";
|
|
22
23
|
import { CHART_PANEL_STYLE } from "astryx-dracula/shared/chart-panel-style";
|
|
23
24
|
import { MetricCard, TableCard } from "astryx-dracula/shared/metric-card";
|
|
24
25
|
import { MetricDelta } from "astryx-dracula/shared/metric-delta";
|
|
25
26
|
import { Sparkline, type SparkPoint } from "astryx-dracula/shared/sparkline";
|
|
27
|
+
import type { CSSProperties } from "react";
|
|
26
28
|
|
|
27
29
|
// ============= DATA =============
|
|
28
30
|
|
|
@@ -530,6 +532,10 @@ const PORTFOLIO_STEP = PORTFOLIO_WIDTH / portfolioBars.length;
|
|
|
530
532
|
const portfolioYFor = (v: number) =>
|
|
531
533
|
PORTFOLIO_BASE - ((v - PORTFOLIO_MIN) / (PORTFOLIO_MAX - PORTFOLIO_MIN)) * PORTFOLIO_PLOT;
|
|
532
534
|
const PORTFOLIO_Y_TICKS = [200000, 240000, 280000, 320000];
|
|
535
|
+
// The chart annotates its own last point. Reading it from the series rather
|
|
536
|
+
// than restating the anchor keeps the label true when the generator's noise
|
|
537
|
+
// moves the closing value off the 294,200 anchor it is interpolated toward.
|
|
538
|
+
const PORTFOLIO_LAST = portfolioData[portfolioData.length - 1].value;
|
|
533
539
|
|
|
534
540
|
function PortfolioChart() {
|
|
535
541
|
return (
|
|
@@ -548,7 +554,7 @@ function PortfolioChart() {
|
|
|
548
554
|
y1={portfolioYFor(t)}
|
|
549
555
|
x2={PORTFOLIO_LEFT + PORTFOLIO_WIDTH}
|
|
550
556
|
y2={portfolioYFor(t)}
|
|
551
|
-
stroke="var(--color-
|
|
557
|
+
stroke="var(--color-graph-gridlines)"
|
|
552
558
|
strokeDasharray="3 3"
|
|
553
559
|
opacity={0.5}
|
|
554
560
|
/>
|
|
@@ -567,7 +573,7 @@ function PortfolioChart() {
|
|
|
567
573
|
width={Math.max(2, PORTFOLIO_STEP - 2)}
|
|
568
574
|
height={h}
|
|
569
575
|
rx={4}
|
|
570
|
-
fill=
|
|
576
|
+
fill={CHART_HUES.green}
|
|
571
577
|
/>
|
|
572
578
|
);
|
|
573
579
|
})}
|
|
@@ -583,11 +589,11 @@ function PortfolioChart() {
|
|
|
583
589
|
))}
|
|
584
590
|
<ChartLabel
|
|
585
591
|
x={PORTFOLIO_LEFT + PORTFOLIO_WIDTH - 4}
|
|
586
|
-
y={portfolioYFor(
|
|
592
|
+
y={portfolioYFor(PORTFOLIO_LAST) - 8}
|
|
587
593
|
textAnchor="end"
|
|
588
594
|
fill="var(--color-text-highlight)"
|
|
589
595
|
>
|
|
590
|
-
$
|
|
596
|
+
${Math.round(PORTFOLIO_LAST / 1000)}k
|
|
591
597
|
</ChartLabel>
|
|
592
598
|
</svg>
|
|
593
599
|
</Card>
|
|
@@ -732,11 +738,18 @@ function AssetRow({
|
|
|
732
738
|
);
|
|
733
739
|
}
|
|
734
740
|
|
|
741
|
+
// A definite ancestor for `Layout height="fill"`. 100dvh, not minHeight: '100%':
|
|
742
|
+
// a percentage min-height against an indefinite containing block computes to 0
|
|
743
|
+
// (CSS 2.1 10.5), so the document would keep the scroll instead of the content
|
|
744
|
+
// pane. Same shape as dashboard.tsx and heatmap-status.
|
|
745
|
+
const pageStyle: CSSProperties = { height: "100dvh" };
|
|
746
|
+
|
|
735
747
|
// ============= MAIN COMPONENT =============
|
|
736
748
|
|
|
737
749
|
export default function DashboardPortfolio() {
|
|
738
750
|
return (
|
|
739
751
|
<Layout
|
|
752
|
+
style={pageStyle}
|
|
740
753
|
height="fill"
|
|
741
754
|
content={
|
|
742
755
|
<LayoutContent padding={6}>
|
package/templates/dashboard.tsx
CHANGED
|
@@ -25,7 +25,7 @@ const SELF_HASH = "#/templates/dashboard";
|
|
|
25
25
|
|
|
26
26
|
// ============= ICONS =============
|
|
27
27
|
|
|
28
|
-
import { CHART_HUES } from "astryx-dracula/shared/chart-hues";
|
|
28
|
+
import { CHART_HUES, type DataColor } from "astryx-dracula/shared/chart-hues";
|
|
29
29
|
import { ChartLegend } from "astryx-dracula/shared/chart-legend";
|
|
30
30
|
import { CHART_PANEL_STYLE } from "astryx-dracula/shared/chart-panel-style";
|
|
31
31
|
import { BarCell, CountCell, MetricCard, TableCard } from "astryx-dracula/shared/metric-card";
|
|
@@ -460,7 +460,7 @@ function ActiveUsersChart() {
|
|
|
460
460
|
y1="30"
|
|
461
461
|
x2="520"
|
|
462
462
|
y2="30"
|
|
463
|
-
stroke="var(--color-
|
|
463
|
+
stroke="var(--color-graph-gridlines)"
|
|
464
464
|
strokeDasharray="3 3"
|
|
465
465
|
opacity={0.5}
|
|
466
466
|
/>
|
|
@@ -469,11 +469,11 @@ function ActiveUsersChart() {
|
|
|
469
469
|
y1="80"
|
|
470
470
|
x2="520"
|
|
471
471
|
y2="80"
|
|
472
|
-
stroke="var(--color-
|
|
472
|
+
stroke="var(--color-graph-gridlines)"
|
|
473
473
|
strokeDasharray="3 3"
|
|
474
474
|
opacity={0.5}
|
|
475
475
|
/>
|
|
476
|
-
<line x1="20" y1="130" x2="520" y2="130" stroke="var(--color-
|
|
476
|
+
<line x1="20" y1="130" x2="520" y2="130" stroke="var(--color-graph-gridlines)" />
|
|
477
477
|
{bars.map((d, i) => {
|
|
478
478
|
const hAll = (d.allUsers / max) * 110;
|
|
479
479
|
const hDesktop = (d.desktop / max) * 110;
|
|
@@ -548,7 +548,7 @@ function StackedBarCard({
|
|
|
548
548
|
data,
|
|
549
549
|
}: {
|
|
550
550
|
title: string;
|
|
551
|
-
data: Array<{ label: string; value: number; color:
|
|
551
|
+
data: Array<{ label: string; value: number; color: DataColor }>;
|
|
552
552
|
}) {
|
|
553
553
|
const total = data.reduce((sum, d) => sum + d.value, 0);
|
|
554
554
|
const clipId = `stacked-${title.toLowerCase().replace(/[^a-z]+/g, "-")}`;
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// XLE (canonical structure of the tree below; the CLI had no XLE validator
|
|
3
3
|
// after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
|
|
4
4
|
// human drift check — update it with the file, never alone):
|
|
5
|
-
// L[h=fill mw=1000] > (LH[divider p=6] > V[g=3] > ((H[g=4 a=start] > (SI[fill] > V[g=2] > (Lk"All orders" + (V[g=0] > Hd"#1001"[level=1] + (H[g=1 a=center wrap] > Tx"5 ordered items"[t=supporting] + (H[g=1 a=center] > Bu + Av"Vlad Dracul" + Tx"Vlad Dracul"[t=supporting]) + (H[g=1 a=center] > Bu + SD"Unfulfilled" + Tx"Unfulfilled"[t=supporting]) + (H[g=1 a=center] > Bu + Ic + Tx"02/23/2026"[t=supporting]) + (H[g=1 a=center] > Bu + Ic + Tx"Needs attention"[t=supporting]) + (H[g=1 a=center] > Bu + Lk"See all")))) + (H[g=2] > B.secondary"Restock" + B.secondary"Edit")) + (H[g=2] > (SI[fill] > V[a=stretch] > B.secondary"Restock") + (SI[fill] > V[a=stretch] > B.secondary"Edit")) + (H[a=center] > (SI[fill] > TL[size=lg] > (Tab"Details"! + Tab"Invoices" + Tab"Timeline" + TabMenu"More")) + B.ghost))) + (LC > V[g=4] > ((S > V[g=4] > ((H[g=2 a=center wrap] > (SI[fill] > (H[g=2 a=center] > Hd"Items"[level=2] + SD"Unfulfilled" + Tx"Unfulfilled"[t=supporting])) + (H[g=2] > B.ghost"Fulfill item" + B.secondary"Create label")) + (UL[density=spacious edge=outset] > LI*5)) + (S > V[g=4] > ((H[g=2 a=center wrap] > (SI[fill] > (H[g=2 a=center] > Hd"Invoice"[level=2] + SD"Paid" + Tx"Paid"[t=supporting])) + (H[g=2] > B.ghost"Refund" + B.secondary"Send Invoice")) + (ML > MLI*5) + D + (ML > MLI*1))) + (S > V[g=4] > ((H[g=2 a=center] > (SI[fill] > Hd"Timeline"[level=2]) + B.ghost"Filters") + (UL[density=spacious edge=outset] > LI*4)))) + (LP[w=320 p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis")) ;; Dlg[variant=fullscreen] > L[h=auto] > (DH"Order details" + (LC[p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis"))
|
|
5
|
+
// L[h=fill mw=1000] > (LH[divider p=6 pb=0] > V[g=3] > ((H[g=4 a=start] > (SI[fill] > V[g=2] > (Lk"All orders" + (V[g=0] > Hd"#1001"[level=1] + (H[g=1 a=center wrap] > Tx"5 ordered items"[t=supporting] + (H[g=1 a=center] > Bu + Av"Vlad Dracul" + Tx"Vlad Dracul"[t=supporting]) + (H[g=1 a=center] > Bu + SD"Unfulfilled" + Tx"Unfulfilled"[t=supporting]) + (H[g=1 a=center] > Bu + Ic + Tx"02/23/2026"[t=supporting]) + (H[g=1 a=center] > Bu + Ic + Tx"Needs attention"[t=supporting]) + (H[g=1 a=center] > Bu + Lk"See all")))) + (H[g=2] > B.secondary"Restock" + B.secondary"Edit")) + (H[g=2] > (SI[fill] > V[a=stretch] > B.secondary"Restock") + (SI[fill] > V[a=stretch] > B.secondary"Edit")) + (H[a=center] > (SI[fill] > TL[size=lg fullBleed] > (Tab"Details"! + Tab"Invoices" + Tab"Timeline" + TabMenu"More")) + B.ghost))) + (LC > V[g=4] > ((S > V[g=4] > ((H[g=2 a=center wrap] > (SI[fill] > (H[g=2 a=center] > Hd"Items"[level=2] + SD"Unfulfilled" + Tx"Unfulfilled"[t=supporting])) + (H[g=2] > B.ghost"Fulfill item" + B.secondary"Create label")) + (UL[density=spacious edge=outset] > LI*5)) + (S > V[g=4] > ((H[g=2 a=center wrap] > (SI[fill] > (H[g=2 a=center] > Hd"Invoice"[level=2] + SD"Paid" + Tx"Paid"[t=supporting])) + (H[g=2] > B.ghost"Refund" + B.secondary"Send Invoice")) + (ML > MLI*5) + D + (ML > MLI*1))) + (S > V[g=4] > ((H[g=2 a=center] > (SI[fill] > Hd"Timeline"[level=2]) + B.ghost"Filters") + (UL[density=spacious edge=outset] > LI*4)))) + (LP[w=320 p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis")) ;; Dlg[variant=fullscreen] > L[h=auto] > (DH"Order details" + (LC[p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis"))
|
|
6
6
|
|
|
7
7
|
import { Avatar } from "@astryxdesign/core/Avatar";
|
|
8
8
|
import { Button } from "@astryxdesign/core/Button";
|
|
9
|
+
import { Center } from "@astryxdesign/core/Center";
|
|
9
10
|
import { Collapsible } from "@astryxdesign/core/Collapsible";
|
|
10
11
|
import { Dialog, DialogHeader } from "@astryxdesign/core/Dialog";
|
|
11
12
|
import { Divider } from "@astryxdesign/core/Divider";
|
|
@@ -43,23 +44,20 @@ import {
|
|
|
43
44
|
} from "lucide-react";
|
|
44
45
|
// ─── Styles ─────────────────────────────────────────────────────────────────
|
|
45
46
|
import type { CSSProperties } from "react";
|
|
46
|
-
import { useState } from "react";
|
|
47
47
|
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
48
|
+
// Definite ancestor for `height="fill"`: 100dvh, not minHeight: '100%' (a
|
|
49
|
+
// percentage min-height against an indefinite block computes to 0 — CSS 2.1
|
|
50
|
+
// 10.5). Templates ship standalone, so the page owns this, not the host.
|
|
51
|
+
const pageStyle: CSSProperties = { height: "100dvh" };
|
|
52
|
+
|
|
53
|
+
import { useState } from "react";
|
|
52
54
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const tabsRow: CSSProperties = {
|
|
60
|
-
marginInline: "calc(var(--spacing-3) * -1)",
|
|
61
|
-
marginBottom: "calc(var(--spacing-4) * -1)",
|
|
62
|
-
marginTop: "var(--spacing-3)",
|
|
55
|
+
// The only custom CSS in this template is the sigil plate's shrink rule: the
|
|
56
|
+
// List row supplies the box, and the plate must not squash beside a long name.
|
|
57
|
+
// Everything else uses props.
|
|
58
|
+
const sigilClip: CSSProperties = {
|
|
59
|
+
flexShrink: 0,
|
|
60
|
+
overflow: "clip",
|
|
63
61
|
};
|
|
64
62
|
|
|
65
63
|
// ─── Product data ───────────────────────────────────────────────────────────
|
|
@@ -111,17 +109,10 @@ const PRODUCT_HUES: readonly SceneHue[] = [
|
|
|
111
109
|
"var(--dracula-green)",
|
|
112
110
|
];
|
|
113
111
|
|
|
114
|
-
const sigilClip: CSSProperties = {
|
|
115
|
-
width: 40,
|
|
116
|
-
height: 40,
|
|
117
|
-
overflow: "clip",
|
|
118
|
-
flexShrink: 0,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
112
|
function ProductSigil({ index }: { name: string; index: number }) {
|
|
122
113
|
const hue = PRODUCT_HUES[index % PRODUCT_HUES.length];
|
|
123
114
|
return (
|
|
124
|
-
<
|
|
115
|
+
<Center width={40} height={40} style={sigilClip}>
|
|
125
116
|
<SceneFrame
|
|
126
117
|
label={`${PRODUCTS[index % PRODUCTS.length].name} sigil`}
|
|
127
118
|
viewBox="0 0 40 40"
|
|
@@ -132,7 +123,7 @@ function ProductSigil({ index }: { name: string; index: number }) {
|
|
|
132
123
|
<circle cx={25} cy={9} r={5.5} fill="var(--dracula-bg-light)" opacity={0.5} />
|
|
133
124
|
<path d="M0 28 Q10 22 20 26 T40 24 V40 H0 Z" fill="var(--dracula-current-line)" />
|
|
134
125
|
</SceneFrame>
|
|
135
|
-
</
|
|
126
|
+
</Center>
|
|
136
127
|
);
|
|
137
128
|
}
|
|
138
129
|
|
|
@@ -199,7 +190,7 @@ function PageHeader({
|
|
|
199
190
|
isNarrow: boolean;
|
|
200
191
|
}) {
|
|
201
192
|
return (
|
|
202
|
-
<LayoutHeader hasDivider padding={6}>
|
|
193
|
+
<LayoutHeader hasDivider padding={6} paddingBlockEnd={0}>
|
|
203
194
|
<VStack gap={3}>
|
|
204
195
|
<HStack gap={4} vAlign="start">
|
|
205
196
|
<StackItem size="fill">
|
|
@@ -284,9 +275,9 @@ function PageHeader({
|
|
|
284
275
|
</HStack>
|
|
285
276
|
)}
|
|
286
277
|
|
|
287
|
-
<HStack vAlign="center"
|
|
278
|
+
<HStack vAlign="center">
|
|
288
279
|
<StackItem size="fill">
|
|
289
|
-
<TabList value={activeTab} onChange={onTabChange} size="lg">
|
|
280
|
+
<TabList value={activeTab} onChange={onTabChange} size="lg" isFullBleed>
|
|
290
281
|
<Tab value="details" label="Details" />
|
|
291
282
|
<Tab value="invoices" label="Invoices" />
|
|
292
283
|
<Tab value="timeline" label="Timeline" />
|
|
@@ -601,6 +592,7 @@ export default function DetailPage() {
|
|
|
601
592
|
return (
|
|
602
593
|
<>
|
|
603
594
|
<Layout
|
|
595
|
+
style={pageStyle}
|
|
604
596
|
height="fill"
|
|
605
597
|
contentWidth={1000}
|
|
606
598
|
defaultHasDividers
|
|
@@ -143,6 +143,9 @@ function ComponentDetailView() {
|
|
|
143
143
|
height="auto"
|
|
144
144
|
contentWidth={960}
|
|
145
145
|
end={
|
|
146
|
+
// Dropped below 768 on purpose: this rail is in-page navigation, not
|
|
147
|
+
// content. Every heading stays reachable by scrolling, and two rails on a
|
|
148
|
+
// phone would eat the column the prose needs.
|
|
146
149
|
isMobile ? undefined : (
|
|
147
150
|
<LayoutPanel
|
|
148
151
|
isScrollable={false}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// XLE (canonical structure of the tree below; the CLI had no XLE validator
|
|
3
3
|
// after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
|
|
4
4
|
// human drift check — update it with the file, never alone):
|
|
5
|
-
// L[h=auto mw=960] > (LC[p=8 !scroll] > V[g=8] > (V[g=2] > Hd"Getting started with Astryx Dracula"[level=1] + Tx"Last updated March 30, 2026"[t=supporting] + SE{Selector}"On this page") + (C > V[g=3] > (H[g=2 a=center] > (SI[fill] > (H[g=2 a=center] > Ic + Tx"AI Assistance"[t=body])) + B.ghost"Copy prompt") + Tx"Help me get set up"[t=body]) + (V[g=4] > Hd"Prerequisites"[level=2] + (UL[density=compact] > LI"Node.js 18+" + LI"React 18 or 19" + LI"A package manager")) + D + (V[g=4] > Hd"Install the package"[level=2] + Tx"Description"[t=body] + (V[g=2] > Tx"Step 1"[t=body] + Cd) + (V[g=2] > Tx"Step 2"[t=body] + Tx"Astryx ships precompiled CSS"[t=body] + Cd) + (V[g=2] > Tx"Step 3"[t=body] + Cd)) + D + (V[g=4] > Hd"Configure theming"[level=2] + Tx"Description"[t=body] + Cd + Tx"Note"[t=body]) + D + (V[g=4] > Hd"Next steps"[level=2] + (UL[density=compact] > LI"Fundamental concepts" + LI"Component API reference" + LI"Accessibility" + LI"CLI tools" + LI"Design tokens"))) + (LP[!scroll] > Outline)
|
|
5
|
+
// L[h=auto mw=960] > (LC[p=8 !scroll] > V[g=8] > (V[g=2] > Hd"Getting started with Astryx Dracula"[level=1 t=display-2] + Tx"Last updated March 30, 2026"[t=supporting] + SE{Selector}"On this page") + (C > V[g=3] > (H[g=2 a=center] > (SI[fill] > (H[g=2 a=center] > Ic + Tx"AI Assistance"[t=body])) + B.ghost"Copy prompt") + Tx"Help me get set up"[t=body]) + (V[g=4] > Hd"Prerequisites"[level=2] + (UL[density=compact] > LI"Node.js 18+" + LI"React 18 or 19" + LI"A package manager")) + D + (V[g=4] > Hd"Install the package"[level=2] + Tx"Description"[t=body] + (V[g=2] > Tx"Step 1"[t=body] + Cd) + (V[g=2] > Tx"Step 2"[t=body] + Tx"Astryx ships precompiled CSS"[t=body] + Cd) + (V[g=2] > Tx"Step 3"[t=body] + Cd)) + D + (V[g=4] > Hd"Configure theming"[level=2] + Tx"Description"[t=body] + Cd + Tx"Note"[t=body]) + D + (V[g=4] > Hd"Next steps"[level=2] + (UL[density=compact] > LI"Fundamental concepts" + LI"Component API reference" + LI"Accessibility" + LI"CLI tools" + LI"Design tokens"))) + (LP[!scroll] > Outline)
|
|
6
6
|
|
|
7
7
|
import { Button } from "@astryxdesign/core/Button";
|
|
8
8
|
import { Card } from "@astryxdesign/core/Card";
|
|
@@ -63,6 +63,9 @@ export default function DocumentationTechnical() {
|
|
|
63
63
|
height="auto"
|
|
64
64
|
contentWidth={960}
|
|
65
65
|
end={
|
|
66
|
+
// Dropped below 768 on purpose: this rail is in-page navigation, not
|
|
67
|
+
// content. Every heading stays reachable by scrolling, and two rails on a
|
|
68
|
+
// phone would eat the column the prose needs.
|
|
66
69
|
isMobile ? undefined : (
|
|
67
70
|
<LayoutPanel
|
|
68
71
|
isScrollable={false}
|
|
@@ -78,7 +81,9 @@ export default function DocumentationTechnical() {
|
|
|
78
81
|
<LayoutContent isScrollable={false} padding={8}>
|
|
79
82
|
<VStack gap={8}>
|
|
80
83
|
<VStack gap={2}>
|
|
81
|
-
<Heading level={1}
|
|
84
|
+
<Heading level={1} type="display-2">
|
|
85
|
+
Getting started with Astryx Dracula
|
|
86
|
+
</Heading>
|
|
82
87
|
<Text type="supporting" color="secondary" hasTabularNumbers>
|
|
83
88
|
Last updated March 30, 2026
|
|
84
89
|
</Text>
|
|
@@ -116,9 +121,7 @@ export default function DocumentationTechnical() {
|
|
|
116
121
|
/>
|
|
117
122
|
</HStack>
|
|
118
123
|
<Text type="body" color="secondary">
|
|
119
|
-
|
|
120
|
-
Install @astryxdesign/core and the StyleX compiler. 2. Wrap my app in the Theme
|
|
121
|
-
component. 3. Replace one existing component with an Astryx equivalent.
|
|
124
|
+
{SETUP_PROMPT}
|
|
122
125
|
</Text>
|
|
123
126
|
</VStack>
|
|
124
127
|
</Card>
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// XLE (canonical structure of the tree below; the CLI had no XLE validator
|
|
3
3
|
// after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
|
|
4
4
|
// human drift check — update it with the file, never alone):
|
|
5
|
-
// Counts: 31 total, Core 18 / Layout 4 / Navigation 4 / Form 5.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
5
|
+
// Counts: 31 total, Core 18 / Layout 4 / Navigation 4 / Form 5. The page derives
|
|
6
|
+
// every rendered count from COMPONENT_CATEGORIES; the numerals here are the
|
|
7
|
+
// header's copy of that count and must be moved by hand with the array — no
|
|
8
|
+
// gate reads them (scripts/check.ts gates component names in a header, not
|
|
9
|
+
// counts).
|
|
10
|
+
// L[h=fill mw=1200] > LC[p=8] > V[g=10] > (C.transparent[p=10] > (H[g=8 a=center] > (SI[fill] > V[g=4] > Hd"The coven grimoire"[level=1 t=display-2] + Tx.lg"Every incantation in the Astryx spellbook"[t=large] + (H > B.primary"Enter the grimoire")))) + (V[g=4] > (H[j=between a=center] > Hd"Core"[level=2] + Tx"18 spells"[t=body]) + (G[c={min:260} g=4] > (CC.transparent[p=4] > V[g=3] > (C.muted[p=0 minH=160] > Scene{SceneTile}) + (V[g=1] > Tx"AppShell"[t=body] + Tx"AppShell provides"[t=body]))*18)) + (V[g=4] > (H[j=between a=center] > Hd"Layout"[level=2] + Tx"4 spells"[t=body]) + (G[c={min:260} g=4] > (CC.transparent[p=4] > V[g=3] > (C.muted[p=0 minH=160] > Scene{SceneTile}) + (V[g=1] > Tx"Card"[t=body] + Tx"Cards group"[t=body]))*4)) + (V[g=4] > (H[j=between a=center] > Hd"Navigation"[level=2] + Tx"4 spells"[t=body]) + (G[c={min:260} g=4] > (CC.transparent[p=4] > V[g=3] > (C.muted[p=0 minH=160] > Scene{SceneTile}) + (V[g=1] > Tx"Breadcrumbs"[t=body] + Tx"Breadcrumbs show"[t=body]))*4)) + (V[g=4] > (H[j=between a=center] > Hd"Form"[level=2] + Tx"5 spells"[t=body]) + (G[c={min:260} g=4] > (CC.transparent[p=4] > V[g=3] > (C.muted[p=0 minH=160] > Scene{SceneTile}) + (V[g=1] > Tx"CheckboxInput"[t=body] + Tx"CheckboxInput renders"[t=body]))*5))
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Documentation catalog — every component shelf in the grimoire. Frame-first layout (see `bunx astryx docs layout`): (Frame/responsive/container: see XLE header above.)
|
|
@@ -24,6 +25,11 @@ import type { SceneHue } from "astryx-dracula/shared/scene-hues";
|
|
|
24
25
|
import { SceneTile } from "astryx-dracula/shared/scene-tile";
|
|
25
26
|
import type { CSSProperties } from "react";
|
|
26
27
|
|
|
28
|
+
// Definite ancestor for `height="fill"`: 100dvh, not minHeight: '100%' (a
|
|
29
|
+
// percentage min-height against an indefinite block computes to 0 — CSS 2.1
|
|
30
|
+
// 10.5). Templates ship standalone, so the page owns this, not the host.
|
|
31
|
+
const pageStyle: CSSProperties = { height: "100dvh" };
|
|
32
|
+
|
|
27
33
|
// Negative margin offsets each card's 8px padding so the grid content stays
|
|
28
34
|
// visually aligned while giving every card a padded hover/click target.
|
|
29
35
|
const cardGrid: CSSProperties = {
|
|
@@ -244,6 +250,7 @@ const COMPONENT_TOTAL = COMPONENT_CATEGORIES.reduce(
|
|
|
244
250
|
export default function DocumentationCatalog() {
|
|
245
251
|
return (
|
|
246
252
|
<Layout
|
|
253
|
+
style={pageStyle}
|
|
247
254
|
height="fill"
|
|
248
255
|
contentWidth={1200}
|
|
249
256
|
content={
|
|
@@ -253,7 +260,7 @@ export default function DocumentationCatalog() {
|
|
|
253
260
|
<HStack gap={8} vAlign="center">
|
|
254
261
|
<StackItem size="fill">
|
|
255
262
|
<VStack gap={4}>
|
|
256
|
-
<Heading level={1} type="display-
|
|
263
|
+
<Heading level={1} type="display-2">
|
|
257
264
|
The coven grimoire
|
|
258
265
|
</Heading>
|
|
259
266
|
<Text type="large" weight="normal" color="secondary">
|
|
@@ -121,6 +121,7 @@ export default function TwoColumnForm() {
|
|
|
121
121
|
label="Full name"
|
|
122
122
|
isRequired
|
|
123
123
|
placeholder="Full name"
|
|
124
|
+
autoComplete="name"
|
|
124
125
|
value={fullName}
|
|
125
126
|
onChange={setFullName}
|
|
126
127
|
status={errors.fullName ? { type: "error", message: errors.fullName } : undefined}
|
|
@@ -129,7 +130,9 @@ export default function TwoColumnForm() {
|
|
|
129
130
|
<TextInput
|
|
130
131
|
label="Email"
|
|
131
132
|
isRequired
|
|
133
|
+
type="email"
|
|
132
134
|
placeholder="Email"
|
|
135
|
+
autoComplete="email"
|
|
133
136
|
value={email}
|
|
134
137
|
onChange={setEmail}
|
|
135
138
|
status={errors.email ? { type: "error", message: errors.email } : undefined}
|
|
@@ -137,6 +140,7 @@ export default function TwoColumnForm() {
|
|
|
137
140
|
<TextInput
|
|
138
141
|
label="Company name"
|
|
139
142
|
placeholder="Company name"
|
|
143
|
+
autoComplete="organization"
|
|
140
144
|
value={company}
|
|
141
145
|
onChange={setCompany}
|
|
142
146
|
/>
|
|
@@ -145,20 +149,25 @@ export default function TwoColumnForm() {
|
|
|
145
149
|
<TextInput
|
|
146
150
|
label="Job title"
|
|
147
151
|
placeholder="Job title"
|
|
152
|
+
autoComplete="organization-title"
|
|
148
153
|
value={jobTitle}
|
|
149
154
|
onChange={setJobTitle}
|
|
150
155
|
/>
|
|
151
156
|
<TextInput
|
|
152
157
|
label="Phone number"
|
|
158
|
+
type="tel"
|
|
153
159
|
placeholder="Phone number"
|
|
160
|
+
autoComplete="tel"
|
|
154
161
|
value={phone}
|
|
155
162
|
onChange={setPhone}
|
|
156
163
|
/>
|
|
157
164
|
</Grid>
|
|
158
165
|
|
|
159
166
|
<VStack gap={2}>
|
|
160
|
-
<Text type="label"
|
|
161
|
-
|
|
167
|
+
<Text type="label" id="inquiry-reasons-label">
|
|
168
|
+
What are you reaching out about?
|
|
169
|
+
</Text>
|
|
170
|
+
<HStack gap={2} wrap="wrap" role="group" aria-labelledby="inquiry-reasons-label">
|
|
162
171
|
{INQUIRY_REASONS.map((reason) => {
|
|
163
172
|
const isSelected = inquiryReason === reason;
|
|
164
173
|
return (
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
type: "page",
|
|
3
|
+
name: "Form Wizard",
|
|
4
|
+
description: "Four-step workspace setup with a stepper, per-step validation, and a review",
|
|
5
|
+
keywords: [
|
|
6
|
+
"wizard",
|
|
7
|
+
"stepper",
|
|
8
|
+
"multi-step form",
|
|
9
|
+
"onboarding",
|
|
10
|
+
"validation",
|
|
11
|
+
"review step",
|
|
12
|
+
"workspace setup",
|
|
13
|
+
],
|
|
14
|
+
category: "forms",
|
|
15
|
+
};
|