From ec38f2eceb62f772b055cc5bef79cf181f41c9bc Mon Sep 17 00:00:00 2001 From: Ivo Date: Thu, 15 Oct 2026 11:20:03 +0200 Subject: [PATCH 001/125] feat(react): add Tailwind, Markdown, code, fonts, async, Link, Head and codegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New entries, each its own import so the main bundle stays within budget: - `/tailwind`: turns classes into each component's props at render time, so the email gets inline styles and the design stays editable; classes no prop takes become inline styles (HTML only, warned); max-sm: → phone settings, hover: → button hover colors. Container text classes reach the text inside, Row padding classes inset content and card grids get gutters built from borders in the surrounding color. - `/markdown` (Markdown → blocks), `/code` (CodeBlock, CodeInline, highlight.js themes), `/font` ( with fallbacks, URL kept in the design), `/head` (), `/async` (async templates and components, Suspense, `pretty`), `/codegen` (designToElements: an Unlayer design → readable Elements code; exact round trip on 48 of 49 real designs and fuzzed ones). - Main entry: Link, style/className/skipInText on every component, plain-text options and toPlainText, prop aliases (Menu color, align/textAlign, Heading as/level) with "did you mean" warnings, `values` as ValuesInput (partial, strict keys), rich inline children. Editor parity fixes (HTML and design now agree): UnlayerProvider config applies (render options > root config > Provider > defaults); Paragraph text is stored as typed; exact px image widths; bare zeros written as 0px; Row background-image fullWidth default; the body link color selector; url() values percent-encoded; right-to-left dir/lang on body; a Row whose Column count doesn't match its layout is left out alone, with an error; render errors keep their cause. Row padding stays the editor's outer padding, with a warning when it sits outside a content background. The main bundle budget rises from 118 KB to 126 KB (125,962 bytes now). Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) --- CLAUDE.md | 9 +- packages/react/THIRD_PARTY_LICENSES.md | 150 + packages/react/package.json | 59 +- .../scripts/storybook-visual-baseline.json | 7815 +++++++++-------- .../golden-template.test.tsx.snap | 10 +- packages/react/src/async/async.test.tsx | 210 + packages/react/src/async/index.ts | 167 + packages/react/src/async/pretty.ts | 20 + packages/react/src/async/stream.ts | 109 + packages/react/src/code/CodeBlock.tsx | 29 + packages/react/src/code/CodeInline.tsx | 46 + packages/react/src/code/code.test.tsx | 131 + packages/react/src/code/highlight.ts | 156 + packages/react/src/code/hljs.d.ts | 5 + packages/react/src/code/index.ts | 9 + packages/react/src/code/themes.ts | 510 ++ .../react/src/codegen/__fixtures__/designs.ts | 313 + .../__fixtures__/editor-kitchen-sink.json | 770 ++ .../src/codegen/__fixtures__/sources.tsx | 81 + packages/react/src/codegen/blocks.ts | 329 + packages/react/src/codegen/checked-values.ts | 37 + packages/react/src/codegen/codegen.test.tsx | 212 + packages/react/src/codegen/compare.test.ts | 51 + packages/react/src/codegen/compare.ts | 179 + packages/react/src/codegen/containers.ts | 111 + packages/react/src/codegen/defaults.ts | 108 + .../react/src/codegen/design-to-elements.ts | 165 + packages/react/src/codegen/device.ts | 74 + packages/react/src/codegen/fonts.ts | 44 + packages/react/src/codegen/format.ts | 22 + packages/react/src/codegen/generated.test.ts | 224 + packages/react/src/codegen/index.ts | 6 + packages/react/src/codegen/print.ts | 244 + packages/react/src/codegen/props.ts | 194 + .../react/src/codegen/round-trip.test.tsx | 39 + packages/react/src/codegen/state.ts | 23 + packages/react/src/codegen/test-harness.ts | 179 + packages/react/src/codegen/text.ts | 56 + packages/react/src/codegen/typecheck.test.ts | 25 + packages/react/src/codegen/values-shapes.ts | 40 + packages/react/src/codegen/values.ts | 59 + packages/react/src/codegen/warnings.ts | 18 + packages/react/src/components/Body.tsx | 61 +- packages/react/src/components/Button.test.tsx | 22 + packages/react/src/components/Button.tsx | 15 +- packages/react/src/components/Column.test.tsx | 48 +- packages/react/src/components/Column.tsx | 223 +- packages/react/src/components/Divider.tsx | 4 +- .../react/src/components/Heading.test.tsx | 52 +- packages/react/src/components/Heading.tsx | 55 +- packages/react/src/components/Image.tsx | 61 +- .../components/Image.width-roundtrip.test.tsx | 36 +- packages/react/src/components/Link.tsx | 30 + packages/react/src/components/Menu.tsx | 20 +- packages/react/src/components/Paragraph.tsx | 13 +- packages/react/src/components/Row.test.tsx | 155 +- packages/react/src/components/Row.tsx | 45 +- packages/react/src/components/Social.tsx | 66 +- packages/react/src/components/Table.tsx | 68 +- packages/react/src/components/Video.tsx | 12 +- .../src/components/multi-column.test.tsx | 15 +- ...UnlayerProvider.server-components.test.tsx | 55 + .../src/context/UnlayerProvider.test.tsx | 129 +- .../react/src/context/UnlayerProvider.tsx | 47 +- packages/react/src/context/layer-config.ts | 39 + packages/react/src/dx-behaviors.test.tsx | 201 +- packages/react/src/dx-types.test-d.tsx | 39 + packages/react/src/font/font.test.tsx | 104 + packages/react/src/font/index.tsx | 98 + packages/react/src/head/head.test.tsx | 113 + packages/react/src/head/index.tsx | 57 + packages/react/src/index.ts | 15 +- packages/react/src/markdown/Markdown.tsx | 292 + packages/react/src/markdown/index.ts | 7 + packages/react/src/markdown/markdown.test.tsx | 117 + packages/react/src/markdown/sanitize.ts | 60 + .../examples/AirbnbConfirmation.html | 2 +- .../examples/LumaEventInvite.html | 6 +- .../examples/MercuryReceipt.html | 2 +- .../examples/MorningBrewDigest.html | 2 +- .../__snapshots__/examples/NikeDrop.html | 2 +- .../__snapshots__/examples/NotionWelcome.html | 2 +- .../__snapshots__/examples/ProductDigest.html | 2 +- .../__snapshots__/examples/StripeReceipt.html | 2 +- .../examples/WarbyOrderShipped.html | 2 +- .../patterns/fragments-and-components.html | 2 +- .../patterns/hidden-on-a-device.html | 4 +- .../__snapshots__/patterns/html-block.html | 2 +- .../patterns/html-from-a-component.html | 4 +- .../patterns/print-document.html | 4 +- .../patterns/reused-element.html | 2 +- .../__snapshots__/patterns/right-to-left.html | 8 +- .../__snapshots__/patterns/right-to-left.json | 4 +- .../patterns/template-component.html | 2 +- .../text-straight-in-rows-and-columns.html | 2 +- .../__snapshots__/patterns/web-page.html | 4 +- .../react/src/regression/edge-cases.test.tsx | 3 +- .../react/src/regression/generated.test.tsx | 10 +- .../react/src/regression/node-exit.test.ts | 138 +- .../src/regression/previous-release.test.tsx | 6 +- packages/react/src/tailwind/Tailwind.tsx | 96 + packages/react/src/tailwind/color.ts | 205 + packages/react/src/tailwind/compile.test.tsx | 210 + .../react/src/tailwind/component.test.tsx | 830 ++ packages/react/src/tailwind/css.ts | 167 + packages/react/src/tailwind/declarations.ts | 105 + packages/react/src/tailwind/html.ts | 54 + packages/react/src/tailwind/index.ts | 11 + packages/react/src/tailwind/inherit.ts | 144 + packages/react/src/tailwind/load.ts | 122 + packages/react/src/tailwind/props.ts | 386 + packages/react/src/tailwind/raw.d.ts | 5 + packages/react/src/tailwind/row.ts | 228 + packages/react/src/tailwind/tables.ts | 32 + packages/react/src/tailwind/values.ts | 112 + packages/react/src/types.ts | 16 + packages/react/src/utils/closest-name.ts | 14 + packages/react/src/utils/collect.ts | 21 +- .../react/src/utils/container-defaults.ts | 20 +- packages/react/src/utils/create-component.tsx | 243 +- packages/react/src/utils/document-layouts.ts | 2 +- .../react/src/utils/extract-head.test.tsx | 29 + packages/react/src/utils/extract-head.ts | 225 +- packages/react/src/utils/image-sizing.ts | 30 +- packages/react/src/utils/inline-html.ts | 26 + packages/react/src/utils/internals.ts | 25 + .../react/src/utils/nested-renders.test.tsx | 52 +- packages/react/src/utils/normalize.tsx | 182 +- packages/react/src/utils/record.tsx | 42 +- packages/react/src/utils/render-scope.ts | 18 + .../react/src/utils/render-to-html.test.tsx | 22 +- packages/react/src/utils/render-to-html.tsx | 31 +- .../react/src/utils/render-to-json.test.tsx | 89 + packages/react/src/utils/render-to-json.ts | 184 +- .../src/utils/render-to-plaintext.test.tsx | 36 +- packages/react/src/utils/server-renderer.ts | 8 +- packages/react/src/utils/tailwind.ts | 48 + .../react/src/utils/text-children.test.tsx | 117 +- packages/react/src/utils/unwrap-root.ts | 35 +- .../react/src/utils/user-components.test.tsx | 24 +- packages/react/tsconfig.json | 13 +- packages/react/tsconfig.typecheck.json | 14 +- packages/react/tsup.config.ts | 29 +- packages/react/vitest.config.ts | 12 + packages/shared/src/index.ts | 7 +- packages/shared/src/layouts.ts | 77 +- packages/shared/src/types.ts | 34 + packages/shared/src/utils/css-values.ts | 13 + .../shared/src/utils/html-to-text.test.ts | 37 + packages/shared/src/utils/html-to-text.ts | 136 +- packages/shared/src/utils/semantic-props.ts | 308 +- pnpm-lock.yaml | 82 +- 152 files changed, 15424 insertions(+), 5213 deletions(-) create mode 100644 packages/react/THIRD_PARTY_LICENSES.md create mode 100644 packages/react/src/async/async.test.tsx create mode 100644 packages/react/src/async/index.ts create mode 100644 packages/react/src/async/pretty.ts create mode 100644 packages/react/src/async/stream.ts create mode 100644 packages/react/src/code/CodeBlock.tsx create mode 100644 packages/react/src/code/CodeInline.tsx create mode 100644 packages/react/src/code/code.test.tsx create mode 100644 packages/react/src/code/highlight.ts create mode 100644 packages/react/src/code/hljs.d.ts create mode 100644 packages/react/src/code/index.ts create mode 100644 packages/react/src/code/themes.ts create mode 100644 packages/react/src/codegen/__fixtures__/designs.ts create mode 100644 packages/react/src/codegen/__fixtures__/editor-kitchen-sink.json create mode 100644 packages/react/src/codegen/__fixtures__/sources.tsx create mode 100644 packages/react/src/codegen/blocks.ts create mode 100644 packages/react/src/codegen/checked-values.ts create mode 100644 packages/react/src/codegen/codegen.test.tsx create mode 100644 packages/react/src/codegen/compare.test.ts create mode 100644 packages/react/src/codegen/compare.ts create mode 100644 packages/react/src/codegen/containers.ts create mode 100644 packages/react/src/codegen/defaults.ts create mode 100644 packages/react/src/codegen/design-to-elements.ts create mode 100644 packages/react/src/codegen/device.ts create mode 100644 packages/react/src/codegen/fonts.ts create mode 100644 packages/react/src/codegen/format.ts create mode 100644 packages/react/src/codegen/generated.test.ts create mode 100644 packages/react/src/codegen/index.ts create mode 100644 packages/react/src/codegen/print.ts create mode 100644 packages/react/src/codegen/props.ts create mode 100644 packages/react/src/codegen/round-trip.test.tsx create mode 100644 packages/react/src/codegen/state.ts create mode 100644 packages/react/src/codegen/test-harness.ts create mode 100644 packages/react/src/codegen/text.ts create mode 100644 packages/react/src/codegen/typecheck.test.ts create mode 100644 packages/react/src/codegen/values-shapes.ts create mode 100644 packages/react/src/codegen/values.ts create mode 100644 packages/react/src/codegen/warnings.ts create mode 100644 packages/react/src/components/Link.tsx create mode 100644 packages/react/src/context/UnlayerProvider.server-components.test.tsx create mode 100644 packages/react/src/context/layer-config.ts create mode 100644 packages/react/src/font/font.test.tsx create mode 100644 packages/react/src/font/index.tsx create mode 100644 packages/react/src/head/head.test.tsx create mode 100644 packages/react/src/head/index.tsx create mode 100644 packages/react/src/markdown/Markdown.tsx create mode 100644 packages/react/src/markdown/index.ts create mode 100644 packages/react/src/markdown/markdown.test.tsx create mode 100644 packages/react/src/markdown/sanitize.ts create mode 100644 packages/react/src/tailwind/Tailwind.tsx create mode 100644 packages/react/src/tailwind/color.ts create mode 100644 packages/react/src/tailwind/compile.test.tsx create mode 100644 packages/react/src/tailwind/component.test.tsx create mode 100644 packages/react/src/tailwind/css.ts create mode 100644 packages/react/src/tailwind/declarations.ts create mode 100644 packages/react/src/tailwind/html.ts create mode 100644 packages/react/src/tailwind/index.ts create mode 100644 packages/react/src/tailwind/inherit.ts create mode 100644 packages/react/src/tailwind/load.ts create mode 100644 packages/react/src/tailwind/props.ts create mode 100644 packages/react/src/tailwind/raw.d.ts create mode 100644 packages/react/src/tailwind/row.ts create mode 100644 packages/react/src/tailwind/tables.ts create mode 100644 packages/react/src/tailwind/values.ts create mode 100644 packages/react/src/utils/closest-name.ts create mode 100644 packages/react/src/utils/inline-html.ts create mode 100644 packages/react/src/utils/internals.ts create mode 100644 packages/react/src/utils/tailwind.ts diff --git a/CLAUDE.md b/CLAUDE.md index 8c7e0821..5d6d7ade 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,6 +96,13 @@ Example: `