From a37e532767bb964abb13cf5da8cf151056adb175 Mon Sep 17 00:00:00 2001 From: Shubhdeep Chhabra Date: Fri, 6 Feb 2026 09:51:59 +0530 Subject: [PATCH 1/3] - Added new components '@react-email/columns' and '@react-email/stack' to package.json and index.ts. - Updated docs.json to include new components in the documentation. --- .changeset/rich-days-love.md | 8 + apps/docs/components/columns.mdx | 116 +++++++++ apps/docs/components/stack.mdx | 117 +++++++++ apps/docs/docs.json | 2 + packages/columns/CHANGELOG.md | 7 + packages/columns/license.md | 7 + packages/columns/package.json | 61 +++++ packages/columns/readme.md | 60 +++++ packages/columns/src/columns.spec.tsx | 69 +++++ packages/columns/src/columns.tsx | 52 ++++ packages/columns/src/index.ts | 1 + packages/columns/tsconfig.json | 5 + packages/components/package.json | 2 + packages/components/src/index.ts | 2 + packages/stack/CHANGELOG.md | 7 + packages/stack/license.md | 7 + packages/stack/package.json | 61 +++++ packages/stack/readme.md | 61 +++++ packages/stack/src/index.ts | 1 + packages/stack/src/stack.spec.tsx | 61 +++++ packages/stack/src/stack.tsx | 43 +++ packages/stack/tsconfig.json | 5 + pnpm-lock.yaml | 359 +++++++++++--------------- 23 files changed, 909 insertions(+), 205 deletions(-) create mode 100644 .changeset/rich-days-love.md create mode 100644 apps/docs/components/columns.mdx create mode 100644 apps/docs/components/stack.mdx create mode 100644 packages/columns/CHANGELOG.md create mode 100644 packages/columns/license.md create mode 100644 packages/columns/package.json create mode 100644 packages/columns/readme.md create mode 100644 packages/columns/src/columns.spec.tsx create mode 100644 packages/columns/src/columns.tsx create mode 100644 packages/columns/src/index.ts create mode 100644 packages/columns/tsconfig.json create mode 100644 packages/stack/CHANGELOG.md create mode 100644 packages/stack/license.md create mode 100644 packages/stack/package.json create mode 100644 packages/stack/readme.md create mode 100644 packages/stack/src/index.ts create mode 100644 packages/stack/src/stack.spec.tsx create mode 100644 packages/stack/src/stack.tsx create mode 100644 packages/stack/tsconfig.json diff --git a/.changeset/rich-days-love.md b/.changeset/rich-days-love.md new file mode 100644 index 0000000000..8a9d9f6e19 --- /dev/null +++ b/.changeset/rich-days-love.md @@ -0,0 +1,8 @@ +--- +"@react-email/columns": minor +"@react-email/stack": minor +"docs": patch +"@react-email/components": patch +--- + +- Add new layout primitives: Columns and Stack components with gap support and docs updates. diff --git a/apps/docs/components/columns.mdx b/apps/docs/components/columns.mdx new file mode 100644 index 0000000000..38f2c393d3 --- /dev/null +++ b/apps/docs/components/columns.mdx @@ -0,0 +1,116 @@ +--- +title: "Columns" +sidebarTitle: "Columns" +description: "Place content side by side in two or more columns. Equal or custom widths, with optional spacing—no need to hand-wire Row and Column." +"og:image": "https://react.email/static/covers/section.png" +icon: "columns" +--- + +import Support from '/snippets/support.mdx' + +## Install + +Install component from your command line. + + + +```sh npm +npm install @react-email/components -E + +# or get the individual package + +npm install @react-email/columns -E +``` + +```sh yarn +yarn add @react-email/components -E + +# or get the individual package + +yarn add @react-email/columns -E +``` + +```sh pnpm +pnpm add @react-email/components -E + +# or get the individual package + +pnpm add @react-email/columns -E +``` + + + +## Getting started + +Use Columns to place content side by side in two or more columns. Add it inside a **Section**; set `gap` for space between columns and optionally `columnWidths` for custom widths. + +```jsx +import { Section, Columns, Text } from "@react-email/components"; + +const Email = () => ( +
+ + Left column + Right column + +
+); +``` + +## Compared to Row + Column + +The same layout can be built with **Row** and **Column**—manual widths and padding for gap. Columns is a thin wrapper that does it with one component and optional `gap` and `columnWidths` props. + +**With Row + Column (primitives):** + +```jsx +import { Section, Row, Column, Text } from "@react-email/components"; + +const Email = () => ( +
+ + + Left column + + + Right column + + +
+); +``` + +**With Columns (same result, less code):** + +```jsx +import { Section, Columns, Text } from "@react-email/components"; + +const Email = () => ( +
+ + Left column + Right column + +
+); +``` + +## Props + + + Number of columns for equal-width layout. Defaults to the number of children. + + + + Space between columns, applied as padding on cells. A number is treated as pixels; a string is used as-is. + + + + Explicit width per column (e.g. `["60%", "40%"]`). When provided, overrides equal splitting. + + + + Inline styles applied to the underlying row. + + + diff --git a/apps/docs/components/stack.mdx b/apps/docs/components/stack.mdx new file mode 100644 index 0000000000..dc3d122806 --- /dev/null +++ b/apps/docs/components/stack.mdx @@ -0,0 +1,117 @@ +--- +title: "Stack" +sidebarTitle: "Stack" +description: "Place blocks one under the other with consistent spacing between them. No need to hand-wire rows or margins." +"og:image": "https://react.email/static/covers/section.png" +icon: "layer-group" +--- + +import Support from '/snippets/support.mdx' + +## Install + +Install component from your command line. + + + +```sh npm +npm install @react-email/components -E + +# or get the individual package + +npm install @react-email/stack -E +``` + +```sh yarn +yarn add @react-email/components -E + +# or get the individual package + +yarn add @react-email/stack -E +``` + +```sh pnpm +pnpm add @react-email/components -E + +# or get the individual package + +pnpm add @react-email/stack -E +``` + + + +## Getting started + +Use Stack to place blocks one under the other with consistent spacing. Add it inside a **Section**; set `gap` for the space between items. + +```jsx +import { Section, Stack, Text } from "@react-email/components"; + +const Email = () => ( +
+ + First block + Second block + Third block + +
+); +``` + +## Compared to Section + Row + +The same layout can be built with **Section**, **Row**, and **Column** — one row per block, each block in a Column. Stack is a thin wrapper around **Row** and **Column** (same primitives as **Columns**) that does it with one component and a single `gap` prop. + +**With Section + Row (primitives):** + +```jsx +import { Section, Row, Column, Text } from "@react-email/components"; + +const Email = () => ( +
+ + + First block + + + + + Second block + + + + + Third block + + +
+); +``` + +**With Stack (same result, less code):** + +```jsx +import { Section, Stack, Text } from "@react-email/components"; + +const Email = () => ( +
+ + First block + Second block + Third block + +
+); +``` + +## Props + + + Spacing between stacked children. A number is treated as pixels (e.g. `16` → `16px`); a string is used as-is (e.g. `"1em"`). + + + + Inline styles applied to each row (Stack renders one Row per child). + + + diff --git a/apps/docs/docs.json b/apps/docs/docs.json index adb6f5849e..4b836804c6 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -70,6 +70,7 @@ "components/code-block", "components/code-inline", "components/column", + "components/columns", "components/row", "components/font", "components/heading", @@ -79,6 +80,7 @@ "components/markdown", "components/preview", "components/section", + "components/stack", "components/tailwind", "components/text" ] diff --git a/packages/columns/CHANGELOG.md b/packages/columns/CHANGELOG.md new file mode 100644 index 0000000000..d2fe948ddd --- /dev/null +++ b/packages/columns/CHANGELOG.md @@ -0,0 +1,7 @@ +# @react-email/columns + +## 0.0.1 + +### Added + +- Initial release: multi-column layout component with configurable widths and gap. diff --git a/packages/columns/license.md b/packages/columns/license.md new file mode 100644 index 0000000000..a2c628c9e0 --- /dev/null +++ b/packages/columns/license.md @@ -0,0 +1,7 @@ +Copyright 2026 Plus Five Five, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/columns/package.json b/packages/columns/package.json new file mode 100644 index 0000000000..b66f2ec069 --- /dev/null +++ b/packages/columns/package.json @@ -0,0 +1,61 @@ +{ + "name": "@react-email/columns", + "version": "0.0.1", + "description": "A multi-column layout component for React Email", + "sideEffects": false, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "engines": { + "node": ">=20.0.0" + }, + "files": [ + "dist/**" + ], + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "license": "MIT", + "scripts": { + "build": "tsdown src/index.ts --format esm,cjs --dts --external react", + "build:watch": "tsdown src/index.ts --format esm,cjs --dts --external react --watch", + "clean": "rm -rf dist", + "test": "vitest run", + "test:watch": "vitest" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + }, + "dependencies": { + "@react-email/column": "workspace:0.0.14", + "@react-email/row": "workspace:0.0.13" + }, + "devDependencies": { + "@react-email/render": "workspace:*", + "tsconfig": "workspace:*", + "typescript": "5.8.3" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/resend/react-email.git", + "directory": "packages/columns" + }, + "keywords": [ + "react", + "email", + "layout", + "columns" + ] +} diff --git a/packages/columns/readme.md b/packages/columns/readme.md new file mode 100644 index 0000000000..0788caaec9 --- /dev/null +++ b/packages/columns/readme.md @@ -0,0 +1,60 @@ +![React Email Section cover](https://react.email/static/covers/section.png) + +
@react-email/columns
+
A multi-column layout component with equal or custom widths and optional gap.
+
+
+Website + · +GitHub + +
+ +## Install + +Install component from your command line. + +#### With yarn + +```sh +yarn add @react-email/columns -E +``` + +#### With npm + +```sh +npm install @react-email/columns -E +``` + +## Getting started + +Add the component inside a Section. Include styles where needed. + +```jsx +import { Section } from '@react-email/section'; +import { Columns } from '@react-email/columns'; +import { Text } from '@react-email/text'; + +const Email = () => { + return ( +
+ + Left column + Right column + +
+ ); +}; +``` + +## Support + +This component was tested using the most popular email clients. + +| Gmail logo | Apple Mail | Outlook logo | Yahoo! Mail logo | HEY logo | Superhuman logo | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| Gmail ✔ | Apple Mail ✔ | Outlook ✔ | Yahoo! Mail ✔ | HEY ✔ | Superhuman ✔ | + +## License + +MIT License diff --git a/packages/columns/src/columns.spec.tsx b/packages/columns/src/columns.spec.tsx new file mode 100644 index 0000000000..172f540beb --- /dev/null +++ b/packages/columns/src/columns.spec.tsx @@ -0,0 +1,69 @@ +import { render } from '@react-email/render'; +import { Columns } from './index'; + +describe(' component', () => { + it('renders children correctly', async () => { + const html = await render( + + Left + Right + , + ); + expect(html).toContain('Left'); + expect(html).toContain('Right'); + }); + + it('passes style and other props correctly', async () => { + const html = await render( + + One + , + ); + expect(html).toContain('data-testid="cols"'); + expect(html).toContain('background-color:red'); + }); + + it('applies equal widths when columnWidths not provided', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('width:50%'); + }); + + it('applies custom columnWidths', async () => { + const html = await render( + + Main + Side + , + ); + expect(html).toContain('width:60%'); + expect(html).toContain('width:40%'); + }); + + it('applies gap as padding between columns', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('padding-left:8px'); + expect(html).toContain('padding-right:8px'); + }); + + it('renders correctly', async () => { + const actualOutput = await render( + + One + Two + , + ); + expect(actualOutput).toMatchInlineSnapshot( + `"
OneTwo
"`, + ); + }); +}); diff --git a/packages/columns/src/columns.tsx b/packages/columns/src/columns.tsx new file mode 100644 index 0000000000..eb34c9310f --- /dev/null +++ b/packages/columns/src/columns.tsx @@ -0,0 +1,52 @@ +import { Column } from '@react-email/column'; +import { Row } from '@react-email/row'; +import * as React from 'react'; + +export type ColumnsProps = Readonly< + Omit, 'children'> & { + /** Number of columns (default: number of children). */ + cols?: number; + /** Space between columns (number as px, or string). Applied as padding on cells. */ + gap?: string | number; + /** Explicit width per column (e.g. ['50%', '50%'] or ['60%', '40%']). */ + columnWidths?: string[]; + children: React.ReactNode; + } +>; + +export const Columns = React.forwardRef( + ( + { children, cols: colsProp, gap = 0, columnWidths, style, ...props }, + ref, + ) => { + const items = React.Children.toArray(children); + const cols = colsProp ?? items.length; + const halfGap = + typeof gap === 'number' ? `${gap / 2}px` : `calc(${gap} / 2)`; + + const widths: string[] = + columnWidths && columnWidths.length >= items.length + ? columnWidths.slice(0, items.length) + : Array.from({ length: items.length }, () => `${100 / cols}%`); + + return ( + + {items.map((child, index) => ( + + {child} + + ))} + + ); + }, +); + +Columns.displayName = 'Columns'; diff --git a/packages/columns/src/index.ts b/packages/columns/src/index.ts new file mode 100644 index 0000000000..a7f066b206 --- /dev/null +++ b/packages/columns/src/index.ts @@ -0,0 +1 @@ +export * from './columns'; diff --git a/packages/columns/tsconfig.json b/packages/columns/tsconfig.json new file mode 100644 index 0000000000..cd6c94d6e8 --- /dev/null +++ b/packages/columns/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["."], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/packages/components/package.json b/packages/components/package.json index d8caba9e17..eb722cc1f7 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -45,6 +45,7 @@ "@react-email/code-block": "workspace:0.2.1", "@react-email/code-inline": "workspace:0.0.6", "@react-email/column": "workspace:0.0.14", + "@react-email/columns": "workspace:*", "@react-email/container": "workspace:0.0.16", "@react-email/font": "workspace:0.0.10", "@react-email/head": "workspace:0.0.13", @@ -58,6 +59,7 @@ "@react-email/render": "workspace:2.0.4", "@react-email/row": "workspace:0.0.13", "@react-email/section": "workspace:0.0.17", + "@react-email/stack": "workspace:*", "@react-email/tailwind": "workspace:2.0.4", "@react-email/text": "workspace:0.1.6" }, diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 6c13454481..664f5c5e44 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -3,6 +3,7 @@ export * from '@react-email/button'; export * from '@react-email/code-block'; export * from '@react-email/code-inline'; export * from '@react-email/column'; +export * from '@react-email/columns'; export * from '@react-email/container'; export * from '@react-email/font'; export * from '@react-email/head'; @@ -16,5 +17,6 @@ export * from '@react-email/preview'; export * from '@react-email/render'; export * from '@react-email/row'; export * from '@react-email/section'; +export * from '@react-email/stack'; export * from '@react-email/tailwind'; export * from '@react-email/text'; diff --git a/packages/stack/CHANGELOG.md b/packages/stack/CHANGELOG.md new file mode 100644 index 0000000000..a7fd9fa3d8 --- /dev/null +++ b/packages/stack/CHANGELOG.md @@ -0,0 +1,7 @@ +# @react-email/stack + +## 0.0.1 + +### Added + +- Initial release: vertical stack layout component with configurable `gap` between children. diff --git a/packages/stack/license.md b/packages/stack/license.md new file mode 100644 index 0000000000..a2c628c9e0 --- /dev/null +++ b/packages/stack/license.md @@ -0,0 +1,7 @@ +Copyright 2026 Plus Five Five, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/stack/package.json b/packages/stack/package.json new file mode 100644 index 0000000000..45eadc81c6 --- /dev/null +++ b/packages/stack/package.json @@ -0,0 +1,61 @@ +{ + "name": "@react-email/stack", + "version": "0.0.1", + "description": "A vertical stack layout component for React Email", + "sideEffects": false, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "engines": { + "node": ">=20.0.0" + }, + "files": [ + "dist/**" + ], + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "license": "MIT", + "scripts": { + "build": "tsdown src/index.ts --format esm,cjs --dts --external react", + "build:watch": "tsdown src/index.ts --format esm,cjs --dts --external react --watch", + "clean": "rm -rf dist", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@react-email/column": "workspace:0.0.14", + "@react-email/row": "workspace:0.0.13" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + }, + "devDependencies": { + "@react-email/render": "workspace:*", + "tsconfig": "workspace:*", + "typescript": "5.8.3" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/resend/react-email.git", + "directory": "packages/stack" + }, + "keywords": [ + "react", + "email", + "layout", + "stack" + ] +} diff --git a/packages/stack/readme.md b/packages/stack/readme.md new file mode 100644 index 0000000000..8190cc2e8a --- /dev/null +++ b/packages/stack/readme.md @@ -0,0 +1,61 @@ +![React Email Section cover](https://react.email/static/covers/section.png) + +
@react-email/stack
+
A vertical stack layout component with consistent spacing between children.
+
+
+Website + · +GitHub + +
+ +## Install + +Install component from your command line. + +#### With yarn + +```sh +yarn add @react-email/stack -E +``` + +#### With npm + +```sh +npm install @react-email/stack -E +``` + +## Getting started + +Add the component inside a Section. Include styles where needed. + +```jsx +import { Section } from '@react-email/section'; +import { Stack } from '@react-email/stack'; +import { Text } from '@react-email/text'; + +const Email = () => { + return ( +
+ + First block + Second block + Third block + +
+ ); +}; +``` + +## Support + +This component was tested using the most popular email clients. + +| Gmail logo | Apple Mail | Outlook logo | Yahoo! Mail logo | HEY logo | Superhuman logo | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| Gmail ✔ | Apple Mail ✔ | Outlook ✔ | Yahoo! Mail ✔ | HEY ✔ | Superhuman ✔ | + +## License + +MIT License diff --git a/packages/stack/src/index.ts b/packages/stack/src/index.ts new file mode 100644 index 0000000000..d39a8e6fcb --- /dev/null +++ b/packages/stack/src/index.ts @@ -0,0 +1 @@ +export * from './stack'; diff --git a/packages/stack/src/stack.spec.tsx b/packages/stack/src/stack.spec.tsx new file mode 100644 index 0000000000..0eb57aeea7 --- /dev/null +++ b/packages/stack/src/stack.spec.tsx @@ -0,0 +1,61 @@ +import { render } from '@react-email/render'; +import { Stack } from './index'; + +describe(' component', () => { + it('renders children correctly', async () => { + const html = await render( + + First + Second + , + ); + expect(html).toContain('First'); + expect(html).toContain('Second'); + }); + + it('passes style and other props correctly', async () => { + const html = await render( + + Only one + , + ); + expect(html).toContain('data-testid="stack"'); + expect(html).toContain('background-color:red'); + }); + + it('applies gap as marginBottom between rows', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('margin-bottom:16px'); + }); + + it('accepts gap as string', async () => { + const html = await render( + + A + B + , + ); + expect(html).toContain('margin-bottom:1em'); + }); + + it('renders correctly', async () => { + const actual = await render( + + One + Two + , + ); + expect(actual).toContain('
One
Two
"`, + ); + }); +}); diff --git a/packages/stack/src/stack.tsx b/packages/stack/src/stack.tsx new file mode 100644 index 0000000000..e4d9692a06 --- /dev/null +++ b/packages/stack/src/stack.tsx @@ -0,0 +1,43 @@ +import { Column } from '@react-email/column'; +import { Row } from '@react-email/row'; +import * as React from 'react'; + +function gapToCss(gap: string | number): string { + if (typeof gap === 'number') return `${gap}px`; + return String(gap); +} + +export type StackProps = Readonly< + Omit, 'children'> & { + /** Spacing between stacked children (number as px, or string e.g. '16px'). */ + gap?: string | number; + children: React.ReactNode; + } +>; + +export const Stack = React.forwardRef( + ({ children, gap = 0, style, ...props }, ref) => { + const items = React.Children.toArray(children); + const gapCss = gapToCss(gap); + + return ( + <> + {items.map((child, index) => ( + + {child} + + ))} + + ); + }, +); + +Stack.displayName = 'Stack'; diff --git a/packages/stack/tsconfig.json b/packages/stack/tsconfig.json new file mode 100644 index 0000000000..cd6c94d6e8 --- /dev/null +++ b/packages/stack/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["."], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4dee38418e..a670e23ea1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,7 +119,7 @@ importers: dependencies: mintlify: specifier: 4.2.280 - version: 4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@22.19.7)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + version: 4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) zod: specifier: 3.24.3 version: 3.24.3 @@ -408,6 +408,28 @@ importers: specifier: 5.8.3 version: 5.8.3 + packages/columns: + dependencies: + '@react-email/column': + specifier: workspace:0.0.14 + version: link:../column + '@react-email/row': + specifier: workspace:0.0.13 + version: link:../row + react: + specifier: ^19.0.0 + version: 19.0.0 + devDependencies: + '@react-email/render': + specifier: workspace:* + version: link:../render + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: 5.8.3 + version: 5.8.3 + packages/components: dependencies: '@react-email/body': @@ -425,6 +447,9 @@ importers: '@react-email/column': specifier: workspace:0.0.14 version: link:../column + '@react-email/columns': + specifier: workspace:* + version: link:../columns '@react-email/container': specifier: workspace:0.0.16 version: link:../container @@ -464,6 +489,9 @@ importers: '@react-email/section': specifier: workspace:0.0.17 version: link:../section + '@react-email/stack': + specifier: workspace:* + version: link:../stack '@react-email/tailwind': specifier: workspace:2.0.4 version: link:../tailwind @@ -1030,6 +1058,28 @@ importers: specifier: 5.8.3 version: 5.8.3 + packages/stack: + dependencies: + '@react-email/column': + specifier: workspace:0.0.14 + version: link:../column + '@react-email/row': + specifier: workspace:0.0.13 + version: link:../row + react: + specifier: ^19.0.0 + version: 19.0.0 + devDependencies: + '@react-email/render': + specifier: workspace:* + version: link:../render + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: 5.8.3 + version: 5.8.3 + packages/tailwind: dependencies: react: @@ -11034,51 +11084,51 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.1(@types/node@22.19.7)': + '@inquirer/checkbox@4.3.1(@types/node@25.0.6)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.1(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/type': 3.0.10(@types/node@25.0.6) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/confirm@5.1.20(@types/node@22.19.7)': + '@inquirer/confirm@5.1.20(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/core@10.3.1(@types/node@22.19.7)': + '@inquirer/core@10.3.1(@types/node@25.0.6)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/type': 3.0.10(@types/node@25.0.6) cli-width: 4.1.0 mute-stream: 3.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/editor@4.2.22(@types/node@22.19.7)': + '@inquirer/editor@4.2.22(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/external-editor': 1.0.3(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/external-editor': 1.0.3(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/expand@4.0.22(@types/node@22.19.7)': + '@inquirer/expand@4.0.22(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 '@inquirer/external-editor@1.0.3(@types/node@22.19.7)': dependencies: @@ -11087,90 +11137,97 @@ snapshots: optionalDependencies: '@types/node': 22.19.7 + '@inquirer/external-editor@1.0.3(@types/node@25.0.6)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.0 + optionalDependencies: + '@types/node': 25.0.6 + '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.0(@types/node@22.19.7)': + '@inquirer/input@4.3.0(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/number@3.0.22(@types/node@22.19.7)': + '@inquirer/number@3.0.22(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/password@4.0.22(@types/node@22.19.7)': + '@inquirer/password@4.0.22(@types/node@25.0.6)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/prompts@7.10.0(@types/node@22.19.7)': - dependencies: - '@inquirer/checkbox': 4.3.1(@types/node@22.19.7) - '@inquirer/confirm': 5.1.20(@types/node@22.19.7) - '@inquirer/editor': 4.2.22(@types/node@22.19.7) - '@inquirer/expand': 4.0.22(@types/node@22.19.7) - '@inquirer/input': 4.3.0(@types/node@22.19.7) - '@inquirer/number': 3.0.22(@types/node@22.19.7) - '@inquirer/password': 4.0.22(@types/node@22.19.7) - '@inquirer/rawlist': 4.1.10(@types/node@22.19.7) - '@inquirer/search': 3.2.1(@types/node@22.19.7) - '@inquirer/select': 4.4.1(@types/node@22.19.7) + '@inquirer/prompts@7.10.0(@types/node@25.0.6)': + dependencies: + '@inquirer/checkbox': 4.3.1(@types/node@25.0.6) + '@inquirer/confirm': 5.1.20(@types/node@25.0.6) + '@inquirer/editor': 4.2.22(@types/node@25.0.6) + '@inquirer/expand': 4.0.22(@types/node@25.0.6) + '@inquirer/input': 4.3.0(@types/node@25.0.6) + '@inquirer/number': 3.0.22(@types/node@25.0.6) + '@inquirer/password': 4.0.22(@types/node@25.0.6) + '@inquirer/rawlist': 4.1.10(@types/node@25.0.6) + '@inquirer/search': 3.2.1(@types/node@25.0.6) + '@inquirer/select': 4.4.1(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/prompts@7.9.0(@types/node@22.19.7)': - dependencies: - '@inquirer/checkbox': 4.3.1(@types/node@22.19.7) - '@inquirer/confirm': 5.1.20(@types/node@22.19.7) - '@inquirer/editor': 4.2.22(@types/node@22.19.7) - '@inquirer/expand': 4.0.22(@types/node@22.19.7) - '@inquirer/input': 4.3.0(@types/node@22.19.7) - '@inquirer/number': 3.0.22(@types/node@22.19.7) - '@inquirer/password': 4.0.22(@types/node@22.19.7) - '@inquirer/rawlist': 4.1.10(@types/node@22.19.7) - '@inquirer/search': 3.2.1(@types/node@22.19.7) - '@inquirer/select': 4.4.1(@types/node@22.19.7) + '@inquirer/prompts@7.9.0(@types/node@25.0.6)': + dependencies: + '@inquirer/checkbox': 4.3.1(@types/node@25.0.6) + '@inquirer/confirm': 5.1.20(@types/node@25.0.6) + '@inquirer/editor': 4.2.22(@types/node@25.0.6) + '@inquirer/expand': 4.0.22(@types/node@25.0.6) + '@inquirer/input': 4.3.0(@types/node@25.0.6) + '@inquirer/number': 3.0.22(@types/node@25.0.6) + '@inquirer/password': 4.0.22(@types/node@25.0.6) + '@inquirer/rawlist': 4.1.10(@types/node@25.0.6) + '@inquirer/search': 3.2.1(@types/node@25.0.6) + '@inquirer/select': 4.4.1(@types/node@25.0.6) optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/rawlist@4.1.10(@types/node@22.19.7)': + '@inquirer/rawlist@4.1.10(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/search@3.2.1(@types/node@22.19.7)': + '@inquirer/search@3.2.1(@types/node@25.0.6)': dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/type': 3.0.10(@types/node@25.0.6) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/select@4.4.1(@types/node@22.19.7)': + '@inquirer/select@4.4.1(@types/node@25.0.6)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.1(@types/node@22.19.7) + '@inquirer/core': 10.3.1(@types/node@25.0.6) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.7) + '@inquirer/type': 3.0.10(@types/node@25.0.6) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 - '@inquirer/type@3.0.10(@types/node@22.19.7)': + '@inquirer/type@3.0.10(@types/node@25.0.6)': optionalDependencies: - '@types/node': 22.19.7 + '@types/node': 25.0.6 '@isaacs/balanced-match@4.0.1': {} @@ -11313,36 +11370,6 @@ snapshots: - acorn - supports-color - '@mdx-js/mdx@3.1.0(acorn@8.15.0)': - dependencies: - '@types/estree': 1.0.8 - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdx': 2.0.13 - collapse-white-space: 2.1.0 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-util-scope: 1.0.0 - estree-walker: 3.0.3 - hast-util-to-jsx-runtime: 2.3.3 - markdown-extensions: 2.0.0 - recma-build-jsx: 1.0.0 - recma-jsx: 1.0.0(acorn@8.15.0) - recma-stringify: 1.0.0 - rehype-recma: 1.0.0 - remark-mdx: 3.1.0 - remark-parse: 11.0.0 - remark-rehype: 11.1.1 - source-map: 0.7.4 - unified: 11.0.5 - unist-util-position-from-estree: 2.0.0 - unist-util-stringify-position: 4.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - transitivePeerDependencies: - - acorn - - supports-color - '@mdx-js/react@3.1.0(@types/react@19.2.10)(react@19.0.0)': dependencies: '@types/mdx': 2.0.13 @@ -11351,15 +11378,15 @@ snapshots: '@mediapipe/tasks-vision@0.10.17': {} - '@mintlify/cli@4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@22.19.7)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': + '@mintlify/cli@4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': dependencies: - '@inquirer/prompts': 7.9.0(@types/node@22.19.7) + '@inquirer/prompts': 7.9.0(@types/node@25.0.6) '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/link-rot': 3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/link-rot': 3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) '@mintlify/models': 0.0.257 - '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) adm-zip: 0.5.16 chalk: 5.2.0 color: 4.2.3 @@ -11367,7 +11394,7 @@ snapshots: front-matter: 4.0.2 fs-extra: 11.2.0 ink: 6.3.0(@types/react@19.2.10)(react@19.0.0) - inquirer: 12.3.0(@types/node@22.19.7) + inquirer: 12.3.0(@types/node@25.0.6) js-yaml: 4.1.0 mdast-util-mdx-jsx: 3.2.0 react: 19.0.0 @@ -11510,13 +11537,13 @@ snapshots: - ts-node - typescript - '@mintlify/link-rot@3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': + '@mintlify/link-rot@3.0.823(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': dependencies: '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/previewing': 4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) '@mintlify/scraping': 4.0.522(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) fs-extra: 11.1.0 unist-util-visit: 4.1.2 transitivePeerDependencies: @@ -11562,33 +11589,6 @@ snapshots: - supports-color - typescript - '@mintlify/mdx@3.0.4(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': - dependencies: - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@shikijs/transformers': 3.15.0 - '@shikijs/twoslash': 3.15.0(typescript@5.9.3) - arktype: 2.1.27 - hast-util-to-string: 3.0.1 - mdast-util-from-markdown: 2.0.2 - mdast-util-gfm: 3.1.0 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-to-hast: 13.2.0 - next-mdx-remote-client: 1.0.7(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - rehype-katex: 7.0.1 - remark-gfm: 4.0.1 - remark-math: 6.0.0 - remark-smartypants: 3.0.2 - shiki: 3.15.0 - unified: 11.0.5 - unist-util-visit: 5.0.0 - transitivePeerDependencies: - - '@types/react' - - acorn - - supports-color - - typescript - '@mintlify/models@0.0.255': dependencies: axios: 1.10.0 @@ -11612,12 +11612,12 @@ snapshots: leven: 4.0.0 yaml: 2.6.1 - '@mintlify/prebuild@1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': + '@mintlify/prebuild@1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': dependencies: '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) '@mintlify/openapi-parser': 0.0.8 '@mintlify/scraping': 4.0.527(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) chalk: 5.3.0 favicons: 7.2.0 front-matter: 4.0.2 @@ -11643,11 +11643,11 @@ snapshots: - typescript - utf-8-validate - '@mintlify/previewing@4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': + '@mintlify/previewing@4.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3)': dependencies: '@mintlify/common': 1.0.666(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/prebuild': 1.0.801(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) + '@mintlify/validation': 0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) better-opn: 3.0.2 chalk: 5.2.0 chokidar: 3.5.3 @@ -11792,29 +11792,6 @@ snapshots: - supports-color - typescript - '@mintlify/validation@0.1.558(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3)': - dependencies: - '@mintlify/mdx': 3.0.4(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.3) - '@mintlify/models': 0.0.257 - arktype: 2.1.27 - js-yaml: 4.1.0 - lcm: 0.0.3 - lodash: 4.17.21 - object-hash: 3.0.0 - openapi-types: 12.1.3 - uuid: 11.1.0 - zod: 3.21.4 - zod-to-json-schema: 3.20.4(zod@3.21.4) - transitivePeerDependencies: - - '@radix-ui/react-popover' - - '@types/react' - - acorn - - debug - - react - - react-dom - - supports-color - - typescript - '@monogrid/gainmap-js@3.1.0(three@0.170.0)': dependencies: promise-worker-transferable: 1.0.4 @@ -13788,7 +13765,6 @@ snapshots: '@types/node@25.0.6': dependencies: undici-types: 7.16.0 - optional: true '@types/normalize-path@3.0.2': {} @@ -16142,12 +16118,12 @@ snapshots: inline-style-parser@0.2.4: {} - inquirer@12.3.0(@types/node@22.19.7): + inquirer@12.3.0(@types/node@25.0.6): dependencies: - '@inquirer/core': 10.3.1(@types/node@22.19.7) - '@inquirer/prompts': 7.10.0(@types/node@22.19.7) - '@inquirer/type': 3.0.10(@types/node@22.19.7) - '@types/node': 22.19.7 + '@inquirer/core': 10.3.1(@types/node@25.0.6) + '@inquirer/prompts': 7.10.0(@types/node@25.0.6) + '@inquirer/type': 3.0.10(@types/node@25.0.6) + '@types/node': 25.0.6 ansi-escapes: 4.3.2 mute-stream: 2.0.0 run-async: 3.0.0 @@ -17193,9 +17169,9 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - mintlify@4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@22.19.7)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3): + mintlify@4.2.280(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3): dependencies: - '@mintlify/cli': 4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@22.19.7)(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) + '@mintlify/cli': 4.0.884(@radix-ui/react-popover@1.1.15(@types/react-dom@19.0.1)(@types/react@19.2.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/node@25.0.6)(@types/react@19.2.10)(acorn@8.11.2)(react-dom@19.0.0(react@19.0.0))(typescript@5.9.3) transitivePeerDependencies: - '@radix-ui/react-popover' - '@types/node' @@ -17273,22 +17249,6 @@ snapshots: - acorn - supports-color - next-mdx-remote-client@1.0.7(@types/react@19.2.10)(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - '@babel/code-frame': 7.27.1 - '@mdx-js/mdx': 3.1.0(acorn@8.15.0) - '@mdx-js/react': 3.1.0(@types/react@19.2.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - remark-mdx-remove-esm: 1.1.0 - serialize-error: 12.0.0 - vfile: 6.0.3 - vfile-matter: 5.0.0 - transitivePeerDependencies: - - '@types/react' - - acorn - - supports-color - next-safe-action@8.0.11(next@16.0.11(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: next: 16.0.11(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -18137,16 +18097,6 @@ snapshots: transitivePeerDependencies: - acorn - recma-jsx@1.0.0(acorn@8.15.0): - dependencies: - acorn-jsx: 5.3.2(acorn@8.15.0) - estree-util-to-js: 2.0.0 - recma-parse: 1.0.0 - recma-stringify: 1.0.0 - unified: 11.0.5 - transitivePeerDependencies: - - acorn - recma-parse@1.0.0: dependencies: '@types/estree': 1.0.8 @@ -19534,8 +19484,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.16.0: - optional: true + undici-types@7.16.0: {} undici@5.29.0: dependencies: From a2b67d0724269933f325730cabccbe0402be1067 Mon Sep 17 00:00:00 2001 From: Shubhdeep Chhabra Date: Fri, 6 Feb 2026 10:28:36 +0530 Subject: [PATCH 2/3] refactor(columns, stack): update gap handling and improve documentation - Enhanced gap handling in and components to use spacer columns and padding for better Outlook compatibility. - Updated documentation to clarify the implementation of gaps and their effects on layout. - Adjusted tests to reflect changes in gap application and ensure accurate rendering. --- apps/docs/components/columns.mdx | 2 +- packages/columns/src/columns.spec.tsx | 8 +-- packages/columns/src/columns.tsx | 98 ++++++++++++++++++++++----- packages/stack/src/stack.spec.tsx | 8 +-- packages/stack/src/stack.tsx | 19 +++--- 5 files changed, 99 insertions(+), 36 deletions(-) diff --git a/apps/docs/components/columns.mdx b/apps/docs/components/columns.mdx index 38f2c393d3..d77b92b39b 100644 --- a/apps/docs/components/columns.mdx +++ b/apps/docs/components/columns.mdx @@ -102,7 +102,7 @@ const Email = () => ( - Space between columns, applied as padding on cells. A number is treated as pixels; a string is used as-is. + Space between columns. Implemented with spacer columns and calc() widths so total width stays 100%. A number is treated as pixels; a string is used as-is. diff --git a/packages/columns/src/columns.spec.tsx b/packages/columns/src/columns.spec.tsx index 172f540beb..2d4c755272 100644 --- a/packages/columns/src/columns.spec.tsx +++ b/packages/columns/src/columns.spec.tsx @@ -44,15 +44,15 @@ describe(' component', () => { expect(html).toContain('width:40%'); }); - it('applies gap as padding between columns', async () => { + it('applies gap via spacer columns (Outlook-safe, no overflow)', async () => { const html = await render( A B , ); - expect(html).toContain('padding-left:8px'); - expect(html).toContain('padding-right:8px'); + expect(html).toContain('width:16px'); + expect(html).toContain('calc((100% - 16px) / 2)'); }); it('renders correctly', async () => { @@ -63,7 +63,7 @@ describe(' component', () => { , ); expect(actualOutput).toMatchInlineSnapshot( - `"
OneTwo
"`, + `"
OneTwo
"`, ); }); }); diff --git a/packages/columns/src/columns.tsx b/packages/columns/src/columns.tsx index eb34c9310f..19a54f1bd1 100644 --- a/packages/columns/src/columns.tsx +++ b/packages/columns/src/columns.tsx @@ -2,11 +2,20 @@ import { Column } from '@react-email/column'; import { Row } from '@react-email/row'; import * as React from 'react'; +function gapToCss(gap: string | number): string { + if (typeof gap === 'number') return `${gap}px`; + return String(gap); +} + export type ColumnsProps = Readonly< Omit, 'children'> & { /** Number of columns (default: number of children). */ cols?: number; - /** Space between columns (number as px, or string). Applied as padding on cells. */ + /** + * Space between columns (number as px, or string). + * Uses spacer cells + calc() widths so total width stays 100% (avoids overflow in Outlook). + * Note: Outlook desktop does not support calc(); it will still show the gap and approximate column widths. + */ gap?: string | number; /** Explicit width per column (e.g. ['50%', '50%'] or ['60%', '40%']). */ columnWidths?: string[]; @@ -21,29 +30,84 @@ export const Columns = React.forwardRef( ) => { const items = React.Children.toArray(children); const cols = colsProp ?? items.length; - const halfGap = - typeof gap === 'number' ? `${gap / 2}px` : `calc(${gap} / 2)`; + const hasGap = gap !== 0 && gap !== undefined; + const gapCss = gapToCss(gap); - const widths: string[] = - columnWidths && columnWidths.length >= items.length - ? columnWidths.slice(0, items.length) - : Array.from({ length: items.length }, () => `${100 / cols}%`); + const n = items.length; + const totalGapCss = + n <= 1 + ? '0px' + : typeof gap === 'number' + ? `${(n - 1) * gap}px` + : `calc(${n - 1} * (${gapCss}))`; - return ( - - {items.map((child, index) => ( + const contentWidths: string[] = + columnWidths && columnWidths.length >= n + ? columnWidths.slice(0, n).map((w) => { + const pct = Number.parseFloat(w); + if (!hasGap || !Number.isFinite(pct)) return w; + const fraction = pct / 100; + return `calc((100% - ${totalGapCss}) * ${fraction})`; + }) + : Array.from( + { length: n }, + () => + hasGap + ? `calc((100% - ${totalGapCss}) / ${n})` + : `${100 / cols}%`, + ); + + if (!hasGap) { + return ( + + {items.map((child, index) => ( + + {child} + + ))} + + ); + } + + const cells: React.ReactNode[] = []; + items.forEach((child, index) => { + cells.push( + + {child} + , + ); + if (index < items.length - 1) { + cells.push( - {child} - - ))} + {'\u00A0'} + , + ); + } + }); + + return ( + + {cells} ); }, diff --git a/packages/stack/src/stack.spec.tsx b/packages/stack/src/stack.spec.tsx index 0eb57aeea7..1624e9e9ad 100644 --- a/packages/stack/src/stack.spec.tsx +++ b/packages/stack/src/stack.spec.tsx @@ -23,14 +23,14 @@ describe(' component', () => { expect(html).toContain('background-color:red'); }); - it('applies gap as marginBottom between rows', async () => { + it('applies gap as paddingBottom on cells (Outlook-safe)', async () => { const html = await render( A B , ); - expect(html).toContain('margin-bottom:16px'); + expect(html).toContain('padding-bottom:16px'); }); it('accepts gap as string', async () => { @@ -40,7 +40,7 @@ describe(' component', () => { B , ); - expect(html).toContain('margin-bottom:1em'); + expect(html).toContain('padding-bottom:1em'); }); it('renders correctly', async () => { @@ -55,7 +55,7 @@ describe(' component', () => { expect(actual).toContain('
One
Two
"`, + `"
One
Two
"`, ); }); }); diff --git a/packages/stack/src/stack.tsx b/packages/stack/src/stack.tsx index e4d9692a06..61ce701d2b 100644 --- a/packages/stack/src/stack.tsx +++ b/packages/stack/src/stack.tsx @@ -23,16 +23,15 @@ export const Stack = React.forwardRef( return ( <> {items.map((child, index) => ( - - {child} + + + {child} + ))} From 6ab88bdb60d9dff7f05c458659922136bab8a172 Mon Sep 17 00:00:00 2001 From: Shubhdeep Chhabra Date: Fri, 6 Feb 2026 10:38:17 +0530 Subject: [PATCH 3/3] fix(columns): correct gap calculation in Columns component - Updated the gap calculation logic in the Columns component to ensure proper distribution of space when gaps are applied. - Adjusted the calculation to use the correct number of columns, improving layout consistency. --- packages/columns/src/columns.tsx | 10 ++++------ packages/stack/src/stack.tsx | 7 ++++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/columns/src/columns.tsx b/packages/columns/src/columns.tsx index 19a54f1bd1..5ccb57d793 100644 --- a/packages/columns/src/columns.tsx +++ b/packages/columns/src/columns.tsx @@ -49,12 +49,10 @@ export const Columns = React.forwardRef( const fraction = pct / 100; return `calc((100% - ${totalGapCss}) * ${fraction})`; }) - : Array.from( - { length: n }, - () => - hasGap - ? `calc((100% - ${totalGapCss}) / ${n})` - : `${100 / cols}%`, + : Array.from({ length: n }, () => + hasGap + ? `calc((100% - ${totalGapCss}) / ${cols})` + : `${100 / cols}%`, ); if (!hasGap) { diff --git a/packages/stack/src/stack.tsx b/packages/stack/src/stack.tsx index 61ce701d2b..93e239deba 100644 --- a/packages/stack/src/stack.tsx +++ b/packages/stack/src/stack.tsx @@ -23,7 +23,12 @@ export const Stack = React.forwardRef( return ( <> {items.map((child, index) => ( - +