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.
- package/BRAND.md +3 -3
- package/README.md +7 -4
- package/USAGE.md +49 -49
- package/astryx-dracula.d.ts +2 -2
- package/astryx-dracula.js +25 -9
- package/astryx-dracula.variants.d.ts +2 -2
- package/astryx.integration.mjs +1 -1
- package/icons.mjs +21 -17
- package/icons.tsx +69 -62
- package/package.json +125 -120
- package/shared/auth-chrome-config.ts +6 -6
- package/shared/auth-chrome.tsx +143 -144
- package/shared/auth-copy.ts +14 -14
- package/shared/chaptered-doc-config.ts +48 -51
- package/shared/chaptered-doc.tsx +267 -289
- package/shared/chart-hues.ts +25 -10
- package/shared/chart-labels.tsx +31 -30
- package/shared/chart-legend.tsx +55 -56
- package/shared/chart-panel-style.ts +3 -3
- package/shared/data-bar.tsx +97 -102
- package/shared/format-money.ts +1 -1
- package/shared/gallery-image.ts +6 -6
- package/shared/login-demo.ts +1 -1
- package/shared/metric-card.tsx +101 -105
- package/shared/metric-delta.tsx +28 -27
- package/shared/order-desk.tsx +205 -216
- package/shared/revenue-chart.tsx +145 -165
- package/shared/scene-castle.tsx +206 -291
- package/shared/scene-frame.tsx +33 -32
- package/shared/scene-hues.ts +17 -17
- package/shared/scene-tile.tsx +88 -92
- package/shared/settings-data.ts +119 -125
- package/shared/settings-rows.tsx +122 -159
- package/shared/sparkline.tsx +75 -88
- package/shared/sso-icons.tsx +10 -22
- package/shared/stars.tsx +16 -25
- package/templates/ai-chat-landing.template.mjs +5 -4
- package/templates/ai-chat-landing.tsx +473 -460
- package/templates/ai-chat.template.mjs +5 -4
- package/templates/ai-chat.tsx +543 -612
- package/templates/blank.template.mjs +5 -4
- package/templates/blank.tsx +18 -14
- package/templates/centered-hero.template.mjs +5 -4
- package/templates/centered-hero.tsx +55 -68
- package/templates/classic-gallery.template.mjs +5 -4
- package/templates/classic-gallery.tsx +97 -104
- package/templates/contact-form.template.mjs +5 -4
- package/templates/contact-form.tsx +247 -282
- package/templates/dashboard-portfolio.template.mjs +5 -4
- package/templates/dashboard-portfolio.tsx +717 -548
- package/templates/dashboard.template.mjs +5 -4
- package/templates/dashboard.tsx +596 -567
- package/templates/detail-page.template.mjs +5 -4
- package/templates/detail-page.tsx +546 -603
- package/templates/documentation-design.template.mjs +5 -4
- package/templates/documentation-design.tsx +231 -243
- package/templates/documentation-technical.template.mjs +5 -4
- package/templates/documentation-technical.tsx +236 -239
- package/templates/documentation.template.mjs +5 -4
- package/templates/documentation.tsx +267 -279
- package/templates/editor.template.mjs +5 -4
- package/templates/editor.tsx +844 -877
- package/templates/file-explorer.template.mjs +5 -4
- package/templates/file-explorer.tsx +566 -590
- package/templates/form-two-column.template.mjs +5 -4
- package/templates/form-two-column.tsx +195 -210
- package/templates/gallery-hero.template.mjs +5 -4
- package/templates/gallery-hero.tsx +93 -114
- package/templates/ide.template.mjs +5 -4
- package/templates/ide.tsx +407 -453
- package/templates/incident-console.template.mjs +5 -4
- package/templates/incident-console.tsx +593 -575
- package/templates/kanban-board.template.mjs +5 -4
- package/templates/kanban-board.tsx +665 -683
- package/templates/library.template.mjs +5 -4
- package/templates/library.tsx +409 -430
- package/templates/login-card.template.mjs +5 -4
- package/templates/login-card.tsx +63 -72
- package/templates/login-split.template.mjs +5 -4
- package/templates/login-split.tsx +192 -193
- package/templates/login-sso.template.mjs +5 -4
- package/templates/login-sso.tsx +257 -258
- package/templates/login.template.mjs +5 -4
- package/templates/login.tsx +53 -49
- package/templates/messaging-shell.template.mjs +5 -4
- package/templates/messaging-shell.tsx +556 -574
- package/templates/mixed-gallery.template.mjs +5 -4
- package/templates/mixed-gallery.tsx +60 -58
- package/templates/payment-form.template.mjs +5 -4
- package/templates/payment-form.tsx +882 -991
- package/templates/product-detail.template.mjs +5 -4
- package/templates/product-detail.tsx +261 -272
- package/templates/product-gallery.template.mjs +5 -4
- package/templates/product-gallery.tsx +126 -137
- package/templates/product-tour.template.mjs +5 -4
- package/templates/product-tour.tsx +741 -773
- package/templates/settings-dialog.template.mjs +5 -4
- package/templates/settings-dialog.tsx +688 -717
- package/templates/settings-sidebar.template.mjs +5 -4
- package/templates/settings-sidebar.tsx +762 -807
- package/templates/settings.template.mjs +5 -4
- package/templates/settings.tsx +289 -304
- package/templates/shell-nav.template.mjs +5 -4
- package/templates/shell-nav.tsx +212 -219
- package/templates/shell-side-nav.template.mjs +5 -4
- package/templates/shell-side-nav.tsx +135 -157
- package/templates/shell-top-nav.template.mjs +5 -4
- package/templates/shell-top-nav.tsx +182 -203
- package/templates/side-gallery.template.mjs +5 -4
- package/templates/side-gallery.tsx +85 -90
- package/templates/table-grouped.template.mjs +5 -4
- package/templates/table-grouped.tsx +1000 -1053
- package/templates/table-page-chart.template.mjs +5 -4
- package/templates/table-page-chart.tsx +400 -398
- package/templates/table-page-heatmap-status.template.mjs +5 -4
- package/templates/table-page-heatmap-status.tsx +483 -512
- package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
- package/templates/table-page-shoe-store-heatmap.tsx +748 -746
- package/templates/table-page.template.mjs +5 -4
- package/templates/table-page.tsx +396 -405
- package/templates/table.template.mjs +5 -4
- package/templates/table.tsx +92 -93
- package/templates/tech-report.template.mjs +5 -4
- package/templates/tech-report.tsx +1316 -1304
- package/templates/theme-showcase.template.mjs +5 -4
- package/templates/theme-showcase.tsx +871 -951
- package/theme.css +35 -77
- package/theme.css.d.ts +9 -0
- package/tokens.css +373 -380
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
// XLE (canonical structure
|
|
3
|
-
//
|
|
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
|
+
// A[cp=0 h=auto @sideNav=(SN[collapsible header=SideNavHeading] > (SNS"Getting started" > (SNI"Introduction"! + SNI"Install")) + (SNS"The brand" > (SNI"Palette" + SNI"Type and shape")) + (SNS"In practice" > (SNI"Components" + SNI"Templates")) + (SNS"Reference" > (SNI"Tokens" + SNI"Migrating"))))] > L[h=auto] > (LC[p=8 !scroll] > V[g=8] > (V[g=2] > (H[g=2 a=center] > MNT + Tx"Documentation"[t=supporting]) + Hd"Introduction"[level=1 t=display-2] + Tx"astryx-dracula v0.6.2"[t=supporting] + SE{Selector}"On this page") + Tx"astryx-dracula is a pure Dracula brand theme"[t=large] + AR[ratio=16/9] + (V[g=8] > (V[g=3] > Hd"What it is"[level=2] + Tx"Four decisions shape everything else"[t=body] + (UL > LI*4)))*2) + D + (H[g=3 j=between a=center] > SI[fill] + B.secondary"Install")) + (LP[!scroll] > Outline)
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Product Tour — a chaptered walkthrough of this theme.
|
|
7
9
|
*
|
|
8
|
-
* Frame-first layout (see `bunx
|
|
10
|
+
* Frame-first layout (see `bunx @astryxdesign/cli docs layout`):
|
|
9
11
|
*
|
|
10
12
|
* Frame: chapter rail 264 (SideNav) | chapter content (fill) | outline 240
|
|
11
13
|
*
|
|
@@ -19,90 +21,87 @@
|
|
|
19
21
|
* and status or metadata rides on Text.
|
|
20
22
|
*/
|
|
21
23
|
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
26
|
-
import {SceneFrame} from 'astryx-dracula/shared/scene-frame';
|
|
27
|
-
|
|
24
|
+
import { Icon } from "@astryxdesign/core/Icon";
|
|
25
|
+
import ChapteredDoc, { ChapterArtFrame } from "astryx-dracula/shared/chaptered-doc";
|
|
26
|
+
import type { ChapterGroup } from "astryx-dracula/shared/chaptered-doc-config";
|
|
27
|
+
import { SceneFrame } from "astryx-dracula/shared/scene-frame";
|
|
28
28
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
Hash,
|
|
30
|
+
LayoutGrid,
|
|
31
|
+
Rocket,
|
|
32
|
+
RotateCw,
|
|
33
|
+
Rows3,
|
|
34
|
+
Sparkles,
|
|
35
|
+
SquareTerminal,
|
|
36
|
+
Type,
|
|
37
|
+
} from "lucide-react";
|
|
38
|
+
import type { ReactNode } from "react";
|
|
39
|
+
|
|
40
|
+
const SELF_HASH = "#/templates/product-tour";
|
|
42
41
|
|
|
43
42
|
// ─── Content ─────────────────────────────────────────────────────────────────
|
|
44
43
|
|
|
45
44
|
const CHAPTER_GROUPS: ChapterGroup[] = [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
45
|
+
{
|
|
46
|
+
title: "Getting started",
|
|
47
|
+
chapters: [
|
|
48
|
+
{
|
|
49
|
+
id: "introduction",
|
|
50
|
+
title: "Introduction",
|
|
51
|
+
icon: Rocket,
|
|
52
|
+
hasArt: true,
|
|
53
|
+
intro:
|
|
54
|
+
"astryx-dracula is a pure Dracula brand theme for Astryx React sites. It pins the official palette, compiles to prebuilt CSS, and costs nothing at runtime. Dark is not a mode here, it is the brand.",
|
|
55
|
+
sections: [
|
|
56
|
+
{
|
|
57
|
+
key: "what-it-is",
|
|
58
|
+
heading: "What it is",
|
|
59
|
+
body: "Four decisions shape everything else in this theme.",
|
|
60
|
+
bullets: [
|
|
61
|
+
"Dark only. The Dracula spec is a dark scheme, so every token pins the same value in both mode slots. No light theme exists or is planned.",
|
|
62
|
+
"Spec exact. Every colour is a hex from the official specification. Nothing is invented, and a new colour need is a brand question, not a one-off value.",
|
|
63
|
+
"Zero runtime cost. The theme compiles to prebuilt CSS, with a runtime injection path for prototyping.",
|
|
64
|
+
"Zero theme dependencies. Fonts and icons are vendored, so adopting the theme installs nothing else.",
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: "how-it-fits",
|
|
69
|
+
heading: "How it fits together",
|
|
70
|
+
body: "One source of truth, three outputs, and a gate that refuses to let them drift.",
|
|
71
|
+
bullets: [
|
|
72
|
+
"astryx-theme.ts is the only place a colour is decided.",
|
|
73
|
+
"theme.css is the compiled result that apps import.",
|
|
74
|
+
"tokens.css is a plain :root fallback for stacks that are not Astryx.",
|
|
75
|
+
"scripts/check.ts enforces the palette and the contrast floors, so drift fails the build instead of shipping.",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "install",
|
|
82
|
+
title: "Install",
|
|
83
|
+
icon: SquareTerminal,
|
|
84
|
+
intro:
|
|
85
|
+
"Three import paths, same hexes. Pick one per site and stay on it. The prebuilt path is the recommended one for an Astryx app.",
|
|
86
|
+
sections: [
|
|
87
|
+
{
|
|
88
|
+
key: "install-package",
|
|
89
|
+
heading: "Install the package",
|
|
90
|
+
body: "The package carries the compiled theme, the plain-CSS fallback, the fonts and the icon registry.",
|
|
91
|
+
code: {
|
|
92
|
+
language: "bash",
|
|
93
|
+
title: "shell",
|
|
94
|
+
source: `bun add astryx-dracula`,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: "wire-the-entry",
|
|
99
|
+
heading: "Wire the entry",
|
|
100
|
+
body: "Import order matters. Core reset and component CSS load first, then the theme tokens, then the compiled theme so its overrides win.",
|
|
101
|
+
code: {
|
|
102
|
+
language: "tsx",
|
|
103
|
+
title: "main.tsx",
|
|
104
|
+
source: `import '@astryxdesign/core/reset.css';
|
|
106
105
|
import '@astryxdesign/core/astryx.css';
|
|
107
106
|
import 'astryx-dracula/tokens.css';
|
|
108
107
|
import 'astryx-dracula/theme.css';
|
|
@@ -112,191 +111,192 @@ import {astryxDraculaTheme} from 'astryx-dracula';
|
|
|
112
111
|
<Theme theme={astryxDraculaTheme} mode="dark">
|
|
113
112
|
<App />
|
|
114
113
|
</Theme>;`,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: "ship-the-fonts",
|
|
118
|
+
heading: "Ship the fonts",
|
|
119
|
+
body: "Copy the package fonts directory into your served static folder. tokens.css declares the font faces, so skipping the copy falls back to a system monospace rather than failing loudly.",
|
|
120
|
+
bullets: [
|
|
121
|
+
"Vite and similar bundlers serve from public/, so copy to public/fonts/.",
|
|
122
|
+
"The theme sets one family, JetBrains Mono, for body, heading and code alike.",
|
|
123
|
+
"If text renders in a system monospace, the copy step is the first thing to check.",
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
title: "The brand",
|
|
132
|
+
chapters: [
|
|
133
|
+
{
|
|
134
|
+
id: "palette",
|
|
135
|
+
title: "Palette",
|
|
136
|
+
icon: Sparkles,
|
|
137
|
+
hasArt: true,
|
|
138
|
+
intro:
|
|
139
|
+
"Fifteen colours carry the entire brand: twelve from the specification and three derived surface steps. Every one is a variable, and none of them is ever written by hand in a component.",
|
|
140
|
+
sections: [
|
|
141
|
+
{
|
|
142
|
+
key: "the-spec",
|
|
143
|
+
heading: "The specification twelve",
|
|
144
|
+
body: "Background, Current Line, Selection and Foreground set the stage. Comment, Cyan, Green, Orange, Pink, Purple, Red and Yellow do the semantic work.",
|
|
145
|
+
bullets: [
|
|
146
|
+
"Current Line doubles as the colour for subtle borders and separators.",
|
|
147
|
+
"Comment and Current Line intentionally share one hex in the spec, which is why they look identical in the strip above.",
|
|
148
|
+
"Purple is reserved for anything tappable, so it never appears as decoration.",
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
key: "surfaces",
|
|
153
|
+
heading: "Surface steps",
|
|
154
|
+
body: "Cards, popovers and shadows come from the spec UI palette rather than from opacity tricks.",
|
|
155
|
+
bullets: [
|
|
156
|
+
"Background Light is the card and surface colour.",
|
|
157
|
+
"Background Lighter is for popovers and floating elements.",
|
|
158
|
+
"Background Dark carries shadows and recessed areas.",
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
key: "derived-lifts",
|
|
163
|
+
heading: "Derived lifts",
|
|
164
|
+
body: "Three text greys are deliberately not spec hexes. The spec Comment colour fails the WCAG AA floor for body text, so these are same-hue lifts that clear it. They are documented as deviations rather than silently invented.",
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: "type-shape",
|
|
170
|
+
title: "Type and shape",
|
|
171
|
+
icon: Type,
|
|
172
|
+
hasArt: true,
|
|
173
|
+
intro:
|
|
174
|
+
"One typeface, a quiet weight range, and borders instead of shadows. The shape language stays flat on purpose.",
|
|
175
|
+
sections: [
|
|
176
|
+
{
|
|
177
|
+
key: "one-family",
|
|
178
|
+
heading: "One family, on purpose",
|
|
179
|
+
body: "JetBrains Mono runs body, heading and code alike. A single family is what makes the brand legible at a glance next to any sans-serif product.",
|
|
180
|
+
bullets: [
|
|
181
|
+
"Roles beat raw sizes: body, supporting, code, large and label.",
|
|
182
|
+
"Nothing meaningful renders below twelve pixels.",
|
|
183
|
+
"Quantities always carry tabular numerals so columns line up.",
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
key: "the-scale",
|
|
188
|
+
heading: "The scale",
|
|
189
|
+
body: "A base of fourteen with a 1.2 ladder generates the steps. Headings take their size from the scale roles, so a level three heading renders the large step rather than a pinned pixel value.",
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
key: "flat-and-crisp",
|
|
193
|
+
heading: "Flat and crisp",
|
|
194
|
+
body: "Five pixel radii on elements and four on inner surfaces. Depth comes from separator borders, never from soft grey shadows, and there are no pills or decorative circles.",
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
title: "In practice",
|
|
202
|
+
chapters: [
|
|
203
|
+
{
|
|
204
|
+
id: "components",
|
|
205
|
+
title: "Components",
|
|
206
|
+
icon: Rows3,
|
|
207
|
+
intro:
|
|
208
|
+
"Styling follows one order: reach for a component prop first, then a theme override, and only then a token in a style object. Per-app CSS is not part of the plan.",
|
|
209
|
+
sections: [
|
|
210
|
+
{
|
|
211
|
+
key: "props-first",
|
|
212
|
+
heading: "Props before styles",
|
|
213
|
+
body: "Every Astryx component already reads the theme through its props. Passing a prop keeps the component in charge of its own layout and states; an inline style takes that control away.",
|
|
214
|
+
bullets: [
|
|
215
|
+
"Layout and spacing come from props, never from a wrapper element.",
|
|
216
|
+
"Dense data renders as rows. Wrapping each row in a card is the fastest way to make a product look templated.",
|
|
217
|
+
"Components do the layout. A raw div for structure is a defect, not a shortcut.",
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
key: "status-vocabulary",
|
|
222
|
+
heading: "Status has a fixed vocabulary",
|
|
223
|
+
body: "Green positive, red negative, yellow tags, cyan information, pink flair, orange warning. Status surfaces use a ten percent categorical wash with a semantic border rather than a direct fill.",
|
|
224
|
+
code: {
|
|
225
|
+
language: "tsx",
|
|
226
|
+
title: "Status.tsx",
|
|
227
|
+
source: `// Status rides on the component built for it.
|
|
229
228
|
<StatusDot variant="success" label="Live" />
|
|
230
229
|
|
|
231
230
|
// Counts are what Badge is for. Status is not.
|
|
232
231
|
<Badge label={unread} variant="neutral" />`,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
bunx
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: "purple-means-tappable",
|
|
236
|
+
heading: "Purple means tappable",
|
|
237
|
+
body: "Links, titles and primary actions are purple. Hover resolves to the foreground with an underline, and visited falls back to body text. Users learn the signal in seconds, which is exactly why nothing decorative is allowed to wear it.",
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: "templates",
|
|
243
|
+
title: "Templates",
|
|
244
|
+
icon: LayoutGrid,
|
|
245
|
+
hasArt: true,
|
|
246
|
+
intro:
|
|
247
|
+
"Forty-five themed pages ship with the package as an Astryx integration pack. Each one is a real page, already retokened and structurally validated, and each one scaffolds in a single command.",
|
|
248
|
+
sections: [
|
|
249
|
+
{
|
|
250
|
+
key: "scaffold-a-page",
|
|
251
|
+
heading: "Scaffold a page",
|
|
252
|
+
body: "List the package in your Astryx config, then pull any page into your project.",
|
|
253
|
+
code: {
|
|
254
|
+
language: "bash",
|
|
255
|
+
title: "shell",
|
|
256
|
+
source: `# any of the 45 page ids — the path argument is required; without it the
|
|
257
|
+
# CLI prints the template source and writes nothing
|
|
258
|
+
bunx @astryxdesign/cli template dashboard --package astryx-dracula ./src/pages/dashboard.tsx
|
|
259
|
+
bunx @astryxdesign/cli template product-tour --package astryx-dracula ./src/pages/product-tour.tsx`,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
key: "pack-rules",
|
|
264
|
+
heading: "What the pages may import",
|
|
265
|
+
body: "Each template is self-contained: React, the Astryx core, Lucide icons, and the kit shared modules (astryx-dracula/shared/*). No chart library, no CSS framework, no other internal module. Every chart is a hand-drawn inline SVG, which is why a scaffolded page has no dependency surprises.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
key: "xle-headers",
|
|
269
|
+
heading: "Read the header first",
|
|
270
|
+
body: "Every template leads with a one-line structural expression describing the frame it renders. Reading that line costs a fraction of reading the file, which makes adapting a page much cheaper than rebuilding it. The CLI no longer ships a validator for it — `astryx layout` was removed in 0.7.0 — so it is a reading aid and a human drift check: update it with the file, never alone.",
|
|
271
|
+
code: {
|
|
272
|
+
language: "bash",
|
|
273
|
+
title: "shell",
|
|
274
|
+
source: `# read the structure comment at the top of the template
|
|
275
|
+
head -4 src/pages/dashboard.tsx`,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
title: "Reference",
|
|
284
|
+
chapters: [
|
|
285
|
+
{
|
|
286
|
+
id: "tokens",
|
|
287
|
+
title: "Tokens",
|
|
288
|
+
icon: Hash,
|
|
289
|
+
intro:
|
|
290
|
+
"The theme is a set of custom properties and nothing else. Knowing the four namespaces is enough to build any surface without inventing a value.",
|
|
291
|
+
sections: [
|
|
292
|
+
{
|
|
293
|
+
key: "namespaces",
|
|
294
|
+
heading: "The four namespaces",
|
|
295
|
+
body: "Each namespace answers a different question, and the answer is always a variable.",
|
|
296
|
+
code: {
|
|
297
|
+
language: "css",
|
|
298
|
+
title: "tokens.css",
|
|
299
|
+
source: `/* semantic role, set by the theme */
|
|
300
300
|
color: var(--color-text-primary);
|
|
301
301
|
|
|
302
302
|
/* raw Dracula hex, for art and one-off data colours */
|
|
@@ -307,61 +307,59 @@ padding: var(--spacing-6);
|
|
|
307
307
|
|
|
308
308
|
/* layout and shape */
|
|
309
309
|
border-radius: var(--radius-container);`,
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
key: "never-invent-a-hex",
|
|
314
|
+
heading: "Never invent a hex",
|
|
315
|
+
body: "A new colour need goes through the theme source and the audit, not into a component. Overriding colour variables in an app root is the other half of the same rule: brand changes belong in the theme, so every site that uses it moves together.",
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
key: "audit-gates",
|
|
319
|
+
heading: "The audit gates",
|
|
320
|
+
body: "Two commands stand between a colour decision and a published package.",
|
|
321
|
+
code: {
|
|
322
|
+
language: "bash",
|
|
323
|
+
title: "shell",
|
|
324
|
+
source: `bun run audit # palette purity plus contrast floors
|
|
325
325
|
bun run theme:check # fails when compiled CSS is stale`,
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: "migrating",
|
|
332
|
+
title: "Migrating",
|
|
333
|
+
icon: RotateCw,
|
|
334
|
+
intro:
|
|
335
|
+
"Bringing an existing site onto the theme is an inventory problem before it is a styling problem. Find the colours first, then map them.",
|
|
336
|
+
sections: [
|
|
337
|
+
{
|
|
338
|
+
key: "inventory",
|
|
339
|
+
heading: "Inventory what is there",
|
|
340
|
+
body: "Search for raw hex values, existing root override blocks, utility frameworks and any theme provider already in the tree. The goal is a complete list before a single file is touched.",
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
key: "map-and-replace",
|
|
344
|
+
heading: "Map, then replace",
|
|
345
|
+
body: "Every found colour maps to a row in the brand guide. A colour that will not map is a brand question rather than a new hex, which is the point of doing the inventory first.",
|
|
346
|
+
bullets: [
|
|
347
|
+
"Delete the old provider and the root overrides in the same change that adds the new ones.",
|
|
348
|
+
"Swap raw elements for the components that already carry the layout.",
|
|
349
|
+
"Keep the diff boring: one mechanism per site, not a migration and a redesign at once.",
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
key: "verify",
|
|
354
|
+
heading: "Verify",
|
|
355
|
+
body: "Build, screenshot the key pages, and grep the source for stray hexes. A surviving hex in application code means the mapping was incomplete, not that the theme needed a new colour.",
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
361
|
];
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
|
|
365
363
|
// ─── Scene: the theme at night ───────────────────────────────────────────────
|
|
366
364
|
// Drawn, not loaded. Inline SVG keeps a template dependency-free and paints in
|
|
367
365
|
// theme tokens instead of a fixed palette, so every scene follows the brand.
|
|
@@ -371,191 +369,179 @@ bun run theme:check # fails when compiled CSS is stale`,
|
|
|
371
369
|
// leading edge out to the tip and a scalloped trailing edge back; the second
|
|
372
370
|
// subpath is the same wing mirrored.
|
|
373
371
|
const BAT_WING =
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
372
|
+
"M 2 -4.6 C 5.6 -8.6 12 -11 19.5 -9.6 C 18 -5 16.5 -1.6 14.2 1.2 " +
|
|
373
|
+
"C 13.6 -1.2 12.6 -2.2 11.2 -2.1 C 10.8 0.4 10 2.2 8.4 3.2 " +
|
|
374
|
+
"C 7.9 0.6 7.2 -0.4 6.1 -0.3 C 5.8 1.6 5.2 2.8 4.2 3.6 " +
|
|
375
|
+
"C 3.4 1.4 2.6 -1.6 2 -4.6 Z " +
|
|
376
|
+
"M -2 -4.6 C -5.6 -8.6 -12 -11 -19.5 -9.6 C -18 -5 -16.5 -1.6 -14.2 1.2 " +
|
|
377
|
+
"C -13.6 -1.2 -12.6 -2.2 -11.2 -2.1 C -10.8 0.4 -10 2.2 -8.4 3.2 " +
|
|
378
|
+
"C -7.9 0.6 -7.2 -0.4 -6.1 -0.3 C -5.8 1.6 -5.2 2.8 -4.2 3.6 " +
|
|
379
|
+
"C -3.4 1.4 -2.6 -1.6 -2 -4.6 Z";
|
|
382
380
|
|
|
383
381
|
// Body between the wings: two ears, a shoulder, a taper to the tail.
|
|
384
382
|
const BAT_BODY =
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
383
|
+
"M -2.2 -5 C -2.5 -6.6 -3.2 -8.2 -4 -9.2 L -2.3 -7.6 " +
|
|
384
|
+
"C -1.5 -8.2 -0.8 -8.4 0 -8.4 C 0.8 -8.4 1.5 -8.2 2.3 -7.6 " +
|
|
385
|
+
"L 4 -9.2 C 3.2 -8.2 2.5 -6.6 2.2 -5 C 2.4 -0.8 1.9 2.6 0 6 " +
|
|
386
|
+
"C -1.9 2.6 -2.4 -0.8 -2.2 -5 Z";
|
|
389
387
|
|
|
390
388
|
// A stepped conifer, 20 tall on its base at the origin. Three of these on the
|
|
391
389
|
// near crest give the castle a scale to be measured against.
|
|
392
390
|
const FIR =
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
function Bat({x, y, scale, opacity}: {x: number; y: number; scale: number; opacity: number}) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
391
|
+
"M 0 -20 L 2.6 -13.5 L 1.4 -13.5 L 4.2 -7.5 L 2.8 -7.5 L 5.6 -1.5 " +
|
|
392
|
+
"L -5.6 -1.5 L -2.8 -7.5 L -4.2 -7.5 L -1.4 -13.5 L -2.6 -13.5 Z";
|
|
393
|
+
|
|
394
|
+
function Bat({ x, y, scale, opacity }: { x: number; y: number; scale: number; opacity: number }) {
|
|
395
|
+
return (
|
|
396
|
+
<g
|
|
397
|
+
fill="var(--dracula-comment)"
|
|
398
|
+
opacity={opacity}
|
|
399
|
+
transform={`translate(${x} ${y}) scale(${scale})`}
|
|
400
|
+
>
|
|
401
|
+
<path d={BAT_WING} />
|
|
402
|
+
<path d={BAT_BODY} />
|
|
403
|
+
</g>
|
|
404
|
+
);
|
|
406
405
|
}
|
|
407
406
|
|
|
408
|
-
function Fir({x, y, scale}: {x: number; y: number; scale: number}) {
|
|
409
|
-
|
|
407
|
+
function Fir({ x, y, scale }: { x: number; y: number; scale: number }) {
|
|
408
|
+
return <path d={FIR} transform={`translate(${x} ${y}) scale(${scale})`} />;
|
|
410
409
|
}
|
|
411
410
|
|
|
412
|
-
function NightScene({alt}: {alt: string}) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
411
|
+
function NightScene({ alt }: { alt: string }) {
|
|
412
|
+
return (
|
|
413
|
+
<SceneFrame label={alt} hasBackdrop={false}>
|
|
414
|
+
<defs>
|
|
415
|
+
{/* The moon's halo and the light it drops on the ridge below. Drawn as
|
|
417
416
|
gradients: stacked flat circles band badly at this contrast. */}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
</g>
|
|
469
|
-
<g fill="var(--dracula-cyan)" opacity="0.85">
|
|
470
|
-
<circle cx="166" cy="46" r="1.4" />
|
|
471
|
-
<circle cx="372" cy="22" r="1.2" />
|
|
472
|
-
</g>
|
|
473
|
-
<g fill="var(--dracula-pink)" opacity="0.8">
|
|
474
|
-
<circle cx="94" cy="44" r="1.3" />
|
|
475
|
-
<circle cx="272" cy="94" r="1.1" />
|
|
476
|
-
</g>
|
|
477
|
-
|
|
478
|
-
{/* Harvest moon: halo, disc, then the maria. */}
|
|
479
|
-
<circle cx="306" cy="62" r="74" fill="url(#pt-night-glow)" />
|
|
480
|
-
<circle cx="306" cy="62" r="25" fill="var(--dracula-yellow)" />
|
|
481
|
-
<circle cx="296" cy="53" r="7" fill="var(--dracula-orange)" opacity="0.5" />
|
|
482
|
-
<circle cx="314" cy="71" r="4.5" fill="var(--dracula-orange)" opacity="0.5" />
|
|
483
|
-
<circle cx="310" cy="50" r="3" fill="var(--dracula-orange)" opacity="0.35" />
|
|
484
|
-
|
|
485
|
-
{/* Far ridge, hazed toward the moon. It carries the castle, so it is the
|
|
417
|
+
<radialGradient id="pt-night-glow">
|
|
418
|
+
<stop offset="30%" style={{ stopColor: "var(--dracula-yellow)", stopOpacity: 0.16 }} />
|
|
419
|
+
<stop offset="65%" style={{ stopColor: "var(--dracula-yellow)", stopOpacity: 0.05 }} />
|
|
420
|
+
<stop offset="100%" style={{ stopColor: "var(--dracula-yellow)", stopOpacity: 0 }} />
|
|
421
|
+
</radialGradient>
|
|
422
|
+
<radialGradient id="pt-night-pool">
|
|
423
|
+
<stop offset="0%" style={{ stopColor: "var(--dracula-yellow)", stopOpacity: 0.13 }} />
|
|
424
|
+
<stop offset="55%" style={{ stopColor: "var(--dracula-yellow)", stopOpacity: 0.04 }} />
|
|
425
|
+
<stop offset="100%" style={{ stopColor: "var(--dracula-yellow)", stopOpacity: 0 }} />
|
|
426
|
+
</radialGradient>
|
|
427
|
+
{/* The far ridge doubles as the clip for its own pool of moonlight. */}
|
|
428
|
+
<clipPath id="pt-night-lit">
|
|
429
|
+
<path d="M0 118 Q34 88 78 82 Q124 74 150 78 Q188 84 226 104 Q268 126 318 128 Q360 130 400 122 V225 H0 Z" />
|
|
430
|
+
</clipPath>
|
|
431
|
+
</defs>
|
|
432
|
+
<rect width="400" height="225" fill="var(--dracula-bg-dark)" />
|
|
433
|
+
|
|
434
|
+
{/* Star field. Comment grey, never purple: purple is reserved. */}
|
|
435
|
+
<g fill="var(--dracula-comment)">
|
|
436
|
+
<circle cx="38" cy="34" r="1.5" opacity="0.85" />
|
|
437
|
+
<circle cx="72" cy="58" r="1.1" opacity="0.6" />
|
|
438
|
+
<circle cx="108" cy="26" r="1.3" opacity="0.75" />
|
|
439
|
+
<circle cx="146" cy="72" r="1" opacity="0.5" />
|
|
440
|
+
<circle cx="176" cy="34" r="1.6" opacity="0.9" />
|
|
441
|
+
<circle cx="206" cy="84" r="1.1" opacity="0.55" />
|
|
442
|
+
<circle cx="232" cy="20" r="1.2" opacity="0.7" />
|
|
443
|
+
<circle cx="258" cy="62" r="1" opacity="0.5" />
|
|
444
|
+
<circle cx="120" cy="74" r="1" opacity="0.45" />
|
|
445
|
+
<circle cx="356" cy="30" r="1.3" opacity="0.75" />
|
|
446
|
+
<circle cx="380" cy="86" r="1.1" opacity="0.55" />
|
|
447
|
+
<circle cx="28" cy="72" r="1.2" opacity="0.6" />
|
|
448
|
+
<circle cx="188" cy="60" r="1" opacity="0.45" />
|
|
449
|
+
</g>
|
|
450
|
+
<g fill="var(--dracula-cyan)" opacity="0.85">
|
|
451
|
+
<circle cx="166" cy="46" r="1.4" />
|
|
452
|
+
<circle cx="372" cy="22" r="1.2" />
|
|
453
|
+
</g>
|
|
454
|
+
<g fill="var(--dracula-pink)" opacity="0.8">
|
|
455
|
+
<circle cx="94" cy="44" r="1.3" />
|
|
456
|
+
<circle cx="272" cy="94" r="1.1" />
|
|
457
|
+
</g>
|
|
458
|
+
|
|
459
|
+
{/* Harvest moon: halo, disc, then the maria. */}
|
|
460
|
+
<circle cx="306" cy="62" r="74" fill="url(#pt-night-glow)" />
|
|
461
|
+
<circle cx="306" cy="62" r="25" fill="var(--dracula-yellow)" />
|
|
462
|
+
<circle cx="296" cy="53" r="7" fill="var(--dracula-orange)" opacity="0.5" />
|
|
463
|
+
<circle cx="314" cy="71" r="4.5" fill="var(--dracula-orange)" opacity="0.5" />
|
|
464
|
+
<circle cx="310" cy="50" r="3" fill="var(--dracula-orange)" opacity="0.35" />
|
|
465
|
+
|
|
466
|
+
{/* Far ridge, hazed toward the moon. It carries the castle, so it is the
|
|
486
467
|
lightest plane in the scene. */}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
468
|
+
<path
|
|
469
|
+
d="M0 118 Q34 88 78 82 Q124 74 150 78 Q188 84 226 104 Q268 126 318 128 Q360 130 400 122 V225 H0 Z"
|
|
470
|
+
fill="var(--dracula-selection)"
|
|
471
|
+
/>
|
|
472
|
+
<g clipPath="url(#pt-night-lit)">
|
|
473
|
+
<ellipse
|
|
474
|
+
cx="304"
|
|
475
|
+
cy="182"
|
|
476
|
+
rx="128"
|
|
477
|
+
ry="68"
|
|
478
|
+
fill="url(#pt-night-pool)"
|
|
479
|
+
transform="rotate(-6 304 182)"
|
|
480
|
+
/>
|
|
481
|
+
</g>
|
|
482
|
+
|
|
483
|
+
{/* Castle, a silhouette against the lit ridge: battlements, two towers,
|
|
503
484
|
the keep breaking the skyline, and four windows lit from inside. */}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
485
|
+
<g fill="var(--dracula-bg-dark)">
|
|
486
|
+
<rect x="104" y="124" width="82" height="16" />
|
|
487
|
+
<rect x="127" y="119" width="7" height="5" />
|
|
488
|
+
<rect x="137" y="119" width="7" height="5" />
|
|
489
|
+
<rect x="147" y="119" width="7" height="5" />
|
|
490
|
+
<rect x="157" y="119" width="7" height="5" />
|
|
491
|
+
<rect x="104" y="100" width="22" height="40" />
|
|
492
|
+
<rect x="104.5" y="95" width="7" height="5" />
|
|
493
|
+
<rect x="114.5" y="95" width="7" height="5" />
|
|
494
|
+
<rect x="164" y="96" width="22" height="44" />
|
|
495
|
+
<rect x="164.5" y="91" width="7" height="5" />
|
|
496
|
+
<rect x="174.5" y="91" width="7" height="5" />
|
|
497
|
+
<rect x="132" y="68" width="28" height="72" />
|
|
498
|
+
<rect x="132.5" y="63" width="7" height="5" />
|
|
499
|
+
<rect x="142" y="63" width="7" height="5" />
|
|
500
|
+
<rect x="151.5" y="63" width="7" height="5" />
|
|
501
|
+
</g>
|
|
502
|
+
<g fill="var(--dracula-yellow)">
|
|
503
|
+
<path d="M139 124 v-10 a6 6 0 0 1 12 0 v10 Z" />
|
|
504
|
+
<rect x="142" y="84" width="6" height="8" rx="1" />
|
|
505
|
+
<rect x="110" y="112" width="5" height="8" rx="1" />
|
|
506
|
+
<rect x="173" y="108" width="5" height="8" rx="1" />
|
|
507
|
+
</g>
|
|
508
|
+
<rect
|
|
509
|
+
x="173"
|
|
510
|
+
y="124"
|
|
511
|
+
width="5"
|
|
512
|
+
height="8"
|
|
513
|
+
rx="1"
|
|
514
|
+
fill="var(--dracula-orange)"
|
|
515
|
+
opacity="0.55"
|
|
516
|
+
/>
|
|
517
|
+
|
|
518
|
+
{/* Three planes of ground, each darker than the one behind it. The near
|
|
530
519
|
rise crops the castle's base, which is what puts it at a distance. */}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
<Bat x={352} y={96} scale={0.5} opacity={0.55} />
|
|
557
|
-
</SceneFrame>
|
|
558
|
-
);
|
|
520
|
+
<path
|
|
521
|
+
d="M0 162 Q58 134 120 131 Q182 128 240 150 Q312 176 400 152 V225 H0 Z"
|
|
522
|
+
fill="var(--dracula-bg-light)"
|
|
523
|
+
/>
|
|
524
|
+
<path
|
|
525
|
+
d="M0 196 Q86 176 168 186 Q250 196 322 182 Q366 174 400 184 V225 H0 Z"
|
|
526
|
+
fill="var(--dracula-bg)"
|
|
527
|
+
/>
|
|
528
|
+
<path d="M0 212 Q118 200 236 210 Q324 217 400 208 V225 H0 Z" fill="var(--dracula-bg-dark)" />
|
|
529
|
+
|
|
530
|
+
<g fill="var(--dracula-bg-dark)">
|
|
531
|
+
<Fir x={30} y={149} scale={0.7} />
|
|
532
|
+
<Fir x={48} y={144} scale={0.5} />
|
|
533
|
+
<Fir x={250} y={152} scale={0.7} />
|
|
534
|
+
<Fir x={266} y={156} scale={0.55} />
|
|
535
|
+
<Fir x={286} y={159} scale={0.8} />
|
|
536
|
+
<Fir x={100} y={185} scale={0.95} />
|
|
537
|
+
</g>
|
|
538
|
+
|
|
539
|
+
{/* Bats: nearest is largest and most solid. */}
|
|
540
|
+
<Bat x={252} y={92} scale={1.1} opacity={0.92} />
|
|
541
|
+
<Bat x={178} y={42} scale={0.72} opacity={0.78} />
|
|
542
|
+
<Bat x={352} y={96} scale={0.5} opacity={0.55} />
|
|
543
|
+
</SceneFrame>
|
|
544
|
+
);
|
|
559
545
|
}
|
|
560
546
|
|
|
561
547
|
// ─── Scene: the palette ──────────────────────────────────────────────────────
|
|
@@ -563,63 +549,63 @@ function NightScene({alt}: {alt: string}) {
|
|
|
563
549
|
// Three rows of five: the six surfaces and neutrals, then the nine accents,
|
|
564
550
|
// in the same order as the palette table in the brand guide.
|
|
565
551
|
const PALETTE_ROWS: string[][] = [
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
552
|
+
[
|
|
553
|
+
"var(--dracula-bg)",
|
|
554
|
+
"var(--dracula-current-line)",
|
|
555
|
+
"var(--dracula-selection)",
|
|
556
|
+
"var(--dracula-bg-light)",
|
|
557
|
+
"var(--dracula-bg-lighter)",
|
|
558
|
+
],
|
|
559
|
+
[
|
|
560
|
+
"var(--dracula-bg-dark)",
|
|
561
|
+
"var(--dracula-fg)",
|
|
562
|
+
"var(--dracula-comment)",
|
|
563
|
+
"var(--dracula-cyan)",
|
|
564
|
+
"var(--dracula-green)",
|
|
565
|
+
],
|
|
566
|
+
[
|
|
567
|
+
"var(--dracula-orange)",
|
|
568
|
+
"var(--dracula-pink)",
|
|
569
|
+
"var(--dracula-purple)",
|
|
570
|
+
"var(--dracula-red)",
|
|
571
|
+
"var(--dracula-yellow)",
|
|
572
|
+
],
|
|
587
573
|
];
|
|
588
574
|
|
|
589
|
-
function PaletteScene({alt}: {alt: string}) {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
575
|
+
function PaletteScene({ alt }: { alt: string }) {
|
|
576
|
+
return (
|
|
577
|
+
<SceneFrame label={alt} hasBackdrop={false}>
|
|
578
|
+
<rect width="400" height="225" fill="var(--dracula-bg-dark)" />
|
|
579
|
+
{/* Swatches run edge to edge with one hairline each. Without the stroke
|
|
594
580
|
the three darkest surfaces vanish into a backdrop of the same tone. */}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
581
|
+
{PALETTE_ROWS.map((row, rowIndex) => (
|
|
582
|
+
<g key={row.join("-")}>
|
|
583
|
+
{row.map((fill, columnIndex) => (
|
|
584
|
+
<rect
|
|
585
|
+
key={fill}
|
|
586
|
+
x={13 + columnIndex * 76}
|
|
587
|
+
y={27 + rowIndex * 62}
|
|
588
|
+
width={70}
|
|
589
|
+
height={50}
|
|
590
|
+
rx={6}
|
|
591
|
+
fill={fill}
|
|
592
|
+
stroke="var(--dracula-comment)"
|
|
593
|
+
strokeOpacity={0.55}
|
|
594
|
+
/>
|
|
595
|
+
))}
|
|
596
|
+
</g>
|
|
597
|
+
))}
|
|
598
|
+
</SceneFrame>
|
|
599
|
+
);
|
|
614
600
|
}
|
|
615
601
|
|
|
616
602
|
// ─── Scene: the type scale ───────────────────────────────────────────────────
|
|
617
603
|
|
|
618
604
|
const TYPE_LADDER = [
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
605
|
+
{ label: "h1", size: 30 },
|
|
606
|
+
{ label: "h2", size: 24 },
|
|
607
|
+
{ label: "body", size: 18 },
|
|
608
|
+
{ label: "supporting", size: 14 },
|
|
623
609
|
];
|
|
624
610
|
|
|
625
611
|
// The specimen is set on a 34px baseline grid and the ladder lands on the same
|
|
@@ -627,99 +613,83 @@ const TYPE_LADDER = [
|
|
|
627
613
|
const TYPE_LEADING = 34;
|
|
628
614
|
const TYPE_BASELINE = 74;
|
|
629
615
|
|
|
630
|
-
function TypeScene({alt}: {alt: string}) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
{step.label}
|
|
695
|
-
</text>
|
|
696
|
-
<text
|
|
697
|
-
x="384"
|
|
698
|
-
y={TYPE_BASELINE + index * TYPE_LEADING}
|
|
699
|
-
fontSize={9}
|
|
700
|
-
textAnchor="end"
|
|
701
|
-
fill="var(--dracula-comment)"
|
|
702
|
-
fillOpacity={0.65}>
|
|
703
|
-
{step.size}
|
|
704
|
-
</text>
|
|
705
|
-
</g>
|
|
706
|
-
))}
|
|
707
|
-
</g>
|
|
708
|
-
</SceneFrame>
|
|
709
|
-
);
|
|
616
|
+
function TypeScene({ alt }: { alt: string }) {
|
|
617
|
+
return (
|
|
618
|
+
<SceneFrame label={alt} hasBackdrop={false}>
|
|
619
|
+
<rect width="400" height="225" fill="var(--dracula-bg-dark)" />
|
|
620
|
+
|
|
621
|
+
{/* One rule per ladder row: the grid is the scale's own rhythm. */}
|
|
622
|
+
<g stroke="var(--dracula-comment)" strokeOpacity={0.3}>
|
|
623
|
+
{TYPE_LADDER.map((step, index) => (
|
|
624
|
+
<path key={step.label} d={`M0 ${TYPE_BASELINE + index * TYPE_LEADING} H400`} />
|
|
625
|
+
))}
|
|
626
|
+
</g>
|
|
627
|
+
{/* Specimen on the left of the rule, the ladder on the right. */}
|
|
628
|
+
<path d="M206 52 V192" stroke="var(--dracula-comment)" strokeOpacity={0.22} />
|
|
629
|
+
|
|
630
|
+
<g fontFamily="var(--font-family-mono)">
|
|
631
|
+
<text x="20" y={TYPE_BASELINE} fontSize={52} fill="var(--dracula-fg)">
|
|
632
|
+
Aa
|
|
633
|
+
</text>
|
|
634
|
+
<text x="98" y={TYPE_BASELINE} fontSize={30} fill="var(--dracula-cyan)">
|
|
635
|
+
{"{}"}
|
|
636
|
+
</text>
|
|
637
|
+
<text x="152" y={TYPE_BASELINE} fontSize={20} fill="var(--dracula-pink)">
|
|
638
|
+
=>
|
|
639
|
+
</text>
|
|
640
|
+
<text x="20" y={TYPE_BASELINE + TYPE_LEADING} fontSize={15} fill="var(--dracula-comment)">
|
|
641
|
+
0O1lI| 5S 2Z
|
|
642
|
+
</text>
|
|
643
|
+
<text x="20" y={TYPE_BASELINE + TYPE_LEADING * 2} fontSize={15} fill="var(--dracula-green)">
|
|
644
|
+
0123456789
|
|
645
|
+
</text>
|
|
646
|
+
<text
|
|
647
|
+
x="20"
|
|
648
|
+
y={TYPE_BASELINE + TYPE_LEADING * 3}
|
|
649
|
+
fontSize={15}
|
|
650
|
+
fill="var(--dracula-orange)"
|
|
651
|
+
>
|
|
652
|
+
$ % @ # <-> []
|
|
653
|
+
</text>
|
|
654
|
+
|
|
655
|
+
{TYPE_LADDER.map((step, index) => (
|
|
656
|
+
<g key={step.label}>
|
|
657
|
+
<text
|
|
658
|
+
x="224"
|
|
659
|
+
y={TYPE_BASELINE + index * TYPE_LEADING}
|
|
660
|
+
fontSize={step.size}
|
|
661
|
+
fill={index === 0 ? "var(--dracula-fg)" : "var(--dracula-comment)"}
|
|
662
|
+
>
|
|
663
|
+
{step.label}
|
|
664
|
+
</text>
|
|
665
|
+
<text
|
|
666
|
+
x="384"
|
|
667
|
+
y={TYPE_BASELINE + index * TYPE_LEADING}
|
|
668
|
+
fontSize={9}
|
|
669
|
+
textAnchor="end"
|
|
670
|
+
fill="var(--dracula-comment)"
|
|
671
|
+
fillOpacity={0.65}
|
|
672
|
+
>
|
|
673
|
+
{step.size}
|
|
674
|
+
</text>
|
|
675
|
+
</g>
|
|
676
|
+
))}
|
|
677
|
+
</g>
|
|
678
|
+
</SceneFrame>
|
|
679
|
+
);
|
|
710
680
|
}
|
|
711
681
|
|
|
712
682
|
// ─── Scene: the page grid ────────────────────────────────────────────────────
|
|
713
683
|
|
|
714
684
|
const PAGE_CELLS = [
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
685
|
+
{ x: 24, y: 28, isAccent: true },
|
|
686
|
+
{ x: 116, y: 28, isAccent: false },
|
|
687
|
+
{ x: 208, y: 28, isAccent: false },
|
|
688
|
+
{ x: 300, y: 28, isAccent: false },
|
|
689
|
+
{ x: 24, y: 118, isAccent: false },
|
|
690
|
+
{ x: 116, y: 118, isAccent: false },
|
|
691
|
+
{ x: 208, y: 118, isAccent: false },
|
|
692
|
+
{ x: 300, y: 118, isAccent: false },
|
|
723
693
|
];
|
|
724
694
|
|
|
725
695
|
const PAGE_LINES = [0, 1, 2];
|
|
@@ -728,167 +698,165 @@ const PAGE_LINES = [0, 1, 2];
|
|
|
728
698
|
// body blocks that give the layout its shape, and copy rows on the PAGE_LINES
|
|
729
699
|
// rhythm. Eight different pages, so the grid reads as a set of layouts.
|
|
730
700
|
const PAGE_LAYOUTS: ReadonlyArray<{
|
|
731
|
-
|
|
732
|
-
|
|
701
|
+
blocks: ReadonlyArray<readonly [number, number, number, number]>;
|
|
702
|
+
rows: { x: number; y: number; width: number; count: number };
|
|
733
703
|
}> = [
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
704
|
+
{ blocks: [[10, 22, 56, 16]], rows: { x: 10, y: 44, width: 56, count: 3 } },
|
|
705
|
+
{
|
|
706
|
+
blocks: [
|
|
707
|
+
[40, 22, 26, 30],
|
|
708
|
+
[10, 58, 56, 8],
|
|
709
|
+
],
|
|
710
|
+
rows: { x: 10, y: 24, width: 26, count: 3 },
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
blocks: [
|
|
714
|
+
[10, 22, 26, 15],
|
|
715
|
+
[40, 22, 26, 15],
|
|
716
|
+
[10, 41, 26, 15],
|
|
717
|
+
[40, 41, 26, 15],
|
|
718
|
+
],
|
|
719
|
+
rows: { x: 10, y: 62, width: 56, count: 1 },
|
|
720
|
+
},
|
|
721
|
+
{ blocks: [[10, 22, 20, 6]], rows: { x: 10, y: 34, width: 56, count: 3 } },
|
|
722
|
+
{
|
|
723
|
+
blocks: [
|
|
724
|
+
[10, 24, 5, 5],
|
|
725
|
+
[10, 36, 5, 5],
|
|
726
|
+
[10, 48, 5, 5],
|
|
727
|
+
],
|
|
728
|
+
rows: { x: 19, y: 24, width: 47, count: 3 },
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
blocks: [
|
|
732
|
+
[10, 22, 56, 9],
|
|
733
|
+
[10, 35, 56, 9],
|
|
734
|
+
[10, 50, 26, 9],
|
|
735
|
+
],
|
|
736
|
+
rows: { x: 42, y: 50, width: 24, count: 1 },
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
blocks: [
|
|
740
|
+
[10, 22, 16, 48],
|
|
741
|
+
[32, 58, 34, 8],
|
|
742
|
+
],
|
|
743
|
+
rows: { x: 32, y: 24, width: 34, count: 3 },
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
blocks: [
|
|
747
|
+
[10, 22, 56, 9],
|
|
748
|
+
[10, 35, 30, 6],
|
|
749
|
+
[44, 35, 22, 6],
|
|
750
|
+
[10, 45, 30, 6],
|
|
751
|
+
[44, 45, 22, 6],
|
|
752
|
+
[10, 55, 30, 6],
|
|
753
|
+
[44, 55, 22, 6],
|
|
754
|
+
],
|
|
755
|
+
rows: { x: 10, y: 65, width: 26, count: 1 },
|
|
756
|
+
},
|
|
787
757
|
];
|
|
788
758
|
|
|
789
|
-
function PagesScene({alt}: {alt: string}) {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
</SceneFrame>
|
|
848
|
-
);
|
|
759
|
+
function PagesScene({ alt }: { alt: string }) {
|
|
760
|
+
return (
|
|
761
|
+
<SceneFrame label={alt} fill="var(--dracula-bg)">
|
|
762
|
+
{PAGE_CELLS.map((cell, index) => {
|
|
763
|
+
const layout = PAGE_LAYOUTS[index];
|
|
764
|
+
// Cyan marks the page being viewed; purple stays reserved for
|
|
765
|
+
// controls, so the art never borrows it.
|
|
766
|
+
const accent = cell.isAccent ? "var(--dracula-cyan)" : "var(--dracula-comment)";
|
|
767
|
+
return (
|
|
768
|
+
<g key={`${cell.x}-${cell.y}`} transform={`translate(${cell.x} ${cell.y})`}>
|
|
769
|
+
<rect
|
|
770
|
+
x="0.5"
|
|
771
|
+
y="0.5"
|
|
772
|
+
width="75"
|
|
773
|
+
height="75"
|
|
774
|
+
rx="5"
|
|
775
|
+
fill="var(--dracula-bg-dark)"
|
|
776
|
+
stroke={accent}
|
|
777
|
+
strokeOpacity={cell.isAccent ? 1 : 0.55}
|
|
778
|
+
/>
|
|
779
|
+
<rect
|
|
780
|
+
x="10"
|
|
781
|
+
y="11"
|
|
782
|
+
width="40"
|
|
783
|
+
height="6"
|
|
784
|
+
rx="2"
|
|
785
|
+
fill={accent}
|
|
786
|
+
fillOpacity={cell.isAccent ? 1 : 0.5}
|
|
787
|
+
/>
|
|
788
|
+
{layout.blocks.map(([x, y, width, height]) => (
|
|
789
|
+
<rect
|
|
790
|
+
key={`${x}-${y}`}
|
|
791
|
+
x={x}
|
|
792
|
+
y={y}
|
|
793
|
+
width={width}
|
|
794
|
+
height={height}
|
|
795
|
+
rx="2"
|
|
796
|
+
fill={accent}
|
|
797
|
+
fillOpacity={cell.isAccent ? 0.35 : 0.5}
|
|
798
|
+
/>
|
|
799
|
+
))}
|
|
800
|
+
{PAGE_LINES.slice(0, layout.rows.count).map((line) => (
|
|
801
|
+
<rect
|
|
802
|
+
key={line}
|
|
803
|
+
x={layout.rows.x}
|
|
804
|
+
y={layout.rows.y + line * 12}
|
|
805
|
+
width={layout.rows.width}
|
|
806
|
+
height="5"
|
|
807
|
+
rx="2"
|
|
808
|
+
fill="var(--dracula-comment)"
|
|
809
|
+
fillOpacity={0.5}
|
|
810
|
+
/>
|
|
811
|
+
))}
|
|
812
|
+
</g>
|
|
813
|
+
);
|
|
814
|
+
})}
|
|
815
|
+
</SceneFrame>
|
|
816
|
+
);
|
|
849
817
|
}
|
|
850
818
|
|
|
851
|
-
type ProductTourScene = {node: (alt: string) => ReactNode; alt: string};
|
|
819
|
+
type ProductTourScene = { node: (alt: string) => ReactNode; alt: string };
|
|
852
820
|
|
|
853
821
|
const PRODUCT_TOUR_SCENES: Record<string, ProductTourScene> = {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
822
|
+
introduction: {
|
|
823
|
+
node: (alt) => <NightScene alt={alt} />,
|
|
824
|
+
alt: "A full moon over a castle on the ridge, with bats crossing a lit sky",
|
|
825
|
+
},
|
|
826
|
+
palette: {
|
|
827
|
+
node: (alt) => <PaletteScene alt={alt} />,
|
|
828
|
+
alt: "Fifteen palette swatches: six surfaces and neutrals, then the accent colours",
|
|
829
|
+
},
|
|
830
|
+
"type-shape": {
|
|
831
|
+
node: (alt) => <TypeScene alt={alt} />,
|
|
832
|
+
alt: "A type specimen: monospace glyphs on a baseline grid beside the type scale ladder",
|
|
833
|
+
},
|
|
834
|
+
templates: {
|
|
835
|
+
node: (alt) => <PagesScene alt={alt} />,
|
|
836
|
+
alt: "Eight page thumbnails in a grid, the first one highlighted",
|
|
837
|
+
},
|
|
870
838
|
};
|
|
871
839
|
|
|
872
|
-
function ChapterArt({chapterId}: {chapterId: string}) {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
840
|
+
function ChapterArt({ chapterId }: { chapterId: string }) {
|
|
841
|
+
const scene = PRODUCT_TOUR_SCENES[chapterId];
|
|
842
|
+
if (scene == null) {
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
return <ChapterArtFrame>{scene.node(scene.alt)}</ChapterArtFrame>;
|
|
878
846
|
}
|
|
879
847
|
|
|
880
848
|
export default function ProductTour() {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
849
|
+
return (
|
|
850
|
+
<ChapteredDoc
|
|
851
|
+
groups={CHAPTER_GROUPS}
|
|
852
|
+
defaultChapter="introduction"
|
|
853
|
+
railHeading="astryx-dracula"
|
|
854
|
+
railSubheading="Dracula theme for Astryx"
|
|
855
|
+
railHref={SELF_HASH}
|
|
856
|
+
railIcon={<Icon icon={Sparkles} size="sm" />}
|
|
857
|
+
eyebrow="Documentation"
|
|
858
|
+
badge={() => "astryx-dracula v0.6.2"}
|
|
859
|
+
art={(chapterId) => <ChapterArt chapterId={chapterId} />}
|
|
860
|
+
/>
|
|
861
|
+
);
|
|
894
862
|
}
|