@comet/mail-react 9.0.0-beta.4 → 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.
Files changed (99) hide show
  1. package/README.md +68 -9
  2. package/lib/blocks/pixelImage/HtmlPixelImageBlock.js +2 -11
  3. package/lib/blocks/pixelImage/MjmlPixelImageBlock.d.ts +2 -2
  4. package/lib/blocks/pixelImage/MjmlPixelImageBlock.js +1 -10
  5. package/lib/blocks.generated.d.ts +1 -0
  6. package/lib/components/button/HtmlButton.d.ts +9 -0
  7. package/lib/components/button/HtmlButton.js +58 -0
  8. package/lib/components/button/MjmlButton.d.ts +10 -0
  9. package/lib/components/button/MjmlButton.js +91 -0
  10. package/lib/components/button/buttonProps.d.ts +35 -0
  11. package/lib/components/button/defaultButtonStyles.d.ts +2 -0
  12. package/lib/components/button/defaultButtonStyles.js +9 -0
  13. package/lib/components/button/generateResponsiveButtonCss.d.ts +8 -0
  14. package/lib/components/button/generateResponsiveButtonCss.js +21 -0
  15. package/lib/components/divider/HtmlDivider.d.ts +9 -0
  16. package/lib/components/divider/HtmlDivider.js +43 -0
  17. package/lib/components/divider/MjmlDivider.d.ts +7 -0
  18. package/lib/components/divider/MjmlDivider.js +13 -0
  19. package/lib/components/divider/defaultDividerStyles.d.ts +2 -0
  20. package/lib/components/divider/defaultDividerStyles.js +4 -0
  21. package/lib/components/divider/dividerProps.d.ts +51 -0
  22. package/lib/components/divider/generateResponsiveDividerCss.d.ts +8 -0
  23. package/lib/components/divider/generateResponsiveDividerCss.js +15 -0
  24. package/lib/components/image/HtmlImage.d.ts +11 -0
  25. package/lib/components/image/HtmlImage.js +23 -0
  26. package/lib/components/image/MjmlImage.d.ts +10 -0
  27. package/lib/components/image/MjmlImage.js +22 -0
  28. package/lib/components/inlineLink/HtmlInlineLink.d.ts +8 -1
  29. package/lib/components/mailRoot/MjmlMailRoot.d.ts +3 -2
  30. package/lib/components/section/MjmlSection.d.ts +7 -1
  31. package/lib/components/text/HtmlText.d.ts +3 -0
  32. package/lib/components/text/MjmlText.d.ts +3 -0
  33. package/lib/components/text/textStyles.d.ts +1 -3
  34. package/lib/components/text/textStyles.js +20 -70
  35. package/lib/components/wrapper/MjmlWrapper.d.ts +9 -2
  36. package/lib/components/wrapper/MjmlWrapper.js +1 -1
  37. package/lib/index.d.ts +14 -2
  38. package/lib/index.js +7 -1
  39. package/lib/styles/generateResponsiveVariantCss.d.ts +26 -0
  40. package/lib/styles/generateResponsiveVariantCss.js +66 -0
  41. package/lib/theme/createTheme.d.ts +3 -1
  42. package/lib/theme/createTheme.js +2 -0
  43. package/lib/theme/defaultTheme.js +4 -0
  44. package/lib/theme/themeTypes.d.ts +98 -0
  45. package/package.json +14 -7
  46. package/lib/__stories__/examples/CustomBackgroundForFooter.stories.d.ts +0 -5
  47. package/lib/__stories__/examples/CustomBackgroundForFooter.stories.js +0 -32
  48. package/lib/__stories__/examples/CustomNestedFooterTheme.stories.d.ts +0 -5
  49. package/lib/__stories__/examples/CustomNestedFooterTheme.stories.js +0 -32
  50. package/lib/__stories__/examples/NotificationEmail.stories.d.ts +0 -5
  51. package/lib/__stories__/examples/NotificationEmail.stories.js +0 -25
  52. package/lib/__stories__/layout-patterns/AsymmetricTwoColumnLayout.stories.d.ts +0 -6
  53. package/lib/__stories__/layout-patterns/AsymmetricTwoColumnLayout.stories.js +0 -107
  54. package/lib/__stories__/layout-patterns/SymmetricFourColumnLayout.stories.d.ts +0 -4
  55. package/lib/__stories__/layout-patterns/SymmetricFourColumnLayout.stories.js +0 -58
  56. package/lib/__stories__/layout-patterns/SymmetricThreeColumnLayout.stories.d.ts +0 -5
  57. package/lib/__stories__/layout-patterns/SymmetricThreeColumnLayout.stories.js +0 -104
  58. package/lib/__stories__/layout-patterns/SymmetricTwoColumnLayout.stories.d.ts +0 -4
  59. package/lib/__stories__/layout-patterns/SymmetricTwoColumnLayout.stories.js +0 -47
  60. package/lib/blocks/pixelImage/__stories__/HtmlPixelImageBlock.stories.d.ts +0 -7
  61. package/lib/blocks/pixelImage/__stories__/HtmlPixelImageBlock.stories.js +0 -54
  62. package/lib/blocks/pixelImage/__stories__/MjmlPixelImageBlock.stories.d.ts +0 -7
  63. package/lib/blocks/pixelImage/__stories__/MjmlPixelImageBlock.stories.js +0 -54
  64. package/lib/blocks/pixelImage/__stories__/exampleBlockData.d.ts +0 -2
  65. package/lib/blocks/pixelImage/__stories__/exampleBlockData.js +0 -19
  66. package/lib/blocks/pixelImage/__tests__/usePixelImageBlockConfig.test.js +0 -21
  67. package/lib/blocks/pixelImage/__tests__/usePixelImageBlockData.test.js +0 -205
  68. package/lib/components/inlineLink/__stories__/HtmlInlineLink.stories.d.ts +0 -12
  69. package/lib/components/inlineLink/__stories__/HtmlInlineLink.stories.js +0 -56
  70. package/lib/components/mailRoot/__stories__/MjmlMailRoot.stories.d.ts +0 -7
  71. package/lib/components/mailRoot/__stories__/MjmlMailRoot.stories.js +0 -18
  72. package/lib/components/section/__stories__/MjmlSection.stories.d.ts +0 -9
  73. package/lib/components/section/__stories__/MjmlSection.stories.js +0 -30
  74. package/lib/components/text/__stories__/HtmlText.stories.d.ts +0 -12
  75. package/lib/components/text/__stories__/HtmlText.stories.js +0 -77
  76. package/lib/components/text/__stories__/MjmlText.stories.d.ts +0 -10
  77. package/lib/components/text/__stories__/MjmlText.stories.js +0 -71
  78. package/lib/components/text/__tests__/HtmlText.test.d.ts +0 -1
  79. package/lib/components/text/__tests__/HtmlText.test.js +0 -157
  80. package/lib/components/text/__tests__/MjmlText.test.d.ts +0 -1
  81. package/lib/components/text/__tests__/MjmlText.test.js +0 -112
  82. package/lib/components/wrapper/__stories__/MjmlWrapper.stories.d.ts +0 -10
  83. package/lib/components/wrapper/__stories__/MjmlWrapper.stories.js +0 -36
  84. package/lib/config/ConfigProvider.test.d.ts +0 -8
  85. package/lib/config/ConfigProvider.test.js +0 -30
  86. package/lib/server/renderMailHtml.test.d.ts +0 -1
  87. package/lib/server/renderMailHtml.test.js +0 -52
  88. package/lib/storybook/replaceImagesWithPublicUrl.test.d.ts +0 -1
  89. package/lib/storybook/replaceImagesWithPublicUrl.test.js +0 -55
  90. package/lib/theme/__stories__/ThemeProvider.stories.d.ts +0 -7
  91. package/lib/theme/__stories__/ThemeProvider.stories.js +0 -23
  92. package/lib/theme/createTheme.test.d.ts +0 -1
  93. package/lib/theme/createTheme.test.js +0 -52
  94. package/lib/theme/responsiveValue.test.d.ts +0 -1
  95. package/lib/theme/responsiveValue.test.js +0 -62
  96. package/lib/utils/css.test.d.ts +0 -1
  97. package/lib/utils/css.test.js +0 -26
  98. /package/lib/{blocks/pixelImage/__tests__/usePixelImageBlockConfig.test.d.ts → components/button/buttonProps.js} +0 -0
  99. /package/lib/{blocks/pixelImage/__tests__/usePixelImageBlockData.test.d.ts → components/divider/dividerProps.js} +0 -0
@@ -0,0 +1,10 @@
1
+ import { type IMjmlImageProps } from "@faire/mjml-react";
2
+ import type { ReactNode } from "react";
3
+ export type MjmlImageProps = IMjmlImageProps;
4
+ /**
5
+ * Renders an MJML image that adapts to the viewport width below the default breakpoint.
6
+ *
7
+ * Must be placed within an `MjmlColumn`. For raw HTML context (e.g. inside `MjmlRaw`),
8
+ * use `HtmlImage` instead.
9
+ */
10
+ export declare function MjmlImage({ className, ...restProps }: MjmlImageProps): ReactNode;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MjmlImage as BaseMjmlImage } from "@faire/mjml-react";
3
+ import clsx from "clsx";
4
+ import { registerStyles } from "../../styles/registerStyles.js";
5
+ import { css } from "../../utils/css.js";
6
+ /**
7
+ * Renders an MJML image that adapts to the viewport width below the default breakpoint.
8
+ *
9
+ * Must be placed within an `MjmlColumn`. For raw HTML context (e.g. inside `MjmlRaw`),
10
+ * use `HtmlImage` instead.
11
+ */
12
+ export function MjmlImage({ className, ...restProps }) {
13
+ return _jsx(BaseMjmlImage, { className: clsx("mjmlImage", className), ...restProps });
14
+ }
15
+ // MJML inlines a fixed `height` on the inner <img>; !important overrides it for responsive scaling.
16
+ registerStyles((theme) => css `
17
+ ${theme.breakpoints.default.belowMediaQuery} {
18
+ .mjmlImage img {
19
+ height: auto !important;
20
+ }
21
+ }
22
+ `);
@@ -1,5 +1,12 @@
1
1
  import type { ComponentProps, ReactNode } from "react";
2
- export type HtmlInlineLinkProps = ComponentProps<"a">;
2
+ export type HtmlInlineLinkProps = Omit<ComponentProps<"a">, "target"> & {
3
+ /**
4
+ * Where to open the linked document.
5
+ *
6
+ * @defaultValue `"_blank"`
7
+ */
8
+ target?: ComponentProps<"a">["target"];
9
+ };
3
10
  /**
4
11
  * Inline link styled to match the surrounding text, for use inside `HtmlText` or `MjmlText`.
5
12
  *
@@ -3,8 +3,9 @@ import { type Config } from "../../config/ConfigProvider.js";
3
3
  import type { Theme } from "../../theme/themeTypes.js";
4
4
  type MjmlMailRootProps = PropsWithChildren<{
5
5
  /**
6
- * Theme to use for the email. When omitted, the default theme
7
- * (equivalent to `createTheme()`) is used.
6
+ * Theme to use for the email.
7
+ *
8
+ * @defaultValue `createTheme()`
8
9
  */
9
10
  theme?: Theme;
10
11
  /** Extra content appended inside the built-in `<MjmlAttributes>`, after the default `<MjmlAll>`. */
@@ -1,6 +1,12 @@
1
1
  import { type IMjmlGroupProps, type IMjmlSectionProps } from "@faire/mjml-react";
2
2
  import type { ReactNode } from "react";
3
- export type MjmlSectionProps = IMjmlSectionProps & {
3
+ export type MjmlSectionProps = Omit<IMjmlSectionProps, "backgroundColor"> & {
4
+ /**
5
+ * Background color of the section.
6
+ *
7
+ * @defaultValue The theme's `colors.background.content`, unless inside an `MjmlWrapper`
8
+ */
9
+ backgroundColor?: IMjmlSectionProps["backgroundColor"];
4
10
  /** Applies theme-based content indentation with responsive overrides. */
5
11
  indent?: boolean;
6
12
  /** When true, child columns remain side-by-side on mobile instead of stacking vertically. */
@@ -21,6 +21,9 @@ interface HtmlTextOwnProps {
21
21
  * },
22
22
  * },
23
23
  * });
24
+ * ```
25
+ *
26
+ * @defaultValue The theme's `text.defaultVariant`, when set
24
27
  */
25
28
  variant?: VariantName;
26
29
  /** When true, applies spacing below the text. */
@@ -22,6 +22,9 @@ export type MjmlTextProps = IMjmlTextProps & {
22
22
  * },
23
23
  * },
24
24
  * });
25
+ * ```
26
+ *
27
+ * @defaultValue The theme's `text.defaultVariant`, when set
25
28
  */
26
29
  variant?: VariantName;
27
30
  /** When true, applies spacing below the text. */
@@ -1,6 +1,4 @@
1
- import type { TextVariantStyles, Theme } from "../../theme/themeTypes.js";
2
- type StylePropertyKey = Exclude<keyof TextVariantStyles, "bottomSpacing">;
3
- export declare const textStyleCssProperties: ReadonlyArray<[StylePropertyKey, string]>;
1
+ import type { Theme } from "../../theme/themeTypes.js";
4
2
  interface GenerateResponsiveTextCssOptions {
5
3
  /** Selector for text style overrides, given a variant name. */
6
4
  styleSelector: (variantName: string) => string;
@@ -1,74 +1,24 @@
1
- import { getResponsiveOverrides } from "../../theme/responsiveValue.js";
2
- import { css } from "../../utils/css.js";
3
- export const textStyleCssProperties = [
4
- ["fontFamily", "font-family"],
5
- ["fontSize", "font-size"],
6
- ["fontWeight", "font-weight"],
7
- ["fontStyle", "font-style"],
8
- ["lineHeight", "line-height"],
9
- ["letterSpacing", "letter-spacing"],
10
- ["textDecoration", "text-decoration"],
11
- ["textTransform", "text-transform"],
12
- ["color", "color"],
1
+ import { generateResponsiveVariantCss } from "../../styles/generateResponsiveVariantCss.js";
2
+ const textStyleProperties = [
3
+ "fontFamily",
4
+ "fontSize",
5
+ "fontWeight",
6
+ "fontStyle",
7
+ "lineHeight",
8
+ "letterSpacing",
9
+ "textDecoration",
10
+ "textTransform",
11
+ "color",
13
12
  ];
13
+ const spacingProperties = [{ themeKey: "bottomSpacing", cssProperties: "padding-bottom" }];
14
14
  /** Generates responsive CSS media queries for text variant overrides. */
15
15
  export function generateResponsiveTextCss(theme, options) {
16
- const { variants } = theme.text;
17
- if (!variants) {
18
- return css ``;
19
- }
20
- const cssChunks = [];
21
- for (const [variantName, variantStyles] of Object.entries(variants)) {
22
- if (!variantStyles) {
23
- continue;
24
- }
25
- const styleOverrides = new Map();
26
- const spacingOverrides = new Map();
27
- for (const [themeKey, cssProperty] of textStyleCssProperties) {
28
- const value = variantStyles[themeKey];
29
- if (value === undefined) {
30
- continue;
31
- }
32
- for (const { breakpointKey, value: breakpointValue } of getResponsiveOverrides(value)) {
33
- const declarations = styleOverrides.get(breakpointKey) ?? [];
34
- declarations.push(`${cssProperty}: ${String(breakpointValue)} !important`);
35
- styleOverrides.set(breakpointKey, declarations);
36
- }
37
- }
38
- const bottomSpacingValue = variantStyles.bottomSpacing;
39
- if (bottomSpacingValue !== undefined) {
40
- for (const { breakpointKey, value: breakpointValue } of getResponsiveOverrides(bottomSpacingValue)) {
41
- const declarations = spacingOverrides.get(breakpointKey) ?? [];
42
- declarations.push(`padding-bottom: ${String(breakpointValue)} !important`);
43
- spacingOverrides.set(breakpointKey, declarations);
44
- }
45
- }
46
- for (const [breakpointKey, declarations] of styleOverrides) {
47
- const breakpoint = theme.breakpoints[breakpointKey];
48
- if (!breakpoint) {
49
- continue;
50
- }
51
- cssChunks.push(css `
52
- ${breakpoint.belowMediaQuery} {
53
- ${options.styleSelector(variantName)} {
54
- ${declarations.join(";\n")}
55
- }
56
- }
57
- `);
58
- }
59
- for (const [breakpointKey, declarations] of spacingOverrides) {
60
- const breakpoint = theme.breakpoints[breakpointKey];
61
- if (!breakpoint) {
62
- continue;
63
- }
64
- cssChunks.push(css `
65
- ${breakpoint.belowMediaQuery} {
66
- ${options.spacingSelector(variantName)} {
67
- ${declarations.join(";\n")}
68
- }
69
- }
70
- `);
71
- }
72
- }
73
- return cssChunks.join("\n");
16
+ return generateResponsiveVariantCss({
17
+ breakpoints: theme.breakpoints,
18
+ variants: theme.text.variants,
19
+ groups: [
20
+ { selector: options.styleSelector, properties: textStyleProperties },
21
+ { selector: options.spacingSelector, properties: spacingProperties },
22
+ ],
23
+ });
74
24
  }
@@ -1,7 +1,14 @@
1
1
  import { type IMjmlWrapperProps } from "@faire/mjml-react";
2
2
  import type { ReactNode } from "react";
3
- export type MjmlWrapperProps = IMjmlWrapperProps;
3
+ export type MjmlWrapperProps = Omit<IMjmlWrapperProps, "backgroundColor"> & {
4
+ /**
5
+ * Background color of the wrapper.
6
+ *
7
+ * @defaultValue The theme's `colors.background.content`
8
+ */
9
+ backgroundColor?: IMjmlWrapperProps["backgroundColor"];
10
+ };
4
11
  /**
5
- * A wrapper that groups multiple sections sharing a background. Must be a direct child of MjmlBody.
12
+ * A wrapper that groups multiple sections sharing a background. Must be a direct child of `MjmlBody`.
6
13
  */
7
14
  export declare function MjmlWrapper({ children, ...restProps }: MjmlWrapperProps): ReactNode;
@@ -3,7 +3,7 @@ import { MjmlWrapper as BaseMjmlWrapper } from "@faire/mjml-react";
3
3
  import { useOptionalTheme } from "../../theme/ThemeProvider.js";
4
4
  import { InsideMjmlWrapperContext } from "./InsideMjmlWrapperContext.js";
5
5
  /**
6
- * A wrapper that groups multiple sections sharing a background. Must be a direct child of MjmlBody.
6
+ * A wrapper that groups multiple sections sharing a background. Must be a direct child of `MjmlBody`.
7
7
  */
8
8
  export function MjmlWrapper({ children, ...restProps }) {
9
9
  const theme = useOptionalTheme();
package/lib/index.d.ts CHANGED
@@ -8,6 +8,18 @@ export type { HtmlPixelImageBlockProps } from "./blocks/pixelImage/HtmlPixelImag
8
8
  export { HtmlPixelImageBlock } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
9
9
  export type { MjmlPixelImageBlockProps } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
10
10
  export { MjmlPixelImageBlock } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
11
+ export type { HtmlButtonProps } from "./components/button/HtmlButton.js";
12
+ export { HtmlButton } from "./components/button/HtmlButton.js";
13
+ export type { MjmlButtonProps } from "./components/button/MjmlButton.js";
14
+ export { MjmlButton } from "./components/button/MjmlButton.js";
15
+ export type { HtmlDividerProps } from "./components/divider/HtmlDivider.js";
16
+ export { HtmlDivider } from "./components/divider/HtmlDivider.js";
17
+ export type { MjmlDividerProps } from "./components/divider/MjmlDivider.js";
18
+ export { MjmlDivider } from "./components/divider/MjmlDivider.js";
19
+ export type { HtmlImageProps } from "./components/image/HtmlImage.js";
20
+ export { HtmlImage } from "./components/image/HtmlImage.js";
21
+ export type { MjmlImageProps } from "./components/image/MjmlImage.js";
22
+ export { MjmlImage } from "./components/image/MjmlImage.js";
11
23
  export type { HtmlInlineLinkProps } from "./components/inlineLink/HtmlInlineLink.js";
12
24
  export { HtmlInlineLink } from "./components/inlineLink/HtmlInlineLink.js";
13
25
  export { MjmlMailRoot } from "./components/mailRoot/MjmlMailRoot.js";
@@ -26,8 +38,8 @@ export { createTheme } from "./theme/createTheme.js";
26
38
  export type { ResponsiveValue } from "./theme/responsiveValue.js";
27
39
  export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
28
40
  export { ThemeProvider, useTheme } from "./theme/ThemeProvider.js";
29
- export type { TextStyles, TextVariants, TextVariantStyles, Theme, ThemeBackgroundColors, ThemeBreakpoint, ThemeBreakpoints, ThemeColors, ThemeSizes, ThemeText, } from "./theme/themeTypes.js";
41
+ export type { ButtonStyles, ButtonVariants, ButtonVariantStyles, DividerStyles, DividerVariants, DividerVariantStyles, TextStyles, TextVariants, TextVariantStyles, Theme, ThemeBackgroundColors, ThemeBreakpoint, ThemeBreakpoints, ThemeButton, ThemeColors, ThemeDivider, ThemeSizes, ThemeText, } from "./theme/themeTypes.js";
30
42
  export { css } from "./utils/css.js";
31
- export { Mjml, MjmlAccordion, MjmlAccordionElement, type IMjmlAccordionElementProps as MjmlAccordionElementProps, type IMjmlAccordionProps as MjmlAccordionProps, MjmlAccordionText, type IMjmlAccordionTextProps as MjmlAccordionTextProps, MjmlAccordionTitle, type IMjmlAccordionTitleProps as MjmlAccordionTitleProps, MjmlAll, type IMjmlAllProps as MjmlAllProps, MjmlAttributes, type IMjmlAttributesProps as MjmlAttributesProps, MjmlBody, type IMjmlBodyProps as MjmlBodyProps, MjmlBreakpoint, type IMjmlBreakpointProps as MjmlBreakpointProps, MjmlButton, type IMjmlButtonProps as MjmlButtonProps, MjmlCarousel, MjmlCarouselImage, type IMjmlCarouselImageProps as MjmlCarouselImageProps, type IMjmlCarouselProps as MjmlCarouselProps, MjmlClass, type IMjmlClassProps as MjmlClassProps, MjmlColumn, type IMjmlColumnProps as MjmlColumnProps, MjmlDivider, type IMjmlDividerProps as MjmlDividerProps, MjmlFont, type IMjmlFontProps as MjmlFontProps, MjmlGroup, type IMjmlGroupProps as MjmlGroupProps, MjmlHead, type IMjmlHeadProps as MjmlHeadProps, MjmlHero, type IMjmlHeroProps as MjmlHeroProps, MjmlHtmlAttribute, type IMjmlHtmlAttributeProps as MjmlHtmlAttributeProps, MjmlHtmlAttributes, type IMjmlHtmlAttributesProps as MjmlHtmlAttributesProps, MjmlImage, type IMjmlImageProps as MjmlImageProps, MjmlInclude, type IMjmlIncludeProps as MjmlIncludeProps, MjmlNavbar, MjmlNavbarLink, type IMjmlNavbarLinkProps as MjmlNavbarLinkProps, type IMjmlNavbarProps as MjmlNavbarProps, MjmlPreview, type IMjmlPreviewProps as MjmlPreviewProps, type IMjmlProps as MjmlProps, MjmlRaw, type IMjmlRawProps as MjmlRawProps, MjmlSelector, type IMjmlSelectorProps as MjmlSelectorProps, MjmlSocial, MjmlSocialElement, type IMjmlSocialElementProps as MjmlSocialElementProps, type IMjmlSocialProps as MjmlSocialProps, MjmlSpacer, type IMjmlSpacerProps as MjmlSpacerProps, MjmlStyle, type IMjmlStyleProps as MjmlStyleProps, MjmlTable, type IMjmlTableProps as MjmlTableProps, MjmlTitle, type IMjmlTitleProps as MjmlTitleProps, } from "@faire/mjml-react";
43
+ export { Mjml, MjmlAccordion, MjmlAccordionElement, type IMjmlAccordionElementProps as MjmlAccordionElementProps, type IMjmlAccordionProps as MjmlAccordionProps, MjmlAccordionText, type IMjmlAccordionTextProps as MjmlAccordionTextProps, MjmlAccordionTitle, type IMjmlAccordionTitleProps as MjmlAccordionTitleProps, MjmlAll, type IMjmlAllProps as MjmlAllProps, MjmlAttributes, type IMjmlAttributesProps as MjmlAttributesProps, MjmlBody, type IMjmlBodyProps as MjmlBodyProps, MjmlBreakpoint, type IMjmlBreakpointProps as MjmlBreakpointProps, MjmlCarousel, MjmlCarouselImage, type IMjmlCarouselImageProps as MjmlCarouselImageProps, type IMjmlCarouselProps as MjmlCarouselProps, MjmlClass, type IMjmlClassProps as MjmlClassProps, MjmlColumn, type IMjmlColumnProps as MjmlColumnProps, MjmlFont, type IMjmlFontProps as MjmlFontProps, MjmlGroup, type IMjmlGroupProps as MjmlGroupProps, MjmlHead, type IMjmlHeadProps as MjmlHeadProps, MjmlHero, type IMjmlHeroProps as MjmlHeroProps, MjmlHtmlAttribute, type IMjmlHtmlAttributeProps as MjmlHtmlAttributeProps, MjmlHtmlAttributes, type IMjmlHtmlAttributesProps as MjmlHtmlAttributesProps, MjmlInclude, type IMjmlIncludeProps as MjmlIncludeProps, MjmlNavbar, MjmlNavbarLink, type IMjmlNavbarLinkProps as MjmlNavbarLinkProps, type IMjmlNavbarProps as MjmlNavbarProps, MjmlPreview, type IMjmlPreviewProps as MjmlPreviewProps, type IMjmlProps as MjmlProps, MjmlRaw, type IMjmlRawProps as MjmlRawProps, MjmlSelector, type IMjmlSelectorProps as MjmlSelectorProps, MjmlSocial, MjmlSocialElement, type IMjmlSocialElementProps as MjmlSocialElementProps, type IMjmlSocialProps as MjmlSocialProps, MjmlSpacer, type IMjmlSpacerProps as MjmlSpacerProps, MjmlStyle, type IMjmlStyleProps as MjmlStyleProps, MjmlTable, type IMjmlTableProps as MjmlTableProps, MjmlTitle, type IMjmlTitleProps as MjmlTitleProps, } from "@faire/mjml-react";
32
44
  export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
33
45
  export { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
package/lib/index.js CHANGED
@@ -4,6 +4,12 @@ export { OneOfBlock } from "./blocks/factories/OneOfBlock.js";
4
4
  export { OptionalBlock } from "./blocks/factories/OptionalBlock.js";
5
5
  export { HtmlPixelImageBlock } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
6
6
  export { MjmlPixelImageBlock } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
7
+ export { HtmlButton } from "./components/button/HtmlButton.js";
8
+ export { MjmlButton } from "./components/button/MjmlButton.js";
9
+ export { HtmlDivider } from "./components/divider/HtmlDivider.js";
10
+ export { MjmlDivider } from "./components/divider/MjmlDivider.js";
11
+ export { HtmlImage } from "./components/image/HtmlImage.js";
12
+ export { MjmlImage } from "./components/image/MjmlImage.js";
7
13
  export { HtmlInlineLink } from "./components/inlineLink/HtmlInlineLink.js";
8
14
  export { MjmlMailRoot } from "./components/mailRoot/MjmlMailRoot.js";
9
15
  export { MjmlSection } from "./components/section/MjmlSection.js";
@@ -17,6 +23,6 @@ export { createTheme } from "./theme/createTheme.js";
17
23
  export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
18
24
  export { ThemeProvider, useTheme } from "./theme/ThemeProvider.js";
19
25
  export { css } from "./utils/css.js";
20
- export { Mjml, MjmlAccordion, MjmlAccordionElement, MjmlAccordionText, MjmlAccordionTitle, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlButton, MjmlCarousel, MjmlCarouselImage, MjmlClass, MjmlColumn, MjmlDivider, MjmlFont, MjmlGroup, MjmlHead, MjmlHero, MjmlHtmlAttribute, MjmlHtmlAttributes, MjmlImage, MjmlInclude, MjmlNavbar, MjmlNavbarLink, MjmlPreview, MjmlRaw, MjmlSelector, MjmlSocial, MjmlSocialElement, MjmlSpacer, MjmlStyle, MjmlTable, MjmlTitle, } from "@faire/mjml-react";
26
+ export { Mjml, MjmlAccordion, MjmlAccordionElement, MjmlAccordionText, MjmlAccordionTitle, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlCarousel, MjmlCarouselImage, MjmlClass, MjmlColumn, MjmlFont, MjmlGroup, MjmlHead, MjmlHero, MjmlHtmlAttribute, MjmlHtmlAttributes, MjmlInclude, MjmlNavbar, MjmlNavbarLink, MjmlPreview, MjmlRaw, MjmlSelector, MjmlSocial, MjmlSocialElement, MjmlSpacer, MjmlStyle, MjmlTable, MjmlTitle, } from "@faire/mjml-react";
21
27
  export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
22
28
  export { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
@@ -0,0 +1,26 @@
1
+ import { type ResponsiveValue } from "../theme/responsiveValue.js";
2
+ import type { ThemeBreakpoints } from "../theme/themeTypes.js";
3
+ type VariantStylesConstraint = Record<string, ResponsiveValue<string | number> | undefined>;
4
+ export interface ResponsiveVariantProperty<TVariantStyles> {
5
+ themeKey: keyof TVariantStyles;
6
+ /** CSS property name(s); defaults to the kebab-case form of `themeKey`. */
7
+ cssProperties?: string | readonly string[];
8
+ unit?: string;
9
+ }
10
+ /** A variant property is either a bare theme key (CSS name derived) or a descriptor for the exceptions. */
11
+ export type ResponsiveVariantProperties<TVariantStyles> = ReadonlyArray<keyof TVariantStyles | ResponsiveVariantProperty<TVariantStyles>>;
12
+ interface ResponsiveVariantGroup<TVariantStyles> {
13
+ selector: (variantName: string) => string;
14
+ properties: ResponsiveVariantProperties<TVariantStyles>;
15
+ }
16
+ interface GenerateResponsiveVariantCssOptions<TVariantStyles> {
17
+ breakpoints: ThemeBreakpoints;
18
+ variants: Record<string, TVariantStyles | undefined> | undefined;
19
+ groups: ReadonlyArray<ResponsiveVariantGroup<TVariantStyles>>;
20
+ }
21
+ /**
22
+ * Generates `max-width` media-query CSS for a themed component's variant overrides.
23
+ * Returns an empty string when no variant has breakpoint overrides.
24
+ */
25
+ export declare function generateResponsiveVariantCss<TVariantStyles extends VariantStylesConstraint>({ breakpoints, variants, groups, }: GenerateResponsiveVariantCssOptions<TVariantStyles>): string;
26
+ export {};
@@ -0,0 +1,66 @@
1
+ import { getResponsiveOverrides } from "../theme/responsiveValue.js";
2
+ import { css } from "../utils/css.js";
3
+ const toKebabCase = (value) => value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
4
+ /**
5
+ * Generates `max-width` media-query CSS for a themed component's variant overrides.
6
+ * Returns an empty string when no variant has breakpoint overrides.
7
+ */
8
+ export function generateResponsiveVariantCss({ breakpoints, variants, groups, }) {
9
+ if (!variants) {
10
+ return "";
11
+ }
12
+ const cssChunks = [];
13
+ for (const [variantName, variantStyles] of Object.entries(variants)) {
14
+ if (!variantStyles) {
15
+ continue;
16
+ }
17
+ for (const group of groups) {
18
+ const overridesByBreakpoint = collectOverridesByBreakpoint(variantStyles, group.properties);
19
+ cssChunks.push(renderMediaQueries({ breakpoints, selector: group.selector(variantName), overridesByBreakpoint }));
20
+ }
21
+ }
22
+ return cssChunks.filter(Boolean).join("\n");
23
+ }
24
+ function collectOverridesByBreakpoint(variantStyles, properties) {
25
+ const overridesByBreakpoint = new Map();
26
+ for (const entry of properties) {
27
+ const property = typeof entry === "object" ? entry : { themeKey: entry };
28
+ const value = variantStyles[property.themeKey];
29
+ if (value === undefined) {
30
+ continue;
31
+ }
32
+ const cssProperties = resolveCssProperties(property);
33
+ for (const { breakpointKey, value: breakpointValue } of getResponsiveOverrides(value)) {
34
+ const declarations = overridesByBreakpoint.get(breakpointKey) ?? [];
35
+ for (const cssProperty of cssProperties) {
36
+ declarations.push(formatDeclaration({ cssProperty, value: breakpointValue, unit: property.unit }));
37
+ }
38
+ overridesByBreakpoint.set(breakpointKey, declarations);
39
+ }
40
+ }
41
+ return overridesByBreakpoint;
42
+ }
43
+ function resolveCssProperties(property) {
44
+ const cssProperties = property.cssProperties ?? toKebabCase(String(property.themeKey));
45
+ return typeof cssProperties === "string" ? [cssProperties] : cssProperties;
46
+ }
47
+ function formatDeclaration({ cssProperty, value, unit }) {
48
+ return `${cssProperty}: ${String(value)}${unit ?? ""} !important`;
49
+ }
50
+ function renderMediaQueries({ breakpoints, selector, overridesByBreakpoint, }) {
51
+ const cssChunks = [];
52
+ for (const [breakpointKey, declarations] of overridesByBreakpoint) {
53
+ const breakpoint = breakpoints[breakpointKey];
54
+ if (!breakpoint) {
55
+ continue;
56
+ }
57
+ cssChunks.push(css `
58
+ ${breakpoint.belowMediaQuery} {
59
+ ${selector} {
60
+ ${declarations.join(";\n")}
61
+ }
62
+ }
63
+ `);
64
+ }
65
+ return cssChunks.join("\n");
66
+ }
@@ -1,8 +1,10 @@
1
- import type { Theme, ThemeBackgroundColors, ThemeBreakpoints, ThemeColors, ThemeSizes, ThemeText } from "./themeTypes.js";
1
+ import type { Theme, ThemeBackgroundColors, ThemeBreakpoints, ThemeButton, ThemeColors, ThemeDivider, ThemeSizes, ThemeText } from "./themeTypes.js";
2
2
  type CreateThemeOverrides = {
3
3
  sizes?: Partial<ThemeSizes>;
4
4
  breakpoints?: Partial<ThemeBreakpoints>;
5
5
  text?: Partial<ThemeText>;
6
+ divider?: Partial<ThemeDivider>;
7
+ button?: Partial<ThemeButton>;
6
8
  colors?: {
7
9
  background?: Partial<ThemeBackgroundColors>;
8
10
  } & Partial<Omit<ThemeColors, "background">>;
@@ -21,6 +21,8 @@ export function createTheme(overrides) {
21
21
  ...overrides?.breakpoints,
22
22
  },
23
23
  text: { ...defaultTheme.text, ...overrides?.text },
24
+ divider: { ...defaultTheme.divider, ...overrides?.divider },
25
+ button: { ...defaultTheme.button, ...overrides?.button },
24
26
  colors: {
25
27
  ...defaultTheme.colors,
26
28
  ...overrides?.colors,
@@ -1,3 +1,5 @@
1
+ import { defaultButtonStyles } from "../components/button/defaultButtonStyles.js";
2
+ import { defaultDividerStyles } from "../components/divider/defaultDividerStyles.js";
1
3
  import { createBreakpoint } from "./createBreakpoint.js";
2
4
  export const defaultTheme = {
3
5
  sizes: {
@@ -14,6 +16,8 @@ export const defaultTheme = {
14
16
  lineHeight: "20px",
15
17
  bottomSpacing: "16px",
16
18
  },
19
+ divider: defaultDividerStyles,
20
+ button: defaultButtonStyles,
17
21
  colors: {
18
22
  background: {
19
23
  body: "#F2F2F2",
@@ -46,6 +46,102 @@ export interface ThemeText extends TextStyles {
46
46
  defaultVariant?: VariantName;
47
47
  variants?: VariantsRecord;
48
48
  }
49
+ /**
50
+ * Single source of truth for divider style property names and their value types.
51
+ * Both `DividerStyles` and `DividerVariantStyles` are derived from this interface.
52
+ */
53
+ interface DividerStyleMap {
54
+ height: number;
55
+ backgroundColor: string;
56
+ backgroundImage: string;
57
+ }
58
+ /** Base divider styles where each property holds a plain value. */
59
+ export type DividerStyles = {
60
+ [K in keyof DividerStyleMap]?: DividerStyleMap[K];
61
+ };
62
+ /** Variant divider styles where each property supports responsive values. */
63
+ export type DividerVariantStyles = {
64
+ [K in keyof DividerStyleMap]?: ResponsiveValue<DividerStyleMap[K]>;
65
+ };
66
+ /**
67
+ * Defines the variants available on the `MjmlDivider` and `HtmlDivider` components.
68
+ *
69
+ * ```ts
70
+ * declare module "@comet/mail-react" {
71
+ * interface DividerVariants {
72
+ * thin: true;
73
+ * thick: true;
74
+ * }
75
+ * }
76
+ * ```
77
+ */
78
+ export interface DividerVariants {
79
+ }
80
+ type DividerVariantsRecord = keyof DividerVariants extends never ? Record<string, DividerVariantStyles> : {
81
+ [K in keyof DividerVariants]?: DividerVariantStyles;
82
+ };
83
+ export type DividerVariantName = keyof DividerVariants extends never ? string : keyof DividerVariants;
84
+ /** Theme configuration for divider styles, variants, and default variant. */
85
+ export interface ThemeDivider extends DividerStyles {
86
+ defaultVariant?: DividerVariantName;
87
+ variants?: DividerVariantsRecord;
88
+ }
89
+ /**
90
+ * Single source of truth for button style property names and their value types.
91
+ * Both `ButtonStyles` and `ButtonVariantStyles` are derived from this interface.
92
+ *
93
+ * `padding` is the button's inner padding (the space around the label). It is
94
+ * deliberately distinct from `MjmlButton`'s `padding` prop, which is the outer
95
+ * spacing around the button.
96
+ */
97
+ interface ButtonStyleMap {
98
+ color: string;
99
+ backgroundColor: string;
100
+ /**
101
+ * A background image — typically a gradient. Rendered as progressive
102
+ * enhancement on top of `backgroundColor`; clients that don't render it
103
+ * (notably Outlook) fall back to the solid color.
104
+ */
105
+ backgroundImage: string;
106
+ border: string;
107
+ borderRadius: string;
108
+ fontFamily: string;
109
+ fontSize: string;
110
+ fontWeight: string | number;
111
+ lineHeight: string | number;
112
+ padding: string;
113
+ }
114
+ /** Base button styles where each property holds a plain value. */
115
+ export type ButtonStyles = {
116
+ [K in keyof ButtonStyleMap]?: ButtonStyleMap[K];
117
+ };
118
+ /** Variant button styles where each property supports responsive values. */
119
+ export type ButtonVariantStyles = {
120
+ [K in keyof ButtonStyleMap]?: ResponsiveValue<ButtonStyleMap[K]>;
121
+ };
122
+ /**
123
+ * Defines the variants available on the `MjmlButton` and `HtmlButton` components.
124
+ *
125
+ * ```ts
126
+ * declare module "@comet/mail-react" {
127
+ * interface ButtonVariants {
128
+ * primary: true;
129
+ * secondary: true;
130
+ * }
131
+ * }
132
+ * ```
133
+ */
134
+ export interface ButtonVariants {
135
+ }
136
+ type ButtonVariantsRecord = keyof ButtonVariants extends never ? Record<string, ButtonVariantStyles> : {
137
+ [K in keyof ButtonVariants]?: ButtonVariantStyles;
138
+ };
139
+ export type ButtonVariantName = keyof ButtonVariants extends never ? string : keyof ButtonVariants;
140
+ /** Theme configuration for button styles, variants, and default variant. */
141
+ export interface ThemeButton extends ButtonStyles {
142
+ defaultVariant?: ButtonVariantName;
143
+ variants?: ButtonVariantsRecord;
144
+ }
49
145
  /**
50
146
  * A resolved breakpoint with its numeric value and a ready-to-use media query
51
147
  * string that targets viewports narrower than this breakpoint.
@@ -101,6 +197,8 @@ export interface Theme {
101
197
  sizes: ThemeSizes;
102
198
  breakpoints: ThemeBreakpoints;
103
199
  text: ThemeText;
200
+ divider: ThemeDivider;
201
+ button: ThemeButton;
104
202
  colors: ThemeColors;
105
203
  }
106
204
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comet/mail-react",
3
- "version": "9.0.0-beta.4",
3
+ "version": "9.0.0-beta.6",
4
4
  "description": "Utilities for building HTML emails with React",
5
5
  "license": "BSD-2-Clause",
6
6
  "type": "module",
@@ -27,23 +27,28 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@storybook/addon-docs": "^10.3.5",
30
+ "@storybook/addon-vitest": "10.3.5",
30
31
  "@storybook/react-vite": "^10.3.5",
31
32
  "@types/mjml": "^4.7.4",
32
33
  "@types/mjml-browser": "^4.15.0",
33
- "@types/react": "^19.2.14",
34
+ "@types/react": "^19.2.15",
34
35
  "@types/react-dom": "^19.2.3",
36
+ "@vitest/browser": "4.1.8",
37
+ "@vitest/browser-playwright": "4.1.8",
35
38
  "chokidar-cli": "^3.0.0",
36
39
  "eslint": "^9.39.4",
37
40
  "npm-run-all2": "^8.0.4",
41
+ "playwright": "^1.49.1",
38
42
  "prettier": "^3.6.2",
39
43
  "react": "^19.2.6",
40
44
  "react-dom": "^19.2.6",
41
45
  "rimraf": "^6.1.2",
42
46
  "storybook": "^10.3.5",
43
47
  "typescript": "^5.9.3",
44
- "vitest": "^4.0.16",
45
- "@comet/cli": "9.0.0-beta.4",
46
- "@comet/eslint-config": "9.0.0-beta.4"
48
+ "vite": "^7.3.1",
49
+ "vitest": "^4.1.8",
50
+ "@comet/cli": "9.0.0-beta.6",
51
+ "@comet/eslint-config": "9.0.0-beta.6"
47
52
  },
48
53
  "peerDependencies": {
49
54
  "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
@@ -62,7 +67,7 @@
62
67
  "registry": "https://registry.npmjs.org"
63
68
  },
64
69
  "scripts": {
65
- "build": "pnpm run clean && pnpm run generate-block-types && tsc",
70
+ "build": "pnpm run clean && pnpm run generate-block-types && tsc -p tsconfig.build.json",
66
71
  "build-storybook": "storybook build",
67
72
  "clean": "rimraf lib 'src/**/*.generated.ts'",
68
73
  "dev": "pnpm run clean && pnpm generate-block-types && tsc --watch",
@@ -77,7 +82,9 @@
77
82
  "lint:prettier": "pnpm exec prettier --check '*.{ts,js,json,md,yml,yaml}'",
78
83
  "lint:tsc": "tsc",
79
84
  "storybook": "storybook dev -p 6066 --no-open",
80
- "test": "vitest --run",
85
+ "test": "pnpm run test:unit && pnpm run test:storybook",
86
+ "test:storybook": "vitest --run --project storybook --outputFile.junit=./junit-storybook.xml",
87
+ "test:unit": "vitest --run --project unit --outputFile.junit=./junit-unit.xml",
81
88
  "test:watch": "vitest --watch"
82
89
  }
83
90
  }
@@ -1,5 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
- declare const config: Meta;
3
- export default config;
4
- type Story = StoryObj;
5
- export declare const Default: Story;
@@ -1,32 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { MjmlColumn, MjmlSpacer, MjmlTable } from "@faire/mjml-react";
3
- import { HtmlInlineLink } from "../../components/inlineLink/HtmlInlineLink.js";
4
- import { MjmlMailRoot } from "../../components/mailRoot/MjmlMailRoot.js";
5
- import { MjmlSection } from "../../components/section/MjmlSection.js";
6
- import { HtmlText } from "../../components/text/HtmlText.js";
7
- import { MjmlText } from "../../components/text/MjmlText.js";
8
- import { MjmlWrapper } from "../../components/wrapper/MjmlWrapper.js";
9
- import { createTheme } from "../../theme/createTheme.js";
10
- const config = {
11
- title: "Examples/CustomBackgroundForFooter",
12
- parameters: { mailRoot: false },
13
- };
14
- export default config;
15
- export const Default = {
16
- render: () => {
17
- const theme = createTheme({
18
- text: {
19
- defaultVariant: "body",
20
- variants: {
21
- heading: { fontSize: "22px", lineHeight: "28px", fontWeight: "bold" },
22
- body: { fontSize: "14px", lineHeight: "20px" },
23
- legal: { fontSize: "12px", lineHeight: "18px" },
24
- },
25
- },
26
- });
27
- const footerTheme = structuredClone(theme);
28
- footerTheme.colors.background.content = "#2d4a6e";
29
- footerTheme.text.color = "#c8d8e9";
30
- return (_jsxs(MjmlMailRoot, { theme: theme, children: [_jsx(MjmlSection, { backgroundColor: "#1a1a1a", indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 15 }), _jsx(MjmlText, { color: "#ffffff", fontWeight: "bold", align: "center", children: "Company Name" }), _jsx(MjmlSpacer, { height: 15 })] }) }), _jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 30 }), _jsx(MjmlText, { variant: "heading", bottomSpacing: true, children: "This is a notification email" }), _jsx(MjmlText, { bottomSpacing: true, children: "Minima ea distinctio quisquam. Illo reiciendis non officiis consectetur. Ratione perferendis distinctio sapiente est. Dolor consequatur qui excepturi natus." }), _jsx(MjmlText, { children: "Numquam aut voluptas numquam aspernatur. Consequatur quidem omnis dolorem natus quis soluta. Est recusandae delectus sed sed deserunt velit quia. Occaecati vel possimus similique reiciendis possimus iure rerum sit architecto." }), _jsx(MjmlSpacer, { height: 30 })] }) }), _jsxs(MjmlWrapper, { backgroundColor: "#dddddd", children: [_jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 20 }), _jsx(MjmlText, { align: "center", bottomSpacing: true, children: "\u00A9 2026 Company Name \u2013 All rights reserved" }), _jsx(MjmlText, { variant: "legal", align: "center", bottomSpacing: true, children: "Legal text, corporis eos et quia. Assumenda eum maiores esse. Voluptas laudantium cupiditate aut repudiandae iste fugiat nam. Quas in debitis. Sed laudantium illum aut occaecati excepturi veniam harum reprehenderit." })] }) }), _jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlTable, { width: "auto", align: "center", children: _jsx("tbody", { children: _jsxs("tr", { children: [_jsx(HtmlText, { align: "center", children: _jsx(HtmlInlineLink, { href: "https://example.com/privacy", children: "Privacy Policy" }) }), _jsx("td", { width: "20px" }), _jsx(HtmlText, { align: "center", children: _jsx(HtmlInlineLink, { href: "https://example.com/imprint", children: "Imprint" }) }), _jsx("td", { width: "20px" }), _jsx(HtmlText, { align: "center", children: _jsx(HtmlInlineLink, { href: "https://example.com", children: "Website" }) })] }) }) }), _jsx(MjmlSpacer, { height: 20 })] }) })] })] }));
31
- },
32
- };