@sableui/react 0.1.1 → 0.3.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/AGENTS.md +3 -1
- package/CHANGELOG.md +120 -0
- package/CONTRIBUTING.md +8 -0
- package/README.md +1 -0
- package/bin/sable-lint.mjs +0 -0
- package/bin/sable.mjs +68 -26
- package/dist/{_contract-CYScOdf5.d.ts → _contract-o3tUc8Ig.d.ts} +1 -1
- package/dist/blocks/index.d.ts +396 -0
- package/dist/blocks/index.js +1997 -0
- package/dist/chunk-6LCIBMMG.js +4908 -0
- package/dist/{chunk-J23ELQQZ.js → chunk-CKNM7KSB.js} +1664 -4270
- package/dist/chunk-K6LII3OE.js +5093 -0
- package/dist/{chunk-RWP4XBSK.js → chunk-KFZ43RKU.js} +1 -1
- package/dist/{chunk-PAOZBZME.js → chunk-NGBDP3BM.js} +1 -1
- package/dist/{chunk-5Z5KLOAU.js → chunk-TH563WLW.js} +14 -1
- package/dist/{chunk-JI7WJP2L.js → chunk-X276MF2W.js} +5 -0
- package/dist/foundations/index.js +1 -1
- package/dist/icon/index.js +2 -2
- package/dist/icon-sets/reicon.js +1 -1
- package/dist/index.d.ts +624 -133
- package/dist/index.js +767 -4964
- package/dist/ios/index.d.ts +14 -2
- package/dist/ios/index.js +10 -9
- package/dist/ios/preview.d.ts +11 -1
- package/dist/ios/preview.js +14 -9
- package/dist/ios/styles.css +26 -6
- package/dist/{meta-6dUzgC6M.d.ts → meta-CcE_SeFu.d.ts} +37 -1
- package/dist/patterns/index.d.ts +1 -1
- package/dist/patterns/index.js +1684 -471
- 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 +34 -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/index.js +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/blocks/api-keys-block.md +79 -0
- package/llms/blocks/chat-thread-block.md +51 -0
- package/llms/blocks/command-palette-block.md +48 -0
- package/llms/blocks/cta-band-block.md +62 -0
- package/llms/blocks/danger-zone-block.md +69 -0
- package/llms/blocks/device-comparison-block.md +91 -0
- package/llms/blocks/event-card-block.md +84 -0
- package/llms/blocks/faq-block.md +72 -0
- package/llms/blocks/feature-grid-block.md +75 -0
- package/llms/blocks/feedback-form-block.md +66 -0
- package/llms/blocks/goal-progress-block.md +54 -0
- package/llms/blocks/hero-block.md +65 -0
- package/llms/blocks/invite-team-block.md +61 -0
- package/llms/blocks/invoice-history-block.md +84 -0
- package/llms/blocks/kpi-sparkline-block.md +57 -0
- package/llms/blocks/media-player-block.md +100 -0
- package/llms/blocks/newsletter-block.md +64 -0
- package/llms/blocks/not-found-block.md +56 -0
- package/llms/blocks/notification-list-block.md +63 -0
- package/llms/blocks/notification-settings-block.md +54 -0
- package/llms/blocks/order-tracking-block.md +62 -0
- package/llms/blocks/pricing-card-block.md +82 -0
- package/llms/blocks/pricing-table-block.md +129 -0
- package/llms/blocks/product-card-block.md +69 -0
- package/llms/blocks/profile-card-block.md +103 -0
- package/llms/blocks/profile-form-block.md +114 -0
- package/llms/blocks/revenue-breakdown-block.md +70 -0
- package/llms/blocks/sign-in-block.md +64 -0
- package/llms/blocks/sign-up-block.md +78 -0
- package/llms/blocks/site-footer-block.md +96 -0
- package/llms/blocks/stat-row-block.md +59 -0
- package/llms/blocks/steps-block.md +73 -0
- package/llms/blocks/task-list-block.md +81 -0
- package/llms/blocks/team-members-block.md +91 -0
- package/llms/blocks/testimonial-block.md +78 -0
- package/llms/blocks/traffic-sources-block.md +63 -0
- package/llms/blocks/traffic-trend-block.md +95 -0
- package/llms/blocks/upload-list-block.md +66 -0
- package/llms/blocks/verify-code-block.md +59 -0
- package/llms/blocks/wallet-block.md +84 -0
- package/llms/components/area-chart.md +112 -0
- package/llms/components/bar-chart.md +131 -0
- package/llms/components/bubble.md +5 -5
- package/llms/components/button.md +1 -1
- package/llms/components/card.md +2 -2
- package/llms/components/checkbox-card.md +1 -1
- package/llms/components/context-card.md +2 -2
- package/llms/components/context-menu.md +1 -1
- package/llms/components/date-tile.md +79 -0
- package/llms/components/dialog.md +1 -1
- package/llms/components/empty-state.md +4 -4
- package/llms/components/entity.md +5 -3
- package/llms/components/fieldset.md +1 -1
- package/llms/components/kbd.md +18 -6
- package/llms/components/line-chart.md +101 -0
- package/llms/components/menu.md +1 -1
- package/llms/components/{dots-menu.md → overflow-menu.md} +9 -9
- package/llms/components/pie-chart.md +103 -0
- package/llms/components/pill.md +1 -1
- package/llms/components/radar-chart.md +106 -0
- package/llms/components/radial-chart.md +102 -0
- package/llms/components/segmented-bar.md +102 -0
- package/llms/components/settings-menu.md +2 -2
- package/llms/components/side-nav.md +1 -1
- package/llms/components/skeleton.md +1 -1
- package/llms/components/sparkline.md +80 -0
- package/llms/components/table.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 +276 -65
- package/llms/patterns/feed-page.md +245 -64
- package/llms/patterns/landing-page.md +473 -75
- package/llms/patterns/list-detail-page.md +377 -68
- package/llms/patterns/settings-page.md +268 -67
- package/llms/reference/token-intent.md +4 -2
- package/llms/reference/utilities.md +20 -5
- package/llms/tasks/building-an-ios-prototype.md +4 -1
- package/llms/tasks/choosing-a-component.md +2 -2
- package/llms-full.txt +6428 -892
- package/llms.txt +67 -16
- package/package.json +5 -1
- package/registry.json +3217 -552
- package/targets/ios.json +88 -18
- package/llms/components/description.md +0 -96
- package/dist/{chunk-L6Y46C62.js → chunk-MXJX3NZI.js} +1 -1
|
@@ -1,119 +1,517 @@
|
|
|
1
1
|
# LandingPage
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Product / open-source landing page: a centered hero with an eyebrow Badge, one display headline, lede, two CTAs and a copyable install command; a Browser-framed product preview composed from real components; a six-up feature grid; a setup section with tabbed code; an agent-workflow section with terminal output; a CTA card; and a link-column footer.
|
|
4
4
|
|
|
5
|
-
**Intent:** Reach for this for the marketing
|
|
5
|
+
**Intent:** Reach for this for the marketing route of a developer product, library, or tool: the page where the product shows itself working and the reader installs or signs up. For a branded, editorial marketing site use BrandLandingPage. Not for product UI (display type and one-per-view rules forbid it inside the app).
|
|
6
6
|
|
|
7
7
|
## Notes
|
|
8
8
|
|
|
9
|
-
- Display type is the marketing voice: exactly one display headline per page
|
|
10
|
-
- The eyebrow is a small outline Badge (`size="sm"`)
|
|
11
|
-
- The lede is body-lg capped at the prose measure
|
|
12
|
-
- Two CTAs max: one primary, one quiet outline.
|
|
13
|
-
-
|
|
14
|
-
-
|
|
9
|
+
- Display type is the marketing voice: exactly one display headline per page (`text-display-lg` on phones, `text-display-2xl` from `sm`). Every section below uses `text-heading-1` with a `text-overline` kicker.
|
|
10
|
+
- The eyebrow is a small outline Badge (`size="sm"`): the badge's own size, never a type class forced onto its label. Hierarchy above the fold is eyebrow, display, lede, CTAs, install command.
|
|
11
|
+
- The lede is body-lg capped at the prose measure. The headline may be wide; reading text may not.
|
|
12
|
+
- Two CTAs max per row: one primary, one quiet outline. The install line is a `CodeBlock` with `prompt="$"`, so it truncates on one line and copy takes only the command.
|
|
13
|
+
- Show the product instead of describing it. The preview is real components (PageHeader, metric Cards, a Meter, a CardList of Entity rows, Message and Bubble, ChatComposer) inside `Browser`, wrapped in an `inert` div so the picture's controls never take focus. Secondary columns hide on small screens rather than stacking into a long mock.
|
|
14
|
+
- Features are Cards in a fixed `Grid cols={{ base: 1, sm: 2, lg: 3 }}`, so six items always land as full rows (3 x 2, 2 x 3, 1 x 6) without an orphan.
|
|
15
|
+
- Multi-line code uses `CodeBlock` with a filename; switch related snippets with `Tabs`, defaulting to the most complete one. Terminal output passes `highlight={false}` so a code highlighter does not colour prose.
|
|
16
|
+
- Reveal wraps below-the-fold blocks only (features, code, CTA). It no-ops under reduced motion via the motion policy.
|
|
17
|
+
- Hide nav groups on narrow screens by wrapping them in a `max-md:hidden` div: a `Row` or `Button` sets its own display, which a `hidden` class on the same element does not reliably override.
|
|
15
18
|
|
|
16
19
|
## Accessibility
|
|
17
20
|
|
|
18
|
-
- The hero headline is the page h1; feature titles are h2s
|
|
21
|
+
- The hero headline is the page h1; section titles are h2s and feature and step titles are h3s. Footer column labels are h2s inside the footer nav.
|
|
22
|
+
- The product preview is a `figure` with a visible figcaption. Its content is `inert`, which removes it from the tab order and the accessibility tree, so its headings and buttons do not pollute the outline.
|
|
19
23
|
- Reveal renders content immediately (no animation) when prefers-reduced-motion is set.
|
|
20
|
-
- CTA buttons are real links
|
|
24
|
+
- CTA buttons are real links with visible text, with no icon-only CTAs. Footer links keep a 36px minimum row height for touch.
|
|
21
25
|
|
|
22
26
|
## Source
|
|
23
27
|
|
|
24
28
|
```tsx
|
|
25
|
-
import { Badge, Button, Card, CardContent, Grid, Icon, Region, Reveal, Row, Stack } from "@sableui/react"
|
|
29
|
+
import { Avatar, Badge, Browser, Bubble, Button, Card, CardAction, CardContent, CardDescription, CardHeader, CardList, CardTitle, ChatComposer, Code, CodeBlock, Entity, Grid, Icon, Message, Meter, MeterLabel, MeterTrack, MeterValue, PageHeader, Region, Reveal, Row, Stack, Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
|
|
26
30
|
|
|
27
|
-
const
|
|
31
|
+
const navLinks = [
|
|
32
|
+
{ label: "Docs", href: "/docs" },
|
|
33
|
+
{ label: "Components", href: "/components" },
|
|
34
|
+
{ label: "Patterns", href: "/patterns" },
|
|
35
|
+
{ label: "Themes", href: "/themes" },
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const deploys = [
|
|
39
|
+
{
|
|
40
|
+
initials: "MO",
|
|
41
|
+
name: "Maya Ortiz",
|
|
42
|
+
title: "Add usage-based billing",
|
|
43
|
+
meta: "main · a3f9c21 · 2m ago",
|
|
44
|
+
status: "Building",
|
|
45
|
+
tone: "info" as const,
|
|
46
|
+
pulse: true,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
initials: "SR",
|
|
50
|
+
name: "Sam Reyes",
|
|
51
|
+
title: "Fix search ranking for long queries",
|
|
52
|
+
meta: "main · 81be04d · 18m ago",
|
|
53
|
+
status: "Ready",
|
|
54
|
+
tone: "success" as const,
|
|
55
|
+
pulse: false,
|
|
56
|
+
},
|
|
28
57
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
initials: "JL",
|
|
59
|
+
name: "Jun Lee",
|
|
60
|
+
title: "Rotate webhook signing keys",
|
|
61
|
+
meta: "main · 5c7e9aa · 1h ago",
|
|
62
|
+
status: "Ready",
|
|
63
|
+
tone: "success" as const,
|
|
64
|
+
pulse: false,
|
|
32
65
|
},
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
const features = [
|
|
33
69
|
{
|
|
34
70
|
icon: "component" as const,
|
|
35
|
-
title: "Behavior
|
|
36
|
-
body: "Focus, keyboard
|
|
71
|
+
title: "Behavior built in",
|
|
72
|
+
body: "Focus management, keyboard navigation, and ARIA come from Base UI primitives under every control.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
icon: "eyedropper" as const,
|
|
76
|
+
title: "Semantic tokens end to end",
|
|
77
|
+
body: "Re-theme the whole product, light and dark, from one preset without touching a component.",
|
|
37
78
|
},
|
|
38
79
|
{
|
|
39
80
|
icon: "layout" as const,
|
|
40
81
|
title: "Pages, not just parts",
|
|
41
|
-
body: "
|
|
82
|
+
body: "Landing, dashboard, settings, auth, and list-detail shells to copy and adapt.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
icon: "book" as const,
|
|
86
|
+
title: "A contract agents can read",
|
|
87
|
+
body: "registry.json, llms.txt, and a CLI hand agents the exact props, examples, and rules.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
icon: "circle-check" as const,
|
|
91
|
+
title: "Lint that knows the design",
|
|
92
|
+
body: "sable-lint flags raw colors, hand-built controls, and motion that ignores reduced motion.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
icon: "play" as const,
|
|
96
|
+
title: "Motion with an off switch",
|
|
97
|
+
body: "Named recipes run on one scale variable, so reduced motion stops every transition at once.",
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
const steps = [
|
|
102
|
+
{
|
|
103
|
+
title: "Install the package",
|
|
104
|
+
body: "One dependency. React 19 and Tailwind v4 are the only peers.",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
title: "Import the styles",
|
|
108
|
+
body: "Tailwind first, then Sable's tokens, then a source glob over the package.",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
title: "Compose your screens",
|
|
112
|
+
body: "Build from components and layout primitives. Your theme does the rest.",
|
|
113
|
+
},
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
const stylesCode = `@import "tailwindcss";
|
|
117
|
+
@import "@sableui/react/styles.css";
|
|
118
|
+
@source "../node_modules/@sableui/react/dist/**/*.{js,mjs}";`
|
|
119
|
+
|
|
120
|
+
// design-lint-disable-next-line self-import -- a consumer code sample displayed on the page, not an import
|
|
121
|
+
const composeCode = `import { Button, Card, CardContent, CardHeader, CardTitle } from "@sableui/react"
|
|
122
|
+
|
|
123
|
+
export function DeployCard() {
|
|
124
|
+
return (
|
|
125
|
+
<Card>
|
|
126
|
+
<CardHeader>
|
|
127
|
+
<CardTitle>Production</CardTitle>
|
|
128
|
+
</CardHeader>
|
|
129
|
+
<CardContent>
|
|
130
|
+
<Button>Deploy</Button>
|
|
131
|
+
</CardContent>
|
|
132
|
+
</Card>
|
|
133
|
+
)
|
|
134
|
+
}`
|
|
135
|
+
|
|
136
|
+
const terminalCode = `$ npx sable search "confirm before deleting"
|
|
137
|
+
component DestructiveActionModal
|
|
138
|
+
component Dialog
|
|
139
|
+
|
|
140
|
+
$ npx sable component DestructiveActionModal --props
|
|
141
|
+
props, a11y contract, composed examples
|
|
142
|
+
|
|
143
|
+
$ npx sable lint src
|
|
144
|
+
0 findings in 42 files`
|
|
145
|
+
|
|
146
|
+
const agentPoints = [
|
|
147
|
+
"Search ranks components, patterns, tokens, and docs for a plain-language need.",
|
|
148
|
+
"Every component ships its props, accessibility contract, and composed examples.",
|
|
149
|
+
"The same lint runs in the editor, in CI, and inside the agent's own loop.",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
const footerGroups = [
|
|
153
|
+
{
|
|
154
|
+
title: "Product",
|
|
155
|
+
links: [
|
|
156
|
+
{ label: "Components", href: "/components" },
|
|
157
|
+
{ label: "Patterns", href: "/patterns" },
|
|
158
|
+
{ label: "Themes", href: "/themes" },
|
|
159
|
+
{ label: "Canvas", href: "/canvas" },
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
title: "Resources",
|
|
164
|
+
links: [
|
|
165
|
+
{ label: "Documentation", href: "/docs" },
|
|
166
|
+
{ label: "Changelog", href: "/changelog" },
|
|
167
|
+
{ label: "llms.txt", href: "/llms.txt" },
|
|
168
|
+
{ label: "registry.json", href: "/registry.json" },
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
title: "Community",
|
|
173
|
+
links: [
|
|
174
|
+
{ label: "GitHub", href: "/github" },
|
|
175
|
+
{ label: "npm", href: "/npm" },
|
|
176
|
+
{ label: "Discussions", href: "/discussions" },
|
|
177
|
+
],
|
|
42
178
|
},
|
|
43
179
|
]
|
|
44
180
|
|
|
45
181
|
export function LandingPage() {
|
|
46
182
|
return (
|
|
47
|
-
<div className="min-h-dvh bg-background">
|
|
48
|
-
<header>
|
|
49
|
-
<Region as="nav" aria-label="Main" className="grid h-16 grid-cols-[1fr_auto_1fr] items-center">
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
183
|
+
<div className="min-h-dvh bg-background text-foreground">
|
|
184
|
+
<header className="border-b border-border">
|
|
185
|
+
<Region as="nav" aria-label="Main" max="wide" className="grid h-16 grid-cols-[1fr_auto_1fr] items-center">
|
|
186
|
+
<a href="/" className="justify-self-start text-title-lg motion-interactive hover:text-muted-foreground">
|
|
187
|
+
Sable
|
|
188
|
+
</a>
|
|
189
|
+
<div className="justify-self-center max-md:hidden">
|
|
190
|
+
<Row gap={1}>
|
|
191
|
+
{navLinks.map((link) => (
|
|
192
|
+
<Button key={link.label} variant="ghost" size="sm" render={<a href={link.href} />}>
|
|
193
|
+
{link.label}
|
|
194
|
+
</Button>
|
|
195
|
+
))}
|
|
196
|
+
</Row>
|
|
197
|
+
</div>
|
|
198
|
+
<Row gap={1} className="col-start-3 justify-self-end">
|
|
199
|
+
<div className="max-sm:hidden">
|
|
200
|
+
<Button variant="ghost" size="sm" render={<a href="/github" />}>
|
|
201
|
+
GitHub
|
|
202
|
+
</Button>
|
|
203
|
+
</div>
|
|
204
|
+
<Button size="sm" render={<a href="/docs/getting-started" />}>
|
|
205
|
+
Get started
|
|
60
206
|
</Button>
|
|
61
207
|
</Row>
|
|
62
|
-
<Button size="sm" className="justify-self-end" render={<a href="/signup" />}>
|
|
63
|
-
Get started
|
|
64
|
-
</Button>
|
|
65
208
|
</Region>
|
|
66
209
|
</header>
|
|
210
|
+
|
|
67
211
|
<main>
|
|
68
|
-
<Region as="section" max="wide" className="
|
|
69
|
-
<Stack gap={
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
212
|
+
<Region as="section" max="wide" className="pb-16 pt-16 sm:pt-24">
|
|
213
|
+
<Stack gap={8} align="center">
|
|
214
|
+
<Stack gap={4} align="center" className="text-center">
|
|
215
|
+
<Badge appearance="outline" size="sm" icon={<Icon name="sparkles" />}>
|
|
216
|
+
Open source · MIT licensed
|
|
217
|
+
</Badge>
|
|
218
|
+
<h1 className="max-w-4xl text-balance text-display-lg sm:text-display-2xl">
|
|
219
|
+
Product UI your agents can't get wrong
|
|
220
|
+
</h1>
|
|
221
|
+
<p className="max-w-(--measure-prose) text-pretty text-body-lg text-muted-foreground">
|
|
222
|
+
Sable is a themeable React component library with enforced tokens,
|
|
223
|
+
typography, and motion, documented for coding agents as carefully
|
|
224
|
+
as for people.
|
|
225
|
+
</p>
|
|
226
|
+
<Row gap={2} justify="center" wrap className="pt-2">
|
|
227
|
+
<Button size="cta" render={<a href="/docs/getting-started" />}>
|
|
228
|
+
Get started
|
|
229
|
+
</Button>
|
|
230
|
+
<Button size="cta" variant="outline" render={<a href="/components" />}>
|
|
231
|
+
Browse components
|
|
232
|
+
</Button>
|
|
233
|
+
</Row>
|
|
234
|
+
</Stack>
|
|
235
|
+
<div className="w-full max-w-sm">
|
|
236
|
+
<CodeBlock prompt="$" code="pnpm add @sableui/react" />
|
|
237
|
+
</div>
|
|
86
238
|
</Stack>
|
|
87
239
|
</Region>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
240
|
+
|
|
241
|
+
<Region as="section" aria-label="Product preview" max="wide" className="pb-20 sm:pb-28">
|
|
242
|
+
<figure>
|
|
243
|
+
<Browser url="atlas.example/deploys" className="shadow-xl">
|
|
244
|
+
{/* A picture of software, not live UI: inert keeps its controls
|
|
245
|
+
out of the tab order and the accessibility tree. */}
|
|
246
|
+
<div inert className="grid gap-4 p-4 sm:p-6 lg:grid-cols-[minmax(0,1fr)_20rem]">
|
|
247
|
+
<Stack gap={4} className="min-w-0">
|
|
248
|
+
<PageHeader
|
|
249
|
+
titleAs="h2"
|
|
250
|
+
title="Deployments"
|
|
251
|
+
description="atlas-web · production"
|
|
252
|
+
actions={
|
|
253
|
+
<Row gap={2}>
|
|
254
|
+
<Button variant="outline" size="sm">
|
|
255
|
+
View logs
|
|
256
|
+
</Button>
|
|
257
|
+
<Button size="sm" iconStart={<Icon name="upload" />}>
|
|
258
|
+
Deploy
|
|
259
|
+
</Button>
|
|
260
|
+
</Row>
|
|
261
|
+
}
|
|
262
|
+
/>
|
|
263
|
+
<Grid cols={{ base: 2, md: 3 }} gap={3}>
|
|
264
|
+
<Card density="compact">
|
|
265
|
+
<CardHeader>
|
|
266
|
+
<CardTitle>Uptime</CardTitle>
|
|
267
|
+
<CardDescription>Last 30 days</CardDescription>
|
|
268
|
+
</CardHeader>
|
|
269
|
+
<CardContent>
|
|
270
|
+
<p className="text-heading-3">99.98%</p>
|
|
271
|
+
</CardContent>
|
|
272
|
+
</Card>
|
|
273
|
+
<Card density="compact">
|
|
274
|
+
<CardHeader>
|
|
275
|
+
<CardTitle>p95 latency</CardTitle>
|
|
276
|
+
<CardDescription>Edge, all regions</CardDescription>
|
|
277
|
+
</CardHeader>
|
|
278
|
+
<CardContent>
|
|
279
|
+
<p className="text-heading-3">182 ms</p>
|
|
280
|
+
</CardContent>
|
|
281
|
+
</Card>
|
|
282
|
+
<Card density="compact" className="max-md:hidden">
|
|
283
|
+
<CardHeader>
|
|
284
|
+
<CardTitle>Build minutes</CardTitle>
|
|
285
|
+
<CardDescription>Team plan</CardDescription>
|
|
286
|
+
</CardHeader>
|
|
287
|
+
<CardContent>
|
|
288
|
+
<Meter value={64} tone="standard">
|
|
289
|
+
<Row justify="between" wrap className="mb-2">
|
|
290
|
+
<MeterLabel>Used</MeterLabel>
|
|
291
|
+
<MeterValue>{(_, value) => `${(value * 30).toLocaleString("en-US")} of 3,000`}</MeterValue>
|
|
292
|
+
</Row>
|
|
293
|
+
<MeterTrack />
|
|
294
|
+
</Meter>
|
|
295
|
+
</CardContent>
|
|
296
|
+
</Card>
|
|
297
|
+
</Grid>
|
|
298
|
+
<Card>
|
|
299
|
+
<CardHeader>
|
|
300
|
+
<CardTitle>Recent deploys</CardTitle>
|
|
301
|
+
<CardDescription>Production branch</CardDescription>
|
|
302
|
+
<CardAction>
|
|
303
|
+
<Badge appearance="plain" tone="success" dot>
|
|
304
|
+
Healthy
|
|
305
|
+
</Badge>
|
|
306
|
+
</CardAction>
|
|
307
|
+
</CardHeader>
|
|
308
|
+
<CardList>
|
|
309
|
+
{deploys.map((deploy) => (
|
|
310
|
+
<Entity
|
|
311
|
+
key={deploy.meta}
|
|
312
|
+
leading={<Avatar size="sm" fallback={deploy.initials} alt={deploy.name} />}
|
|
313
|
+
title={deploy.title}
|
|
314
|
+
subtitle={deploy.meta}
|
|
315
|
+
action={
|
|
316
|
+
<Badge appearance="plain" tone={deploy.tone} dot pulse={deploy.pulse}>
|
|
317
|
+
{deploy.status}
|
|
318
|
+
</Badge>
|
|
319
|
+
}
|
|
320
|
+
/>
|
|
321
|
+
))}
|
|
322
|
+
</CardList>
|
|
323
|
+
</Card>
|
|
324
|
+
</Stack>
|
|
325
|
+
<Card className="max-lg:hidden">
|
|
326
|
+
<CardHeader>
|
|
327
|
+
<CardTitle>Release assistant</CardTitle>
|
|
328
|
+
<CardDescription>Summaries from today's deploys</CardDescription>
|
|
329
|
+
</CardHeader>
|
|
330
|
+
<CardContent className="flex flex-col gap-4">
|
|
331
|
+
<Message align="end">
|
|
332
|
+
<Bubble variant="primary" align="end">
|
|
333
|
+
What shipped to production today?
|
|
334
|
+
</Bubble>
|
|
335
|
+
</Message>
|
|
336
|
+
<Message>
|
|
337
|
+
<Bubble>
|
|
338
|
+
Three deploys. Search ranking is fixed, webhook keys are
|
|
339
|
+
rotated, and usage-based billing is building now.
|
|
340
|
+
</Bubble>
|
|
341
|
+
</Message>
|
|
342
|
+
<ChatComposer placeholder="Ask about a deploy" />
|
|
99
343
|
</CardContent>
|
|
100
344
|
</Card>
|
|
101
|
-
|
|
102
|
-
</
|
|
345
|
+
</div>
|
|
346
|
+
</Browser>
|
|
347
|
+
<figcaption className="mt-4 text-center text-body-sm text-muted-foreground">
|
|
348
|
+
Every surface in this preview is a Sable component, styled by the active theme.
|
|
349
|
+
</figcaption>
|
|
350
|
+
</figure>
|
|
351
|
+
</Region>
|
|
352
|
+
|
|
353
|
+
<section aria-labelledby="landing-features" className="border-y border-border bg-muted/40 py-20 sm:py-28">
|
|
354
|
+
<Region max="wide">
|
|
355
|
+
<Stack gap={10}>
|
|
356
|
+
<Stack gap={3} className="max-w-2xl">
|
|
357
|
+
<p className="text-overline text-muted-foreground">Why Sable</p>
|
|
358
|
+
<h2 id="landing-features" className="text-balance text-heading-1">
|
|
359
|
+
A design system with the rules written down
|
|
360
|
+
</h2>
|
|
361
|
+
<p className="text-body-lg text-muted-foreground">
|
|
362
|
+
The decisions a senior designer would make are encoded once, so every
|
|
363
|
+
screen your team or your agent builds starts from them.
|
|
364
|
+
</p>
|
|
365
|
+
</Stack>
|
|
366
|
+
<Reveal>
|
|
367
|
+
<Grid cols={{ base: 1, sm: 2, lg: 3 }} gap={4}>
|
|
368
|
+
{features.map((feature) => (
|
|
369
|
+
<Card key={feature.title} render={<article />} className="h-full">
|
|
370
|
+
<CardContent className="flex flex-col gap-3">
|
|
371
|
+
<span className="grid size-10 place-items-center rounded-md sable-well">
|
|
372
|
+
<Icon name={feature.icon} className="text-xl" />
|
|
373
|
+
</span>
|
|
374
|
+
<h3 className="pt-2 text-title-lg">{feature.title}</h3>
|
|
375
|
+
<p className="text-body-sm text-muted-foreground">{feature.body}</p>
|
|
376
|
+
</CardContent>
|
|
377
|
+
</Card>
|
|
378
|
+
))}
|
|
379
|
+
</Grid>
|
|
380
|
+
</Reveal>
|
|
381
|
+
</Stack>
|
|
382
|
+
</Region>
|
|
383
|
+
</section>
|
|
384
|
+
|
|
385
|
+
<Region as="section" aria-labelledby="landing-setup" max="wide" className="py-20 sm:py-28">
|
|
386
|
+
<div className="grid items-start gap-10 lg:grid-cols-[minmax(0,5fr)_minmax(0,7fr)] lg:gap-16">
|
|
387
|
+
<Stack gap={8}>
|
|
388
|
+
<Stack gap={3}>
|
|
389
|
+
<p className="text-overline text-muted-foreground">Setup</p>
|
|
390
|
+
<h2 id="landing-setup" className="text-balance text-heading-1">
|
|
391
|
+
From install to first screen in three steps
|
|
392
|
+
</h2>
|
|
393
|
+
</Stack>
|
|
394
|
+
<ol className="flex flex-col gap-6">
|
|
395
|
+
{steps.map((step, index) => (
|
|
396
|
+
<li key={step.title} className="grid grid-cols-[auto_1fr] gap-4">
|
|
397
|
+
<span className="pt-0.5 text-mono-md text-muted-foreground">0{index + 1}</span>
|
|
398
|
+
<div>
|
|
399
|
+
<h3 className="text-title-lg">{step.title}</h3>
|
|
400
|
+
<p className="mt-1 text-body-md text-muted-foreground">{step.body}</p>
|
|
401
|
+
</div>
|
|
402
|
+
</li>
|
|
403
|
+
))}
|
|
404
|
+
</ol>
|
|
405
|
+
</Stack>
|
|
406
|
+
<Reveal className="min-w-0">
|
|
407
|
+
<Tabs defaultValue="compose">
|
|
408
|
+
<TabsList>
|
|
409
|
+
<TabsTrigger value="install">Install</TabsTrigger>
|
|
410
|
+
<TabsTrigger value="styles">Styles</TabsTrigger>
|
|
411
|
+
<TabsTrigger value="compose">Compose</TabsTrigger>
|
|
412
|
+
</TabsList>
|
|
413
|
+
<TabsPanel value="install" className="pt-4">
|
|
414
|
+
<CodeBlock prompt="$" code="pnpm add @sableui/react" />
|
|
415
|
+
</TabsPanel>
|
|
416
|
+
<TabsPanel value="styles" className="pt-4">
|
|
417
|
+
<CodeBlock filename="app.css" code={stylesCode} />
|
|
418
|
+
</TabsPanel>
|
|
419
|
+
<TabsPanel value="compose" className="pt-4">
|
|
420
|
+
<CodeBlock filename="deploy-card.tsx" code={composeCode} />
|
|
421
|
+
</TabsPanel>
|
|
422
|
+
</Tabs>
|
|
423
|
+
</Reveal>
|
|
424
|
+
</div>
|
|
425
|
+
</Region>
|
|
426
|
+
|
|
427
|
+
<section aria-labelledby="landing-agents" className="border-t border-border py-20 sm:py-28">
|
|
428
|
+
<Region max="wide">
|
|
429
|
+
<div className="grid items-center gap-10 lg:grid-cols-2 lg:gap-16">
|
|
430
|
+
<Stack gap={5}>
|
|
431
|
+
<Stack gap={3}>
|
|
432
|
+
<p className="text-overline text-muted-foreground">Built for agents</p>
|
|
433
|
+
<h2 id="landing-agents" className="text-balance text-heading-1">
|
|
434
|
+
Your agent reads the same contract you do
|
|
435
|
+
</h2>
|
|
436
|
+
<p className="text-body-lg text-muted-foreground">
|
|
437
|
+
Point Claude Code, Codex, or Cursor at the installed package. The{" "}
|
|
438
|
+
<Code>sable</Code> CLI answers what to use, how to use it, and whether
|
|
439
|
+
the result holds up.
|
|
440
|
+
</p>
|
|
441
|
+
</Stack>
|
|
442
|
+
<ul className="sable-list">
|
|
443
|
+
{agentPoints.map((point) => (
|
|
444
|
+
<li key={point}>{point}</li>
|
|
445
|
+
))}
|
|
446
|
+
</ul>
|
|
447
|
+
</Stack>
|
|
448
|
+
<Reveal className="min-w-0">
|
|
449
|
+
<CodeBlock filename="Terminal" code={terminalCode} highlight={false} />
|
|
450
|
+
</Reveal>
|
|
451
|
+
</div>
|
|
452
|
+
</Region>
|
|
453
|
+
</section>
|
|
454
|
+
|
|
455
|
+
<Region as="section" aria-labelledby="landing-cta" max="wide" className="pb-20 sm:pb-28">
|
|
456
|
+
<Reveal>
|
|
457
|
+
<Card>
|
|
458
|
+
<CardContent>
|
|
459
|
+
<Stack gap={4} align="center" className="py-8 text-center sm:py-12">
|
|
460
|
+
<h2 id="landing-cta" className="max-w-2xl text-balance text-heading-1">
|
|
461
|
+
Build the product, not another component library
|
|
462
|
+
</h2>
|
|
463
|
+
<p className="max-w-(--measure-prose) text-body-lg text-muted-foreground">
|
|
464
|
+
Install Sable, pick a preset, and ship the first screen today.
|
|
465
|
+
</p>
|
|
466
|
+
<Row gap={2} justify="center" wrap className="pt-2">
|
|
467
|
+
<Button size="cta" render={<a href="/docs/getting-started" />}>
|
|
468
|
+
Get started
|
|
469
|
+
</Button>
|
|
470
|
+
<Button size="cta" variant="outline" render={<a href="/github" />}>
|
|
471
|
+
Star on GitHub
|
|
472
|
+
</Button>
|
|
473
|
+
</Row>
|
|
474
|
+
</Stack>
|
|
475
|
+
</CardContent>
|
|
476
|
+
</Card>
|
|
103
477
|
</Reveal>
|
|
104
478
|
</Region>
|
|
105
479
|
</main>
|
|
480
|
+
|
|
106
481
|
<footer className="border-t border-border">
|
|
107
|
-
<Region className="
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
482
|
+
<Region max="wide" className="py-12">
|
|
483
|
+
<div className="grid gap-10 md:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]">
|
|
484
|
+
<Stack gap={3} className="max-w-xs">
|
|
485
|
+
<a href="/" className="w-fit text-title-lg motion-interactive hover:text-muted-foreground">
|
|
486
|
+
Sable
|
|
487
|
+
</a>
|
|
488
|
+
<p className="text-body-sm text-muted-foreground">
|
|
489
|
+
React components, patterns, and tokens for products built with AI agents.
|
|
490
|
+
</p>
|
|
491
|
+
</Stack>
|
|
492
|
+
<nav aria-label="Footer" className="grid grid-cols-2 gap-8 sm:grid-cols-3">
|
|
493
|
+
{footerGroups.map((group) => (
|
|
494
|
+
<div key={group.title}>
|
|
495
|
+
<h2 className="text-label-md text-muted-foreground">{group.title}</h2>
|
|
496
|
+
<ul className="mt-3 flex flex-col gap-1">
|
|
497
|
+
{group.links.map((link) => (
|
|
498
|
+
<li key={link.label}>
|
|
499
|
+
<a
|
|
500
|
+
href={link.href}
|
|
501
|
+
className="flex min-h-9 items-center text-body-sm motion-interactive hover:text-muted-foreground"
|
|
502
|
+
>
|
|
503
|
+
{link.label}
|
|
504
|
+
</a>
|
|
505
|
+
</li>
|
|
506
|
+
))}
|
|
507
|
+
</ul>
|
|
508
|
+
</div>
|
|
509
|
+
))}
|
|
510
|
+
</nav>
|
|
511
|
+
</div>
|
|
512
|
+
<div className="mt-12 border-t border-border pt-6">
|
|
513
|
+
<p className="text-caption text-muted-foreground">© 2026 Sable. Released under the MIT license.</p>
|
|
514
|
+
</div>
|
|
117
515
|
</Region>
|
|
118
516
|
</footer>
|
|
119
517
|
</div>
|