Skip to content

Add the block content classname contract foundations (#200 Phase 1a) - #206

Open
sneridagh wants to merge 4 commits into
a3-content-css-pilotfrom
b1a-content-contract-foundation
Open

sneridagh wants to merge 4 commits into
a3-content-css-pilotfrom
b1a-content-contract-foundation

Conversation

@sneridagh

Copy link
Copy Markdown
Member

Phase 1a of #200: the foundations of the block content classname contract, with zero visual change.

Stacked on #205 (#199 A3), which is on #204 and #203. The base is a3-content-css-pilot. This PR's own change is only the last commit.

Why Phase 1 is split

#200's Phase 1 mixes zero-diff groundwork with changes that visibly alter spacing:

  • Anatomy gaps: adding hr to plateBlocksConfig gives it .category-separator, which @plone/layout's content-area.css already styles.
  • Shared spacing rules: moving them from content-area.css into content.css settles the view/edit drift.

So the phase is split:

  • 1a (this PR): the guardrails, the list parts and the docs. Zero diff.
  • 1b (next): the hr / h1 anatomy and the shared spacing rules, with every accepted diff listed.

Changes

Class-contract test (plate/acceptance/tests/content-class-contract.test.ts)

  • What it checks: it renders every native-block fixture in the public view and collects every class inside the content, grouped by the Plate node that renders it (p, h2, table, code, …, and editor for the editor root).
  • What's allowed:
    • the contract: slate-*, block, block-<type>, block-<type>__<part>, category-*;
    • third-party classes rendered inside the content: highlight.js (hljs-*, function_), lucide icons, React Aria (react-aria-*).
  • PENDING map: every remaining class must match the map of Tailwind classes still waiting for conversion, per node, exactly.
    • Ratchet: a new utility anywhere fails the test, and so does a converted node whose entry wasn't removed. The map can only shrink; each later phase deletes its entries.
  • Where it runs: it's an acceptance test rather than a unit test. The somersault renderer depends on the app's full registry config, and acceptance tests run on every PR in CI, so it still gates each phase.
  • Second test: the list hooks below render as expected.

Stylelint rules for styles/content.css

These are overrides in the .stylelintrc of @plone/plate, @plone/blocks and @plone/agave, the packages that ship one. They enforce #199's authoring rules:

  • at-rule-disallowed-list: layer;
  • selector-disallowed-list (with splitList) for :root, html, body and selectors that start with a bare element, including inside :where() / :is().

I checked them against a sample: it flags @layer, :root, html, body .x, h1, figure img and :where(figure img), and it allows .ok h1, :where(.x) and .content-area. All three real content.css files pass.

List parts

  • Part classes: block-p__list on ul / ol and block-p__item on li, in both the static and the editable list renderers.
  • Data attribute: BlockAnatomyPlugin adds data-list-style-type to list blocks (paragraphs with a listStyleType), with a unit test.
  • No CSS targets them yet, so nothing changes visually. Phase 4 (lists) uses them.

Docs

  • docs/development/block-anatomy.md gets a "Classname contract for themers" section: hooks, rules, shared type names (.block-video.slate-video / .slate-ploneBlock), BEM parts, variants as data attributes, and lists.
  • @plone/plate AGENTS.md: how to maintain PENDING.
  • @plone/blocks AGENTS.md: "keep CSS colocated" now points block content styles to styles/content.css.

Validation

  • CI=1 pnpm visual-test --retries=0: 22 passed, pixel-identical to the reference.
  • pnpm acceptance-test: 168 passed, including the 2 new contract tests.
  • pnpm --filter @plone/plate test --run: 75 passed, 1 of them new. check:ts, eslint and stylelint: clean.

Part of #200.

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.
@sneridagh
sneridagh requested a review from pnicolli October 2, 2026 21:28
* a3-content-css-pilot:
  Make the image block reset test independent of fonts
* a3-content-css-pilot:
  Measure the image block width once again in the style fields test
  Fix the server not loading translations, which broke hydration (#207)
* a3-content-css-pilot:
  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)

This branch has not been deployed

No deployments
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