Skip to content

feat: maizzle 6#1708

Open
cossssmin wants to merge 380 commits into
masterfrom
next
Open

feat: maizzle 6#1708
cossssmin wants to merge 380 commits into
masterfrom
next

Conversation

@cossssmin
Copy link
Copy Markdown
Member

No description provided.

@cossssmin cossssmin changed the title feat: add tailwindcss v4 support feat: maizzle 6 Apr 4, 2026
wraps it in a table that makes it look better in old outlooks
adds touch events to drag handlers
normalize and align properly
simplify and catch more cases
change how data is presented
add background-position prop, improve dx
increase coverage
wrap in article div with font size
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Important

Review skipped

Too many files!

This PR contains 293 files, which is 143 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 590a50ba-1a17-43c4-a58c-7d1eb02ca1da

📥 Commits

Reviewing files that changed from the base of the PR and between 2610b8d and 89cf98e.

⛔ Files ignored due to path filters (7)
  • package-lock.json is excluded by !**/package-lock.json
  • src/server/ui/favicon.svg is excluded by !**/*.svg
  • src/server/ui/logo-gradient.svg is excluded by !**/*.svg
  • src/server/ui/logo.svg is excluded by !**/*.svg
  • src/server/ui/mark-gradient.svg is excluded by !**/*.svg
  • src/server/ui/mark.svg is excluded by !**/*.svg
  • src/server/ui/stripes.svg is excluded by !**/*.svg
📒 Files selected for processing (293)
  • .editorconfig
  • .github/dependabot.yml
  • .github/workflows/nodejs.yml
  • .gitignore
  • CHANGELOG.md
  • README.md
  • bin/maizzle
  • bin/maizzle.mjs
  • biome.json
  • components.json
  • package.json
  • skills/maizzle/README.md
  • skills/maizzle/SKILL.md
  • skills/maizzle/references/CLI.md
  • skills/maizzle/references/COMPONENTS.md
  • skills/maizzle/references/COMPOSABLES.md
  • skills/maizzle/references/CONFIGURATION.md
  • skills/maizzle/references/CONVERT-MAIZZLE-V5.md
  • skills/maizzle/references/CONVERT-MJML.md
  • skills/maizzle/references/CONVERT-REACT-EMAIL.md
  • skills/maizzle/references/PATTERNS.md
  • skills/maizzle/references/STYLING.md
  • skills/maizzle/references/TRANSFORMERS.md
  • src/build.ts
  • src/commands/build.js
  • src/commands/serve.js
  • src/components/Body.vue
  • src/components/Button.vue
  • src/components/CodeBlock.vue
  • src/components/CodeInline.vue
  • src/components/Column.vue
  • src/components/Container.vue
  • src/components/Font.vue
  • src/components/Head.vue
  • src/components/Heading.vue
  • src/components/Hr.vue
  • src/components/Html.vue
  • src/components/Img.vue
  • src/components/Layout.vue
  • src/components/Link.vue
  • src/components/Markdown.vue
  • src/components/MarkdownLayout.vue
  • src/components/NoWidows.vue
  • src/components/NotOutlook.vue
  • src/components/NotPlaintext.vue
  • src/components/Outlook.vue
  • src/components/OutlookBg.vue
  • src/components/Overlap.vue
  • src/components/Plaintext.vue
  • src/components/Preheader.vue
  • src/components/QrCode.vue
  • src/components/Raw.vue
  • src/components/Row.vue
  • src/components/Section.vue
  • src/components/Spacer.vue
  • src/components/Tailwind.vue
  • src/components/Text.vue
  • src/components/WithUrl.vue
  • src/components/utils.ts
  • src/composables/defineConfig.ts
  • src/composables/renderContext.ts
  • src/composables/useBaseUrl.ts
  • src/composables/useConfig.ts
  • src/composables/useCurrentTemplate.ts
  • src/composables/useDoctype.ts
  • src/composables/useEvent.ts
  • src/composables/useFont.ts
  • src/composables/useOutlookFallback.ts
  • src/composables/usePlaintext.ts
  • src/composables/usePreheader.ts
  • src/composables/useTransformers.ts
  • src/composables/useUrlQuery.ts
  • src/config/defaults.ts
  • src/config/index.ts
  • src/events/index.ts
  • src/generators/plaintext.js
  • src/generators/render.js
  • src/index.js
  • src/index.ts
  • src/plaintext.ts
  • src/plugin.ts
  • src/plugins/postcss/mergeMediaQueries.ts
  • src/plugins/postcss/pruneVars.ts
  • src/plugins/postcss/quoteFontFamilies.ts
  • src/plugins/postcss/removeDeclarations.ts
  • src/plugins/postcss/resolveMaizzleImports.ts
  • src/plugins/postcss/resolveProps.ts
  • src/plugins/postcss/tailwindCleanup.ts
  • src/posthtml/defaultComponentsConfig.js
  • src/posthtml/defaultConfig.js
  • src/posthtml/index.js
  • src/posthtml/plugins/envAttributes.js
  • src/posthtml/plugins/envTags.js
  • src/posthtml/plugins/expandLinkTag.js
  • src/prepare.ts
  • src/render/active.ts
  • src/render/createRenderer.ts
  • src/render/index.ts
  • src/render/injectFonts.ts
  • src/render/plugins/codeBlockExtract.ts
  • src/render/plugins/markdownExtract.ts
  • src/render/plugins/rawExtract.ts
  • src/render/plugins/rowSourceLocation.ts
  • src/serve.ts
  • src/server/client.js
  • src/server/compatibility.ts
  • src/server/email.ts
  • src/server/index.js
  • src/server/linter.ts
  • src/server/routes/hmr.js
  • src/server/routes/index.js
  • src/server/sfc-utils.ts
  • src/server/ui/App.vue
  • src/server/ui/components/SidebarClose.vue
  • src/server/ui/components/ui/button/Button.vue
  • src/server/ui/components/ui/button/index.ts
  • src/server/ui/components/ui/checkbox/Checkbox.vue
  • src/server/ui/components/ui/checkbox/index.ts
  • src/server/ui/components/ui/command/Command.vue
  • src/server/ui/components/ui/command/CommandDialog.vue
  • src/server/ui/components/ui/command/CommandEmpty.vue
  • src/server/ui/components/ui/command/CommandGroup.vue
  • src/server/ui/components/ui/command/CommandInput.vue
  • src/server/ui/components/ui/command/CommandItem.vue
  • src/server/ui/components/ui/command/CommandList.vue
  • src/server/ui/components/ui/command/CommandSeparator.vue
  • src/server/ui/components/ui/command/CommandShortcut.vue
  • src/server/ui/components/ui/command/index.ts
  • src/server/ui/components/ui/dialog/Dialog.vue
  • src/server/ui/components/ui/dialog/DialogClose.vue
  • src/server/ui/components/ui/dialog/DialogContent.vue
  • src/server/ui/components/ui/dialog/DialogDescription.vue
  • src/server/ui/components/ui/dialog/DialogFooter.vue
  • src/server/ui/components/ui/dialog/DialogHeader.vue
  • src/server/ui/components/ui/dialog/DialogOverlay.vue
  • src/server/ui/components/ui/dialog/DialogScrollContent.vue
  • src/server/ui/components/ui/dialog/DialogTitle.vue
  • src/server/ui/components/ui/dialog/DialogTrigger.vue
  • src/server/ui/components/ui/dialog/index.ts
  • src/server/ui/components/ui/dropdown-menu/DropdownMenu.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuContent.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuGroup.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuItem.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuLabel.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuRadioItem.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuSeparator.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuShortcut.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuSub.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuSubContent.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
  • src/server/ui/components/ui/dropdown-menu/DropdownMenuTrigger.vue
  • src/server/ui/components/ui/dropdown-menu/index.ts
  • src/server/ui/components/ui/empty/Empty.vue
  • src/server/ui/components/ui/empty/EmptyContent.vue
  • src/server/ui/components/ui/empty/EmptyDescription.vue
  • src/server/ui/components/ui/empty/EmptyHeader.vue
  • src/server/ui/components/ui/empty/EmptyMedia.vue
  • src/server/ui/components/ui/empty/EmptyTitle.vue
  • src/server/ui/components/ui/empty/index.ts
  • src/server/ui/components/ui/input/Input.vue
  • src/server/ui/components/ui/input/index.ts
  • src/server/ui/components/ui/kbd/Kbd.vue
  • src/server/ui/components/ui/kbd/KbdGroup.vue
  • src/server/ui/components/ui/kbd/index.ts
  • src/server/ui/components/ui/scroll-area/ScrollArea.vue
  • src/server/ui/components/ui/scroll-area/ScrollBar.vue
  • src/server/ui/components/ui/scroll-area/index.ts
  • src/server/ui/components/ui/separator/Separator.vue
  • src/server/ui/components/ui/separator/index.ts
  • src/server/ui/components/ui/sheet/Sheet.vue
  • src/server/ui/components/ui/sheet/SheetClose.vue
  • src/server/ui/components/ui/sheet/SheetContent.vue
  • src/server/ui/components/ui/sheet/SheetDescription.vue
  • src/server/ui/components/ui/sheet/SheetFooter.vue
  • src/server/ui/components/ui/sheet/SheetHeader.vue
  • src/server/ui/components/ui/sheet/SheetOverlay.vue
  • src/server/ui/components/ui/sheet/SheetTitle.vue
  • src/server/ui/components/ui/sheet/SheetTrigger.vue
  • src/server/ui/components/ui/sheet/index.ts
  • src/server/ui/components/ui/sidebar/Sidebar.vue
  • src/server/ui/components/ui/sidebar/SidebarContent.vue
  • src/server/ui/components/ui/sidebar/SidebarFooter.vue
  • src/server/ui/components/ui/sidebar/SidebarGroup.vue
  • src/server/ui/components/ui/sidebar/SidebarGroupAction.vue
  • src/server/ui/components/ui/sidebar/SidebarGroupContent.vue
  • src/server/ui/components/ui/sidebar/SidebarGroupLabel.vue
  • src/server/ui/components/ui/sidebar/SidebarHeader.vue
  • src/server/ui/components/ui/sidebar/SidebarInput.vue
  • src/server/ui/components/ui/sidebar/SidebarInset.vue
  • src/server/ui/components/ui/sidebar/SidebarMenu.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuAction.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuBadge.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuButton.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuButtonChild.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuItem.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuSkeleton.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuSub.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuSubButton.vue
  • src/server/ui/components/ui/sidebar/SidebarMenuSubItem.vue
  • src/server/ui/components/ui/sidebar/SidebarProvider.vue
  • src/server/ui/components/ui/sidebar/SidebarRail.vue
  • src/server/ui/components/ui/sidebar/SidebarSeparator.vue
  • src/server/ui/components/ui/sidebar/SidebarTrigger.vue
  • src/server/ui/components/ui/sidebar/index.ts
  • src/server/ui/components/ui/sidebar/utils.ts
  • src/server/ui/components/ui/skeleton/Skeleton.vue
  • src/server/ui/components/ui/skeleton/index.ts
  • src/server/ui/components/ui/tabs/Tabs.vue
  • src/server/ui/components/ui/tabs/TabsContent.vue
  • src/server/ui/components/ui/tabs/TabsList.vue
  • src/server/ui/components/ui/tabs/TabsTrigger.vue
  • src/server/ui/components/ui/tabs/index.ts
  • src/server/ui/components/ui/tags-input/TagsInput.vue
  • src/server/ui/components/ui/tags-input/TagsInputInput.vue
  • src/server/ui/components/ui/tags-input/TagsInputItem.vue
  • src/server/ui/components/ui/tags-input/TagsInputItemDelete.vue
  • src/server/ui/components/ui/tags-input/TagsInputItemText.vue
  • src/server/ui/components/ui/tags-input/index.ts
  • src/server/ui/components/ui/toggle-group/ToggleGroup.vue
  • src/server/ui/components/ui/toggle-group/ToggleGroupItem.vue
  • src/server/ui/components/ui/toggle-group/index.ts
  • src/server/ui/components/ui/toggle/Toggle.vue
  • src/server/ui/components/ui/toggle/index.ts
  • src/server/ui/components/ui/tooltip/Tooltip.vue
  • src/server/ui/components/ui/tooltip/TooltipContent.vue
  • src/server/ui/components/ui/tooltip/TooltipProvider.vue
  • src/server/ui/components/ui/tooltip/TooltipTrigger.vue
  • src/server/ui/components/ui/tooltip/index.ts
  • src/server/ui/index.html
  • src/server/ui/lib/emulated-dark-mode.ts
  • src/server/ui/lib/utils.ts
  • src/server/ui/main.css
  • src/server/ui/main.ts
  • src/server/ui/pages/Home.vue
  • src/server/ui/pages/Preview.vue
  • src/server/views/404.html
  • src/server/views/error.html
  • src/server/views/index.html
  • src/server/websockets.js
  • src/tests/ast.test.ts
  • src/tests/build.test.ts
  • src/tests/components/Body.test.ts
  • src/tests/components/CodeBlock.test.ts
  • src/tests/components/CodeInline.test.ts
  • src/tests/components/Column.test.ts
  • src/tests/components/Container.test.ts
  • src/tests/components/Font.test.ts
  • src/tests/components/Head.test.ts
  • src/tests/components/Hr.test.ts
  • src/tests/components/Html.test.ts
  • src/tests/components/Img.test.ts
  • src/tests/components/Layout.test.ts
  • src/tests/components/Markdown.test.ts
  • src/tests/components/NoWidows.test.ts
  • src/tests/components/NotOutlook.test.ts
  • src/tests/components/NotPlaintext.test.ts
  • src/tests/components/Outlook.test.ts
  • src/tests/components/OutlookBg.test.ts
  • src/tests/components/Overlap.test.ts
  • src/tests/components/Plaintext.test.ts
  • src/tests/components/Preheader.test.ts
  • src/tests/components/QrCode.test.ts
  • src/tests/components/Raw.test.ts
  • src/tests/components/Row.test.ts
  • src/tests/components/Section.test.ts
  • src/tests/components/Spacer.test.ts
  • src/tests/components/Text.test.ts
  • src/tests/components/WithUrl.test.ts
  • src/tests/composables/defineConfig.test.ts
  • src/tests/composables/useConfig.test.ts
  • src/tests/composables/useCurrentTemplate.test.ts
  • src/tests/composables/useFont.test.ts
  • src/tests/composables/useOutlookFallback.test.ts
  • src/tests/config.test.ts
  • src/tests/plaintext.test.ts
  • src/tests/plugins/pruneVars.test.ts
  • src/tests/plugins/quoteFontFamilies.test.ts
  • src/tests/plugins/resolveMaizzleImports.test.ts
  • src/tests/plugins/resolveProps.test.ts
  • src/tests/render/_helpers.ts
  • src/tests/render/active.test.ts
  • src/tests/render/basic.test.ts
  • src/tests/render/composables.test.ts
  • src/tests/render/config.test.ts
  • src/tests/render/injectFonts.test.ts
  • src/tests/render/output-markers.test.ts
  • src/tests/render/plugins-render.test.ts
  • src/tests/render/plugins/rowSourceLocation.test.ts
  • src/tests/render/transformers.test.ts
  • src/tests/render/vue.test.ts
  • src/tests/transformers/addAttributes.test.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch next

Comment @coderabbitai help to get the list of available commands and usage tips.

cossssmin added 25 commits May 12, 2026 21:27
Column's auto-derived width now subtracts the column's own horizontal
border + padding from the emitted min-width — without this, two bordered
or padded cols summed past the container and wrapped under content-box
sizing. data-maizzle-cw and the MSO td slot still hold the outer slice so
nested rows and Outlook cells render correctly.

When the column has padding but no border, Word drops div padding outright,
so the padding decls are hoisted to the MSO td and the td width is
shrunk by 2*horizontal-padding to keep the cell at the outer slot.
background-color is always mirrored to the td so Word paints the cell
behind any padded area or whitespace, not just the div.

shorthandCss now also merges longhand inside MSO conditional comments so
the hoisted td styles end up as clean shorthand (e.g. padding: 8px 8px
8px 16px) instead of staying as longhand-only.

Column's font-size reset switched from text-base (which planted a 24px
line-height that propagated into Outlook) to text-[medium] — Word-safe
absolute-size keyword, no line-height, no CSS-var dependency.
Section now uses the same data-maizzle-mso-td-id plumbing as Container so
that class-derived padding and background-color reach the Outlook td
after CSS inlining. Before this, only the user's inline style attribute
got mirrored, so `<Section class="p-6 bg-white">` was a no-op in Word.

msoPlaceholders now also mirrors background-color (always, when present)
and skips padding hoist when the element has a horizontal border, since
Word renders div padding fine with a border and a td copy would double-
pad. Affects Container too, which is a strict improvement: bg-color now
paints the Outlook cell, and the rare border + padding combo no longer
doubles.

CSS box-model parsers moved from columnWidth into a shared
src/utils/cssBox.ts so msoPlaceholders can reuse horizontalBorderPx
without duplication.
normalize with Preheader component
Adds `aspect`, `position`, `size`, and `outlookFallback` props to the
Img component. When `aspect` is set (or a Tailwind `aspect-*` class is
detected), Img renders a padding-bottom + background-image wrapper with
role="img" plus a VML `<v:rect>` for Outlook. `size` maps to VML
`aspect` (cover → atleast, contain → atmost).
The post-process pass added a space after every `:` and `;`, which broke
URLs in inline style values (`url('https://...')` → `url('https: //...')`)
and forced visual consistency with Juice's spaced output that email
clients don't care about. Dropped the colon/semicolon rewriting and the
trailing-semicolon ensure; kept `preferUnitlessValues` (0px → 0) and
gated its walk on the option being enabled.
The Shiki theme background only landed on the `<pre>`, leaving the td's
mso-padding-alt-4 area to show through as white in Outlook — visible as a
white frame around dark themes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant