Skip to content

Split Quanta and icons out of @plone/components into @plone/quanta and @plone/icons - #212

Merged
sneridagh merged 8 commits into
mainfrom
quanta-components
Oct 3, 2026
Merged

sneridagh merged 8 commits into
mainfrom
quanta-components

Conversation

@pnicolli

@pnicolli pnicolli commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Why

@plone/components shipped two component libraries in one package:

  • a basic set: CSS-styled, white-label React Aria components;
  • the Quanta set (@plone/components/quanta): the Tailwind design system the CMS UI uses.

It also owned the icon set. So every consumer of the basic set pulled in Quanta, and the two couldn't evolve independently.

This PR splits them into three packages:

Package Role
@plone/quanta (new, 1.0.0-alpha.0) The Quanta design system: the design system of @plone/cmsui and related editor-facing packages, notably @plone/contents. It has its own Storybook.
@plone/icons (new, 1.0.0-alpha.0) The icon set, the Icon component, the *.svg?react types, the SVGR Vite plugin and the base icon CSS. It has no Plone dependencies.
@plone/components Only the basic set. It's the starting point for a future barebones library that @plone/layout, @plone/publicui and Aurora add-ons can build on. That library isn't part of this PR.

Key decisions

  • @plone/quanta doesn't depend on @plone/components, and the reverse is also true. Quanta builds directly on react-aria-components. It relied on two tiny pieces of the basic set, the Select section header/types and menuTriggerChildren, and those were copied into it (about 20 lines).
  • Icons became their own package, because both libraries need them and Quanta can't depend on components. This is a minimal, static version of the idea in Add new @plone/icons package for icon management #144. The registry-based <Icon name> and icon packs are left for later.
  • Removed outright, with no deprecated re-exports: @plone/components/quanta, /Icons, /icons/*, /vite-plugin-svgr, and the Icon and widget root exports. All in-repo consumers are switched in this PR.
  • Quanta is Tailwind-only. The old CSS-based QuantaTextField, QuantaSelect and QuantaTextAreaField wrappers were removed; nothing used them. There's a new Tailwind TextAreaField.
  • Same public API. @plone/quanta exports exactly what @plone/components/quanta exported (77 runtime exports), plus TextAreaField and the SearchField that main added in the meantime. All 35 Quanta story titles are unchanged.
  • Base icon styles ship from @plone/icons as @plone/icons/icons.css (fill: currentColor, inline-block, pointer-events: none). Without them, icons render black. Quanta consumers import the file themselves, because the lightningcss CLI that builds dist/*.css can't resolve imports from other packages.
  • Deliberately left as-is: layout, publicui, plate and theming only switched their import paths to @plone/quanta. Whether they should use Quanta at all is a separate review. The overlap between the Quanta colour tokens in @plone/theming and the Quanta palette is also untouched; basic theme.css now carries its own copy, basic/quanta-colors.css.

How to review

Review commit by commit and skip the merge commit. Each step is self-contained and was green on its own:

Commit What to look at Size
step00 New @plone/icons; all icon imports switched Mostly renames (git mv), plus about 40 import rewrites in components and the consumers
step01 Inside components: CSS-based Quanta wrappers removed, Tailwind TextAreaField added Small; worth reading in full
step02 New @plone/quanta with Storybook; Quanta code moved; consumers switched The biggest; mostly renames that drop the .quanta suffix
step03 Components dependency and Tailwind cleanup; Tailwind now scans @plone/icons; docs and AGENTS.md Mostly prose
step04 @plone/icons/icons.css base styles, loaded by the Quanta Storybook and cmsui Small
merge Brings in main. Main's new Sharing and contents modals now import from the new packages, and SearchField is exported from @plone/quanta Skim only

About 385 of the files are pure renames, so let GitHub's rename detection collapse them and focus on files with real diffs. Suggested reading:

  1. packages/icons/package.json and packages/quanta/package.json: exports and dependencies.
  2. packages/quanta/src/index.ts: the public surface.
  3. packages/quanta/.storybook/.
  4. packages/components/package.json and src/index.ts: what's left.
  5. The consumers: skim the import rewrites.
  6. docs/upgrade-guide/plone-components.md: does the migration story read well?

Manual checks

  • pnpm --filter @plone/quanta storybook: compare Button, Select, Table, DateTimePicker, Menu, Tooltip, TextAreaField and the three widgets with the components Storybook on main. Look especially at icon size and colour.
  • pnpm --filter @plone/components storybook: it should show only the basic set now.
  • pnpm dev, then log in and check a content edit form, the contents view, the object browser, the recurrence widget and the sharing page. They should look like before.

What to expect

  • Breaking changes for @plone/components users. The upgrade guide has a new 5.0.0 section with an old → new import table.
  • One intended visual change in the CMS UI. Icons now take the surrounding text colour instead of rendering black, and they no longer catch pointer events. cmsui.css never loaded the base icon rules before. The new visual regression tests (Add visual regression tests for the CMS chrome and the site frame (#199 A0) #202) may flag this.
  • Known failures that aren't caused by this PR:
    • pnpm --filter @plone/contents check:ts reports 13 errors. 12 existed before this work started. The 13th is a duplicated path key in routes/layout.test.tsx that's already on main. This check is disabled in CI.
    • check:exports (attw) flags the wildcard and non-JS subpath exports (@plone/icons/svg, /icons.css, @plone/quanta/dist/*.css, …). The old components exports did the same. Every root . export is green.
  • Release order. @plone/icons releases first (new level 0 in preleaser.js); @plone/quanta releases with the core packages.
  • Read the Docs. The Quanta Storybook is set up for https://plone-quanta.readthedocs.io/, and a maintainer needs to create that project.

What changed, per package

  • @plone/icons (new):
    • Moved in: the SVGs, the generated <Name>Icon components, Icon, the svg.d.ts types and PloneSVGRVitePlugin (its template now imports from @plone/icons).
    • New: base icons.css, tests, README and AGENTS.md, and the release tooling copied from components.
  • @plone/quanta (new):
    • All the Quanta components and stories, without the .quanta suffix.
    • The SizeWidget, AlignWidget and WidthWidget widgets, Quanta's utils.ts, and the Quanta CSS, typography and fonts (built to dist/quanta.css).
    • Its own Storybook, README, AGENTS.md and the release tooling.
  • @plone/components:
    • Basic set only; the Quanta, widget and icon exports are removed.
    • Depends on @plone/icons.
    • Dropped unused dependencies (tailwind-*, react-stately, @internationalized/date, @react-aria/utils, …) and its Tailwind setup.
    • README, AGENTS.md and the Storybook introduction are rewritten.
  • @plone/cmsui: imports from @plone/quanta and @plone/icons. cmsui.css scans both packages for Tailwind classes and imports @plone/icons/icons.css. The widget config takes the widgets from @plone/quanta.
  • @plone/contents: imports from @plone/quanta and @plone/icons.
  • @plone/layout, @plone/publicui, @plone/plate, @plone/blocks: import path switches only. layout's toolbar CSS takes Popover.css from @plone/quanta.
  • @plone/theming: tailwind.css scans @plone/quanta and @plone/icons instead of the components Quanta build.
  • apps/aurora: new dependencies, the SVGR plugin import, tsconfig types, and the optimizeDeps entries for the two new packages.
  • Repo: build scripts, Makefile, the release order, check-vite-optimize-deps, the ESLint non-add-ons list, CI matrices (unit, typecheck, stylelint, changelog) and the root AGENTS.md.
  • docs/: icons.md rewritten for @plone/icons; the import paths, package overviews and conventions updated; the new upgrade-guide section.

Not in this PR

pnicolli and others added 6 commits September 27, 2026 09:53
Resolve conflicts and move main's new code to the new packages:
- packages/publicui/routes/index.tsx: keep main's ShareIcon, import icons from @plone/icons
- packages/components/src/quanta/index.ts: stays deleted; main's new SearchField export moves to @plone/quanta
- packages/plate/components/ui/media-image-node.tsx: stays deleted, as on main (#201)
- Sharing form/route (cmsui) and the Properties, Rename, Tags and Workflow modals (contents): import from @plone/quanta and @plone/icons
- @plone/icons and @plone/quanta release config: use uvx and the gh token, as main now does for @plone/components

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KwNSgxJwJZ5YVusHLghbnn
@pnicolli
pnicolli requested a review from sneridagh October 3, 2026 12:14

@sneridagh sneridagh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!!!!! flawless!

Only one small thing: I saw the introduction of the plone-icons layer. Shouldn't we declare it in the default layers order? Or integrate it inside some other existing layer?

…ne/icons split

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@sneridagh

Copy link
Copy Markdown
Member

Merging, as cookieplone is generating an egg-chicken problem. Once merged and released it will go away.

@sneridagh
sneridagh merged commit f46ae98 into main Oct 3, 2026
39 of 41 checks passed
@sneridagh
sneridagh deleted the quanta-components branch October 3, 2026 16:58
sneridagh added a commit that referenced this pull request Oct 3, 2026
* b10-plone-blocks:
  Move the table drag handle fragment to @plone/quanta
  Releasing @plone/aurora 1.0.0-alpha.16
  Release @plone/contents 1.0.0-alpha.3
  Release @plone/publicui 1.0.0-alpha.8
  Release @plone/cmsui 1.0.0-alpha.11
  Release @plone/agave 1.0.0-alpha.8
  Release @plone/theming 1.0.0-alpha.8
  Release @plone/layout 1.0.0-alpha.13
  Release @plone/blocks 1.0.0-alpha.17
  Release @plone/plate 1.0.0-alpha.22
  Release @plone/react-router 2.0.0-alpha.7
  Release @plone/helpers 2.0.0-alpha.9
  Release @plone/registry 4.0.0-alpha.4
  Release @plone/quanta 1.0.0-alpha.1
  Release @plone/components 5.0.0-alpha.5
  Release @plone/client 2.0.0-alpha.8
  Release @plone/icons 1.0.0-alpha.1
  Release @plone/types 3.0.0-alpha.7
  Split Quanta and icons out of @plone/components into @plone/quanta and @plone/icons (#212)
sneridagh added a commit that referenced this pull request Oct 6, 2026
 A1) (#203)

* Move the Tailwind reset out of the top cascade layer in the CMS UI

Step A1 of #199. The CMS UI loads Tailwind with a plain import, so its
theme variables, preflight and utilities land in the declared theme,
base and utilities layers instead of a top-level cmsui layer. The reset
now sits below every other layer. Removes the cmsui layer and adds the
plone-content layer for block content CSS.

The quanta table row drag handle no longer relies on the global reset
to drop the basic button styles.

* Move the table drag handle fragment to @plone/quanta

The quanta Table moved from @plone/components to @plone/quanta in #212.
sneridagh added a commit that referenced this pull request Oct 6, 2026
…tent.css (#216)

* Move the Tailwind reset out of the top cascade layer in the CMS UI

Step A1 of #199. The CMS UI loads Tailwind with a plain import, so its
theme variables, preflight and utilities land in the declared theme,
base and utilities layers instead of a top-level cmsui layer. The reset
now sits below every other layer. Removes the cmsui layer and adds the
plone-content layer for block content CSS.

The quanta table row drag handle no longer relies on the global reset
to drop the basic button styles.

* Add the styles/content.css entry point to the add-on styles loader

Step A2 of #199. Every add-on's styles/content.css is aggregated into a
generated .plone/content.css, which both the Public UI and the CMSUI
loaders import first, inside the plone-content cascade layer. Add-ons
write block styles once and get them in both user interfaces. Documents
the convention, its authoring rules and how to override block styles.

* Move the image block and inner container styles to styles/content.css

Step A3 of #199, the pilot for the content CSS architecture. The image
block drops its CSS Module and the block inner container its duplicated
Public UI and CMSUI rules: both now live in styles/content.css, loaded in
both user interfaces inside the plone-content cascade layer. The editor
gets the .content-area content root, and Agave ships its first content
token. Acceptance tests prove that a theme token reaches the image block
in both user interfaces and that its layout doesn't depend on the
public theme's reset.

* Add the block content classname contract foundations

Phase 1a of #200. An acceptance test checks that the public block content
only uses contract classnames, against a list of pending Tailwind classes
that may only shrink as nodes are converted. Stylelint rules guard
styles/content.css. Lists get the block-p__list and block-p__item parts
and a data-list-style-type attribute, and the classname contract is
documented for themers.

* Make the image block reset test independent of fonts

A centered image right after a float is pushed below it by an amount
that depends on how tall the text next to the float is, which varies with
the fonts installed. The test page now puts the floated image last.

* Add h1 and hr to the Plate block anatomy

Phase 1b of #200. h1 (category text) and hr (category separator) were the
Plate-native blocks without anatomy classnames. In the public view, the
separator now gets the existing separator category spacing.

* Measure the image block width once again in the style fields test

The retry worked around React replacing the server-rendered DOM on
hydration. #207 fixed the cause (the server didn't load the
translations), so the nodes now stay connected.

* Move the text block styles to styles/content.css

Phase 2 of #200. Paragraph, title, headings, blockquote and separator are
styled by plain CSS in @plone/plate's styles/content.css instead of
Tailwind utilities, in both the public renderer and the editor. Values
read the theme's Tailwind variables with Tailwind's defaults as fallback.
Editor affordances stay Tailwind. A new acceptance test checks the text
blocks render the same under any public theme reset; the reset helpers
move to the shared Playwright tooling.

* Move the inline mark styles to styles/content.css

Phase 3 of #200. Inline code, keyboard input, highlight, links and
mentions are styled by plain CSS in @plone/plate's styles/content.css
instead of Tailwind utilities. Mention marks become data attributes.
Comment and suggestion marks render as plain text in the public view;
the editor keeps showing them. New tests: a visual test for the inline
marks, a contract check for inline and editorial marks, and a reset
independence test for the inline styles.

* Move the list styles to styles/content.css

Phase 4 of #200. Lists, to-do items and the read-only to-do checkbox of
the rendered content are styled by plain CSS in @plone/plate's
styles/content.css instead of Tailwind utilities. Checked to-do items get
a data-checked attribute. The editor's interactive checkbox stays a
Tailwind editor control. A new acceptance test checks lists render the
same under any public theme reset, and the reset tests share their
measuring helpers.

* Move the code block styles to styles/content.css

Part of #200.

* Move the table styles to styles/content.css

Part of #200.

* Remove the Plate media nodes from Aurora's presets

Nothing in Aurora's editor could insert them, pasted iframes became embeds the public view didn't render, and file drops needed an upload backend Aurora doesn't have. Aurora uses Plone blocks for media. The stock full preset keeps them.

Part of #200.

* Move the structural block and content root styles to styles/content.css

Callout, toggle, columns, table of contents and the rendered content root. The rendered content no longer has any Tailwind classes, so the contract test's PENDING list is gone.

Part of #200.

* Move the table drag handle fragment to @plone/quanta

The quanta Table moved from @plone/components to @plone/quanta in #212.

* Keep EditorView as the Tailwind-free content root

PlateRenderer renders EditorView again instead of PlateView directly, and EditorView no longer applies editorVariants: the content root is styled by styles/content.css. EditorView was only used by PlateRenderer, so it isn't left as dead code that would bring the Tailwind root classes back.
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.

2 participants