@sableui/react 0.1.0 → 0.2.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/CHANGELOG.md +66 -0
- package/dist/{_contract-CYScOdf5.d.ts → _contract-o3tUc8Ig.d.ts} +1 -1
- package/dist/{chunk-J23ELQQZ.js → chunk-AOWBYEQ6.js} +4177 -1917
- package/dist/{chunk-RWP4XBSK.js → chunk-KFZ43RKU.js} +1 -1
- package/dist/{chunk-JI7WJP2L.js → chunk-X276MF2W.js} +5 -0
- package/dist/foundations/index.js +1 -1
- package/dist/index.d.ts +155 -101
- package/dist/index.js +582 -2778
- package/dist/ios/index.d.ts +6 -1
- package/dist/ios/index.js +4 -4
- package/dist/ios/preview.js +1 -1
- package/dist/ios/styles.css +20 -6
- package/dist/patterns/index.js +1683 -469
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.js +1 -1
- package/dist/styles.css +238 -40
- package/dist/{theme-provider-BWcizhAt.d.ts → theme-provider-BE-KqUG-.d.ts} +1 -1
- package/dist/themes/atelier/atelier.css +3 -0
- package/dist/themes/atelier/index.d.ts +1 -1
- package/dist/themes/base/base.css +11 -4
- package/dist/themes/base/index.d.ts +1 -1
- package/dist/themes/ember/ember.css +3 -0
- package/dist/themes/ember/index.d.ts +1 -1
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/meridian/index.d.ts +1 -1
- package/dist/themes/meridian/meridian.css +10 -5
- package/dist/themes/poppy/index.d.ts +1 -1
- package/dist/themes/poppy/poppy.css +16 -0
- package/dist/themes/prism/index.d.ts +1 -1
- package/dist/themes/prism/prism.css +4 -1
- package/dist/themes/verdant/index.d.ts +1 -1
- package/dist/themes/verdant/verdant.css +7 -2
- package/llms/components/bubble.md +5 -5
- package/llms/components/checkbox-card.md +1 -1
- package/llms/components/context-card.md +2 -2
- package/llms/components/dialog.md +1 -1
- package/llms/components/empty-state.md +4 -4
- package/llms/components/entity.md +1 -1
- package/llms/components/fieldset.md +1 -1
- package/llms/components/kbd.md +18 -6
- package/llms/components/pill.md +1 -1
- package/llms/components/side-nav.md +1 -1
- package/llms/components/skeleton.md +1 -1
- package/llms/components/theme-selector.md +1 -1
- package/llms/foundations/shape.md +1 -1
- package/llms/patterns/auth-page.md +132 -42
- package/llms/patterns/brand-landing-page.md +271 -175
- package/llms/patterns/chat-panel.md +120 -39
- package/llms/patterns/dashboard-page.md +277 -65
- package/llms/patterns/feed-page.md +245 -64
- package/llms/patterns/landing-page.md +473 -75
- package/llms/patterns/list-detail-page.md +385 -68
- package/llms/patterns/settings-page.md +279 -67
- package/llms/reference/token-intent.md +4 -2
- package/llms/reference/utilities.md +20 -5
- package/llms-full.txt +2244 -727
- package/llms.txt +14 -15
- package/package.json +1 -1
- package/registry.json +261 -238
- package/targets/ios.json +4 -15
- package/llms/components/description.md +0 -96
|
@@ -1,67 +1,157 @@
|
|
|
1
1
|
# AuthPage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Split sign-in page: a form column (brand mark, heading, email + password, primary sign-in, passkey and wallet alternatives, sign-up switch, terms) beside a brand panel painted from the theme's tones with a featured release card. Below lg the panel drops away and the form is the whole screen.
|
|
4
4
|
|
|
5
5
|
**Intent:** Reach for this for sign-in, sign-up, forgot-password, and any single-purpose one-column form route (invite accept, unsubscribe). Not for multi-section forms (use SettingsPage).
|
|
6
6
|
|
|
7
7
|
## Notes
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- The form column is a `form`-capped Region (480px), so the controls keep the same measure whether the panel is present or not.
|
|
10
|
+
- No card around the form: the page itself is the surface, and the brand panel carries the identity. A card on a blank page reads as a template.
|
|
11
|
+
- The heading is `text-heading-1` in an h1 — it is the page outline, so it takes a heading tier, not a card title.
|
|
12
|
+
- Brand mark, form and terms are a three-part column: mark pinned to the top, form vertically centred in the remaining space, terms pinned to the bottom.
|
|
13
|
+
- Forgot password sits under the password field, right-aligned, before the submit button — so it comes right after the field in tab order. Field has no label-row action slot yet.
|
|
14
|
+
- One primary action. Passkey and wallet are outline buttons under an `or` Separator; switching to sign-up is a quiet `link-underline` link.
|
|
15
|
+
- Validation uses Field `validate` for product copy instead of the browser's generic required message. A rejected sign-in comes back through Form `errors` on the password field; the submit Button shows `loading` while the request is in flight.
|
|
16
|
+
- The brand panel artwork is a mesh of the theme's decorative tones over `--primary`, so it re-paints for every theme and mode. The relative-colour `oklch(from var(--tone-*) …)` lift needs an `inline-color` disable with a reason.
|
|
17
|
+
- The panel is `hidden lg:flex`: on a phone the form is the whole screen, with no decorative scroll above it.
|
|
14
18
|
|
|
15
19
|
## Accessibility
|
|
16
20
|
|
|
17
|
-
- The route renders inside <main> with
|
|
18
|
-
- Field auto-wires labels, describedby, and invalid state onto the inputs.
|
|
19
|
-
- Autocomplete attributes (email
|
|
21
|
+
- The route renders inside <main> with one h1; the brand panel is an <aside> labelled "Featured release".
|
|
22
|
+
- Field auto-wires labels, describedby, and invalid state onto the inputs; on submit, the first invalid field is focused.
|
|
23
|
+
- Autocomplete attributes (`email webauthn`, `current-password`) keep password managers and passkey autofill working.
|
|
24
|
+
- The Forgot password and Create one links use `hit-target` to meet the 44px touch floor without changing layout.
|
|
25
|
+
- The artwork swatches are aria-hidden decoration; the brand link's accessible name is the wordmark.
|
|
20
26
|
|
|
21
27
|
## Source
|
|
22
28
|
|
|
23
29
|
```tsx
|
|
24
|
-
import {
|
|
30
|
+
import { useState } from "react"
|
|
31
|
+
import { Avatar, Badge, Button, Card, CardContent, Entity, Field, Form, Icon, Input, Region, Separator, Stack } from "@sableui/react"
|
|
32
|
+
|
|
33
|
+
// A soft mesh of the theme's decorative tones over its brand colour, so the
|
|
34
|
+
// artwork follows every theme and mode. Each tone is lifted from its text
|
|
35
|
+
// strength (dark, restrained) to an art strength.
|
|
36
|
+
const artwork = [
|
|
37
|
+
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
38
|
+
"radial-gradient(in oklab at 18% 22%, oklch(from var(--tone-info) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent 58%)",
|
|
39
|
+
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
40
|
+
"radial-gradient(in oklab at 82% 18%, oklch(from var(--tone-collectible) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent 55%)",
|
|
41
|
+
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
42
|
+
"radial-gradient(in oklab at 70% 88%, oklch(from var(--tone-edition) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent 60%)",
|
|
43
|
+
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
44
|
+
"radial-gradient(in oklab at 12% 92%, oklch(from var(--tone-standard) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent 55%)",
|
|
45
|
+
"var(--primary)",
|
|
46
|
+
].join(", ")
|
|
25
47
|
|
|
26
48
|
export function AuthPage() {
|
|
49
|
+
const [pending, setPending] = useState(false)
|
|
50
|
+
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
51
|
+
|
|
27
52
|
return (
|
|
28
|
-
<main className="grid min-h-dvh
|
|
29
|
-
<Region max="form" safeArea={["top", "bottom"]}>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
<main className="grid min-h-dvh bg-background lg:grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)]">
|
|
54
|
+
<Region max="form" safeArea={["top", "bottom"]} className="flex flex-col">
|
|
55
|
+
{/* safeArea owns the region's own padding, so the breathing room sits one level in. */}
|
|
56
|
+
<Stack gap={0} className="flex-1 py-8 sm:py-10">
|
|
57
|
+
<a href="/" className="flex items-center gap-2.5 self-start rounded-sm motion-interactive motion-press">
|
|
58
|
+
{/* The same mark the app shell's sidebar and header carry. */}
|
|
59
|
+
<Icon name="gem" variant="solid" className="text-2xl" />
|
|
60
|
+
<span className="text-title-lg">Atelier</span>
|
|
61
|
+
</a>
|
|
62
|
+
|
|
63
|
+
<Stack gap={4} justify="center" className="flex-1 py-12">
|
|
64
|
+
<Stack gap={1.5}>
|
|
65
|
+
<h1 className="text-heading-1">Welcome back</h1>
|
|
66
|
+
<p className="text-body-lg text-muted-foreground">
|
|
67
|
+
Sign in to collect new releases and manage your editions.
|
|
68
|
+
</p>
|
|
69
|
+
</Stack>
|
|
70
|
+
|
|
71
|
+
<Form
|
|
72
|
+
className="gap-5"
|
|
73
|
+
errors={errors}
|
|
74
|
+
onFormSubmit={() => {
|
|
75
|
+
setPending(true)
|
|
76
|
+
// Replace with your auth call; a rejected sign-in comes back as a field error.
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
setPending(false)
|
|
79
|
+
setErrors({ password: "That email and password don’t match. Try again or reset your password." })
|
|
80
|
+
}, 900)
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
<Field label="Email" name="email" validate={(value) => (value ? null : "Enter your email address.")}>
|
|
84
|
+
<Input type="email" autoComplete="email webauthn" placeholder="you@example.com" />
|
|
85
|
+
</Field>
|
|
86
|
+
<Stack gap={1.5}>
|
|
87
|
+
<Field label="Password" name="password" validate={(value) => (value ? null : "Enter your password.")}>
|
|
88
|
+
<Input type="password" autoComplete="current-password" />
|
|
46
89
|
</Field>
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
</
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
<a href="/forgot-password" className="hit-target self-end text-body-sm text-muted-foreground link-underline motion-interactive">
|
|
91
|
+
Forgot password?
|
|
92
|
+
</a>
|
|
93
|
+
</Stack>
|
|
94
|
+
<Button type="submit" size="cta" loading={pending} loadingLabel="Signing in" className="w-full">
|
|
95
|
+
Sign in
|
|
96
|
+
</Button>
|
|
97
|
+
</Form>
|
|
98
|
+
|
|
99
|
+
<Separator label="or" />
|
|
100
|
+
|
|
101
|
+
<Stack gap={1.5}>
|
|
102
|
+
<Button variant="outline" size="cta" className="w-full">
|
|
103
|
+
Sign in with a passkey
|
|
104
|
+
</Button>
|
|
105
|
+
<Button variant="outline" size="cta" className="w-full">
|
|
106
|
+
Connect a wallet
|
|
107
|
+
</Button>
|
|
108
|
+
</Stack>
|
|
109
|
+
|
|
110
|
+
<p className="text-body-md text-muted-foreground">
|
|
111
|
+
No account?{" "}
|
|
112
|
+
<a href="/signup" className="hit-target link-underline motion-interactive text-foreground">
|
|
113
|
+
Create one
|
|
114
|
+
</a>
|
|
115
|
+
</p>
|
|
116
|
+
</Stack>
|
|
117
|
+
|
|
118
|
+
<p className="text-body-xs text-muted-foreground">
|
|
119
|
+
By signing in you agree to the{" "}
|
|
120
|
+
<a href="/terms" className="link-underline motion-interactive">
|
|
121
|
+
Terms
|
|
122
|
+
</a>{" "}
|
|
123
|
+
and{" "}
|
|
124
|
+
<a href="/privacy" className="link-underline motion-interactive">
|
|
125
|
+
Privacy Policy
|
|
61
126
|
</a>
|
|
127
|
+
.
|
|
62
128
|
</p>
|
|
63
129
|
</Stack>
|
|
64
130
|
</Region>
|
|
131
|
+
|
|
132
|
+
{/* The brand panel is decoration: on a phone the form is the whole screen. */}
|
|
133
|
+
<aside aria-label="Featured release" className="hidden p-3 lg:flex">
|
|
134
|
+
<div className="flex flex-1 flex-col justify-end rounded-xl p-8" style={{ background: artwork }}>
|
|
135
|
+
<Card className="max-w-md">
|
|
136
|
+
<CardContent>
|
|
137
|
+
<Stack gap={3}>
|
|
138
|
+
<Badge tone="edition" className="self-start">
|
|
139
|
+
Edition of 50
|
|
140
|
+
</Badge>
|
|
141
|
+
<p className="text-title-lg">
|
|
142
|
+
“Every edition is a conversation with whoever holds it next.”
|
|
143
|
+
</p>
|
|
144
|
+
<Entity
|
|
145
|
+
className="p-0"
|
|
146
|
+
leading={<Avatar fallback="MO" />}
|
|
147
|
+
title="Mara Okafor"
|
|
148
|
+
subtitle="Untitled (gold rush), 2026"
|
|
149
|
+
/>
|
|
150
|
+
</Stack>
|
|
151
|
+
</CardContent>
|
|
152
|
+
</Card>
|
|
153
|
+
</div>
|
|
154
|
+
</aside>
|
|
65
155
|
</main>
|
|
66
156
|
)
|
|
67
157
|
}
|