@vadimcomanescu/nadicode-design-system 4.0.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.agents/skills/seed/SKILL.md +34 -163
  2. package/.agents/skills/seed/references/animation.md +2 -2
  3. package/.agents/skills/seed/references/responsive.md +1 -1
  4. package/README.md +2 -2
  5. package/eslint-rules/nadicode/rules/no-has-svg-selector.js +1 -1
  6. package/package.json +1 -2
  7. package/scripts/ds-check.mjs +0 -10
  8. package/scripts/sync-seed-skill.mjs +0 -3
  9. package/.agents/skills/seed/contract.md +0 -110
  10. package/.agents/skills/seed/intent-map.md +0 -320
  11. package/.agents/skills/seed/recipes/agency-home.md +0 -311
  12. package/.agents/skills/seed/recipes/agents-chat.md +0 -305
  13. package/.agents/skills/seed/recipes/analytics.md +0 -253
  14. package/.agents/skills/seed/recipes/auth.md +0 -254
  15. package/.agents/skills/seed/recipes/blog-content.md +0 -307
  16. package/.agents/skills/seed/recipes/checkout.md +0 -311
  17. package/.agents/skills/seed/recipes/company-about.md +0 -276
  18. package/.agents/skills/seed/recipes/company-contact.md +0 -234
  19. package/.agents/skills/seed/recipes/crud-form.md +0 -233
  20. package/.agents/skills/seed/recipes/crud-list-detail.md +0 -230
  21. package/.agents/skills/seed/recipes/dashboard.md +0 -354
  22. package/.agents/skills/seed/recipes/digital-workers.md +0 -314
  23. package/.agents/skills/seed/recipes/error-pages.md +0 -199
  24. package/.agents/skills/seed/recipes/marketing-landing.md +0 -293
  25. package/.agents/skills/seed/recipes/marketing-shell.md +0 -156
  26. package/.agents/skills/seed/recipes/navigation-shell.md +0 -787
  27. package/.agents/skills/seed/recipes/onboarding.md +0 -258
  28. package/.agents/skills/seed/recipes/pricing.md +0 -271
  29. package/.agents/skills/seed/recipes/service-detail.md +0 -302
  30. package/.agents/skills/seed/recipes/settings.md +0 -252
  31. package/.agents/skills/seed/references/blocks.md +0 -128
  32. package/.agents/skills/seed/references/components.md +0 -287
  33. package/.agents/skills/seed/references/icons.md +0 -169
  34. package/.agents/skills/seed/references/nextjs.md +0 -49
  35. package/.agents/skills/seed/references/tokens.md +0 -88
@@ -1,254 +0,0 @@
1
- # Recipe: Auth
2
-
3
- Login, signup, password reset, and 2FA flows with split-screen or centered layout.
4
-
5
- ## Route Patterns
6
-
7
- - `/login`
8
- - `/signup`
9
- - `/reset-password`
10
- - `/verify` (2FA challenge)
11
-
12
- ## Shell
13
-
14
- `auth-shell` (AuthLayout: split-screen or centered, minimal chrome)
15
-
16
- ---
17
-
18
- ## Layout Blueprint (Desktop)
19
-
20
- ```
21
- +------------------------------------------------+
22
- | |
23
- | +--------------------+ +-------------------+ |
24
- | | | | | |
25
- | | Brand Panel | | Auth Card | |
26
- | | (illustration, | | (glass-floating) | |
27
- | | testimonial, | | | |
28
- | | or shader bg) | | Logo | |
29
- | | | | Title | |
30
- | | | | Subtitle | |
31
- | | | | | |
32
- | | | | [Email input] | |
33
- | | | | [Password input] | |
34
- | | | | | |
35
- | | | | [Sign In] | |
36
- | | | | Or continue with | |
37
- | | | | [Google] [GitHub] | |
38
- | | | | | |
39
- | | | | Forgot password? | |
40
- | | | | Don't have acct? | |
41
- | +--------------------+ +-------------------+ |
42
- | |
43
- +------------------------------------------------+
44
-
45
- Mobile: Brand panel hidden, auth card full-width centered
46
- ```
47
-
48
- ---
49
-
50
- ## Section Sequence
51
-
52
- ### 1. Auth Layout Wrapper
53
-
54
- ```tsx
55
- <AuthLayout>
56
- {/* AuthLayout provides split-screen on desktop, centered on mobile */}
57
- <LoginBlock
58
- type="login"
59
- variant="glass-panel"
60
- showSocial
61
- title="Welcome back"
62
- description="Sign in to your account"
63
- onLogin={handleLogin}
64
- forgotPasswordHref="/reset-password"
65
- signUpHref="/signup"
66
- />
67
- </AuthLayout>
68
- ```
69
-
70
- ### 2. Login Form (within LoginBlock or custom)
71
-
72
- ```tsx
73
- <Card className="glass-floating p-8 w-full max-w-md">
74
- <div className="flex flex-col items-center mb-6">
75
- <Logo className="h-8 mb-4" />
76
- <Heading level={3} size="subsection" >Welcome back</Heading>
77
- <Typography variant="small" className="text-text-secondary mt-1">
78
- Sign in to your account
79
- </Typography>
80
- </div>
81
-
82
- {error && <Alert variant="destructive" className="mb-4">{error}</Alert>}
83
-
84
- <form onSubmit={handleSubmit} className="space-y-4">
85
- <div>
86
- <Label htmlFor="email">Email</Label>
87
- <Input id="email" type="email" placeholder="name@example.com" />
88
- </div>
89
- <div>
90
- <Label htmlFor="password">Password</Label>
91
- <PasswordInput id="password" />
92
- </div>
93
- <Button type="submit" className="w-full" disabled={isSubmitting}>
94
- {isSubmitting ? <Spinner className="mr-2" /> : null}
95
- Sign In
96
- </Button>
97
- </form>
98
-
99
- <div className="relative my-6">
100
- <Separator />
101
- <span className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 bg-surface px-2 text-text-tertiary text-sm">
102
- Or continue with
103
- </span>
104
- </div>
105
-
106
- <div className="grid grid-cols-2 gap-3">
107
- <Button variant="outline"><BrandIcons.Google className="mr-2 h-4 w-4" /> Google</Button>
108
- <Button variant="outline"><BrandIcons.GitHub className="mr-2 h-4 w-4" /> GitHub</Button>
109
- </div>
110
-
111
- <div className="mt-6 text-center space-y-2">
112
- <a href="/reset-password" className="text-sm text-link">Forgot password?</a>
113
- <p className="text-sm text-text-secondary">
114
- Don't have an account? <a href="/signup" className="text-link">Sign up</a>
115
- </p>
116
- </div>
117
- </Card>
118
- ```
119
-
120
- ### 3. 2FA Challenge (separate route)
121
-
122
- ```tsx
123
- <Card className="glass-floating p-8 w-full max-w-md text-center">
124
- <Heading level={3} size="subsection" >Two-factor authentication</Heading>
125
- <Typography variant="small" className="text-text-secondary mt-2 mb-6">
126
- Enter the 6-digit code from your authenticator app
127
- </Typography>
128
- <InputOTP maxLength={6} onComplete={handleVerify}>
129
- <InputOTPGroup>
130
- <InputOTPSlot index={0} />
131
- <InputOTPSlot index={1} />
132
- <InputOTPSlot index={2} />
133
- </InputOTPGroup>
134
- <InputOTPSeparator />
135
- <InputOTPGroup>
136
- <InputOTPSlot index={3} />
137
- <InputOTPSlot index={4} />
138
- <InputOTPSlot index={5} />
139
- </InputOTPGroup>
140
- </InputOTP>
141
- <Button className="w-full mt-6" disabled={isSubmitting}>Verify</Button>
142
- </Card>
143
- ```
144
-
145
- ---
146
-
147
- ## Animation Storyboard
148
-
149
- ```
150
- ANIMATION STORYBOARD
151
- ====================
152
- BUDGET: 350ms | SPRING: gentle | REDUCED: opacity-only
153
-
154
- T+0ms [layout] Split-screen panels visible (instant)
155
- T+0ms [brand] Brand panel bg/shader (instant)
156
- T+100ms [card] Auth card scales in {scaleIn, gentle}
157
- T+200ms [logo] Logo fades in {fadeIn, snappy}
158
- T+250ms [fields] Form fields stagger {blockStagger, 60ms, max 4}
159
- T+350ms [cta] Sign-in button fades in {fadeIn, snappy}
160
-
161
- REDUCED MOTION: Card and all children visible immediately
162
- ```
163
-
164
- ---
165
-
166
- ## Required Components
167
-
168
- | Component | Import Path | Purpose |
169
- |-----------|-------------|---------|
170
- | `AuthLayout` | `@vadimcomanescu/nadicode-design-system/catalog/components` via `seedComponents` | Split-screen wrapper |
171
- | `LoginBlock` | `@vadimcomanescu/nadicode-design-system/catalog/components` via `seedComponents` | Pre-built login form |
172
- | `SignUpBlock` | `@vadimcomanescu/nadicode-design-system/catalog/components` via `seedComponents` | Pre-built signup form |
173
- | `Card` | `@vadimcomanescu/nadicode-design-system/card` | Auth card container |
174
- | `Input` | `@vadimcomanescu/nadicode-design-system/input` | Email, name fields |
175
- | `PasswordInput` | `@vadimcomanescu/nadicode-design-system/password-input` | Password with toggle |
176
- | `Label` | `@vadimcomanescu/nadicode-design-system/label` | Stacked labels |
177
- | `Button` | `@vadimcomanescu/nadicode-design-system/button` | Submit, social login |
178
- | `Alert` | `@vadimcomanescu/nadicode-design-system/alert` | Error messages |
179
- | `Logo` | `@vadimcomanescu/nadicode-design-system/logo` | Brand mark |
180
- | `BrandIcons` | `@vadimcomanescu/nadicode-design-system/brand-icons` | Google, GitHub icons |
181
- | `Separator` | `@vadimcomanescu/nadicode-design-system/separator` | "Or" divider |
182
- | `InputOTP` | `@vadimcomanescu/nadicode-design-system/input-otp` | 2FA code entry |
183
- | `Spinner` | `@vadimcomanescu/nadicode-design-system/spinner` | Submit loading |
184
- | `Typography` | `@vadimcomanescu/nadicode-design-system/typography` | Headings, descriptions |
185
-
186
- ### Allowed (optional)
187
-
188
- `PasswordRecoveryBlock`, `ResetPasswordBlock`, `TwoFactorChallengeBlock`, `TwoFactorSetupBlock`, `AccountLockedBlock`, `AuthSuccessBlock`, `Checkbox` (remember me), `ShaderBackground`
189
-
190
- ### Forbidden
191
-
192
- `Sidebar`, `DataTable`, chart components, `NavigationMenu`, decorative effects (except on brand panel)
193
-
194
- ---
195
-
196
- ## Required States
197
-
198
- | State | Trigger | Visual |
199
- |-------|---------|--------|
200
- | `default` | Page loaded | Form ready for input |
201
- | `submitting` | Form submitted | Button shows Spinner, form disabled |
202
- | `error` | Auth failure | `Alert` variant="destructive" with message |
203
- | `success` | Auth succeeded | Redirect to dashboard (or `AuthSuccessBlock`) |
204
- | `2fa-challenge` | Server requests 2FA | Show 2FA code entry screen |
205
-
206
- ---
207
-
208
- ## Responsive Contract
209
-
210
- | Breakpoint | Layout | Card Width |
211
- |------------|--------|------------|
212
- | Mobile | Centered card, no brand panel | Full width, max-w-md, px-4 |
213
- | `lg:` | Split screen: brand (50%) + form (50%) | max-w-md centered in right half |
214
-
215
- ---
216
-
217
- ## Styling Rules
218
-
219
- - Auth card: `glass-floating p-8`
220
- - Brand panel: full-height, may use `ShaderBackground` or illustration
221
- - Labels: stacked above inputs (always)
222
- - Social buttons: `variant="outline"`, 2-column grid
223
- - Links: `text-link` token
224
- - Error: `Alert` at top of form, not inline per-field (for auth errors)
225
-
226
- ---
227
-
228
- ## Accessibility
229
-
230
- - Form inputs have associated labels
231
- - Error alert uses `role="alert"`
232
- - Social login buttons include brand name in text
233
- - OTP input is keyboard-navigable (auto-advance)
234
- - Focus moves to first field on mount
235
- - Escape key behavior is handled (no accidental navigation away)
236
-
237
- ---
238
-
239
- ## Reference Implementations
240
-
241
- - `src/components/blocks/LoginBlock.tsx`
242
- - `src/components/blocks/AuthLayout.tsx`
243
- - `src/app/login/vanta/*/page.tsx` (7 variants)
244
-
245
- ---
246
-
247
- ## Verification
248
-
249
- ```bash
250
- npx tsc --noEmit
251
- npm run lint
252
- npm run test
253
- npx vitest run src/test/css-variable-usage.test.ts
254
- ```
@@ -1,307 +0,0 @@
1
- # Recipe: Blog / Content
2
-
3
- Blog index, article pages, and changelog display with reading-optimized layout.
4
-
5
- ## Route Patterns
6
-
7
- - `/blog` (index with post cards)
8
- - `/blog/[slug]` (individual article)
9
- - `/changelog` (version changelog)
10
-
11
- ## Shell
12
-
13
- `marketing-shell` (HeaderBlock + FooterBlock)
14
-
15
- ---
16
-
17
- ## Layout Blueprint: Blog Index (Desktop)
18
-
19
- ```
20
- +------------------------------------------------+
21
- | HeaderBlock |
22
- +------------------------------------------------+
23
- | |
24
- | Blog [Category filter] |
25
- | Latest thoughts and updates |
26
- | |
27
- | +--------------------+ +-------------------+ |
28
- | | Featured Post | | Post 2 | |
29
- | | (large card) | | (standard card) | |
30
- | | | +-------------------+ |
31
- | | | | Post 3 | |
32
- | | | | (standard card) | |
33
- | +--------------------+ +-------------------+ |
34
- | |
35
- | +----------+ +----------+ +----------+ |
36
- | | Post 4 | | Post 5 | | Post 6 | |
37
- | +----------+ +----------+ +----------+ |
38
- | |
39
- | [Load More / Pagination] |
40
- +------------------------------------------------+
41
- | FooterBlock |
42
- +------------------------------------------------+
43
- ```
44
-
45
- ## Layout Blueprint: Article Page (Desktop)
46
-
47
- ```
48
- +------------------------------------------------+
49
- | HeaderBlock |
50
- +------------------------------------------------+
51
- | |
52
- | max-w-3xl centered |
53
- | |
54
- | Category Badge 5 min read |
55
- | Article Title (h1) |
56
- | Published Jan 1, 2025 by Author Name |
57
- | |
58
- | [Article hero image] |
59
- | |
60
- | Article body content with prose styling... |
61
- | Headings, paragraphs, code blocks, |
62
- | images, blockquotes... |
63
- | |
64
- | --- |
65
- | Author card |
66
- | Share buttons |
67
- | |
68
- | Related Posts (3 cards) |
69
- | |
70
- +------------------------------------------------+
71
- | FooterBlock |
72
- +------------------------------------------------+
73
- ```
74
-
75
- ---
76
-
77
- ## Section Sequence: Blog Index
78
-
79
- ### 1. Page Header
80
-
81
- ```tsx
82
- <section className="py-12 md:py-16">
83
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
84
- <div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
85
- <div>
86
- <Heading level={1} size="display" >Blog</Heading>
87
- <Typography variant="lead" className="text-text-secondary mt-2">
88
- Latest thoughts and updates
89
- </Typography>
90
- </div>
91
- <Select value={category} onValueChange={setCategory}>
92
- <SelectTrigger className="w-48"><SelectValue placeholder="All categories" /></SelectTrigger>
93
- <SelectContent>{/* Category options */}</SelectContent>
94
- </Select>
95
- </div>
96
- </div>
97
- </section>
98
- ```
99
-
100
- ### 2. Featured + Recent Posts Grid
101
-
102
- ```tsx
103
- <section className="pb-16">
104
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
105
- {/* Featured row */}
106
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
107
- <Card className="glass-panel overflow-hidden lg:row-span-2">
108
- {/* Featured post with large image */}
109
- <img src={featured.image} alt="" className="w-full h-48 lg:h-64 object-cover" />
110
- <div className="p-6">
111
- <Badge variant="outline">{featured.category}</Badge>
112
- <Heading level={3} size="subsection" className="mt-3">{featured.title}</Heading>
113
- <Typography variant="p" className="text-text-secondary mt-2 line-clamp-3">
114
- {featured.excerpt}
115
- </Typography>
116
- <div className="flex items-center gap-2 mt-4 text-text-tertiary text-sm">
117
- <Avatar className="h-6 w-6"><AvatarImage src={featured.author.avatar} /></Avatar>
118
- <span>{featured.author.name}</span>
119
- <span>-</span>
120
- <span>{featured.date}</span>
121
- </div>
122
- </div>
123
- </Card>
124
- <div className="space-y-6">
125
- {recentPosts.slice(0, 2).map(post => (
126
- <Card key={post.id} className="glass-panel p-6">
127
- <Badge variant="outline">{post.category}</Badge>
128
- <Heading level={4} size="title" className="mt-2">{post.title}</Heading>
129
- <Typography variant="small" className="text-text-secondary mt-1 line-clamp-2">
130
- {post.excerpt}
131
- </Typography>
132
- </Card>
133
- ))}
134
- </div>
135
- </div>
136
-
137
- {/* Remaining posts grid */}
138
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
139
- {remainingPosts.map(post => (
140
- <Card key={post.id} className="glass-panel p-6">
141
- <Badge variant="outline">{post.category}</Badge>
142
- <Heading level={4} size="title" className="mt-2">{post.title}</Heading>
143
- <Typography variant="small" className="text-text-secondary mt-1 line-clamp-2">
144
- {post.excerpt}
145
- </Typography>
146
- </Card>
147
- ))}
148
- </div>
149
-
150
- {/* Pagination */}
151
- <div className="mt-12 flex justify-center">
152
- <Pagination current={page} total={totalPages} onPageChange={setPage} />
153
- </div>
154
- </div>
155
- </section>
156
- ```
157
-
158
- ### Article Page: Prose Content
159
-
160
- ```tsx
161
- <article className="max-w-3xl mx-auto px-4 sm:px-6 py-12 md:py-16">
162
- <header className="mb-8">
163
- <div className="flex items-center gap-3 mb-4">
164
- <Badge variant="outline">{post.category}</Badge>
165
- <Typography variant="small" className="text-text-tertiary">
166
- {post.readingTime} min read
167
- </Typography>
168
- </div>
169
- <Heading level={1} size="display" >{post.title}</Heading>
170
- <div className="flex items-center gap-3 mt-4 text-text-secondary">
171
- <Avatar><AvatarImage src={post.author.avatar} /></Avatar>
172
- <div>
173
- <Typography variant="small" className="font-medium text-text-primary">
174
- {post.author.name}
175
- </Typography>
176
- <Typography variant="small">{post.date}</Typography>
177
- </div>
178
- </div>
179
- </header>
180
-
181
- {/* Prose content */}
182
- <div className="prose prose-neutral dark:prose-invert max-w-none">
183
- {post.content}
184
- </div>
185
-
186
- {/* Author card + share */}
187
- <Separator className="my-12" />
188
- <Card className="glass-panel p-6 flex items-center gap-4">
189
- <Avatar className="h-12 w-12"><AvatarImage src={post.author.avatar} /></Avatar>
190
- <div>
191
- <Heading level={4} size="title" >{post.author.name}</Heading>
192
- <Typography variant="small" className="text-text-secondary">{post.author.bio}</Typography>
193
- </div>
194
- </Card>
195
- </article>
196
- ```
197
-
198
- ---
199
-
200
- ## Animation Storyboard
201
-
202
- ```
203
- ANIMATION STORYBOARD
204
- ====================
205
- BUDGET: 600ms (index) / 300ms (article) | SPRING: snappy | REDUCED: opacity-only
206
-
207
- BLOG INDEX:
208
- T+0ms [nav] Header visible (instant)
209
- T+50ms [header] Blog title + filter {fadeInUp, snappy}
210
- T+130ms [featured] Featured post card {fadeInUp, snappy}
211
- T+210ms [recent] Recent post cards stagger {blockStagger, 80ms}
212
- T+450ms [grid] Remaining posts stagger {blockStagger, 80ms, max 6}
213
-
214
- ARTICLE:
215
- T+0ms [nav] Header visible (instant)
216
- T+50ms [meta] Category + reading time {fadeIn, snappy}
217
- T+100ms [title] Article title {fadeInUp, snappy}
218
- T+200ms [content] Article body fades in {fadeIn, gentle}
219
-
220
- REDUCED MOTION: All items visible immediately
221
- ```
222
-
223
- ---
224
-
225
- ## Required Components
226
-
227
- | Component | Import Path | Purpose |
228
- |-----------|-------------|---------|
229
- | `Card` | `@vadimcomanescu/nadicode-design-system/card` | Post cards |
230
- | `Badge` | `@vadimcomanescu/nadicode-design-system/badge` | Category tags |
231
- | `Avatar` | `@vadimcomanescu/nadicode-design-system/avatar` | Author avatars |
232
- | `Typography` | `@vadimcomanescu/nadicode-design-system/typography` | Headings, body |
233
- | `Pagination` | `@vadimcomanescu/nadicode-design-system/pagination` | Blog index pages |
234
- | `Separator` | `@vadimcomanescu/nadicode-design-system/separator` | Content dividers |
235
- | `Select` | `@vadimcomanescu/nadicode-design-system/select` | Category filter |
236
- | `HeaderBlock` | `@vadimcomanescu/nadicode-design-system/catalog/components` via `seedComponents` | Site navigation |
237
- | `FooterBlock` | `@vadimcomanescu/nadicode-design-system/catalog/components` via `seedComponents` | Site footer |
238
- | `ScrollFadeIn` | `@vadimcomanescu/nadicode-design-system/scroll-fade-in` | Below-fold animations |
239
- | `Button` | `@vadimcomanescu/nadicode-design-system/button` | Share, read more |
240
-
241
- ### Allowed (optional)
242
-
243
- `ChangelogBlock`, `NewsletterBlock`, `Tabs`, `Input` (search), `Breadcrumb`, `Tooltip`, `CodeBlock`
244
-
245
- ### Forbidden
246
-
247
- `Sidebar`, `DataTable`, `FormWizard`, chart components, agentic components
248
-
249
- ---
250
-
251
- ## Required States
252
-
253
- | State | Trigger | Visual |
254
- |-------|---------|--------|
255
- | `loading` | Fetching posts | Skeleton post cards |
256
- | `empty` | No posts (unlikely for blog) | `Empty` with "No posts yet" |
257
- | `has-data` | Posts loaded | Full grid layout |
258
- | `category-filtered` | Category selected | Filtered post grid |
259
-
260
- ---
261
-
262
- ## Responsive Contract
263
-
264
- | Breakpoint | Blog Index Grid | Article Width |
265
- |------------|----------------|---------------|
266
- | Mobile | 1 col | Full width, px-4 |
267
- | `sm:` | 2 col | max-w-3xl, px-6 |
268
- | `lg:` | Featured 2-col + 3-col grid | max-w-3xl, px-6 |
269
-
270
- ---
271
-
272
- ## Styling Rules
273
-
274
- - Post cards: `glass-panel` with overflow-hidden for images
275
- - Article prose: Tailwind typography plugin (`prose prose-neutral dark:prose-invert`)
276
- - Reading width: `max-w-3xl` (768px) for article content
277
- - Category badges: `variant="outline"`
278
- - No raw palette colors
279
-
280
- ---
281
-
282
- ## Accessibility
283
-
284
- - Blog index is a `<main>` with article cards as `<article>` elements
285
- - Article page uses semantic `<article>`, `<header>`, heading hierarchy
286
- - Images have alt text
287
- - Pagination uses `<nav>` with `aria-label="Blog pagination"`
288
-
289
- ---
290
-
291
- ## Reference Implementations
292
-
293
- - `src/app/blog/page.tsx`
294
- - `src/app/changelog/page.tsx`
295
- - `src/components/blocks/ChangelogBlock.tsx`
296
- - `src/components/blocks/NewsletterBlock.tsx`
297
-
298
- ---
299
-
300
- ## Verification
301
-
302
- ```bash
303
- npx tsc --noEmit
304
- npm run lint
305
- npm run test
306
- npx vitest run src/test/css-variable-usage.test.ts
307
- ```