@comet/agent-features 9.0.0-beta.5 → 9.0.0-beta.6

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": "@comet/agent-features",
3
- "version": "9.0.0-beta.5",
3
+ "version": "9.0.0-beta.6",
4
4
  "description": "Agent features (skills and rules) for Comet projects",
5
5
  "repository": {
6
6
  "directory": "packages/agent-features",
@@ -16,7 +16,7 @@ alwaysApply: false
16
16
  - Prefer descriptive identifiers over explanatory comments — comments drift out of sync with the code.
17
17
  - Boolean variables must read as booleans: prefix with `is` / `has` / `should` (exceptions: well-known flags like `loading`, `disabled`).
18
18
  - Name booleans in the **affirmative**: `isComplete`, not `isNotComplete`. Negate at the use site with `!`.
19
- - Do **not** use abbreviations. Exceptions: widely-known protocols/acronyms (HTML, CSS, TCP, API, SSO…) and names dictated by third parties.
19
+ - Avoid non-obvious abbreviations. Exceptions: obvious, conventional abbreviations whose meaning is unambiguous in context (e.g. `i` for an index, `id`, `ref`, `props`), widely-known protocols/acronyms (HTML, CSS, TCP, API, SSO…), and names dictated by third parties. When in doubt, spell it out.
20
20
  - When an acronym appears in PascalCase / camelCase, treat it like a word: `GuiController`, `UiElement` — not `GUIController` / `UIElement`.
21
21
 
22
22
  ## Control flow
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: comet-admin-ui
3
+ description: Building or editing admin UI in a project that uses @comet/admin and its sibling packages — pages, dashboards, dialogs, widgets, layouts, or component styling. Use even for small UI changes, to build with Comet's theme, components, and helpers instead of custom sx/styled CSS, hard-coded values, or Box layouts.
4
+ ---
5
+
6
+ # Building admin UIs with @comet/admin
7
+
8
+ `@comet/admin` and its sibling packages ship a design system: a theme (spacing,
9
+ colors, shadows, typography, breakpoints) and a library of ready-made components. For
10
+ internationalization, the project uses `react-intl` to translate text, numbers, and dates. The
11
+ components and types are available in the consuming project through the installed packages —
12
+ import them directly (e.g. `import { Button, MainContent } from "@comet/admin"`).
13
+
14
+ Build admin UI by composing what the design system already provides. Add custom styling only
15
+ after the system genuinely can't express what you need.
16
+
17
+ ## Core principle
18
+
19
+ **Prefer Comet's theme values, components, and helpers over custom styling.** Three reasons:
20
+
21
+ 1. **Reviewability.** When styling lives in the theme and in components, the markup stays
22
+ declarative and diffs stay small. A component that mixes `sx`, inline `style`, and `styled()`
23
+ is hard to read and hard to review — the layout, the styling, and the logic blur together.
24
+ 2. **Automatic upgrades.** A project's visual design is often built against a _future_ version of
25
+ the design system, so it won't fully match what the currently installed components and theme
26
+ produce. That gap is expected — it is not a reason to add custom styling to force the match.
27
+ Use the current Comet components and tokens as they are; a later library upgrade closes the gap
28
+ on its own, with no hand-written CSS to find and rework.
29
+ 3. **Consistency.** Every screen built from the same components and tokens looks and behaves the
30
+ same way.
31
+
32
+ This holds **even when a project's design deliberately differs** from the current library
33
+ defaults: prefer the Comet component or token, and apply that project-specific difference by
34
+ configuring the theme — not by re-styling individual components. Add custom styling only when
35
+ explicitly instructed, or when no component, prop, or token can produce the result.
36
+
37
+ ## Decision framework
38
+
39
+ Before writing any styling or markup, work down this list and stop at the first step that applies:
40
+
41
+ 1. **Is there a component for this?** Use it rather than assembling the same thing from
42
+ `Box` + CSS (page structure, cards, toolbars, dialogs, alerts, buttons, …).
43
+ 2. **Is there a prop for this?** Props apply the correct theme values with no CSS — e.g.
44
+ `elevation` / `square` on `Paper` and `Card`, `variant` on `Button` and `Typography`,
45
+ `spacing` on `Stack` and `Grid`.
46
+ 3. **Is there a theme value for this?** Read spacing, colors, and shadows from the theme
47
+ (`theme.spacing(n)`, `theme.palette.*`, `theme.shadows[n]`) instead of hard-coding pixels,
48
+ hex colors, or shadow strings.
49
+ 4. **Is there a helper for this?** User-facing text, numbers, and dates go through the i18n
50
+ helpers (`FormattedMessage`, `FormattedNumber`, `FormattedDate`), never hard-coded.
51
+ 5. **Only then, custom-style** — using `styled()` (not `sx` or inline `style`) and reading
52
+ values from the theme.
53
+
54
+ ## Styling and theme
55
+
56
+ ### Custom styling: `styled()`, not `sx` or inline `style`
57
+
58
+ When you do need custom styling, write it with `styled()` from `@mui/material/styles` and give the
59
+ result a name that says what it is. Styling in `sx` props or inline `style` mixes the look into the
60
+ markup, so layout, styling, and logic blur together and the diff is harder to follow. A named
61
+ styled component keeps the markup declarative and the styling in one place.
62
+
63
+ ```tsx
64
+ // Avoid — sx and inline style mixed into the markup
65
+ <Box sx={{ padding: 2, backgroundColor: "#fff", borderRadius: 1 }} style={{ marginTop: 16 }}>
66
+ {children}
67
+ </Box>;
68
+
69
+ // Prefer — a named styled component, styling separated from markup
70
+ const Panel = styled("div")`
71
+ padding: ${({ theme }) => theme.spacing(2)};
72
+ background-color: ${({ theme }) => theme.palette.background.paper};
73
+ `;
74
+
75
+ <Panel>{children}</Panel>;
76
+ ```
77
+
78
+ ### Spacing and color: theme tokens, not hard-coded values
79
+
80
+ Read spacing and color from the theme instead of typing pixels and hex codes. The theme is the
81
+ single place those values are defined, so reading from it keeps every screen consistent and lets a
82
+ theme change reach all of them at once. Comet's spacing base is `5px` — `theme.spacing(1)` is `5px`,
83
+ `theme.spacing(2)` is `10px` — and it takes up to four arguments for top, right, bottom, and left.
84
+
85
+ ```tsx
86
+ // Avoid — hard-coded pixels and colors
87
+ const Header = styled("header")`
88
+ padding: 16px 24px;
89
+ color: #1a1a1a;
90
+ border-bottom: 1px solid #e0e0e0;
91
+ `;
92
+
93
+ // Prefer — spacing and palette tokens from the theme
94
+ const Header = styled("header")`
95
+ padding: ${({ theme }) => theme.spacing(2, 3)};
96
+ color: ${({ theme }) => theme.palette.text.primary};
97
+ border-bottom: 1px solid ${({ theme }) => theme.palette.divider};
98
+ `;
99
+ ```
100
+
101
+ Use the palette tokens — `primary`, `secondary`, `error`, `warning`, `info`, `success`,
102
+ `grey[50…900]`, `divider`, `text`, `background`, `action` — rather than naming raw colors.
103
+
104
+ ### Elevation and shape: `elevation` and `square`, not manual CSS
105
+
106
+ Shadows and corner radius come from props on `Paper` and `Card`, not hand-written CSS. Comet defines
107
+ four shadow elevations (1–4); higher values are `none`. The `elevation` prop selects one, and the
108
+ `square` prop toggles the rounded corner. Read `theme.shadows[n]` directly only inside a `styled()`
109
+ component that cannot be a `Paper` or `Card`.
110
+
111
+ ```tsx
112
+ // Avoid — manual shadow and radius on a plain element
113
+ <div style={{ boxShadow: "0 0 8px rgba(0,0,0,0.1)", borderRadius: 4 }}>{children}</div>
114
+
115
+ // Prefer — a Paper carrying the theme's elevation and shape
116
+ <Paper elevation={2}>{children}</Paper>
117
+ ```
118
+
119
+ ### Typography: `<Typography variant>`, not manual font CSS
120
+
121
+ Render text through `<Typography>` with a variant rather than setting font size, weight, and line
122
+ height by hand. The variant carries the type scale and its responsive steps, so headings and body
123
+ text stay in proportion across breakpoints. Available variants: `h1`–`h6`, `body1`, `body2`,
124
+ `subtitle1`, `subtitle2`, `caption`, `overline`, `list`, `listItem`, `button`.
125
+
126
+ ```tsx
127
+ // Avoid — font properties set by hand
128
+ const Title = styled("h2")`
129
+ font-size: 20px;
130
+ font-weight: 600;
131
+ line-height: 26px;
132
+ `;
133
+
134
+ // Prefer — a Typography variant from the type scale
135
+ <Typography variant="h4">{title}</Typography>;
136
+ ```
137
+
138
+ ## Organizing styled components
139
+
140
+ By default, define a component's styled parts at the bottom of its own file, below the component
141
+ that uses them:
142
+
143
+ ```
144
+ imports → types → component → styled components
145
+ ```
146
+
147
+ When a file grows hard to read, refactor the component itself first — split it into smaller
148
+ components and compose them, each keeping its own styled parts at the bottom. Move styles to a
149
+ separate `*.sc.ts` sibling only when you are asked to, or when the styles grow but the component
150
+ cannot be split logically. A `*.sc.ts` file is private to its equally-named component
151
+ (`FooButton.sc.ts` belongs to `FooButton.tsx`). Don't import one component's `*.sc.ts` from another:
152
+ that couples them through styling neither owns. When styling is shared, give it a single owner — a
153
+ reusable component (below).
154
+
155
+ When the same styled component is used by several components, it is no longer a styled part of any
156
+ one of them. Promote it to its own reusable component: one export per file, named exactly as that
157
+ export so it is easy to find, e.g. `SpecialButton.ts` exporting
158
+ `export const SpecialButton = styled(Button)`. Group several small related ones into a single
159
+ generically-named file only when explicitly instructed.
@@ -71,11 +71,17 @@ For block type decision guidance, see [block-types.md](references/block-types.md
71
71
 
72
72
  Before generating any code:
73
73
 
74
- 1. **Locate `api`, `admin`, `site` directories.** The `site` directory is optional.
75
- 2. **Find existing blocks directories**typically `src/documents/pages/blocks/`. Some shared blocks live in `common/blocks/` or similar. Check both.
76
- 3. **Verify referenced blocks exist** — search for any blocks named in the prompt (e.g., `HeadingBlock`, `LinkBlock`) in all layers and note their import paths.
77
- 4. **Find registration targets** search for `createBlocksBlock` usages to locate `PageContentBlock`, `ContentGroupBlock`, or other targets. Note file paths in all layers.
78
- 5. **Confirm site directory** — if absent, skip all site steps.
74
+ 1. **Locate the `api` and `admin` directories.**
75
+ 2. **Locate the site package or packages by their dependencies, not the folder name.** `site` is only the conventional name a project may name the package differently or have several. A `package.json` (outside `node_modules`) that depends on `@comet/site-nextjs` or `@comet/site-react` is certainly a site package, since site blocks render through these (`withPreview`, `PropsWithData`):
76
+ ```bash
77
+ grep -rl --include='package.json' --exclude-dir=node_modules -E '"@comet/site-(nextjs|react)"' . | xargs -n1 dirname
78
+ ```
79
+ A frontend package can also exist without those dependencies — for example one that renders blocks for email with `@comet/mail-react`. Skip the site steps only if you find neither a site package nor another frontend package.
80
+ 3. **Find existing blocks directories** — typically `src/documents/pages/blocks/`. Some shared blocks live in `common/blocks/` or similar. Check both.
81
+ 4. **Verify referenced blocks exist** — search for any blocks named in the prompt (e.g., `HeadingBlock`, `LinkBlock`) in all layers and note their import paths.
82
+ 5. **Find registration targets** — search for `createBlocksBlock` usages to locate `PageContentBlock`, `ContentGroupBlock`, or other targets. Note file paths in all layers.
83
+
84
+ In the later steps and the reference files, example paths written as `site/…` mean the site package's root and `@src/…` means its `src/` directory. They show the conventional layout — substitute the actual package folder found here.
79
85
 
80
86
  ---
81
87
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Comet-specific conventions for the site layer. All site blocks live in `{BlockName}Block.tsx` (PascalCase) under `site/src/`, typically `documents/pages/blocks/` or `common/blocks/`.
4
4
 
5
- Skip site block creation when the project has no `site` directory.
5
+ Skip site block creation when the project has no site package (see [Step 2](../SKILL.md#step-2--discover-the-project) for how to detect one).
6
6
 
7
7
  ---
8
8
 
@@ -171,10 +171,10 @@ function MyEmail() {
171
171
 
172
172
  ### Contributing to `<MjmlHead>` and `<MjmlAttributes>`
173
173
 
174
- `MjmlMailRoot` accepts two optional slot props for content that can't be expressed via `registerStyles`:
174
+ `MjmlMailRoot` accepts `head` and `attributes` slot props for content that can't be expressed via `registerStyles`:
175
175
 
176
- - `head` — appended inside `<MjmlHead>` after the registered-styles block. Use for `<MjmlFont>`, `<MjmlConditionalComment>`, `<MjmlPreview>`, or `<MjmlStyle>` content that depends on React context at render time.
177
- - `attributes` — appended inside the built-in `<MjmlAttributes>` after the default `<MjmlAll>`. Use for `<MjmlClass>` or per-element defaults (e.g. `<MjmlText fontSize="14px" />`).
176
+ - `head` — use for `<MjmlFont>`, `<MjmlConditionalComment>`, `<MjmlPreview>`, or `<MjmlStyle>` content that depends on React context at render time.
177
+ - `attributes` — use for `<MjmlClass>` or per-element defaults (e.g. `<MjmlText fontSize="14px" />`).
178
178
 
179
179
  Pass the children directly — do not wrap them in another `<MjmlHead>` / `<MjmlAttributes>`:
180
180
 
@@ -227,20 +227,13 @@ declare module "@comet/mail-react" {
227
227
 
228
228
  Place `declare module` blocks in your theme file below the `createTheme()` call.
229
229
 
230
- → For the full theme structure, defaults, and all component props, read [`references/components-and-theme.md`](references/components-and-theme.md).
230
+ → For the full theme structure, responsive values, module augmentation, and scoped theming, read [`references/components-and-theme.md`](references/components-and-theme.md).
231
231
 
232
232
  ---
233
233
 
234
234
  ## Configuration
235
235
 
236
- `Config` is an augmentable interface that can be used to expose, e.g., environment-specific values such as asset base URLs.
237
-
238
- - **`Config`** — augmentable interface, no built-in keys. All keys optional.
239
- - **`MjmlMailRoot.config`** — optional prop that exposes a `Config` value to descendants.
240
- - **`useConfig`** — hook returning the nearest `Config`, or `{}` if no provider is mounted.
241
- - **`ConfigProvider`** — standalone provider for cases that bypass `MjmlMailRoot`.
242
-
243
- Add keys via module augmentation:
236
+ `Config` exposes environment-specific values e.g. asset base URLs to every component in the tree. Add keys via module augmentation:
244
237
 
245
238
  ```ts
246
239
  declare module "@comet/mail-react" {
@@ -270,12 +263,12 @@ const config: Config = { assetBaseUrl: process.env.ASSET_BASE_URL };
270
263
  | --------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------- |
271
264
  | `MjmlMailRoot` | Root element, provides theme, renders `<mjml>` skeleton | — |
272
265
  | `MjmlWrapper` | Groups sections sharing a background; theme-aware default bg | — |
273
- | `MjmlSection` | Full-width row, supports `indent` and `disableResponsiveBehavior` | `.mjmlSection`, `.mjmlSection--indented` |
266
+ | `MjmlSection` | Full-width row; theme indentation, columns stack on mobile | `.mjmlSection`, `.mjmlSection--indented` |
274
267
  | `MjmlColumn` | Vertical column inside a section | — |
275
- | `MjmlText` | Themed text block with `variant` and `bottomSpacing` | `.mjmlText`, `.mjmlText--{variant}`, `.mjmlText--bottomSpacing` |
268
+ | `MjmlText` | Themed text block with typography variants | `.mjmlText`, `.mjmlText--{variant}`, `.mjmlText--bottomSpacing` |
276
269
  | `MjmlImage` | Responsive image | `.mjmlImage` |
277
270
  | `MjmlPixelImageBlock` | Renders a Comet CMS `PixelImageBlockData` via `MjmlImage` | `.mjmlPixelImageBlock` |
278
- | `MjmlButton` | Button (ending tag) | |
271
+ | `MjmlButton` | Themed button (ending tag), theme styling and variants | `.mjmlButton`, `.mjmlButton--{variant}` |
279
272
  | `MjmlDivider` | Themed horizontal divider, configurable through theme and variants | `.mjmlDivider`, `.mjmlDivider--{variant}` |
280
273
  | `MjmlSpacer` | Vertical spacing | — |
281
274
  | `MjmlRaw` | Raw HTML escape hatch (ending tag) | — |
@@ -284,17 +277,18 @@ const config: Config = { assetBaseUrl: process.env.ASSET_BASE_URL };
284
277
 
285
278
  | Component | Purpose | CSS Classes |
286
279
  | --------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------- |
287
- | `HtmlText` | Themed text as HTML element (default `<td>`) | `.htmlText`, `.htmlText--{variant}`, `.htmlText--bottomSpacing` |
280
+ | `HtmlText` | Themed text rendered as an HTML element | `.htmlText`, `.htmlText--{variant}`, `.htmlText--bottomSpacing` |
288
281
  | `HtmlInlineLink` | `<a>` that inherits parent text styles, works in Outlook | `.htmlInlineLink` |
289
282
  | `HtmlImage` | Responsive image (`<img>`) | `.htmlImage` |
290
283
  | `HtmlPixelImageBlock` | Renders a Comet CMS `PixelImageBlockData` as `<img>` | `.htmlPixelImageBlock` |
284
+ | `HtmlButton` | Themed button for ending tags or non-MJML contexts | `.htmlButton`, `.htmlButton--{variant}` |
291
285
  | `HtmlDivider` | Themed horizontal divider, configurable through theme and variants | `.htmlDivider`, `.htmlDivider--{variant}` |
292
286
 
293
- Text components (`MjmlText`, `HtmlText`) support `variant` and `bottomSpacing` props tied to the theme. Variants define typography presets (font size, weight, line height, color). Both support responsive values that change at different breakpoints. Set a `defaultVariant` in the theme to apply a variant automatically when none is specified.
287
+ Variants are named typography presets (font size, weight, line height, color) defined in the theme; their values can change per breakpoint.
294
288
 
295
289
  All components are imported from `@comet/mail-react` — never from `@faire/mjml-react` directly.
296
290
 
297
- → For complete component props, responsive values, scoped theming, and MJML re-exports, read [`references/components-and-theme.md`](references/components-and-theme.md).
291
+ → For theme tokens, responsive values, component behavior, scoped theming, and MJML re-exports, read [`references/components-and-theme.md`](references/components-and-theme.md).
298
292
 
299
293
  ---
300
294
 
@@ -319,7 +313,7 @@ All components are imported from `@comet/mail-react` — never from `@faire/mjml
319
313
 
320
314
  #### Configuration
321
315
 
322
- Both blocks read `validSizes` and `baseUrl` from `config.pixelImageBlock` and throw if it's missing. Wire it once on `MjmlMailRoot.config`. In a typical Comet project, `validSizes` is the union of `cometConfig.images.imageSizes` and `cometConfig.images.deviceSizes`; `baseUrl` is the API URL.
316
+ Both blocks require `config.pixelImageBlock` and throw without it. Wire it once on `MjmlMailRoot.config`:
323
317
 
324
318
  ```tsx
325
319
  const config: Config = {
@@ -332,9 +326,7 @@ const config: Config = {
332
326
 
333
327
  #### Render width
334
328
 
335
- The required `width` prop is the desktop render width the width at which the image displays in the default breakpoint. The block picks the actual source size from `validSizes`, accounting for retina displays.
336
-
337
- Use `largestPossibleRenderWidth` when the image stretches wider on a narrower breakpoint than its desktop width — e.g. a two-column layout that stacks on mobile. Defaults to `theme.sizes.bodyWidth`.
329
+ The block picks the source size from the configured `validSizes`, accounting for retina displays. Use `largestPossibleRenderWidth` when the image stretches wider on a narrower breakpoint than its desktop width e.g. a two-column layout that stacks on mobile.
338
330
 
339
331
  ```tsx
340
332
  <MjmlPixelImageBlock data={pixelImageData} width={300} largestPossibleRenderWidth={420} />
@@ -342,7 +334,7 @@ Use `largestPossibleRenderWidth` when the image stretches wider on a narrower br
342
334
 
343
335
  #### Aspect ratio
344
336
 
345
- By default, the rendered aspect ratio comes from the DAM crop area. Override it with the `aspectRatio` prop accepts a number or a `"WxH"` / `"W:H"` / `"W/H"` string.
337
+ Without `aspectRatio`, the rendered ratio comes from the DAM crop area. Pass `aspectRatio` to override it.
346
338
 
347
339
  ```tsx
348
340
  <MjmlPixelImageBlock data={pixelImageData} width={536} aspectRatio="16x9" />
@@ -1,10 +1,10 @@
1
1
  # Components & Theme Reference
2
2
 
3
- Detailed reference for all `@comet/mail-react` components, the theme system, module augmentation, and scoped theming.
3
+ Theme system, module augmentation, scoped theming, and the component behavior for `@comet/mail-react` that its types and TSDoc don't capture on their own. For prop names, types, and defaults, read the types and their TSDoc.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- 1. [Theme Structure & Defaults](#theme-structure--defaults)
7
+ 1. [Theme tokens](#theme-tokens)
8
8
  2. [Module Augmentation Interfaces](#module-augmentation-interfaces)
9
9
  3. [MjmlMailRoot](#mjmlmailroot)
10
10
  4. [MjmlSection](#mjmlsection)
@@ -13,29 +13,15 @@ Detailed reference for all `@comet/mail-react` components, the theme system, mod
13
13
  7. [HtmlInlineLink](#htmlinlinelink)
14
14
  8. [Image](#image)
15
15
  9. [Divider](#divider)
16
- 10. [Scoped Theming](#scoped-theming)
17
- 11. [MJML Component Re-exports](#mjml-component-re-exports)
16
+ 10. [Button](#button)
17
+ 11. [Scoped Theming](#scoped-theming)
18
+ 12. [MJML Component Re-exports](#mjml-component-re-exports)
18
19
 
19
20
  ---
20
21
 
21
- ## Theme Structure & Defaults
22
-
23
- `createTheme(overrides?)` merges partial overrides into the default theme:
24
-
25
- | Path | Default | Description |
26
- | --------------------------- | ----------------------------- | ----------------------------------------------------------- |
27
- | `sizes.bodyWidth` | `600` | Email container width in pixels |
28
- | `sizes.contentIndentation` | `{ default: 32, mobile: 16 }` | Left/right padding when `indent` is used |
29
- | `breakpoints.default` | `createBreakpoint(bodyWidth)` | Auto-set to body width unless explicitly overridden |
30
- | `breakpoints.mobile` | `createBreakpoint(420)` | Mobile breakpoint (`max-width: 419px`) |
31
- | `text.fontFamily` | System default | Base font family |
32
- | `text.fontSize` | `"16px"` | Base font size |
33
- | `text.lineHeight` | `"20px"` | Base line height |
34
- | `text.bottomSpacing` | `"16px"` | Default spacing below text when `bottomSpacing` prop is set |
35
- | `text.defaultVariant` | `undefined` | Variant applied when no `variant` prop is specified |
36
- | `text.variants` | `{}` | Named typography presets |
37
- | `colors.background.body` | `"#F2F2F2"` | Email body background |
38
- | `colors.background.content` | `"#FFFFFF"` | Section content background |
22
+ ## Theme tokens
23
+
24
+ `createTheme(overrides?)` merges partial overrides into the default theme; every token is optional and the `Theme` type lists them. Defaults live in `createTheme`. One behavior isn't visible from the types: **the default breakpoint derives from `sizes.bodyWidth`** unless `breakpoints.default` is overridden — they describe the same boundary, so setting them apart silently desyncs the layout.
39
25
 
40
26
  ### Breakpoints
41
27
 
@@ -129,6 +115,34 @@ declare module "@comet/mail-react" {
129
115
 
130
116
  Variant properties (`height`, `backgroundColor`, `backgroundImage`) accept responsive values.
131
117
 
118
+ ### ButtonVariants
119
+
120
+ Controls the `variant` prop on `MjmlButton` and `HtmlButton`:
121
+
122
+ ```ts
123
+ const theme = createTheme({
124
+ button: {
125
+ defaultVariant: "primary",
126
+ variants: {
127
+ primary: { backgroundColor: "#5B4FC7", color: "#FFFFFF" },
128
+ gradient: {
129
+ backgroundColor: "#5B4FC7",
130
+ backgroundImage: "linear-gradient(90deg, #5B4FC7, #9C5BC7)",
131
+ },
132
+ },
133
+ },
134
+ });
135
+
136
+ declare module "@comet/mail-react" {
137
+ interface ButtonVariants {
138
+ primary: true;
139
+ gradient: true;
140
+ }
141
+ }
142
+ ```
143
+
144
+ Variant properties (`color`, `backgroundColor`, `backgroundImage`, `border`, `borderRadius`, `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, `padding`) accept responsive values.
145
+
132
146
  ### ThemeBreakpoints
133
147
 
134
148
  Built-in keys are `default` and `mobile`. Add custom breakpoints:
@@ -181,14 +195,6 @@ declare module "@comet/mail-react" {
181
195
 
182
196
  Root element for every email template. Renders the full MJML skeleton (`<mjml>`, `<mj-head>`, `<mj-body>`) and provides the theme to all descendant components.
183
197
 
184
- **Props:**
185
-
186
- | Prop | Type | Default | Description |
187
- | ---------- | ----------- | --------------- | --------------------------------------------------------- |
188
- | `theme` | `Theme` | `createTheme()` | Theme for the email |
189
- | `config` | `Config` | — | Augmentable values exposed to descendants via `useConfig` |
190
- | `children` | `ReactNode` | — | Email content |
191
-
192
198
  **What it configures from the theme:**
193
199
 
194
200
  - Body width from `theme.sizes.bodyWidth`
@@ -206,15 +212,6 @@ Root element for every email template. Renders the full MJML skeleton (`<mjml>`,
206
212
 
207
213
  Full-width horizontal row with theme integration.
208
214
 
209
- **Props** (in addition to standard MJML section props):
210
-
211
- | Prop | Type | Default | Description |
212
- | --------------------------- | ------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------- |
213
- | `indent` | `boolean` | `false` | Applies theme-based left/right padding |
214
- | `disableResponsiveBehavior` | `boolean` | `false` | Prevents columns from stacking on mobile |
215
- | `slotProps` | `{ group?: Partial<MjmlGroupProps> }` | — | Forward props to internal `MjmlGroup` (when `disableResponsiveBehavior` is set) |
216
- | `backgroundColor` | `string` | `theme.colors.background.content` | Override section background color |
217
-
218
215
  **CSS classes:** `.mjmlSection`, `.mjmlSection--indented` (when `indent` is set).
219
216
 
220
217
  ```tsx
@@ -238,14 +235,7 @@ Indentation values come from `theme.sizes.contentIndentation`, which supports re
238
235
 
239
236
  Groups multiple `MjmlSection`s that share a background. Must be a direct child of `MjmlBody`; sections go inside.
240
237
 
241
- **Props** (standard MJML wrapper props):
242
-
243
- | Prop | Type | Default | Description |
244
- | ----------------- | ----------- | --------------------------------- | --------------------------------------------------------------------- |
245
- | `backgroundColor` | `string` | `theme.colors.background.content` | Background applied to the whole wrapper; shows through inner sections |
246
- | `children` | `ReactNode` | — | Sections to group |
247
-
248
- When a theme is present, `MjmlWrapper` applies `theme.colors.background.content` as its default `backgroundColor`. Inner `MjmlSection`s suppress their own theme-default background so the wrapper's color is visible; an explicit `backgroundColor` prop on an inner section still wins.
238
+ The wrapper takes the theme's content background by default. Inner `MjmlSection`s suppress their own theme-default background so the wrapper's color shows through; an explicit `backgroundColor` on an inner section still wins.
249
239
 
250
240
  ```tsx
251
241
  <MjmlWrapper backgroundColor="#dddddd">
@@ -272,13 +262,6 @@ Typical uses: multi-section footers with their own color, or grouping sections b
272
262
 
273
263
  MJML text component — use inside `MjmlColumn` following the standard layout model.
274
264
 
275
- **Props** (in addition to standard MJML text props):
276
-
277
- | Prop | Type | Description |
278
- | --------------- | -------------------- | ----------------------------------------- |
279
- | `variant` | `keyof TextVariants` | Named typography preset from theme |
280
- | `bottomSpacing` | `boolean` | Add spacing below (from theme or variant) |
281
-
282
265
  **CSS classes:** `.mjmlText`, `.mjmlText--{variant}`, `.mjmlText--bottomSpacing`.
283
266
 
284
267
  ```tsx
@@ -293,14 +276,6 @@ Variant styles merge on top of base theme text styles — properties not set by
293
276
 
294
277
  Themed text for use inside ending tags (`MjmlText`, `MjmlRaw`) or custom HTML structures where MJML components can't be used. Renders a plain HTML element.
295
278
 
296
- **Props:**
297
-
298
- | Prop | Type | Default | Description |
299
- | --------------- | -------------------- | ------- | ---------------------- |
300
- | `variant` | `keyof TextVariants` | — | Typography preset |
301
- | `bottomSpacing` | `boolean` | `false` | Add spacing below |
302
- | `element` | `string` | `"td"` | HTML element to render |
303
-
304
279
  **CSS classes:** `.htmlText`, `.htmlText--{variant}`, `.htmlText--bottomSpacing`.
305
280
 
306
281
  ```tsx
@@ -335,8 +310,6 @@ The base spacing value comes from `theme.text.bottomSpacing`. Variants can overr
335
310
 
336
311
  `<a>` element for use inside `MjmlText` or `HtmlText`. Inherits parent's font styles — even on Outlook Desktop, which normally overrides link typography with its own defaults.
337
312
 
338
- **Props:** Standard `<a>` props. Defaults to `target="_blank"` and `text-decoration: underline`.
339
-
340
313
  **CSS class:** `.htmlInlineLink`.
341
314
 
342
315
  ```tsx
@@ -380,6 +353,23 @@ Themed horizontal line, styled from `theme.divider` (base styles plus variants).
380
353
 
381
354
  ---
382
355
 
356
+ ## Button
357
+
358
+ Themed button, styled from `theme.button` (base styles plus variants). Falls back to a built-in default when no theme is in scope; only `variant` requires a theme.
359
+
360
+ - **`MjmlButton`** — inside `MjmlColumn`. Classes `.mjmlButton`, `.mjmlButton--{variant}`, `.mjmlButton--fullWidth`.
361
+ - **`HtmlButton`** — inside ending tags (`MjmlRaw`) or any non-MJML context. Classes `.htmlButton`, `.htmlButton--{variant}`, `.htmlButton--fullWidth`.
362
+
363
+ `theme.button.padding` is the inner spacing around the label; `MjmlButton`'s `padding` prop is the outer spacing. `fullWidth` makes the button span its container. A gradient `backgroundImage` overlays the solid `backgroundColor`, which stays the fallback for clients that drop `background-image` (Outlook). Outlook ignores `border-radius`, so rounded buttons render with square corners there.
364
+
365
+ ```tsx
366
+ <MjmlButton variant="primary" fullWidth href="https://example.com">
367
+ Click me
368
+ </MjmlButton>
369
+ ```
370
+
371
+ ---
372
+
383
373
  ## Scoped Theming
384
374
 
385
375
  `ThemeProvider` applies a different theme to a subtree. Common use: dark-background sections.
@@ -436,6 +426,6 @@ Theme-aware `registerStyles` entries always resolve against the **root theme** f
436
426
 
437
427
  `@comet/mail-react` re-exports all MJML components from `@faire/mjml-react`. Consumers import everything from `@comet/mail-react` — never from `@faire/mjml-react` directly.
438
428
 
439
- Common re-exports: `MjmlColumn`, `MjmlButton`, `MjmlSpacer`, `MjmlTable`, `MjmlRaw`, `MjmlGroup`, `MjmlAttributes`, `MjmlAll`, `MjmlClass`, `MjmlStyle`, `MjmlComment`, `MjmlConditionalComment`.
429
+ Common re-exports: `MjmlColumn`, `MjmlSpacer`, `MjmlTable`, `MjmlRaw`, `MjmlGroup`, `MjmlAttributes`, `MjmlAll`, `MjmlClass`, `MjmlStyle`, `MjmlComment`, `MjmlConditionalComment`.
440
430
 
441
431
  For the full MJML tag reference: https://documentation.mjml.io/