astryx-dracula 0.2.1 → 0.3.1
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 +30 -18
- package/README.md +17 -12
- package/USAGE.md +30 -10
- package/astryx-dracula.js +19 -13
- package/astryx-dracula.variants.d.ts +15 -0
- package/package.json +59 -17
- package/shared/auth-chrome-config.ts +18 -0
- package/shared/auth-chrome.tsx +25 -0
- package/shared/auth-copy.ts +22 -0
- package/shared/chaptered-doc-config.ts +92 -0
- package/shared/chaptered-doc.tsx +332 -0
- package/shared/chart-hues.ts +27 -0
- package/shared/chart-labels.tsx +44 -0
- package/shared/chart-legend.tsx +100 -0
- package/shared/chart-panel-style.ts +13 -0
- package/shared/data-bar.tsx +160 -0
- package/shared/gallery-image.ts +20 -0
- package/shared/login-demo.ts +7 -0
- package/shared/metric-delta.tsx +36 -0
- package/shared/order-desk.tsx +273 -0
- package/shared/revenue-chart.tsx +181 -0
- package/shared/scene-castle.tsx +322 -0
- package/shared/scene-frame.tsx +88 -0
- package/shared/scene-hues.ts +49 -0
- package/shared/scene-tile.tsx +128 -0
- package/shared/settings-data.ts +125 -0
- package/shared/settings-rows.tsx +179 -0
- package/shared/sparkline.tsx +102 -0
- package/shared/sso-icons.tsx +30 -0
- package/templates/ai-chat-landing.tsx +66 -33
- package/templates/ai-chat.tsx +36 -19
- package/templates/blank.tsx +4 -3
- package/templates/centered-hero.tsx +14 -125
- package/templates/classic-gallery.tsx +20 -66
- package/templates/contact-form.tsx +59 -39
- package/templates/dashboard-portfolio.tsx +143 -226
- package/templates/dashboard.tsx +122 -122
- package/templates/detail-page.tsx +95 -91
- package/templates/documentation-design.template.mjs +1 -1
- package/templates/documentation-design.tsx +83 -96
- package/templates/documentation-technical.template.mjs +1 -1
- package/templates/documentation-technical.tsx +29 -19
- package/templates/documentation.template.mjs +1 -1
- package/templates/documentation.tsx +52 -74
- package/templates/editor.tsx +112 -84
- package/templates/file-explorer.tsx +131 -84
- package/templates/form-two-column.tsx +61 -125
- package/templates/gallery-hero.tsx +15 -171
- package/templates/ide.tsx +22 -20
- package/templates/incident-console.tsx +104 -70
- package/templates/kanban-board.tsx +99 -42
- package/templates/library.template.mjs +2 -2
- package/templates/library.tsx +37 -42
- package/templates/login-card.tsx +58 -87
- package/templates/login-split.tsx +88 -126
- package/templates/login-sso.tsx +105 -64
- package/templates/login.tsx +107 -53
- package/templates/messaging-shell.tsx +171 -144
- package/templates/mixed-gallery.tsx +29 -130
- package/templates/payment-form.tsx +368 -290
- package/templates/product-detail.tsx +39 -58
- package/templates/product-gallery.tsx +30 -41
- package/templates/product-tour.template.mjs +6 -0
- package/templates/product-tour.tsx +916 -0
- package/templates/settings-dialog.tsx +135 -282
- package/templates/settings-sidebar.tsx +178 -268
- package/templates/settings.tsx +41 -14
- package/templates/shell-nav.tsx +31 -24
- package/templates/shell-side-nav.tsx +29 -20
- package/templates/shell-top-nav.tsx +80 -44
- package/templates/side-gallery.tsx +38 -94
- package/templates/table-grouped.tsx +62 -34
- package/templates/table-page-chart.tsx +23 -302
- package/templates/table-page-heatmap-status.tsx +156 -92
- package/templates/table-page-shoe-store-heatmap.tsx +18 -293
- package/templates/table-page.tsx +23 -7
- package/templates/table.tsx +23 -8
- package/templates/tech-report.template.mjs +6 -0
- package/templates/tech-report.tsx +1486 -0
- package/templates/theme-showcase.template.mjs +1 -1
- package/templates/theme-showcase.tsx +299 -237
- package/theme.css +76 -14
- package/tokens.css +433 -30
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// Shared castle scenes for owned hero/gallery/form/auth surfaces.
|
|
2
|
+
// `wide` is the centered-hero banner (800x450); `card` and `tall` are the
|
|
3
|
+
// gallery-hero castle and pines tiles (400x500); `rooftops` is the
|
|
4
|
+
// login-split cover (400x300, crescent-overlay moon, rooftop skyline).
|
|
5
|
+
// Chevron bats stay foreground stroke (w4 wide, w3 card/rooftops); stars
|
|
6
|
+
// carry a `comment` label so the decorative-purple rule survives refactors.
|
|
7
|
+
// Moons are harvest yellow in every scene. Hills live in per-scene
|
|
8
|
+
// constants below.
|
|
9
|
+
|
|
10
|
+
import {galleryImage} from 'astryx-dracula/shared/gallery-image';
|
|
11
|
+
|
|
12
|
+
export type SceneCastleVariant = 'wide' | 'card' | 'tall' | 'rooftops';
|
|
13
|
+
|
|
14
|
+
const WIDE_HILLS = {
|
|
15
|
+
back: 'M0 330 Q200 260 400 320 T800 300 V450 H0 Z',
|
|
16
|
+
front: 'M0 385 Q240 320 480 375 T800 360 V450 H0 Z',
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
const CARD_HILLS = {
|
|
20
|
+
back: 'M0 360 Q140 300 260 350 T400 335 V500 H0 Z',
|
|
21
|
+
front: 'M0 420 Q160 365 300 410 T400 400 V500 H0 Z',
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
const TALL_HILL = 'M0 380 Q200 320 400 370 V500 H0 Z' as const;
|
|
25
|
+
|
|
26
|
+
function WideCastle() {
|
|
27
|
+
return (
|
|
28
|
+
<svg
|
|
29
|
+
style={galleryImage}
|
|
30
|
+
viewBox="0 0 800 450"
|
|
31
|
+
role="img"
|
|
32
|
+
aria-label="Moonlit castle on a hill in Dracula theme colors">
|
|
33
|
+
<rect width="800" height="450" fill="var(--dracula-bg-dark)" />
|
|
34
|
+
{/* Stars */}
|
|
35
|
+
<g fill="var(--dracula-comment)">
|
|
36
|
+
<circle cx="60" cy="60" r="2.5" />
|
|
37
|
+
<circle cx="180" cy="120" r="2" />
|
|
38
|
+
<circle cx="330" cy="50" r="2.5" />
|
|
39
|
+
<circle cx="470" cy="90" r="2" />
|
|
40
|
+
<circle cx="700" cy="200" r="2.5" />
|
|
41
|
+
<circle cx="750" cy="80" r="2" />
|
|
42
|
+
</g>
|
|
43
|
+
<g fill="var(--dracula-cyan)">
|
|
44
|
+
<circle cx="120" cy="90" r="2" />
|
|
45
|
+
<circle cx="270" cy="70" r="2.5" />
|
|
46
|
+
<circle cx="540" cy="50" r="2" />
|
|
47
|
+
<circle cx="660" cy="160" r="2" />
|
|
48
|
+
</g>
|
|
49
|
+
<g fill="var(--dracula-pink)">
|
|
50
|
+
<circle cx="90" cy="170" r="2" />
|
|
51
|
+
<circle cx="400" cy="130" r="2.5" />
|
|
52
|
+
<circle cx="590" cy="200" r="2" />
|
|
53
|
+
<circle cx="730" cy="260" r="2" />
|
|
54
|
+
</g>
|
|
55
|
+
{/* Harvest moon */}
|
|
56
|
+
<circle cx="620" cy="110" r="56" fill="var(--dracula-yellow)" />
|
|
57
|
+
<circle
|
|
58
|
+
cx="600"
|
|
59
|
+
cy="95"
|
|
60
|
+
r="10"
|
|
61
|
+
fill="var(--dracula-orange)"
|
|
62
|
+
opacity={0.5}
|
|
63
|
+
/>
|
|
64
|
+
<circle
|
|
65
|
+
cx="638"
|
|
66
|
+
cy="125"
|
|
67
|
+
r="7"
|
|
68
|
+
fill="var(--dracula-orange)"
|
|
69
|
+
opacity={0.5}
|
|
70
|
+
/>
|
|
71
|
+
{/* Bats */}
|
|
72
|
+
<g
|
|
73
|
+
fill="none"
|
|
74
|
+
stroke="var(--dracula-fg)"
|
|
75
|
+
strokeWidth={4}
|
|
76
|
+
strokeLinecap="round">
|
|
77
|
+
<path d="M120 130 q12 -12 24 0 q12 -12 24 0" />
|
|
78
|
+
<path d="M210 90 q9 -9 18 0 q9 -9 18 0" />
|
|
79
|
+
<path d="M470 160 q9 -9 18 0 q9 -9 18 0" />
|
|
80
|
+
</g>
|
|
81
|
+
{/* Hills */}
|
|
82
|
+
<path d={WIDE_HILLS.back} fill="var(--dracula-selection)" />
|
|
83
|
+
<path d={WIDE_HILLS.front} fill="var(--dracula-bg-light)" />
|
|
84
|
+
{/* Castle */}
|
|
85
|
+
<g>
|
|
86
|
+
<rect
|
|
87
|
+
x="150"
|
|
88
|
+
y="225"
|
|
89
|
+
width="120"
|
|
90
|
+
height="120"
|
|
91
|
+
rx={4}
|
|
92
|
+
fill="var(--dracula-bg-lighter)"
|
|
93
|
+
/>
|
|
94
|
+
<rect
|
|
95
|
+
x="130"
|
|
96
|
+
y="185"
|
|
97
|
+
width="44"
|
|
98
|
+
height="160"
|
|
99
|
+
rx={4}
|
|
100
|
+
fill="var(--dracula-bg-lighter)"
|
|
101
|
+
/>
|
|
102
|
+
<rect
|
|
103
|
+
x="246"
|
|
104
|
+
y="185"
|
|
105
|
+
width="44"
|
|
106
|
+
height="160"
|
|
107
|
+
rx={4}
|
|
108
|
+
fill="var(--dracula-bg-lighter)"
|
|
109
|
+
/>
|
|
110
|
+
<g fill="var(--dracula-bg-lighter)">
|
|
111
|
+
<rect x="130" y="175" width="12" height="14" rx={2} />
|
|
112
|
+
<rect x="149" y="175" width="12" height="14" rx={2} />
|
|
113
|
+
<rect x="246" y="175" width="12" height="14" rx={2} />
|
|
114
|
+
<rect x="265" y="175" width="12" height="14" rx={2} />
|
|
115
|
+
<rect x="150" y="215" width="14" height="16" rx={2} />
|
|
116
|
+
<rect x="173" y="215" width="14" height="16" rx={2} />
|
|
117
|
+
<rect x="196" y="215" width="14" height="16" rx={2} />
|
|
118
|
+
<rect x="219" y="215" width="14" height="16" rx={2} />
|
|
119
|
+
<rect x="242" y="215" width="14" height="16" rx={2} />
|
|
120
|
+
</g>
|
|
121
|
+
<g fill="var(--dracula-yellow)">
|
|
122
|
+
<rect x="168" y="255" width="14" height="20" rx={2} />
|
|
123
|
+
<rect x="203" y="255" width="14" height="20" rx={2} />
|
|
124
|
+
<rect x="238" y="255" width="14" height="20" rx={2} />
|
|
125
|
+
<rect x="143" y="215" width="10" height="14" rx={2} />
|
|
126
|
+
<rect x="259" y="215" width="10" height="14" rx={2} />
|
|
127
|
+
</g>
|
|
128
|
+
<path
|
|
129
|
+
d="M195 345 v-30 a15 15 0 0 1 30 0 v30 Z"
|
|
130
|
+
fill="var(--dracula-bg-dark)"
|
|
131
|
+
/>
|
|
132
|
+
</g>
|
|
133
|
+
</svg>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function CardCastle() {
|
|
138
|
+
return (
|
|
139
|
+
<svg
|
|
140
|
+
style={galleryImage}
|
|
141
|
+
viewBox="0 0 400 500"
|
|
142
|
+
role="img"
|
|
143
|
+
aria-label="Moonlit castle on midnight hills">
|
|
144
|
+
<rect width="400" height="500" fill="var(--dracula-bg-dark)" />
|
|
145
|
+
{/* Stars */}
|
|
146
|
+
<g fill="var(--dracula-comment)">
|
|
147
|
+
<circle cx="50" cy="60" r="2.5" />
|
|
148
|
+
<circle cx="140" cy="130" r="2" />
|
|
149
|
+
<circle cx="240" cy="50" r="2.5" />
|
|
150
|
+
<circle cx="330" cy="110" r="2" />
|
|
151
|
+
<circle cx="300" cy="200" r="2.5" />
|
|
152
|
+
</g>
|
|
153
|
+
<g fill="var(--dracula-cyan)">
|
|
154
|
+
<circle cx="95" cy="90" r="2" />
|
|
155
|
+
<circle cx="200" cy="100" r="2.5" />
|
|
156
|
+
<circle cx="360" cy="60" r="2" />
|
|
157
|
+
</g>
|
|
158
|
+
<circle cx="290" cy="120" r="52" fill="var(--dracula-yellow)" />
|
|
159
|
+
<circle
|
|
160
|
+
cx="272"
|
|
161
|
+
cy="106"
|
|
162
|
+
r="9"
|
|
163
|
+
fill="var(--dracula-orange)"
|
|
164
|
+
opacity={0.5}
|
|
165
|
+
/>
|
|
166
|
+
<g
|
|
167
|
+
fill="none"
|
|
168
|
+
stroke="var(--dracula-fg)"
|
|
169
|
+
strokeWidth={3}
|
|
170
|
+
strokeLinecap="round">
|
|
171
|
+
<path d="M70 170 q12 -12 24 0 q12 -12 24 0" />
|
|
172
|
+
<path d="M150 130 q9 -9 18 0 q9 -9 18 0" />
|
|
173
|
+
</g>
|
|
174
|
+
<path d={CARD_HILLS.back} fill="var(--dracula-selection)" />
|
|
175
|
+
<path d={CARD_HILLS.front} fill="var(--dracula-bg-light)" />
|
|
176
|
+
<g>
|
|
177
|
+
<rect
|
|
178
|
+
x="140"
|
|
179
|
+
y="270"
|
|
180
|
+
width="120"
|
|
181
|
+
height="110"
|
|
182
|
+
rx={4}
|
|
183
|
+
fill="var(--dracula-bg-lighter)"
|
|
184
|
+
/>
|
|
185
|
+
<rect
|
|
186
|
+
x="118"
|
|
187
|
+
y="235"
|
|
188
|
+
width="44"
|
|
189
|
+
height="145"
|
|
190
|
+
rx={4}
|
|
191
|
+
fill="var(--dracula-bg-lighter)"
|
|
192
|
+
/>
|
|
193
|
+
<rect
|
|
194
|
+
x="238"
|
|
195
|
+
y="235"
|
|
196
|
+
width="44"
|
|
197
|
+
height="145"
|
|
198
|
+
rx={4}
|
|
199
|
+
fill="var(--dracula-bg-lighter)"
|
|
200
|
+
/>
|
|
201
|
+
<g fill="var(--dracula-yellow)">
|
|
202
|
+
<rect x="160" y="300" width="14" height="20" rx={2} />
|
|
203
|
+
<rect x="196" y="300" width="14" height="20" rx={2} />
|
|
204
|
+
<rect x="232" y="300" width="14" height="20" rx={2} />
|
|
205
|
+
<rect x="131" y="262" width="10" height="14" rx={2} />
|
|
206
|
+
<rect x="253" y="262" width="10" height="14" rx={2} />
|
|
207
|
+
</g>
|
|
208
|
+
<path
|
|
209
|
+
d="M185 380 v-28 a15 15 0 0 1 30 0 v28 Z"
|
|
210
|
+
fill="var(--dracula-bg-dark)"
|
|
211
|
+
/>
|
|
212
|
+
</g>
|
|
213
|
+
</svg>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function TallPines() {
|
|
218
|
+
return (
|
|
219
|
+
<svg
|
|
220
|
+
style={galleryImage}
|
|
221
|
+
viewBox="0 0 400 500"
|
|
222
|
+
role="img"
|
|
223
|
+
aria-label="Harvest moon over midnight pines">
|
|
224
|
+
<rect width="400" height="500" fill="var(--dracula-bg-dark)" />
|
|
225
|
+
{/* Stars */}
|
|
226
|
+
<g fill="var(--dracula-pink)">
|
|
227
|
+
<circle cx="60" cy="80" r="2" />
|
|
228
|
+
<circle cx="160" cy="50" r="2.5" />
|
|
229
|
+
<circle cx="320" cy="70" r="2" />
|
|
230
|
+
<circle cx="90" cy="190" r="2.5" />
|
|
231
|
+
<circle cx="250" cy="160" r="2" />
|
|
232
|
+
</g>
|
|
233
|
+
<g fill="var(--dracula-cyan)">
|
|
234
|
+
<circle cx="110" cy="120" r="2" />
|
|
235
|
+
<circle cx="220" cy="90" r="2" />
|
|
236
|
+
<circle cx="350" cy="150" r="2.5" />
|
|
237
|
+
</g>
|
|
238
|
+
<circle cx="200" cy="190" r="72" fill="var(--dracula-yellow)" />
|
|
239
|
+
<circle
|
|
240
|
+
cx="176"
|
|
241
|
+
cy="170"
|
|
242
|
+
r="12"
|
|
243
|
+
fill="var(--dracula-orange)"
|
|
244
|
+
opacity={0.5}
|
|
245
|
+
/>
|
|
246
|
+
<circle
|
|
247
|
+
cx="222"
|
|
248
|
+
cy="210"
|
|
249
|
+
r="8"
|
|
250
|
+
fill="var(--dracula-orange)"
|
|
251
|
+
opacity={0.5}
|
|
252
|
+
/>
|
|
253
|
+
<g
|
|
254
|
+
fill="none"
|
|
255
|
+
stroke="var(--dracula-fg)"
|
|
256
|
+
strokeWidth={3}
|
|
257
|
+
strokeLinecap="round">
|
|
258
|
+
<path d="M90 260 q12 -12 24 0 q12 -12 24 0" />
|
|
259
|
+
<path d="M260 250 q9 -9 18 0 q9 -9 18 0" />
|
|
260
|
+
</g>
|
|
261
|
+
<path d={TALL_HILL} fill="var(--dracula-selection)" />
|
|
262
|
+
<g fill="var(--dracula-bg-light)">
|
|
263
|
+
<path d="M70 500 V400 l28 -44 28 44 v100 Z" />
|
|
264
|
+
<path d="M170 500 V420 l28 -44 28 44 v80 Z" />
|
|
265
|
+
<path d="M270 500 V410 l28 -44 28 44 v90 Z" />
|
|
266
|
+
</g>
|
|
267
|
+
<g fill="var(--dracula-yellow)">
|
|
268
|
+
<rect x="81" y="442" width="8" height="12" rx={2} />
|
|
269
|
+
<rect x="181" y="458" width="8" height="12" rx={2} />
|
|
270
|
+
<rect x="281" y="450" width="8" height="12" rx={2} />
|
|
271
|
+
</g>
|
|
272
|
+
</svg>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function Rooftops() {
|
|
277
|
+
return (
|
|
278
|
+
<svg
|
|
279
|
+
style={galleryImage}
|
|
280
|
+
viewBox="0 0 400 300"
|
|
281
|
+
preserveAspectRatio="xMidYMid slice"
|
|
282
|
+
role="img"
|
|
283
|
+
aria-label="Harvest moon over the castle rooftops">
|
|
284
|
+
<rect width="400" height="300" fill="var(--dracula-bg)" />
|
|
285
|
+
<g fill="var(--dracula-fg)">
|
|
286
|
+
<circle cx="68" cy="52" r="2" />
|
|
287
|
+
<circle cx="330" cy="70" r="2" />
|
|
288
|
+
</g>
|
|
289
|
+
<g fill="var(--dracula-comment)">
|
|
290
|
+
<circle cx="140" cy="36" r="1.5" />
|
|
291
|
+
<circle cx="292" cy="34" r="1.5" />
|
|
292
|
+
<circle cx="360" cy="140" r="1.5" />
|
|
293
|
+
</g>
|
|
294
|
+
<circle cx="200" cy="118" r="52" fill="var(--dracula-yellow)" />
|
|
295
|
+
<circle cx="182" cy="104" r="44" fill="var(--dracula-bg)" />
|
|
296
|
+
<g
|
|
297
|
+
fill="var(--dracula-current-line)"
|
|
298
|
+
stroke="var(--dracula-comment)"
|
|
299
|
+
strokeWidth="3"
|
|
300
|
+
strokeLinejoin="round">
|
|
301
|
+
<rect x="20" y="220" width="110" height="80" />
|
|
302
|
+
<path d="M20 220 L75 178 L130 220 Z" />
|
|
303
|
+
<rect x="150" y="200" width="100" height="100" />
|
|
304
|
+
<path d="M150 200 L200 162 L250 200 Z" />
|
|
305
|
+
<rect x="270" y="228" width="110" height="72" />
|
|
306
|
+
<path d="M270 228 L325 190 L380 228 Z" />
|
|
307
|
+
</g>
|
|
308
|
+
<g fill="var(--dracula-yellow)">
|
|
309
|
+
<rect x="58" y="244" width="14" height="18" />
|
|
310
|
+
<rect x="188" y="224" width="14" height="18" />
|
|
311
|
+
<rect x="312" y="250" width="14" height="18" />
|
|
312
|
+
</g>
|
|
313
|
+
</svg>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function SceneCastle({variant}: {variant: SceneCastleVariant}) {
|
|
318
|
+
if (variant === 'wide') return <WideCastle />;
|
|
319
|
+
if (variant === 'card') return <CardCastle />;
|
|
320
|
+
if (variant === 'rooftops') return <Rooftops />;
|
|
321
|
+
return <TallPines />;
|
|
322
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Shared scene frame: the accessible, colour-correct wrapper every hand-drawn
|
|
2
|
+
// scene SVG in the kit sits inside.
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS EXISTS: twelve scene components across tech-report (795 lines) and
|
|
5
|
+
// product-tour (504 lines) each open with the same seven lines — viewBox,
|
|
6
|
+
// preserveAspectRatio, a fill style, role="img", aria-label, and a backdrop
|
|
7
|
+
// rect — and shared/scene-tile.tsx and shared/scene-castle.tsx carry the same
|
|
8
|
+
// frame a third and fourth time. Beyond the duplication, those scenes painted
|
|
9
|
+
// 45 values with --dracula-comment, which is a BORDER colour, not a text or
|
|
10
|
+
// data colour. This module exists so that mistake has exactly one place to
|
|
11
|
+
// happen.
|
|
12
|
+
//
|
|
13
|
+
// COLOUR CONTRACT (UIAuditColorMotion, measured against --dracula-bg-dark
|
|
14
|
+
// #21222C, the tier every scene paints on — a tier darker than the page #282A36,
|
|
15
|
+
// which is why the old ratios were worse than they looked):
|
|
16
|
+
// value #F8F8F2 14.81:1 --color-text-highlight
|
|
17
|
+
// para #B0B3C4 7.59:1 --color-text-paragraph (axes, ticks, captions)
|
|
18
|
+
// sec #9AA1BC 6.16:1 --color-text-secondary
|
|
19
|
+
// cyan #8BE9FD 11.41:1 --color-data-categorical-cyan (accent marks)
|
|
20
|
+
// COMMENT #6272A4 3.36:1 clears the 3:1 GRAPHICAL floor, FAILS the 4.5:1
|
|
21
|
+
// TEXT floor, and any fillOpacity below 0.9 makes
|
|
22
|
+
// it worse: 0.85 -> 2.81, 0.60 -> 2.06. The two
|
|
23
|
+
// floors are different rules and Comment sits
|
|
24
|
+
// between them, so its correctness depends entirely
|
|
25
|
+
// on what it paints:
|
|
26
|
+
// TEXT -> never Comment. It is disabled text.
|
|
27
|
+
// Use paragraph (7.59:1) or highlight
|
|
28
|
+
// (14.81:1) for values. This is the
|
|
29
|
+
// 45-instance defect.
|
|
30
|
+
// EMPTY CELL / subtle grid line -> Comment is
|
|
31
|
+
// CORRECT at full opacity (3.36:1).
|
|
32
|
+
// An empty cell is a graphical object
|
|
33
|
+
// carrying data — a reader must see
|
|
34
|
+
// that a position exists and was not
|
|
35
|
+
// kept — so the 3:1 floor applies, and
|
|
36
|
+
// 3.36:1 clears it.
|
|
37
|
+
// Do NOT "improve" an empty cell to --color-separator #44475A. Measured on
|
|
38
|
+
// #21222C: separator's CEILING is 1.73:1 at full opacity, and at 0.55 it is
|
|
39
|
+
// 1.32:1. No alpha fixes it, and a grid whose empty cells have vanished is not
|
|
40
|
+
// a grid. Subtle is not the same as invisible.
|
|
41
|
+
//
|
|
42
|
+
// CORRECTNESS CONSTRAINT, not a style note: this module MUST NOT own a <defs>
|
|
43
|
+
// id namespace. product-tour hardcodes gradient ids pt-night-glow and
|
|
44
|
+
// pt-night-pool, and twelve scenes can render on one page. If this file ever
|
|
45
|
+
// generates ids, those scenes collide and the second scene's gradients resolve
|
|
46
|
+
// to the first's. Callers pass their own gradient ids in via children.
|
|
47
|
+
//
|
|
48
|
+
// Not interactive. A scene is artwork, not a control: no hover, no focus ring.
|
|
49
|
+
|
|
50
|
+
import type {CSSProperties, ReactNode} from 'react';
|
|
51
|
+
|
|
52
|
+
export interface SceneFrameProps {
|
|
53
|
+
/** Accessible name for the art. Required and unique per instance: four or
|
|
54
|
+
* more scenes sharing one name is the a11y defect the Sparkline `label` prop
|
|
55
|
+
* was extracted to fix. */
|
|
56
|
+
label: string;
|
|
57
|
+
/** Scene contents. Any <defs> and gradient ids belong to the caller. */
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
/** @default '0 0 400 225' */
|
|
60
|
+
viewBox?: string;
|
|
61
|
+
/** Backdrop fill painted behind the scene. @default 'var(--dracula-bg-dark)' */
|
|
62
|
+
fill?: string;
|
|
63
|
+
/** Paints the backdrop rect. Set false when the scene draws its own. @default true */
|
|
64
|
+
hasBackdrop?: boolean;
|
|
65
|
+
/** Forwarded to the root svg. */
|
|
66
|
+
style?: CSSProperties;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function SceneFrame({
|
|
70
|
+
label,
|
|
71
|
+
children,
|
|
72
|
+
viewBox = '0 0 400 225',
|
|
73
|
+
fill = 'var(--dracula-bg-dark)',
|
|
74
|
+
hasBackdrop = true,
|
|
75
|
+
style,
|
|
76
|
+
}: SceneFrameProps) {
|
|
77
|
+
return (
|
|
78
|
+
<svg
|
|
79
|
+
viewBox={viewBox}
|
|
80
|
+
preserveAspectRatio="xMidYMid slice"
|
|
81
|
+
style={{width: '100%', height: '100%', display: 'block', ...style}}
|
|
82
|
+
role="img"
|
|
83
|
+
aria-label={label}>
|
|
84
|
+
{hasBackdrop ? <rect width="100%" height="100%" fill={fill} /> : null}
|
|
85
|
+
{children}
|
|
86
|
+
</svg>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Scene art-treatment vocabulary, split from the component that renders it.
|
|
2
|
+
//
|
|
3
|
+
// WHY A SEPARATE MODULE: a .tsx that exports both components and
|
|
4
|
+
// non-components breaks React Fast Refresh -- editing a constant forces a
|
|
5
|
+
// full remount instead of a hot update. `react-doctor/only-export-components`
|
|
6
|
+
// catches it; this is the fix it asks for. It also matches shared/chart-hues.ts,
|
|
7
|
+
// which the chart vocabulary already uses, so the kit has one shape rather than
|
|
8
|
+
// one precedent and one exception.
|
|
9
|
+
|
|
10
|
+
export type SceneTileSize = 'lg' | 'sm';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Art-treatment hues for scene tiles. Purple-free by this module's remit.
|
|
14
|
+
* Membership is not a contrast decision — every candidate clears WCAG 1.4.11
|
|
15
|
+
* on both dark surfaces at the disc's 0.9 opacity, including purple.
|
|
16
|
+
*/
|
|
17
|
+
export const SCENE_HUES = {
|
|
18
|
+
muted: 'var(--dracula-comment)',
|
|
19
|
+
cyan: 'var(--dracula-cyan)',
|
|
20
|
+
green: 'var(--dracula-green)',
|
|
21
|
+
yellow: 'var(--dracula-yellow)',
|
|
22
|
+
orange: 'var(--dracula-orange)',
|
|
23
|
+
pink: 'var(--dracula-pink)',
|
|
24
|
+
red: 'var(--dracula-red)',
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type SceneHue = (typeof SCENE_HUES)[keyof typeof SCENE_HUES];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The union above constrains the KEY, not the value it resolves to. An entry
|
|
31
|
+
* rewritten to `var(--dracula-purple)` still typechecks. `scripts/check.ts`
|
|
32
|
+
* gates the values of this object; add the gate there if it is ever removed.
|
|
33
|
+
* A prose rule does not stop a caller — see the remit note above.
|
|
34
|
+
*/
|
|
35
|
+
export const SCENE_HUE_VALUES: readonly SceneHue[] = Object.values(SCENE_HUES);
|
|
36
|
+
|
|
37
|
+
// Shared alt list for the nine-tile side/classic wall (folded into one
|
|
38
|
+
// module so the two surfaces cannot drift apart again).
|
|
39
|
+
export const SCENE_TILE_ALTS = [
|
|
40
|
+
'Moonlit ridge trail under a harvest moon',
|
|
41
|
+
'Late portrait in violet lamplight',
|
|
42
|
+
'Lamplit reading nook after midnight',
|
|
43
|
+
'Fog rolling over the night pines',
|
|
44
|
+
'Dancer caught mid-step in stage pink',
|
|
45
|
+
'Kitchen table set for a midnight feast',
|
|
46
|
+
'Castle silhouette over sleeping hills',
|
|
47
|
+
'Attic window glowing amber at 2am',
|
|
48
|
+
'Cellar shelves lined with bottled dusk',
|
|
49
|
+
] as const;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Shared mountain-glyph scene tile for owned gallery surfaces.
|
|
2
|
+
// Large fork: documentation/library/mixed/payment/product-detail/
|
|
3
|
+
// product-gallery — centered glyph on a flat field. Small fork:
|
|
4
|
+
// classic/side — full night landscape (stars, moon, hills) with a tighter
|
|
5
|
+
// glyph frame. The glyph stroke stays comment so tiles stay on-brand in the
|
|
6
|
+
// dark-only theme.
|
|
7
|
+
// Wave 2 owns the documentation + payment-form migration — those templates
|
|
8
|
+
// keep their local glyphs until then.
|
|
9
|
+
//
|
|
10
|
+
// REMIT (art treatment, not data): this module's scenes take a `SceneHue` from
|
|
11
|
+
// `SCENE_HUES` below. Purple is excluded because this module reserves it for
|
|
12
|
+
// tappables — NOT because of contrast, which it passes (4.27:1 on the surface
|
|
13
|
+
// tier at the disc's 0.9 opacity). This is a separate vocabulary from
|
|
14
|
+
// `CHART_HUES` on purpose: that set is purple-free because purple must never
|
|
15
|
+
// encode DATA, this one is purple-free because this module says so. Two sets,
|
|
16
|
+
// two stated reasons. Reusing `CHART_HUES` here would make it purple-free for
|
|
17
|
+
// a reason that has nothing to do with this rule.
|
|
18
|
+
//
|
|
19
|
+
// The type is the enforcement, not this comment. `hue: string` accepted any
|
|
20
|
+
// `var(--dracula-*)` and every caller decided alone, which is how purple
|
|
21
|
+
// reached a 34px disc. The union turns that into a compile error at the call
|
|
22
|
+
// site with the exact offending value named.
|
|
23
|
+
|
|
24
|
+
import {galleryImage} from 'astryx-dracula/shared/gallery-image';
|
|
25
|
+
import {
|
|
26
|
+
SCENE_HUES,
|
|
27
|
+
type SceneHue,
|
|
28
|
+
type SceneTileSize,
|
|
29
|
+
} from 'astryx-dracula/shared/scene-hues';
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
interface SceneTileProps {
|
|
33
|
+
/**
|
|
34
|
+
* Per-tile accent: the sm fork paints it as the moon disc (`:70`, a 34px
|
|
35
|
+
* radius at 0.9 opacity) and as glyph dots; the lg fork paints the glyph.
|
|
36
|
+
* One prop, three areas of very different size — the split by role is a
|
|
37
|
+
* deliberate follow-up, not an oversight. Both areas clear 1.4.11 at the
|
|
38
|
+
* same `SceneHue`, so the split is about which role may take which subset,
|
|
39
|
+
* not about a second contrast axis.
|
|
40
|
+
*/
|
|
41
|
+
hue: SceneHue;
|
|
42
|
+
/** Accessible name for the tile art. */
|
|
43
|
+
label: string;
|
|
44
|
+
/** `lg` for grid thumbs, `sm` for the classic/side landscape tiles. */
|
|
45
|
+
size?: SceneTileSize;
|
|
46
|
+
/** Star wash for the sm landscape. Decorative, and purple-free by the same
|
|
47
|
+
* remit as `hue`, so it takes the same union. */
|
|
48
|
+
stars?: SceneHue;
|
|
49
|
+
/** Positional seed so repeated sm tiles vary moon, hills, and star field.
|
|
50
|
+
* Ignored by the lg fork (one centered glyph, no variance). */
|
|
51
|
+
index?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
export function SceneTile({
|
|
56
|
+
hue,
|
|
57
|
+
label,
|
|
58
|
+
size = 'lg',
|
|
59
|
+
stars = 'var(--dracula-comment)',
|
|
60
|
+
index = 0,
|
|
61
|
+
}: SceneTileProps) {
|
|
62
|
+
const moonX = 90 + ((index * 53) % 220);
|
|
63
|
+
const moonY = 62 + ((index * 29) % 60);
|
|
64
|
+
const hillA = 190 + ((index * 13) % 40);
|
|
65
|
+
const hillB = 215 + ((index * 17) % 40);
|
|
66
|
+
return (
|
|
67
|
+
<svg
|
|
68
|
+
viewBox="0 0 400 300"
|
|
69
|
+
preserveAspectRatio="xMidYMid slice"
|
|
70
|
+
style={galleryImage}
|
|
71
|
+
role="img"
|
|
72
|
+
aria-label={label}>
|
|
73
|
+
<rect width="400" height="300" fill="var(--dracula-bg-light)" />
|
|
74
|
+
{size === 'sm' && (
|
|
75
|
+
<>
|
|
76
|
+
{/* Stars */}
|
|
77
|
+
<g fill={stars} opacity={0.55}>
|
|
78
|
+
<circle cx={40 + ((index * 37) % 320)} cy={30} r={2} />
|
|
79
|
+
<circle cx={120 + ((index * 23) % 200)} cy={52} r={1.6} />
|
|
80
|
+
<circle cx={260 + ((index * 11) % 110)} cy={26} r={2.2} />
|
|
81
|
+
<circle cx={330} cy={70 + ((index * 7) % 30)} r={1.6} />
|
|
82
|
+
</g>
|
|
83
|
+
<circle cx={moonX} cy={moonY} r={34} fill={hue} opacity={0.9} />
|
|
84
|
+
<circle
|
|
85
|
+
cx={moonX - 12}
|
|
86
|
+
cy={moonY - 8}
|
|
87
|
+
r={28}
|
|
88
|
+
fill="var(--dracula-bg-light)"
|
|
89
|
+
opacity={0.55}
|
|
90
|
+
/>
|
|
91
|
+
<path
|
|
92
|
+
d={`M0 ${hillA} Q100 ${hillA - 50} 200 ${hillA - 10} T400 ${hillA - 30} V300 H0 Z`}
|
|
93
|
+
fill="var(--dracula-current-line)"
|
|
94
|
+
/>
|
|
95
|
+
<path
|
|
96
|
+
d={`M0 ${hillB} Q120 ${hillB - 40} 240 ${hillB} T400 ${hillB - 20} V300 H0 Z`}
|
|
97
|
+
fill="var(--dracula-bg)"
|
|
98
|
+
/>
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
{size === 'sm' ? (
|
|
102
|
+
<g
|
|
103
|
+
transform={`translate(${60 + ((index * 41) % 280)} ${hillB - 34})`}
|
|
104
|
+
fill="none"
|
|
105
|
+
stroke={hue}
|
|
106
|
+
strokeWidth="4"
|
|
107
|
+
strokeLinecap="round"
|
|
108
|
+
strokeLinejoin="round">
|
|
109
|
+
<rect x="-22" y="-22" width="44" height="44" rx={4} />
|
|
110
|
+
<circle cx="9" cy="-9" r="2" fill={hue} stroke="none" />
|
|
111
|
+
<path d="M-17 15 L-4 0 L5 9 L10 4 L17 12" />
|
|
112
|
+
</g>
|
|
113
|
+
) : (
|
|
114
|
+
<g
|
|
115
|
+
transform="translate(200 150)"
|
|
116
|
+
fill="none"
|
|
117
|
+
stroke="var(--dracula-comment)"
|
|
118
|
+
strokeWidth="5"
|
|
119
|
+
strokeLinecap="round"
|
|
120
|
+
strokeLinejoin="round">
|
|
121
|
+
<rect x="-44" y="-44" width="88" height="88" rx="5" />
|
|
122
|
+
<circle cx="18" cy="-18" r="2.5" fill={hue} stroke="none" />
|
|
123
|
+
<path d="M-34 30 L-8 0 L10 18 L20 8 L34 24" />
|
|
124
|
+
</g>
|
|
125
|
+
)}
|
|
126
|
+
</svg>
|
|
127
|
+
);
|
|
128
|
+
}
|