astryx-dracula 0.5.0 → 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 (129) 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/astryx.integration.mjs +1 -1
  8. package/icons.mjs +21 -17
  9. package/icons.tsx +69 -62
  10. package/package.json +125 -120
  11. package/shared/auth-chrome-config.ts +6 -6
  12. package/shared/auth-chrome.tsx +143 -144
  13. package/shared/auth-copy.ts +14 -14
  14. package/shared/chaptered-doc-config.ts +48 -51
  15. package/shared/chaptered-doc.tsx +267 -289
  16. package/shared/chart-hues.ts +25 -10
  17. package/shared/chart-labels.tsx +31 -30
  18. package/shared/chart-legend.tsx +55 -56
  19. package/shared/chart-panel-style.ts +3 -3
  20. package/shared/data-bar.tsx +97 -102
  21. package/shared/format-money.ts +1 -1
  22. package/shared/gallery-image.ts +6 -6
  23. package/shared/login-demo.ts +1 -1
  24. package/shared/metric-card.tsx +101 -105
  25. package/shared/metric-delta.tsx +28 -27
  26. package/shared/order-desk.tsx +205 -216
  27. package/shared/revenue-chart.tsx +145 -165
  28. package/shared/scene-castle.tsx +206 -291
  29. package/shared/scene-frame.tsx +33 -32
  30. package/shared/scene-hues.ts +17 -17
  31. package/shared/scene-tile.tsx +88 -92
  32. package/shared/settings-data.ts +119 -125
  33. package/shared/settings-rows.tsx +122 -159
  34. package/shared/sparkline.tsx +75 -88
  35. package/shared/sso-icons.tsx +10 -22
  36. package/shared/stars.tsx +16 -25
  37. package/templates/ai-chat-landing.template.mjs +5 -4
  38. package/templates/ai-chat-landing.tsx +473 -460
  39. package/templates/ai-chat.template.mjs +5 -4
  40. package/templates/ai-chat.tsx +543 -612
  41. package/templates/blank.template.mjs +5 -4
  42. package/templates/blank.tsx +18 -14
  43. package/templates/centered-hero.template.mjs +5 -4
  44. package/templates/centered-hero.tsx +55 -68
  45. package/templates/classic-gallery.template.mjs +5 -4
  46. package/templates/classic-gallery.tsx +97 -104
  47. package/templates/contact-form.template.mjs +5 -4
  48. package/templates/contact-form.tsx +247 -282
  49. package/templates/dashboard-portfolio.template.mjs +5 -4
  50. package/templates/dashboard-portfolio.tsx +717 -548
  51. package/templates/dashboard.template.mjs +5 -4
  52. package/templates/dashboard.tsx +596 -567
  53. package/templates/detail-page.template.mjs +5 -4
  54. package/templates/detail-page.tsx +546 -603
  55. package/templates/documentation-design.template.mjs +5 -4
  56. package/templates/documentation-design.tsx +231 -243
  57. package/templates/documentation-technical.template.mjs +5 -4
  58. package/templates/documentation-technical.tsx +236 -239
  59. package/templates/documentation.template.mjs +5 -4
  60. package/templates/documentation.tsx +267 -279
  61. package/templates/editor.template.mjs +5 -4
  62. package/templates/editor.tsx +844 -877
  63. package/templates/file-explorer.template.mjs +5 -4
  64. package/templates/file-explorer.tsx +566 -590
  65. package/templates/form-two-column.template.mjs +5 -4
  66. package/templates/form-two-column.tsx +195 -210
  67. package/templates/gallery-hero.template.mjs +5 -4
  68. package/templates/gallery-hero.tsx +93 -114
  69. package/templates/ide.template.mjs +5 -4
  70. package/templates/ide.tsx +407 -453
  71. package/templates/incident-console.template.mjs +5 -4
  72. package/templates/incident-console.tsx +593 -575
  73. package/templates/kanban-board.template.mjs +5 -4
  74. package/templates/kanban-board.tsx +665 -683
  75. package/templates/library.template.mjs +5 -4
  76. package/templates/library.tsx +409 -430
  77. package/templates/login-card.template.mjs +5 -4
  78. package/templates/login-card.tsx +63 -72
  79. package/templates/login-split.template.mjs +5 -4
  80. package/templates/login-split.tsx +192 -193
  81. package/templates/login-sso.template.mjs +5 -4
  82. package/templates/login-sso.tsx +257 -258
  83. package/templates/login.template.mjs +5 -4
  84. package/templates/login.tsx +53 -49
  85. package/templates/messaging-shell.template.mjs +5 -4
  86. package/templates/messaging-shell.tsx +556 -574
  87. package/templates/mixed-gallery.template.mjs +5 -4
  88. package/templates/mixed-gallery.tsx +60 -58
  89. package/templates/payment-form.template.mjs +5 -4
  90. package/templates/payment-form.tsx +882 -991
  91. package/templates/product-detail.template.mjs +5 -4
  92. package/templates/product-detail.tsx +261 -272
  93. package/templates/product-gallery.template.mjs +5 -4
  94. package/templates/product-gallery.tsx +126 -137
  95. package/templates/product-tour.template.mjs +5 -4
  96. package/templates/product-tour.tsx +741 -773
  97. package/templates/settings-dialog.template.mjs +5 -4
  98. package/templates/settings-dialog.tsx +688 -717
  99. package/templates/settings-sidebar.template.mjs +5 -4
  100. package/templates/settings-sidebar.tsx +762 -807
  101. package/templates/settings.template.mjs +5 -4
  102. package/templates/settings.tsx +289 -304
  103. package/templates/shell-nav.template.mjs +5 -4
  104. package/templates/shell-nav.tsx +212 -219
  105. package/templates/shell-side-nav.template.mjs +5 -4
  106. package/templates/shell-side-nav.tsx +135 -157
  107. package/templates/shell-top-nav.template.mjs +5 -4
  108. package/templates/shell-top-nav.tsx +182 -203
  109. package/templates/side-gallery.template.mjs +5 -4
  110. package/templates/side-gallery.tsx +85 -90
  111. package/templates/table-grouped.template.mjs +5 -4
  112. package/templates/table-grouped.tsx +1000 -1053
  113. package/templates/table-page-chart.template.mjs +5 -4
  114. package/templates/table-page-chart.tsx +400 -398
  115. package/templates/table-page-heatmap-status.template.mjs +5 -4
  116. package/templates/table-page-heatmap-status.tsx +483 -512
  117. package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
  118. package/templates/table-page-shoe-store-heatmap.tsx +748 -746
  119. package/templates/table-page.template.mjs +5 -4
  120. package/templates/table-page.tsx +396 -405
  121. package/templates/table.template.mjs +5 -4
  122. package/templates/table.tsx +92 -93
  123. package/templates/tech-report.template.mjs +5 -4
  124. package/templates/tech-report.tsx +1316 -1304
  125. package/templates/theme-showcase.template.mjs +5 -4
  126. package/templates/theme-showcase.tsx +871 -951
  127. package/theme.css +35 -77
  128. package/theme.css.d.ts +9 -0
  129. package/tokens.css +373 -380
@@ -1,6 +1,7 @@
1
1
  export default {
2
- type: 'page',
3
- name: 'Login Card',
4
- description: 'Centered auth card with email and password',
5
- category: 'auth',
2
+ type: "page",
3
+ name: "Login Card",
4
+ description: "Centered auth card with email and password",
5
+ keywords: ["login", "sign in", "auth", "card", "credentials", "account"],
6
+ category: "auth",
6
7
  };
@@ -1,83 +1,74 @@
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
- import {useState, useTransition} from 'react';
6
- import {demoLogin} from 'astryx-dracula/shared/login-demo';
7
- import {AppleIcon, GoogleIcon} from 'astryx-dracula/shared/sso-icons';
7
+ import { Button } from "@astryxdesign/core/Button";
8
+ import { Center } from "@astryxdesign/core/Center";
9
+ import { Divider } from "@astryxdesign/core/Divider";
10
+ import { VStack } from "@astryxdesign/core/Layout";
11
+ import { AuthCard, AuthLoginFields, LoginBrand } from "astryx-dracula/shared/auth-chrome";
8
12
  import {
9
- AUTH_SSO_DIVIDER,
10
- AUTH_ERROR_MESSAGE,
11
- } from 'astryx-dracula/shared/auth-copy';
12
- import {VStack} from '@astryxdesign/core/Layout';
13
- import {Center} from '@astryxdesign/core/Center';
14
- import {Button} from '@astryxdesign/core/Button';
15
- import {Divider} from '@astryxdesign/core/Divider';
16
-
17
- import {authPageStyle as pageStyle, authContentStyle as contentStyle} from 'astryx-dracula/shared/auth-chrome-config';
18
- import {LoginBrand, AuthCard, AuthLoginFields} from 'astryx-dracula/shared/auth-chrome';
13
+ authContentStyle as contentStyle,
14
+ authPageStyle as pageStyle,
15
+ } from "astryx-dracula/shared/auth-chrome-config";
16
+ import { AUTH_ERROR_MESSAGE, AUTH_SSO_DIVIDER } from "astryx-dracula/shared/auth-copy";
17
+ import { demoLogin } from "astryx-dracula/shared/login-demo";
18
+ import { AppleIcon, GoogleIcon } from "astryx-dracula/shared/sso-icons";
19
+ import { useState, useTransition } from "react";
19
20
 
20
21
  export default function LoginCard() {
21
- const [email, setEmail] = useState('');
22
- const [password, setPassword] = useState('');
23
- const [error, setError] = useState<string | null>(null);
24
- const [isLoading, startTransition] = useTransition();
22
+ const [email, setEmail] = useState("");
23
+ const [password, setPassword] = useState("");
24
+ const [error, setError] = useState<string | null>(null);
25
+ const [isLoading, startTransition] = useTransition();
25
26
 
26
- const handleLogin = () => {
27
- if (!email || !password) {
28
- setError(AUTH_ERROR_MESSAGE);
29
- return;
30
- }
31
- setError(null);
32
- startTransition(async () => {
33
- await demoLogin();
34
- setError(AUTH_ERROR_MESSAGE);
35
- });
36
- };
27
+ const handleLogin = () => {
28
+ if (!email || !password) {
29
+ setError(AUTH_ERROR_MESSAGE);
30
+ return;
31
+ }
32
+ setError(null);
33
+ startTransition(async () => {
34
+ await demoLogin();
35
+ setError(AUTH_ERROR_MESSAGE);
36
+ });
37
+ };
37
38
 
38
- return (
39
- <Center axis="both" style={pageStyle}>
40
- <VStack gap={4} hAlign="center" style={contentStyle}>
41
- <LoginBrand />
39
+ return (
40
+ <Center axis="both" style={pageStyle}>
41
+ <VStack gap={4} hAlign="center" style={contentStyle}>
42
+ <LoginBrand />
42
43
 
43
- <AuthCard selfHash="#/templates/login-card">
44
- <AuthLoginFields
45
- email={email}
46
- password={password}
47
- error={error}
48
- selfHash="#/templates/login-card"
49
- isLoading={isLoading}
50
- onEmailChange={(v: string) => {
51
- setEmail(v);
52
- setError(null);
53
- }}
54
- onPasswordChange={(v: string) => {
55
- setPassword(v);
56
- setError(null);
57
- }}
58
- onSubmit={handleLogin}
59
- />
44
+ <AuthCard selfHash="#/templates/login-card">
45
+ <AuthLoginFields
46
+ email={email}
47
+ password={password}
48
+ error={error}
49
+ selfHash="#/templates/login-card"
50
+ isLoading={isLoading}
51
+ onEmailChange={(v: string) => {
52
+ setEmail(v);
53
+ setError(null);
54
+ }}
55
+ onPasswordChange={(v: string) => {
56
+ setPassword(v);
57
+ setError(null);
58
+ }}
59
+ onSubmit={handleLogin}
60
+ />
60
61
 
61
- {/* Divider */}
62
- <Divider label={AUTH_SSO_DIVIDER} />
62
+ {/* Divider */}
63
+ <Divider label={AUTH_SSO_DIVIDER} />
63
64
 
64
- {/* Social buttons */}
65
- <VStack gap={3} hAlign="stretch">
66
- <Button
67
- label="Login with Apple"
68
- variant="secondary"
69
- icon={<AppleIcon />}
70
- size="lg"
71
- />
72
- <Button
73
- label="Login with Google"
74
- variant="secondary"
75
- icon={<GoogleIcon />}
76
- size="lg"
77
- />
78
- </VStack>
79
- </AuthCard>
80
- </VStack>
81
- </Center>
82
- );
65
+ {/* Social buttons */}
66
+ <VStack gap={3} hAlign="stretch">
67
+ <Button label="Login with Apple" variant="secondary" icon={<AppleIcon />} size="lg" />
68
+ <Button label="Login with Google" variant="secondary" icon={<GoogleIcon />} size="lg" />
69
+ </VStack>
70
+ </AuthCard>
71
+ </VStack>
72
+ </Center>
73
+ );
83
74
  }
@@ -1,6 +1,7 @@
1
1
  export default {
2
- type: 'page',
3
- name: 'Login Split',
4
- description: 'Two-column auth with cover art panel',
5
- category: 'auth',
2
+ type: "page",
3
+ name: "Login Split",
4
+ description: "Two-column auth with cover art panel",
5
+ keywords: ["login", "sign in", "auth", "split", "credentials", "account"],
6
+ category: "auth",
6
7
  };
@@ -1,51 +1,53 @@
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
- import {useState, useTransition} from 'react';
6
- import {authPageStyle, inputAutoComplete} from 'astryx-dracula/shared/auth-chrome-config';
7
- import {demoLogin} from 'astryx-dracula/shared/login-demo';
8
- import {AppleIcon, GoogleIcon} from 'astryx-dracula/shared/sso-icons';
7
+ import { Button } from "@astryxdesign/core/Button";
8
+ import { Card } from "@astryxdesign/core/Card";
9
+ import { Center } from "@astryxdesign/core/Center";
10
+ import { Divider } from "@astryxdesign/core/Divider";
11
+ import { Grid } from "@astryxdesign/core/Grid";
12
+ import { Icon } from "@astryxdesign/core/Icon";
13
+ import { HStack, StackItem, VStack } from "@astryxdesign/core/Layout";
14
+ import { Link } from "@astryxdesign/core/Link";
15
+ import { Section } from "@astryxdesign/core/Section";
16
+ import { Heading, Text } from "@astryxdesign/core/Text";
17
+ import { TextInput } from "@astryxdesign/core/TextInput";
18
+ import { authPageStyle, inputAutoComplete } from "astryx-dracula/shared/auth-chrome-config";
9
19
  import {
10
- AUTH_HEADING,
11
- AUTH_SUBTITLE,
12
- AUTH_PRIMARY_CTA,
13
- AUTH_SSO_DIVIDER,
14
- AUTH_FORGOT_PASSWORD,
15
- AUTH_ERROR_MESSAGE,
16
- AUTH_EMAIL_PLACEHOLDER,
17
- AUTH_PASSWORD_PLACEHOLDER,
18
- AUTH_BRAND_NAME,
19
- AUTH_SIGNUP_PROMPT,
20
- AUTH_SIGNUP_LINK,
21
- AUTH_TERMS_PREFIX,
22
- AUTH_TERMS_SERVICE,
23
- AUTH_TERMS_PRIVACY,
24
- } from 'astryx-dracula/shared/auth-copy';
25
- import {SceneCastle} from 'astryx-dracula/shared/scene-castle';
26
- import {VStack, HStack, StackItem} from '@astryxdesign/core/Layout';
27
- import {Grid} from '@astryxdesign/core/Grid';
28
- import {Center} from '@astryxdesign/core/Center';
29
- import {Card} from '@astryxdesign/core/Card';
30
- import {Section} from '@astryxdesign/core/Section';
31
- import {Text, Heading} from '@astryxdesign/core/Text';
32
- import {Icon} from '@astryxdesign/core/Icon';
33
- import {TextInput} from '@astryxdesign/core/TextInput';
34
- import {Button} from '@astryxdesign/core/Button';
35
- import {Link} from '@astryxdesign/core/Link';
36
- import {Divider} from '@astryxdesign/core/Divider';
20
+ AUTH_BRAND_NAME,
21
+ AUTH_EMAIL_PLACEHOLDER,
22
+ AUTH_ERROR_MESSAGE,
23
+ AUTH_FORGOT_PASSWORD,
24
+ AUTH_HEADING,
25
+ AUTH_PASSWORD_PLACEHOLDER,
26
+ AUTH_PRIMARY_CTA,
27
+ AUTH_SIGNUP_LINK,
28
+ AUTH_SIGNUP_PROMPT,
29
+ AUTH_SSO_DIVIDER,
30
+ AUTH_SUBTITLE,
31
+ AUTH_TERMS_PREFIX,
32
+ AUTH_TERMS_PRIVACY,
33
+ AUTH_TERMS_SERVICE,
34
+ } from "astryx-dracula/shared/auth-copy";
37
35
 
36
+ import { demoLogin } from "astryx-dracula/shared/login-demo";
37
+ import { SceneCastle } from "astryx-dracula/shared/scene-castle";
38
+ import { AppleIcon, GoogleIcon } from "astryx-dracula/shared/sso-icons";
38
39
  // ============= ICONS (verified lucide-react exports) =============
39
40
  // Moon is the castle brand mark (see login, login-card).
40
- import {Moon} from 'lucide-react';
41
+ import { Moon } from "lucide-react";
42
+ import { useState, useTransition } from "react";
41
43
 
42
44
  // Cover art lives in shared/scene-castle (`rooftops` variant:
43
45
  // crescent-overlay moon over a rooftop skyline). The transparent Card clips
44
46
  // it to rounded corners (overflow:clip + radius), so the art needs no radius.
45
47
  const cardWrap = {
46
- width: '100%',
47
- maxWidth: 1000,
48
- marginInline: 'auto',
48
+ width: "100%",
49
+ maxWidth: 1000,
50
+ marginInline: "auto",
49
51
  } as const;
50
52
 
51
53
  // The container query lives in a plain <style> tag so it needs NO CSS compiler.
@@ -79,177 +81,174 @@ const LOGIN_SPLIT_CSS = `
79
81
  `;
80
82
 
81
83
  export default function LoginSplit() {
82
- const [email, setEmail] = useState('');
83
- const [password, setPassword] = useState('');
84
- const [error, setError] = useState<string | null>(null);
85
- const [isLoading, startTransition] = useTransition();
84
+ const [email, setEmail] = useState("");
85
+ const [password, setPassword] = useState("");
86
+ const [error, setError] = useState<string | null>(null);
87
+ const [isLoading, startTransition] = useTransition();
86
88
 
87
- const handleLogin = () => {
88
- if (!email || !password) {
89
- setError(AUTH_ERROR_MESSAGE);
90
- return;
91
- }
92
- setError(null);
93
- startTransition(async () => {
94
- await demoLogin();
95
- });
96
- };
89
+ const handleLogin = () => {
90
+ if (!email || !password) {
91
+ setError(AUTH_ERROR_MESSAGE);
92
+ return;
93
+ }
94
+ setError(null);
95
+ startTransition(async () => {
96
+ await demoLogin();
97
+ });
98
+ };
97
99
 
98
- return (
99
- <Center axis="both" style={authPageStyle}>
100
- <style>{LOGIN_SPLIT_CSS}</style>
101
- <VStack gap={4} width="100%">
102
- <Center axis="horizontal" style={cardWrap}>
103
- <Card padding={0} width="100%">
104
- <Grid
105
- columns={{minWidth: 240, repeat: 'fit'}}
106
- gap={8}
107
- align="stretch"
108
- className="login-split-grid">
109
- {/* Form */}
110
- <Section variant="transparent" padding={0} height="100%">
111
- <VStack gap={4} height="100%">
112
- <HStack gap={2} vAlign="center">
113
- <Icon icon={Moon} color="secondary" size="lg" />
114
- <Text type="body" weight="semibold">
115
- {AUTH_BRAND_NAME}
116
- </Text>
117
- </HStack>
100
+ return (
101
+ <Center axis="both" style={authPageStyle}>
102
+ <style>{LOGIN_SPLIT_CSS}</style>
103
+ <VStack gap={4} width="100%">
104
+ <Center axis="horizontal" style={cardWrap}>
105
+ <Card padding={0} width="100%">
106
+ <Grid
107
+ columns={{ minWidth: 240, repeat: "fit" }}
108
+ gap={8}
109
+ align="stretch"
110
+ className="login-split-grid"
111
+ >
112
+ {/* Form */}
113
+ <Section variant="transparent" padding={0} height="100%">
114
+ <VStack gap={4} height="100%">
115
+ <HStack gap={2} vAlign="center">
116
+ <Icon icon={Moon} color="secondary" size="lg" />
117
+ <Text type="body" weight="semibold">
118
+ {AUTH_BRAND_NAME}
119
+ </Text>
120
+ </HStack>
118
121
 
119
- <StackItem size="fill">
120
- <Center axis="vertical" height="100%">
121
- <VStack gap={4} hAlign="stretch" width="100%">
122
- <VStack gap={1}>
123
- <Heading level={1} type="display-2">
124
- {AUTH_HEADING}
125
- </Heading>
126
- <Text type="body" color="secondary">
127
- {AUTH_SUBTITLE}
128
- </Text>
129
- </VStack>
122
+ <StackItem size="fill">
123
+ <Center axis="vertical" height="100%">
124
+ <VStack gap={4} hAlign="stretch" width="100%">
125
+ <VStack gap={1}>
126
+ <Heading level={1} type="display-2">
127
+ {AUTH_HEADING}
128
+ </Heading>
129
+ <Text type="body" color="secondary">
130
+ {AUTH_SUBTITLE}
131
+ </Text>
132
+ </VStack>
130
133
 
131
- <VStack gap={2}>
132
- <TextInput
133
- label="Email"
134
- isLabelHidden
135
- type="email"
136
- {...inputAutoComplete('email')}
137
- placeholder={AUTH_EMAIL_PLACEHOLDER}
138
- value={email}
139
- onChange={(v: string) => {
140
- setEmail(v);
141
- setError(null);
142
- }}
143
- size="lg"
144
- onEnter={handleLogin}
145
- status={error ? {type: 'error', message: error} : undefined}
146
- />
147
- <VStack gap={1}>
148
- <TextInput
149
- label="Password"
150
- isLabelHidden
151
- placeholder={AUTH_PASSWORD_PLACEHOLDER}
152
- type="password"
153
- {...inputAutoComplete('current-password')}
154
- value={password}
155
- onChange={(v: string) => {
156
- setPassword(v);
157
- setError(null);
158
- }}
159
- size="lg"
160
- onEnter={handleLogin}
161
- status={error ? {type: 'error', message: error} : undefined}
162
- />
163
- {error && (
164
- <VStack hAlign="end">
165
- <Link
166
- href="#/templates/login-split"
167
- isStandalone>
168
- {AUTH_FORGOT_PASSWORD}
169
- </Link>
170
- </VStack>
171
- )}
172
- </VStack>
173
- </VStack>
134
+ <VStack gap={2}>
135
+ <TextInput
136
+ label="Email"
137
+ isLabelHidden
138
+ type="email"
139
+ {...inputAutoComplete("email")}
140
+ placeholder={AUTH_EMAIL_PLACEHOLDER}
141
+ value={email}
142
+ onChange={(v: string) => {
143
+ setEmail(v);
144
+ setError(null);
145
+ }}
146
+ size="lg"
147
+ onEnter={handleLogin}
148
+ status={error ? { type: "error", message: error } : undefined}
149
+ />
150
+ <VStack gap={1}>
151
+ <TextInput
152
+ label="Password"
153
+ isLabelHidden
154
+ placeholder={AUTH_PASSWORD_PLACEHOLDER}
155
+ type="password"
156
+ {...inputAutoComplete("current-password")}
157
+ value={password}
158
+ onChange={(v: string) => {
159
+ setPassword(v);
160
+ setError(null);
161
+ }}
162
+ size="lg"
163
+ onEnter={handleLogin}
164
+ status={error ? { type: "error", message: error } : undefined}
165
+ />
166
+ {error && (
167
+ <VStack hAlign="end">
168
+ <Link href="#/templates/login-split" isStandalone>
169
+ {AUTH_FORGOT_PASSWORD}
170
+ </Link>
171
+ </VStack>
172
+ )}
173
+ </VStack>
174
+ </VStack>
174
175
 
175
- <Button
176
- label={AUTH_PRIMARY_CTA}
177
- variant="primary"
178
- size="lg"
179
- isLoading={isLoading}
180
- onClick={handleLogin}
181
- />
176
+ <Button
177
+ label={AUTH_PRIMARY_CTA}
178
+ variant="primary"
179
+ size="lg"
180
+ isLoading={isLoading}
181
+ onClick={handleLogin}
182
+ />
182
183
 
183
- <Divider label={AUTH_SSO_DIVIDER} />
184
+ <Divider label={AUTH_SSO_DIVIDER} />
184
185
 
185
- {/* minWidth (not a fixed 2-up) so the pair stacks inside
186
+ {/* minWidth (not a fixed 2-up) so the pair stacks inside
186
187
  the narrow single-column container instead of
187
188
  giving each button ~114px for an icon + label. */}
188
- <Grid
189
- columns={{minWidth: 200, repeat: 'fit'}}
190
- gap={3}
191
- justify="stretch">
192
- <Button
193
- label="Login with Apple"
194
- variant="secondary"
195
- icon={<AppleIcon />}
196
- size="lg"
197
- />
198
- <Button
199
- label="Login with Google"
200
- variant="secondary"
201
- icon={<GoogleIcon />}
202
- size="lg"
203
- />
204
- </Grid>
205
- </VStack>
206
- </Center>
207
- </StackItem>
189
+ <Grid columns={{ minWidth: 200, repeat: "fit" }} gap={3} justify="stretch">
190
+ <Button
191
+ label="Login with Apple"
192
+ variant="secondary"
193
+ icon={<AppleIcon />}
194
+ size="lg"
195
+ />
196
+ <Button
197
+ label="Login with Google"
198
+ variant="secondary"
199
+ icon={<GoogleIcon />}
200
+ size="lg"
201
+ />
202
+ </Grid>
203
+ </VStack>
204
+ </Center>
205
+ </StackItem>
208
206
 
209
- {/* Prose link: colour alone is not a cue inside a sentence
207
+ {/* Prose link: colour alone is not a cue inside a sentence
210
208
  (WCAG 1.4.1 / F73), so it asks for the underline the kit
211
209
  only gives on hover. The VStack centres the line, as the
212
210
  other three login templates do — it was sitting flush
213
211
  left under a left-aligned form. */}
214
- <VStack hAlign="center">
215
- <Text type="supporting" color="secondary">
216
- {AUTH_SIGNUP_PROMPT}{' '}
217
- <Link href="#/templates/login-split" type="supporting" hasUnderline>
218
- {AUTH_SIGNUP_LINK}
219
- </Link>
220
- </Text>
221
- </VStack>
222
- </VStack>
223
- </Section>
212
+ <VStack hAlign="center">
213
+ <Text type="supporting" color="secondary">
214
+ {AUTH_SIGNUP_PROMPT}{" "}
215
+ <Link href="#/templates/login-split" type="supporting" hasUnderline>
216
+ {AUTH_SIGNUP_LINK}
217
+ </Link>
218
+ </Text>
219
+ </VStack>
220
+ </VStack>
221
+ </Section>
224
222
 
225
- {/* Cover art — the transparent Card clips it to rounded
223
+ {/* Cover art — the transparent Card clips it to rounded
226
224
  corners (overflow:clip + radius), so the art needs no radius. */}
227
- <Card
228
- variant="transparent"
229
- padding={0}
230
- width="100%"
231
- height="100%"
232
- className="login-split-image">
233
- <SceneCastle variant="rooftops" />
234
- </Card>
235
- </Grid>
236
- </Card>
237
- </Center>
225
+ <Card
226
+ variant="transparent"
227
+ padding={0}
228
+ width="100%"
229
+ height="100%"
230
+ className="login-split-image"
231
+ >
232
+ <SceneCastle variant="rooftops" />
233
+ </Card>
234
+ </Grid>
235
+ </Card>
236
+ </Center>
238
237
 
239
- <VStack hAlign="center" width="100%">
240
- <Text type="supporting" color="secondary" justify="center">
241
- {AUTH_TERMS_PREFIX}{' '}
242
- <Link href="#/templates/login-split" type="supporting" hasUnderline>
243
- {AUTH_TERMS_SERVICE}
244
- </Link>{' '}
245
- and{' '}
246
- <Link href="#/templates/login-split" type="supporting" hasUnderline>
247
- {AUTH_TERMS_PRIVACY}
248
- </Link>
249
- .
250
- </Text>
251
- </VStack>
252
- </VStack>
253
- </Center>
254
- );
238
+ <VStack hAlign="center" width="100%">
239
+ <Text type="supporting" color="secondary" justify="center">
240
+ {AUTH_TERMS_PREFIX}{" "}
241
+ <Link href="#/templates/login-split" type="supporting" hasUnderline>
242
+ {AUTH_TERMS_SERVICE}
243
+ </Link>{" "}
244
+ and{" "}
245
+ <Link href="#/templates/login-split" type="supporting" hasUnderline>
246
+ {AUTH_TERMS_PRIVACY}
247
+ </Link>
248
+ .
249
+ </Text>
250
+ </VStack>
251
+ </VStack>
252
+ </Center>
253
+ );
255
254
  }
@@ -1,6 +1,7 @@
1
1
  export default {
2
- type: 'page',
3
- name: 'Login SSO',
4
- description: 'Email-first SSO with password fallback',
5
- category: 'auth',
2
+ type: "page",
3
+ name: "Login SSO",
4
+ description: "Email-first SSO with password fallback",
5
+ keywords: ["login", "sso", "saml", "oauth", "auth", "providers", "account"],
6
+ category: "auth",
6
7
  };