astryx-dracula 0.3.0 → 0.4.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/USAGE.md +27 -1
- package/astryx-dracula.d.ts +2 -2
- package/astryx-dracula.js +384 -3
- package/astryx-dracula.variants.d.ts +2 -14
- package/icons.mjs +125 -0
- package/package.json +16 -8
- package/shared/revenue-chart.tsx +72 -9
- package/shared/settings-data.ts +43 -1
- package/shared/settings-rows.tsx +81 -30
- package/shared/sparkline.tsx +9 -1
- package/templates/ai-chat-landing.tsx +17 -9
- package/templates/ai-chat.tsx +2 -2
- package/templates/centered-hero.tsx +2 -2
- package/templates/contact-form.tsx +11 -10
- package/templates/dashboard.tsx +7 -3
- package/templates/detail-page.tsx +11 -4
- package/templates/documentation-design.tsx +43 -29
- package/templates/documentation-technical.tsx +11 -9
- package/templates/file-explorer.tsx +82 -70
- package/templates/form-two-column.tsx +1 -1
- package/templates/ide.tsx +6 -6
- package/templates/incident-console.tsx +27 -4
- package/templates/login-card.tsx +8 -5
- package/templates/login-split.tsx +21 -12
- package/templates/login-sso.tsx +57 -41
- package/templates/login.tsx +23 -20
- package/templates/payment-form.tsx +39 -45
- package/templates/product-detail.tsx +4 -6
- package/templates/product-tour.tsx +2 -2
- package/templates/settings-dialog.tsx +84 -87
- package/templates/settings-sidebar.tsx +92 -80
- package/templates/table-grouped.tsx +126 -29
- package/templates/table.tsx +21 -2
- package/templates/tech-report.tsx +1 -1
- package/theme.css +211 -119
- package/tokens.css +15 -7
|
@@ -216,7 +216,9 @@ function PageHeader({
|
|
|
216
216
|
<VStack gap={3}>
|
|
217
217
|
<HStack gap={4} vAlign="start">
|
|
218
218
|
<StackItem size="fill">
|
|
219
|
-
|
|
219
|
+
{/* gap={2}: the breadcrumb is a control, not a text-baseline
|
|
220
|
+
pairing, so it needs separation from the H1 rather than 0. */}
|
|
221
|
+
<VStack gap={2}>
|
|
220
222
|
<Link href="#/templates/detail-page">
|
|
221
223
|
<HStack gap={1} vAlign="center">
|
|
222
224
|
<Icon icon={ArrowLeft} size="sm" color="inherit" />
|
|
@@ -502,8 +504,8 @@ function TimelineSection() {
|
|
|
502
504
|
return (
|
|
503
505
|
<Section>
|
|
504
506
|
<VStack gap={4}>
|
|
505
|
-
{/* gap={2}, matching the byte-identical header rows at :
|
|
506
|
-
:
|
|
507
|
+
{/* gap={2}, matching the byte-identical header rows at :340 and
|
|
508
|
+
:408. Core's Stack has no default gap (Stack.tsx:211, pass-through
|
|
507
509
|
at :255/:285), so this rendered 0px -- the heading and the Filters
|
|
508
510
|
button touched -- while both siblings render 8px. */}
|
|
509
511
|
<HStack gap={2} vAlign="center">
|
|
@@ -582,11 +584,16 @@ function PanelContent() {
|
|
|
582
584
|
return (
|
|
583
585
|
<VStack gap={4}>
|
|
584
586
|
<Collapsible trigger={<Heading level={3}>Notes</Heading>}>
|
|
587
|
+
{/* "Show more" closes the sentence inside the Text below, so it is
|
|
588
|
+
prose and asks for `hasUnderline` — colour alone is not a
|
|
589
|
+
sufficient cue inline (1.4.1 / F73). `type="inherit"` adopts the
|
|
590
|
+
paragraph's size and leading. The two links in the header are
|
|
591
|
+
navigation, so they pass neither. */}
|
|
585
592
|
<Text type="body">
|
|
586
593
|
Customer is a repeat buyer, 3rd order this quarter. Prefers
|
|
587
594
|
moonlight and sage glazes. Requested gift wrapping for the mug set.
|
|
588
595
|
Ships to a residential address in CA.{' '}
|
|
589
|
-
<Link href="#/templates/detail-page">
|
|
596
|
+
<Link href="#/templates/detail-page" hasUnderline type="inherit">
|
|
590
597
|
Show more
|
|
591
598
|
</Link>
|
|
592
599
|
</Text>
|
|
@@ -29,7 +29,7 @@ import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
|
29
29
|
import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
|
|
30
30
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
31
31
|
import {Tooltip} from '@astryxdesign/core/Tooltip';
|
|
32
|
-
import {Table, pixel} from '@astryxdesign/core/Table';
|
|
32
|
+
import {Table, pixel, proportional} from '@astryxdesign/core/Table';
|
|
33
33
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
34
34
|
import {Section} from '@astryxdesign/core/Section';
|
|
35
35
|
import {Center} from '@astryxdesign/core/Center';
|
|
@@ -42,6 +42,17 @@ const tabListFlush: CSSProperties = {
|
|
|
42
42
|
marginInlineStart: 'calc(var(--spacing-3) * -1)',
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// The best-practices table lives in the prose column, where the eye tracks a
|
|
46
|
+
// single measure. Table's scroll wrapper bleeds edge-to-edge by reading the
|
|
47
|
+
// container padding vars LayoutContent publishes (32px here), which pushes
|
|
48
|
+
// its dividers 32px past the measure on both sides at every width. This table
|
|
49
|
+
// never overflows, so the bleed buys nothing — zero the vars for this subtree
|
|
50
|
+
// so it sits on the measure with the headings above it.
|
|
51
|
+
const practicesOnMeasure: CSSProperties = ({
|
|
52
|
+
'--container-padding-inline-start': '0px',
|
|
53
|
+
'--container-padding-inline-end': '0px',
|
|
54
|
+
}) as CSSProperties;
|
|
55
|
+
|
|
45
56
|
const COMPONENT_OUTLINE_ITEMS: OutlineItem[] = [
|
|
46
57
|
{id: 'usage', label: 'Usage', level: 2},
|
|
47
58
|
{id: 'best-practices', label: 'Best practices', level: 3},
|
|
@@ -635,34 +646,37 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
635
646
|
<Heading id="best-practices" level={3}>
|
|
636
647
|
Best practices
|
|
637
648
|
</Heading>
|
|
638
|
-
<
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
{
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
<
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
649
|
+
<div style={practicesOnMeasure}>
|
|
650
|
+
<Table
|
|
651
|
+
data={docs.bestPractices as Record<string, unknown>[]}
|
|
652
|
+
dividers="none"
|
|
653
|
+
hasHover
|
|
654
|
+
columns={[
|
|
655
|
+
{
|
|
656
|
+
key: 'type',
|
|
657
|
+
header: 'Guidance',
|
|
658
|
+
width: pixel(125),
|
|
659
|
+
renderCell: (item: Record<string, unknown>) => (
|
|
660
|
+
<Badge
|
|
661
|
+
label={item.type === 'do' ? 'Do' : "Don't"}
|
|
662
|
+
variant={item.type === 'do' ? 'green' : 'red'}
|
|
663
|
+
/>
|
|
664
|
+
),
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
key: 'text',
|
|
668
|
+
header: 'Practices',
|
|
669
|
+
width: proportional(1),
|
|
670
|
+
renderCell: (item: Record<string, unknown>) => (
|
|
671
|
+
<Text type="body" textWrap="wrap">
|
|
672
|
+
{item.text as string}
|
|
673
|
+
</Text>
|
|
674
|
+
),
|
|
675
|
+
},
|
|
676
|
+
]}
|
|
677
|
+
density="spacious"
|
|
678
|
+
/>
|
|
679
|
+
</div>
|
|
666
680
|
</VStack>
|
|
667
681
|
|
|
668
682
|
<Divider />
|
|
@@ -30,7 +30,7 @@ import {outlinePanel} from 'astryx-dracula/shared/chaptered-doc-config';
|
|
|
30
30
|
// Same prompt in the copy action and the visible body: one string, so the
|
|
31
31
|
// two can never drift.
|
|
32
32
|
const SETUP_PROMPT =
|
|
33
|
-
'Help me get set up with Astryx Dracula. Based on my project, do the following: 1. Install @astryxdesign/core and the StyleX compiler. 2. Wrap my app in
|
|
33
|
+
'Help me get set up with Astryx Dracula. Based on my project, do the following: 1. Install @astryxdesign/core and the StyleX compiler. 2. Wrap my app in the Theme component. 3. Replace one existing component with an Astryx equivalent. After setup, suggest relevant next steps based on my project.';
|
|
34
34
|
|
|
35
35
|
// Copies the setup prompt in the shape each tool wants to receive it. The
|
|
36
36
|
// menu cannot navigate anywhere from a static template, so the action says
|
|
@@ -138,8 +138,8 @@ export default function DocumentationTechnical() {
|
|
|
138
138
|
<Text type="body" color="secondary">
|
|
139
139
|
Help me get set up with Astryx Dracula. Based on my project,
|
|
140
140
|
do the following: 1. Install @astryxdesign/core and the
|
|
141
|
-
StyleX compiler. 2. Wrap my app in
|
|
142
|
-
one existing component with an Astryx equivalent.
|
|
141
|
+
StyleX compiler. 2. Wrap my app in the Theme component. 3.
|
|
142
|
+
Replace one existing component with an Astryx equivalent.
|
|
143
143
|
</Text>
|
|
144
144
|
</VStack>
|
|
145
145
|
</Card>
|
|
@@ -225,17 +225,19 @@ export default function App() {
|
|
|
225
225
|
</Heading>
|
|
226
226
|
<Text type="body">
|
|
227
227
|
Astryx ships with a default theme that works out of the box. To
|
|
228
|
-
customize colors, typography, and spacing, wrap your app in
|
|
229
|
-
|
|
228
|
+
customize colors, typography, and spacing, wrap your app in the
|
|
229
|
+
Theme component.
|
|
230
230
|
</Text>
|
|
231
231
|
<CodeBlock
|
|
232
|
-
code={`import {
|
|
232
|
+
code={`import { type ReactNode } from 'react';
|
|
233
|
+
import { Theme } from '@astryxdesign/core/theme';
|
|
234
|
+
import { astryxDraculaTheme } from 'astryx-dracula';
|
|
233
235
|
|
|
234
|
-
export default function App({ children }) {
|
|
236
|
+
export default function App({ children }: { children: ReactNode }) {
|
|
235
237
|
return (
|
|
236
|
-
<
|
|
238
|
+
<Theme theme={astryxDraculaTheme} mode="dark">
|
|
237
239
|
{children}
|
|
238
|
-
</
|
|
240
|
+
</Theme>
|
|
239
241
|
);
|
|
240
242
|
}`}
|
|
241
243
|
language="tsx"
|
|
@@ -436,6 +436,15 @@ export default function FileExplorer() {
|
|
|
436
436
|
return item?.type === 'file' ? item : null;
|
|
437
437
|
}, [selectedPath]);
|
|
438
438
|
|
|
439
|
+
// Narrow mode is one pane at a time, the same master→detail shape the
|
|
440
|
+
// folder columns already use: the deepest column, or the file preview when
|
|
441
|
+
// the selection ends on a file. Beside each other the column and the
|
|
442
|
+
// preview need 240 + 320 = 560px, so in a 390px pane the preview was cut at
|
|
443
|
+
// the viewport edge with nothing to scroll it into view. "Go back" (:464)
|
|
444
|
+
// pops the selected file off the path, so it is also the way back from the
|
|
445
|
+
// preview to the column.
|
|
446
|
+
const isPreviewPane = isNarrow && selectedFile != null;
|
|
447
|
+
|
|
439
448
|
const handleSelect = (columnIndex: number, itemId: string) => {
|
|
440
449
|
setSelectedPath([...selectedPath.slice(0, columnIndex), itemId]);
|
|
441
450
|
};
|
|
@@ -508,81 +517,84 @@ export default function FileExplorer() {
|
|
|
508
517
|
File Explorer
|
|
509
518
|
</VisuallyHidden>
|
|
510
519
|
<HStack height="100%" style={columnRow}>
|
|
511
|
-
{
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
{
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
icon={
|
|
551
|
-
item.type === 'folder'
|
|
552
|
-
? Folder
|
|
553
|
-
: FileText
|
|
554
|
-
}
|
|
555
|
-
color={
|
|
556
|
-
item.type === 'folder' ? 'accent' : 'secondary'
|
|
557
|
-
}
|
|
558
|
-
size="sm"
|
|
559
|
-
/>
|
|
560
|
-
}
|
|
561
|
-
endContent={
|
|
562
|
-
hasChildren ? (
|
|
520
|
+
{!isPreviewPane &&
|
|
521
|
+
visibleColumns.map((col, colIndex) => {
|
|
522
|
+
const trueIndex = columnOffset + colIndex;
|
|
523
|
+
const showDivider =
|
|
524
|
+
trueIndex < columns.length - 1 || selectedFile != null;
|
|
525
|
+
return (
|
|
526
|
+
<Section
|
|
527
|
+
key={col.id}
|
|
528
|
+
width={240}
|
|
529
|
+
padding={2}
|
|
530
|
+
variant="transparent"
|
|
531
|
+
dividers={showDivider ? ['end'] : undefined}
|
|
532
|
+
style={{...scrollable, ...fixedColumn}}
|
|
533
|
+
role="region"
|
|
534
|
+
aria-label={`Folder column ${trueIndex + 1}`}
|
|
535
|
+
tabIndex={0}>
|
|
536
|
+
{col.items.length === 0 ? (
|
|
537
|
+
<EmptyState
|
|
538
|
+
title="Quiet in the crypt"
|
|
539
|
+
description="This folder holds no scrolls yet."
|
|
540
|
+
isCompact
|
|
541
|
+
/>
|
|
542
|
+
) : (
|
|
543
|
+
<List density="compact" hasDividers={false}>
|
|
544
|
+
{col.items.map(item => {
|
|
545
|
+
const isSelected = col.selectedId === item.id;
|
|
546
|
+
const hasChildren =
|
|
547
|
+
item.type === 'folder' &&
|
|
548
|
+
item.children != null &&
|
|
549
|
+
item.children.length > 0;
|
|
550
|
+
return (
|
|
551
|
+
<ListItem
|
|
552
|
+
key={item.id}
|
|
553
|
+
label={
|
|
554
|
+
<Text type="body" maxLines={1}>
|
|
555
|
+
{item.name}
|
|
556
|
+
</Text>
|
|
557
|
+
}
|
|
558
|
+
startContent={
|
|
563
559
|
<Icon
|
|
564
|
-
icon={
|
|
565
|
-
|
|
566
|
-
|
|
560
|
+
icon={
|
|
561
|
+
item.type === 'folder'
|
|
562
|
+
? Folder
|
|
563
|
+
: FileText
|
|
564
|
+
}
|
|
565
|
+
color={
|
|
566
|
+
item.type === 'folder'
|
|
567
|
+
? 'accent'
|
|
568
|
+
: 'secondary'
|
|
569
|
+
}
|
|
570
|
+
size="sm"
|
|
567
571
|
/>
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
572
|
+
}
|
|
573
|
+
endContent={
|
|
574
|
+
hasChildren ? (
|
|
575
|
+
<Icon
|
|
576
|
+
icon={ChevronRight}
|
|
577
|
+
size="xsm"
|
|
578
|
+
color="secondary"
|
|
579
|
+
/>
|
|
580
|
+
) : undefined
|
|
581
|
+
}
|
|
582
|
+
onClick={() => handleSelect(trueIndex, item.id)}
|
|
583
|
+
isSelected={isSelected}
|
|
584
|
+
/>
|
|
585
|
+
);
|
|
586
|
+
})}
|
|
587
|
+
</List>
|
|
588
|
+
)}
|
|
589
|
+
</Section>
|
|
590
|
+
);
|
|
591
|
+
})}
|
|
580
592
|
{selectedFile && (
|
|
581
593
|
<Section
|
|
582
|
-
width={320}
|
|
583
|
-
// 16px, not 24. The sibling column at :
|
|
594
|
+
width={isPreviewPane ? '100%' : 320}
|
|
595
|
+
// 16px, not 24. The sibling column at :529 is padding={2}
|
|
584
596
|
// (8px) because it holds a dense compact List, and these two
|
|
585
|
-
// sit in one HStack (:
|
|
597
|
+
// sit in one HStack (:519) as columns of the same view --
|
|
586
598
|
// 24 beside 8 made the compact column look broken rather
|
|
587
599
|
// than compact. 16 is the outer-inset value this repo already
|
|
588
600
|
// uses for exactly this (scaffolds.md new-site recipe).
|
package/templates/ide.tsx
CHANGED
|
@@ -226,16 +226,16 @@ export default function IdeWorkspace() {
|
|
|
226
226
|
|
|
227
227
|
const startPanel = useResizable({
|
|
228
228
|
defaultSize: 256,
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
minSize: 160,
|
|
230
|
+
maxSize: 400,
|
|
231
231
|
collapsible: true,
|
|
232
232
|
collapsedSize: 50,
|
|
233
233
|
});
|
|
234
234
|
|
|
235
235
|
const endPanel = useResizable({
|
|
236
236
|
defaultSize: 320,
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
minSize: 180,
|
|
238
|
+
maxSize: 500,
|
|
239
239
|
collapsible: true,
|
|
240
240
|
collapsedSize: 50,
|
|
241
241
|
});
|
|
@@ -244,8 +244,8 @@ export default function IdeWorkspace() {
|
|
|
244
244
|
// has no minimum of its own, and an unbounded drag leaves nothing to grab.
|
|
245
245
|
const bottomPanel = useResizable({
|
|
246
246
|
defaultSize: 300,
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
minSize: 80,
|
|
248
|
+
maxSize: 520,
|
|
249
249
|
collapsible: true,
|
|
250
250
|
collapsedSize: 40,
|
|
251
251
|
});
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
//
|
|
3
|
+
// NOTE on `t=display-2`: the h1 asked for display-2 from the start, but
|
|
4
|
+
// core 0.3.0's Heading emitted no `data-type`, and the 0.3.0 CLI emitted no
|
|
5
|
+
// `.astryx-heading[data-type]` rule, so --text-display-2-size never reached a
|
|
6
|
+
// heading and the 24px level-1 default leaked through. core+CLI 0.6.3 restored
|
|
7
|
+
// the path, so this title now renders at its declared 35px. Verified by
|
|
8
|
+
// pixel-diffing the pre-upgrade build against this one at 1440 and 390: the
|
|
9
|
+
// only regression was this header detaching at 390, fixed by stacking the
|
|
10
|
+
// 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))
|
|
4
12
|
|
|
5
13
|
/**
|
|
6
14
|
* 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.)
|
|
@@ -484,8 +492,8 @@ export default function IncidentConsole() {
|
|
|
484
492
|
|
|
485
493
|
const inspectorPanel = useResizable({
|
|
486
494
|
defaultSize: 380,
|
|
487
|
-
|
|
488
|
-
|
|
495
|
+
minSize: 320,
|
|
496
|
+
maxSize: 480,
|
|
489
497
|
});
|
|
490
498
|
|
|
491
499
|
const visible = useMemo(() => {
|
|
@@ -521,7 +529,22 @@ export default function IncidentConsole() {
|
|
|
521
529
|
icon={<Icon icon={Plus} size="sm" />}
|
|
522
530
|
/>
|
|
523
531
|
);
|
|
524
|
-
|
|
532
|
+
// Stacks below 1024px. The H1 asks for `display-2` (35px), which is the
|
|
533
|
+
// theme's own declared value but only started rendering in core 0.6.3 —
|
|
534
|
+
// see the note in the XLE header. At 35px the title wraps to two lines on a
|
|
535
|
+
// phone, and the counter beside it then wraps too and detaches into the
|
|
536
|
+
// corner. Stacking keeps the counter on the title's own line at any width
|
|
537
|
+
// the title needs more than one, and costs nothing at desktop.
|
|
538
|
+
const titleGroup = isNarrow ? (
|
|
539
|
+
<VStack gap={1}>
|
|
540
|
+
<Heading level={1} type="display-2">
|
|
541
|
+
Night watch
|
|
542
|
+
</Heading>
|
|
543
|
+
<Text type="supporting" color="secondary" hasTabularNumbers>
|
|
544
|
+
{openCount} investigating
|
|
545
|
+
</Text>
|
|
546
|
+
</VStack>
|
|
547
|
+
) : (
|
|
525
548
|
<HStack gap={2} vAlign="center">
|
|
526
549
|
<Heading level={1} type="display-2">Night watch</Heading>
|
|
527
550
|
<Text type="supporting" color="secondary" hasTabularNumbers>
|
package/templates/login-card.tsx
CHANGED
|
@@ -105,7 +105,8 @@ export default function LoginCard() {
|
|
|
105
105
|
<VStack hAlign="end">
|
|
106
106
|
<Link
|
|
107
107
|
href="#/templates/login-card"
|
|
108
|
-
color="secondary"
|
|
108
|
+
color="secondary"
|
|
109
|
+
isStandalone>
|
|
109
110
|
{AUTH_FORGOT_PASSWORD}
|
|
110
111
|
</Link>
|
|
111
112
|
</VStack>
|
|
@@ -141,11 +142,13 @@ export default function LoginCard() {
|
|
|
141
142
|
/>
|
|
142
143
|
</VStack>
|
|
143
144
|
|
|
144
|
-
{/*
|
|
145
|
+
{/* Prose links. A link inside a sentence needs a persistent
|
|
146
|
+
underline — colour alone is not a cue (WCAG 1.4.1 / F73),
|
|
147
|
+
and the kit underlines on hover only. */}
|
|
145
148
|
<VStack hAlign="center">
|
|
146
149
|
<Text type="supporting" color="secondary">
|
|
147
150
|
{AUTH_SIGNUP_PROMPT}{' '}
|
|
148
|
-
<Link href="#/templates/login-card" type="supporting">
|
|
151
|
+
<Link href="#/templates/login-card" type="supporting" hasUnderline>
|
|
149
152
|
{AUTH_SIGNUP_LINK}
|
|
150
153
|
</Link>
|
|
151
154
|
</Text>
|
|
@@ -157,11 +160,11 @@ export default function LoginCard() {
|
|
|
157
160
|
<VStack hAlign="center" width="100%">
|
|
158
161
|
<Text type="supporting" color="secondary" justify="center">
|
|
159
162
|
{AUTH_TERMS_PREFIX}{' '}
|
|
160
|
-
<Link href="#/templates/login-card" type="supporting">
|
|
163
|
+
<Link href="#/templates/login-card" type="supporting" hasUnderline>
|
|
161
164
|
{AUTH_TERMS_SERVICE}
|
|
162
165
|
</Link>{' '}
|
|
163
166
|
and{' '}
|
|
164
|
-
<Link href="#/templates/login-card" type="supporting">
|
|
167
|
+
<Link href="#/templates/login-card" type="supporting" hasUnderline>
|
|
165
168
|
{AUTH_TERMS_PRIVACY}
|
|
166
169
|
</Link>
|
|
167
170
|
.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
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")) + Tx"New to the castle?"[t=supporting]) + (C[p=0] > AR)) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
|
|
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])
|
|
4
4
|
|
|
5
5
|
import {useState, useTransition, type CSSProperties} from 'react';
|
|
6
6
|
import {inputAutoComplete} from 'astryx-dracula/shared/auth-chrome-config';
|
|
@@ -191,7 +191,9 @@ export default function LoginSplit() {
|
|
|
191
191
|
/>
|
|
192
192
|
{error && (
|
|
193
193
|
<VStack hAlign="end">
|
|
194
|
-
<Link
|
|
194
|
+
<Link
|
|
195
|
+
href="#/templates/login-split"
|
|
196
|
+
isStandalone>
|
|
195
197
|
{AUTH_FORGOT_PASSWORD}
|
|
196
198
|
</Link>
|
|
197
199
|
</VStack>
|
|
@@ -234,13 +236,20 @@ export default function LoginSplit() {
|
|
|
234
236
|
</Center>
|
|
235
237
|
</StackItem>
|
|
236
238
|
|
|
239
|
+
{/* Prose link: colour alone is not a cue inside a sentence
|
|
240
|
+
(WCAG 1.4.1 / F73), so it asks for the underline the kit
|
|
241
|
+
only gives on hover. The VStack centres the line, as the
|
|
242
|
+
other three login templates do — it was sitting flush
|
|
243
|
+
left under a left-aligned form. */}
|
|
237
244
|
{!isSuccess && (
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
245
|
+
<VStack hAlign="center">
|
|
246
|
+
<Text type="supporting" color="secondary">
|
|
247
|
+
{AUTH_SIGNUP_PROMPT}{' '}
|
|
248
|
+
<Link href="#/templates/login-split" type="supporting" hasUnderline>
|
|
249
|
+
{AUTH_SIGNUP_LINK}
|
|
250
|
+
</Link>
|
|
251
|
+
</Text>
|
|
252
|
+
</VStack>
|
|
244
253
|
)}
|
|
245
254
|
</VStack>
|
|
246
255
|
</Section>
|
|
@@ -259,14 +268,14 @@ export default function LoginSplit() {
|
|
|
259
268
|
</Card>
|
|
260
269
|
</Center>
|
|
261
270
|
|
|
262
|
-
<VStack hAlign="center">
|
|
263
|
-
<Text type="supporting" color="secondary">
|
|
271
|
+
<VStack hAlign="center" width="100%">
|
|
272
|
+
<Text type="supporting" color="secondary" justify="center">
|
|
264
273
|
{AUTH_TERMS_PREFIX}{' '}
|
|
265
|
-
<Link href="#/templates/login-split" type="supporting">
|
|
274
|
+
<Link href="#/templates/login-split" type="supporting" hasUnderline>
|
|
266
275
|
{AUTH_TERMS_SERVICE}
|
|
267
276
|
</Link>{' '}
|
|
268
277
|
and{' '}
|
|
269
|
-
<Link href="#/templates/login-split" type="supporting">
|
|
278
|
+
<Link href="#/templates/login-split" type="supporting" hasUnderline>
|
|
270
279
|
{AUTH_TERMS_PRIVACY}
|
|
271
280
|
</Link>
|
|
272
281
|
.
|