@sudajs/cli 0.18.11 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudajs/cli",
3
- "version": "0.18.11",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "suda": "./bin/suda.js"
@@ -24,7 +24,6 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@modelcontextprotocol/sdk": "^1.29.0",
28
27
  "@puckeditor/core": "^0.22.4",
29
28
  "commander": "^12.1.0",
30
29
  "esbuild": "^0.25.12",
@@ -34,7 +33,7 @@
34
33
  "react": "^19.2.7",
35
34
  "react-dom": "^19.2.7",
36
35
  "zod": "^3.24.1",
37
- "@sudajs/theme-engine": "6.5.1"
36
+ "@sudajs/theme-engine": "6.7.0"
38
37
  },
39
38
  "devDependencies": {
40
39
  "@tailwindcss/postcss": "^4.3.0",
@@ -80,6 +80,11 @@ Extract shared code only when multiple sections genuinely reuse it.
80
80
  names and filler suffixes such as `Section`, `Component`, `New`, or `Custom`.
81
81
  - Prefer semantic variant names. Use numbered variants only when the variants
82
82
  truly share a purpose and no concise name describes the difference.
83
+ - Every page section must declare `section.motion`. Use `"reveal"` for ordinary
84
+ sections that use the shared viewport entrance animation. Use `"custom"` when
85
+ the section owns its own client animation (timeline, carousel, marquee, or
86
+ scroll-scrub), and use `"none"` when it must remain static. TypeScript will
87
+ report an error when the motion declaration is missing.
83
88
  - Keep `fields`, TypeScript props, `defaultProps`, and `render` aligned. Every
84
89
  normal field needs a sensible serializable default.
85
90
  - Use the most specific field: `url`, `image`, `video`, `media`, `icon`,
@@ -100,7 +105,8 @@ Default ownership is strict:
100
105
  <div>{val || "defaultValue"}</div>;
101
106
 
102
107
  // Correct: defaultProps owns the default.
103
- export const Example: SudaComponentConfig<ExampleProps> = {
108
+ export const Example: SudaSectionConfig<ExampleProps> = {
109
+ section: { category: "features", variant: "example", motion: "reveal" },
104
110
  fields: { val: { type: "text", label: t("sections.example.fields.val") } },
105
111
  defaultProps: { val: "defaultValue" },
106
112
  render: ({ val }) => <div>{val}</div>,
@@ -155,9 +161,119 @@ it must not substitute placeholder copy, labels, links, icons, or menu items.
155
161
 
156
162
  - Define one editable design system in `sourceManifest.designSystem`, expose it
157
163
  at the layout root, resolve it once there, and render components through the
158
- resulting `--suda-*` CSS variables.
159
- - Tailwind token mappings use `@theme inline`. Hand-written CSS consumes
160
- `--suda-*` variables directly, not `--color-*` or `--radius-*` aliases.
164
+ resulting unprefixed CSS variables. Do not add or consume `--suda-*` aliases.
165
+ - Set `tokens.layout.contentWidth` in every preset in `src/manifest.ts`. Choose only
166
+ `"1024px"`, `"1200px"`, `"1280px"`, or `"1440px"`:
167
+
168
+ ```ts
169
+ tokens: {
170
+ colors: { /* ... */ },
171
+ radius: { /* ... */ },
172
+ layout: { contentWidth: "1280px" },
173
+ }
174
+ ```
175
+
176
+ - Configure the three font slots in every preset's `tokens`. Use font IDs, not
177
+ display names:
178
+
179
+ ```ts
180
+ typography: {
181
+ heading: "inter",
182
+ body: "noto-sans-sc",
183
+ mono: "jetbrains-mono",
184
+ },
185
+ ```
186
+
187
+ Read available IDs from `SUDA_THEME_FONT_FAMILIES`. System fonts such as
188
+ `system-sans`, `microsoft-yahei`, and `songti-sc` do not make network requests.
189
+
190
+ - Connect the design system field in the layout root's `fields` and
191
+ `defaultProps`:
192
+
193
+ ```tsx
194
+ fields: {
195
+ designSystem: designSystemField(t("layout.root.fields.designSystem"), sourceManifest.designSystem),
196
+ },
197
+ defaultProps: {
198
+ designSystem: createThemeDesignDefault(sourceManifest.designSystem),
199
+ },
200
+ ```
201
+
202
+ - Use the resolved CSS variables for theme typography:
203
+
204
+ ```css
205
+ .my-theme-root {
206
+ font-family: var(--font-sans);
207
+ }
208
+
209
+ .my-theme-root h1,
210
+ .my-theme-root h2,
211
+ .my-theme-root h3,
212
+ .my-theme-root h4,
213
+ .my-theme-root h5,
214
+ .my-theme-root h6 {
215
+ font-family: var(--font-heading);
216
+ }
217
+
218
+ .my-theme-root code,
219
+ .my-theme-root pre {
220
+ font-family: var(--font-mono);
221
+ }
222
+ ```
223
+
224
+ - In the layout root, resolve the selected preset and apply its CSS variables
225
+ to the element that wraps `children`. Use this exact pattern:
226
+
227
+ ```tsx
228
+ render: ({ children, designSystem }) => {
229
+ const tokens = resolveThemeDesignTokens(sourceManifest.designSystem, designSystem);
230
+ return (
231
+ <div
232
+ className="my-theme-root"
233
+ style={createThemeDesignCssVariables(tokens)}
234
+ >
235
+ {children}
236
+ </div>
237
+ );
238
+ },
239
+ ```
240
+
241
+ - Do not set `max-width` on the layout root or `PageOutlet`; doing so prevents
242
+ full-width sections. Apply `--content-max-width` only to inner content
243
+ containers. Wrap Header and Footer contents in that same container when they
244
+ must align with page sections.
245
+
246
+ - Every page section root must include `w-full`. Put the section background,
247
+ border, and full-bleed media on this root. Do not set `max-width` on it.
248
+ - Put normal section content inside one shared container class and define that
249
+ class once in `src/styles.css`:
250
+
251
+ ```css
252
+ .my-theme-container {
253
+ width: min(var(--content-max-width), 100%);
254
+ margin-inline: auto;
255
+ }
256
+ ```
257
+
258
+ - Use the same container inside full-width Hero and band sections when their
259
+ text, buttons, cards, or navigation must align with ordinary sections. Omit
260
+ the inner container only when the section content itself must be edge to
261
+ edge. Do not repeat `1024px`, `1200px`, `1280px`, or `1440px` in section CSS.
262
+ - `--background` / `--foreground`: default page surface and readable default
263
+ content. `--primary` / `--primary-foreground`: main brand and action surface
264
+ plus readable content on it. `--secondary` / `--secondary-foreground`:
265
+ supporting surface plus its content; it is not assumed light or dark.
266
+ - `--accent` / `--accent-foreground`: high-attention emphasis, selection, or
267
+ decorative surface plus its content, not a default CTA substitute.
268
+ `--muted` / `--muted-foreground`: subdued surface and secondary text/icons.
269
+ Always pair a chosen surface with its corresponding foreground token.
270
+ - `--radius` is the generic fallback; use `--radius-card`,
271
+ `--radius-button`, and `--radius-input` for cards, command buttons, and form
272
+ controls. Tailwind token mappings use `@theme inline`; hand-written CSS may
273
+ consume these variables directly.
274
+ - The theme decides which semantic surface each of its components uses. Do not
275
+ alter built-in section styling to accommodate a single theme palette, and do
276
+ not hardcode replacement colors in individual sections.
161
277
  - Keep typography, spacing, containers, buttons, cards, forms, and media frames
162
278
  consistent across ordinary sections, CMS sections, and starter pages.
163
279
  - Theme-local assets live under top-level `assets/`. Required preview images are
@@ -177,12 +293,14 @@ Before editing:
177
293
  When adding or changing a section:
178
294
 
179
295
  1. Define serializable props in its dedicated `.tsx` file.
180
- 2. Add `label`, useful `ai.instructions`, typed `fields`, optional
296
+ 2. Add `section` metadata with `category`, semantic `variant`, and an explicit
297
+ `motion` mode (`reveal`, `custom`, or `none`).
298
+ 3. Add `label`, useful `ai.instructions`, typed `fields`, optional
181
299
  `blockSlots`, `defaultProps`, and `render`.
182
- 3. Add all editor label keys to `src/locales/en.json`.
183
- 4. Register it in `pageConfig` and the appropriate translated category.
184
- 5. Add realistic usage to starter pages or CMS templates where appropriate.
185
- 6. Verify public rendering plus editor desktop, tablet, and mobile behavior.
300
+ 4. Add all editor label keys to `src/locales/en.json`.
301
+ 5. Register it in `pageConfig` and the appropriate translated category.
302
+ 6. Add realistic usage to starter pages or CMS templates where appropriate.
303
+ 7. Verify public rendering plus editor desktop, tablet, and mobile behavior.
186
304
 
187
305
  Before handoff:
188
306
 
@@ -192,7 +310,17 @@ pnpm typecheck
192
310
  pnpm build
193
311
  pnpm validate
194
312
  suda theme check
313
+ suda theme visual-check
195
314
  ```
196
315
 
197
- Run `suda theme capture` when visuals change. Do not use removed skip flags or
198
- bypass build, validation, AI metadata, locale, or screenshot checks.
316
+ Run `suda theme visual-check` after developing or substantially changing a
317
+ theme. It renders every page component declared by the theme's
318
+ `pageConfig.components` with each declared design preset at desktop, tablet,
319
+ and mobile sizes, writes screenshots and `report.json` to
320
+ `.suda-build/visual-check/`, and fails on token contrast, semantic surface,
321
+ or page-overflow errors. Fixed-header overlap is reported as a layout warning;
322
+ solve it in the theme layout. Platform built-in sections are covered by
323
+ theme-engine tests, not repeated for every theme. Run `suda theme capture` when
324
+ publish-preview visuals change.
325
+ Do not use removed skip flags or bypass build, validation, AI metadata, locale,
326
+ visual, or screenshot checks.
@@ -34,6 +34,20 @@ pnpm build
34
34
 
35
35
  By default capture writes `assets/preview/desktop.png`, `assets/preview/tablet.png`, and `assets/preview/mobile.png`.
36
36
 
37
+ Run a reusable visual audit after creating or substantially changing the theme:
38
+
39
+ ```bash
40
+ suda theme visual-check
41
+ ```
42
+
43
+ It renders every page component declared by the theme's `pageConfig.components`
44
+ with every design preset at desktop, tablet, and mobile sizes. Screenshots and
45
+ `report.json` are written to `.suda-build/visual-check/`; the command fails on
46
+ token contrast, semantic surface-color, or page-overflow errors. Platform
47
+ built-in sections are covered by theme-engine tests rather than being repeated
48
+ for every theme. Use `--preset <id>`, `--device <device>`, or `--full-page` to
49
+ narrow a review.
50
+
37
51
  Fields can use `visibleIf: ({ props }, { fields }) => boolean` for synchronous dynamic visibility. The starter hero shows this by hiding the Logo picker when `showLogo` is false. Suda converts `visibleIf` to Puck `resolveFields` during normalization, and the generated AI/MCP section schema marks those fields as dynamically visible.
38
52
 
39
53
  When adding fields, prefer purpose-built Suda field types over generic text inputs: use `url` for links, `image` / `video` / `media` for media, `color` for colors, `icon` for icon names, `range` for bounded numbers, `spacing` for spacing tokens, `menu` for navigation, `select` / `radio` for fixed choices, and `array` / `object` for structured values. Multi-column sections such as card grids should expose a `columns` prop.
@@ -21,10 +21,18 @@ icons, AI metadata, or block slots. The root `AGENTS.md` rules still apply.
21
21
 
22
22
  ## Field source of truth
23
23
 
24
- Type props first. Use `SudaComponentConfig<Props>`, `SudaFields<Props>`, and
24
+ Type props first. Use `SudaSectionConfig<Props>`, `SudaFields<Props>`, and
25
25
  `defineSudaPageData(pageConfig, data)` to catch shape mismatches. Do not invent a
26
26
  parallel Zod schema for theme fields.
27
27
 
28
+ Every page section must include `section` metadata. Its `motion` value is
29
+ required: ordinary sections use `"reveal"`, sections with their own client-side
30
+ animation use `"custom"`, and intentionally static sections use `"none"`.
31
+ The rendered section root must also carry the matching
32
+ `data-section-motion="reveal"`, `"custom"`, or `"none"` attribute. Do not rely
33
+ on a class-name convention; TypeScript checks the metadata contract and the
34
+ runtime uses the data attribute to avoid hiding content before hydration.
35
+
28
36
  Keep these four structures aligned:
29
37
 
30
38
  1. TypeScript props.
@@ -219,7 +227,8 @@ pricing cards, feature rows, stats, timeline items, or contact methods.
219
227
  ```tsx
220
228
  type HeroProps = { title?: string; actions?: Slot };
221
229
 
222
- export const Hero: SudaComponentConfig<HeroProps> = {
230
+ export const Hero: SudaSectionConfig<HeroProps> = {
231
+ section: { category: "hero", variant: "centered", motion: "reveal" },
223
232
  label: t("sections.hero.label"),
224
233
  ai: { instructions: "Primary landing-page introduction. Use once near the top." },
225
234
  fields: {
@@ -54,6 +54,10 @@ scales, radii, shadows, or spacing.
54
54
  consumes (`background`, `foreground`, `primary`, `primaryForeground`,
55
55
  `secondary`, `secondaryForeground`, `accent`, `accentForeground`, `muted`,
56
56
  `mutedForeground`) and consumed radius tokens (`card`, `button`, `input`).
57
+ - Define `typography.heading`, `typography.body`, and `typography.mono` with
58
+ font ids exported by `@sudajs/theme-engine`. Use
59
+ `SUDA_THEME_FONT_FAMILIES` when presenting or validating available choices;
60
+ never persist raw family names, CSS, or custom font URLs.
57
61
  - Do not invent platform-facing token names without checking generated types.
58
62
  - Expose `designSystemField(...)` only at the layout root and initialize it with
59
63
  `createThemeDesignDefault(sourceManifest.designSystem)`.
@@ -64,17 +68,39 @@ scales, radii, shadows, or spacing.
64
68
 
65
69
  For Tailwind, map Suda tokens through `@theme inline` and consume semantic
66
70
  utilities such as `bg-primary`, `text-primary-foreground`, and `rounded-card`.
67
- The `inline` keyword is required because Suda injects `--suda-*` variables on
71
+ The `inline` keyword is required because Suda injects unprefixed variables on
68
72
  the rendered theme root.
69
73
 
70
- Hand-written CSS consumes `--suda-*` directly. Do not use Suda-backed aliases
71
- such as `var(--color-primary)`, `var(--color-background)`, or
72
- `var(--radius-card)` in hand-written rules. For derived values, declare a
73
- theme-prefixed variable on the root:
74
+ Use the token meanings below rather than guessing from a theme's current
75
+ palette. Foreground tokens always pair with the surface token actually used.
76
+
77
+ | CSS variables | Meaning and use |
78
+ | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
79
+ | `--background` / `--foreground` | Default page surface and readable default content. |
80
+ | `--primary` / `--primary-foreground` | Main brand and action surface, plus readable content on it. |
81
+ | `--secondary` / `--secondary-foreground` | Supporting surface, plus readable content on it. It is not assumed light or dark. |
82
+ | `--accent` / `--accent-foreground` | High-attention emphasis, selection, or decorative surface, plus readable content on it. Do not use it as a default CTA substitute. |
83
+ | `--muted` / `--muted-foreground` | Low-emphasis surface and secondary text/icons. |
84
+ | `--radius` | Generic radius fallback. |
85
+ | `--radius-card` / `--radius-button` / `--radius-input` | Radius for cards, command buttons, and form controls. |
86
+ | `--font-heading` | Selected heading font family stack. |
87
+ | `--font-sans` | Selected body font family stack. |
88
+ | `--font-mono` | Selected monospace font family stack. |
89
+
90
+ Apply `--font-sans` to the theme root and body copy, `--font-heading` to
91
+ headings, and `--font-mono` to code-like content. The host loads one deduplicated
92
+ Google Fonts stylesheet for the selected web fonts with `display=swap`.
93
+ System-font choices do not make a network request, and every variable includes
94
+ a readable cross-platform fallback stack.
95
+
96
+ Hand-written CSS consumes these unprefixed variables directly. Do not use
97
+ Tailwind aliases such as `var(--color-primary)` or `var(--color-background)`
98
+ in hand-written rules. For derived values, declare a theme-prefixed variable on
99
+ the root:
74
100
 
75
101
  ```css
76
102
  .my-theme-root {
77
- --my-theme-primary-hover: color-mix(in srgb, var(--suda-color-primary) 84%, black);
103
+ --my-theme-primary-hover: color-mix(in srgb, var(--primary) 84%, black);
78
104
  }
79
105
 
80
106
  .my-theme-button:hover {
@@ -82,8 +108,10 @@ theme-prefixed variable on the root:
82
108
  }
83
109
  ```
84
110
 
85
- Build/check/publish reject hand-written consumption of Suda-backed Tailwind
86
- aliases.
111
+ Theme authors decide which semantic surface each component uses. Do not alter
112
+ built-in section styling to match one theme palette, and do not hardcode
113
+ replacement colors in individual sections. Build/check/publish reject
114
+ hand-written consumption of Suda-backed Tailwind aliases.
87
115
 
88
116
  ## Visual consistency
89
117
 
@@ -35,6 +35,14 @@ export const sourceManifest: ThemeSourceManifest = {
35
35
  button: "8px",
36
36
  input: "8px",
37
37
  },
38
+ layout: {
39
+ contentWidth: "1280px",
40
+ },
41
+ typography: {
42
+ heading: "inter",
43
+ body: "inter",
44
+ mono: "jetbrains-mono",
45
+ },
38
46
  },
39
47
  },
40
48
  {
@@ -58,6 +66,14 @@ export const sourceManifest: ThemeSourceManifest = {
58
66
  button: "9999px",
59
67
  input: "10px",
60
68
  },
69
+ layout: {
70
+ contentWidth: "1280px",
71
+ },
72
+ typography: {
73
+ heading: "space-grotesk",
74
+ body: "inter",
75
+ mono: "geist-mono",
76
+ },
61
77
  },
62
78
  },
63
79
  ],
@@ -1,4 +1,4 @@
1
- import type { SudaComponentConfig } from "@sudajs/theme-engine";
1
+ import type { SudaSectionConfig, SudaSectionMetadata } from "@sudajs/theme-engine";
2
2
  import {
3
3
  SudaIcon,
4
4
  type SudaIconName,
@@ -48,7 +48,8 @@ type FeaturedPostsProps = {
48
48
  postList?: ThemePostResourceQuery;
49
49
  } & PuckExtras;
50
50
 
51
- export const Hero: SudaComponentConfig<HeroProps> = {
51
+ export const Hero: SudaSectionConfig<HeroProps> = {
52
+ section: { category: "hero", variant: "centered", motion: "reveal" } satisfies SudaSectionMetadata,
52
53
  label: t("sections.hero.label"),
53
54
  ai: {
54
55
  instructions:
@@ -98,7 +99,7 @@ export const Hero: SudaComponentConfig<HeroProps> = {
98
99
  }) => {
99
100
  const logoSrc = resolveAsset(puck?.metadata, logo);
100
101
  return (
101
- <section className="__SUDA_THEME_KEY__-section __SUDA_THEME_KEY__-hero">
102
+ <section data-section-motion="reveal" className="w-full __SUDA_THEME_KEY__-section __SUDA_THEME_KEY__-hero">
102
103
  {showLogo && logoSrc ? (
103
104
  <img className="__SUDA_THEME_KEY__-hero-logo" src={logoSrc} alt="" />
104
105
  ) : null}
@@ -113,7 +114,8 @@ export const Hero: SudaComponentConfig<HeroProps> = {
113
114
  },
114
115
  };
115
116
 
116
- export const FeatureGrid: SudaComponentConfig<FeatureGridProps> = {
117
+ export const FeatureGrid: SudaSectionConfig<FeatureGridProps> = {
118
+ section: { category: "features", variant: "feature-grid", motion: "reveal" } satisfies SudaSectionMetadata,
117
119
  label: t("sections.featureGrid.label"),
118
120
  ai: {
119
121
  instructions:
@@ -146,7 +148,7 @@ export const FeatureGrid: SudaComponentConfig<FeatureGridProps> = {
146
148
  ],
147
149
  },
148
150
  render: ({ title, description, columns = 3, features = [] }) => (
149
- <section className="__SUDA_THEME_KEY__-section">
151
+ <section data-section-motion="reveal" className="w-full __SUDA_THEME_KEY__-section">
150
152
  <h2>{title}</h2>
151
153
  <p>{description}</p>
152
154
  <div
@@ -165,7 +167,8 @@ export const FeatureGrid: SudaComponentConfig<FeatureGridProps> = {
165
167
  ),
166
168
  };
167
169
 
168
- export const Testimonial: SudaComponentConfig<TestimonialProps> = {
170
+ export const Testimonial: SudaSectionConfig<TestimonialProps> = {
171
+ section: { category: "social-proof", variant: "testimonial", motion: "reveal" } satisfies SudaSectionMetadata,
169
172
  label: t("sections.testimonial.label"),
170
173
  ai: {
171
174
  instructions:
@@ -184,7 +187,7 @@ export const Testimonial: SudaComponentConfig<TestimonialProps> = {
184
187
  role: "Founder",
185
188
  },
186
189
  render: ({ quote, author, role }) => (
187
- <section className="__SUDA_THEME_KEY__-section __SUDA_THEME_KEY__-quote">
190
+ <section data-section-motion="reveal" className="w-full __SUDA_THEME_KEY__-section __SUDA_THEME_KEY__-quote">
188
191
  <blockquote>{quote}</blockquote>
189
192
  <p>
190
193
  {author} · {role}
@@ -193,7 +196,8 @@ export const Testimonial: SudaComponentConfig<TestimonialProps> = {
193
196
  ),
194
197
  };
195
198
 
196
- export const CallToAction: SudaComponentConfig<CallToActionProps> = {
199
+ export const CallToAction: SudaSectionConfig<CallToActionProps> = {
200
+ section: { category: "conversion", variant: "cta", motion: "reveal" } satisfies SudaSectionMetadata,
197
201
  label: t("sections.callToAction.label"),
198
202
  ai: {
199
203
  instructions:
@@ -214,7 +218,7 @@ export const CallToAction: SudaComponentConfig<CallToActionProps> = {
214
218
  buttonHref: "#contact",
215
219
  },
216
220
  render: ({ title, description, buttonLabel, buttonHref }) => (
217
- <section className="__SUDA_THEME_KEY__-section __SUDA_THEME_KEY__-cta" id="contact">
221
+ <section data-section-motion="reveal" className="w-full __SUDA_THEME_KEY__-section __SUDA_THEME_KEY__-cta" id="contact">
218
222
  <h2>{title}</h2>
219
223
  <p>{description}</p>
220
224
  <a className="__SUDA_THEME_KEY__-button" href={buttonHref}>
@@ -224,7 +228,8 @@ export const CallToAction: SudaComponentConfig<CallToActionProps> = {
224
228
  ),
225
229
  };
226
230
 
227
- export const FeaturedPosts: SudaComponentConfig<FeaturedPostsProps> = {
231
+ export const FeaturedPosts: SudaSectionConfig<FeaturedPostsProps> = {
232
+ section: { category: "content", variant: "featured-posts", motion: "reveal" } satisfies SudaSectionMetadata,
228
233
  label: t("sections.featuredPosts.label"),
229
234
  ai: {
230
235
  instructions:
@@ -251,7 +256,7 @@ export const FeaturedPosts: SudaComponentConfig<FeaturedPostsProps> = {
251
256
  const postHref = (post: (typeof posts)[number]) =>
252
257
  post.url || (post.slug ? `/posts/${encodeURIComponent(post.slug)}` : "#");
253
258
  return (
254
- <section className="__SUDA_THEME_KEY__-section">
259
+ <section data-section-motion="reveal" className="w-full __SUDA_THEME_KEY__-section">
255
260
  <h2>{title}</h2>
256
261
  <p>{description}</p>
257
262
  <div className="__SUDA_THEME_KEY__-grid" style={{ "--__SUDA_THEME_KEY__-columns": 3 }}>
@@ -1,24 +1,58 @@
1
1
  @import "tailwindcss";
2
2
  @source "./**/*.{ts,tsx}";
3
3
 
4
+ [data-suda-motion-ready] [data-section-motion="reveal"] {
5
+ opacity: 0;
6
+ transform: translateY(24px);
7
+ transition:
8
+ opacity 700ms ease,
9
+ transform 700ms ease;
10
+ }
11
+ [data-suda-motion-ready] [data-section-motion="reveal"][data-motion-visible="true"],
12
+ [data-suda-motion-reduced] [data-section-motion="reveal"] {
13
+ opacity: 1;
14
+ transform: none;
15
+ }
16
+ @media (prefers-reduced-motion: reduce) {
17
+ [data-section-motion="reveal"] {
18
+ opacity: 1;
19
+ transform: none;
20
+ transition: none;
21
+ }
22
+ }
23
+
4
24
  .__SUDA_THEME_KEY__-root {
5
- font-family: Inter, ui-sans-serif, system-ui, sans-serif;
6
- color: var(--suda-color-foreground, #111827);
7
- background: var(--suda-color-background, #ffffff);
25
+ font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
26
+ color: var(--foreground, #111827);
27
+ background: var(--background, #ffffff);
28
+ }
29
+ .__SUDA_THEME_KEY__-root h1,
30
+ .__SUDA_THEME_KEY__-root h2,
31
+ .__SUDA_THEME_KEY__-root h3,
32
+ .__SUDA_THEME_KEY__-root h4,
33
+ .__SUDA_THEME_KEY__-root h5,
34
+ .__SUDA_THEME_KEY__-root h6 {
35
+ font-family: var(--font-heading, ui-serif, Georgia, serif);
36
+ }
37
+ .__SUDA_THEME_KEY__-root code,
38
+ .__SUDA_THEME_KEY__-root pre,
39
+ .__SUDA_THEME_KEY__-root kbd,
40
+ .__SUDA_THEME_KEY__-root samp {
41
+ font-family: var(--font-mono, ui-monospace, monospace);
8
42
  }
9
43
  .__SUDA_THEME_KEY__-header,
10
44
  .__SUDA_THEME_KEY__-footer {
11
45
  padding: 20px clamp(20px, 5vw, 64px);
12
- border-bottom: 1px solid var(--suda-color-muted, #e5e7eb);
46
+ border-bottom: 1px solid var(--muted, #e5e7eb);
13
47
  }
14
48
  .__SUDA_THEME_KEY__-footer {
15
49
  display: flex;
16
50
  flex-wrap: wrap;
17
51
  align-items: center;
18
52
  gap: 12px 20px;
19
- border-top: 1px solid var(--suda-color-muted, #e5e7eb);
53
+ border-top: 1px solid var(--muted, #e5e7eb);
20
54
  border-bottom: 0;
21
- color: var(--suda-color-muted-foreground, #6b7280);
55
+ color: var(--muted-foreground, #6b7280);
22
56
  }
23
57
  .__SUDA_THEME_KEY__-footer a {
24
58
  display: inline-flex;
@@ -35,19 +69,19 @@
35
69
  padding: 64px clamp(20px, 5vw, 64px);
36
70
  }
37
71
  .__SUDA_THEME_KEY__-hero {
38
- background: var(--suda-color-accent, #f8fafc);
72
+ background: var(--accent, #f8fafc);
39
73
  }
40
74
  .__SUDA_THEME_KEY__-hero-logo {
41
75
  width: 64px;
42
76
  height: 64px;
43
- border-radius: var(--suda-radius-card, 14px);
77
+ border-radius: var(--radius-card, 14px);
44
78
  margin-bottom: 24px;
45
79
  }
46
80
  .__SUDA_THEME_KEY__-eyebrow {
47
81
  text-transform: uppercase;
48
82
  letter-spacing: 0.08em;
49
83
  font-size: 12px;
50
- color: var(--suda-color-primary, #2563eb);
84
+ color: var(--primary, #2563eb);
51
85
  font-weight: 700;
52
86
  }
53
87
  .__SUDA_THEME_KEY__-section h1 {
@@ -65,16 +99,16 @@
65
99
  .__SUDA_THEME_KEY__-section p {
66
100
  max-width: 680px;
67
101
  line-height: 1.7;
68
- color: var(--suda-color-muted-foreground, #4b5563);
102
+ color: var(--muted-foreground, #4b5563);
69
103
  }
70
104
  .__SUDA_THEME_KEY__-button {
71
105
  display: inline-flex;
72
106
  align-items: center;
73
107
  min-height: 42px;
74
108
  padding: 0 18px;
75
- border-radius: var(--suda-radius-button, 8px);
76
- background: var(--suda-color-primary, #111827);
77
- color: var(--suda-color-primary-foreground, #ffffff);
109
+ border-radius: var(--radius-button, 8px);
110
+ background: var(--primary, #111827);
111
+ color: var(--primary-foreground, #ffffff);
78
112
  text-decoration: none;
79
113
  font-weight: 700;
80
114
  }
@@ -85,14 +119,14 @@
85
119
  margin-top: 28px;
86
120
  }
87
121
  .__SUDA_THEME_KEY__-card {
88
- border: 1px solid var(--suda-color-muted, #e5e7eb);
89
- border-radius: var(--suda-radius-card, 8px);
122
+ border: 1px solid var(--muted, #e5e7eb);
123
+ border-radius: var(--radius-card, 8px);
90
124
  padding: 20px;
91
125
  }
92
126
  .__SUDA_THEME_KEY__-card-icon {
93
127
  width: 24px;
94
128
  height: 24px;
95
- color: var(--suda-color-primary, #2563eb);
129
+ color: var(--primary, #2563eb);
96
130
  }
97
131
  .__SUDA_THEME_KEY__-quote blockquote {
98
132
  max-width: 780px;
@@ -101,15 +135,15 @@
101
135
  margin: 0 0 16px;
102
136
  }
103
137
  .__SUDA_THEME_KEY__-cta {
104
- background: var(--suda-color-foreground, #111827);
105
- color: var(--suda-color-background, #ffffff);
138
+ background: var(--foreground, #111827);
139
+ color: var(--background, #ffffff);
106
140
  }
107
141
  .__SUDA_THEME_KEY__-cta p {
108
- color: var(--suda-color-muted, #d1d5db);
142
+ color: var(--muted, #d1d5db);
109
143
  }
110
144
  .__SUDA_THEME_KEY__-cta .__SUDA_THEME_KEY__-button {
111
- background: var(--suda-color-background, #ffffff);
112
- color: var(--suda-color-foreground, #111827);
145
+ background: var(--background, #ffffff);
146
+ color: var(--foreground, #111827);
113
147
  }
114
148
  @media (max-width: 760px) {
115
149
  .__SUDA_THEME_KEY__-grid {