Skip to content

Boot: Migrate styles to CSS Modules - #81756

Merged
ciampo merged 9 commits into
trunkfrom
codex/boot-css-modules-local
Aug 20, 2026
Merged

ciampo merged 9 commits into
trunkfrom
codex/boot-css-modules-local

Conversation

@ciampo

@ciampo ciampo commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

What?

Migrate all @wordpress/boot styles to CSS Modules and encapsulate Boot-owned DOM classes.

Why?

This uses the supported @wordpress/style-runtime registration path, prevents Boot's private shell styles from leaking, and makes the unsupported internal-class boundary explicit.

How?

  • Rename imported stylesheets to .module.scss and use local module mappings.
  • Share one Root CSS Module across Root, RootSinglePage, and Router, with separate layout and view-transition Sass partials.
  • Keep document-level view-transition and popover rules scoped to documents that contain Boot.
  • Add the @wordpress/style-runtime dependency and its TypeScript project reference.
  • Replace global active-state hooks with existing aria-current="page" and data-status="active" state.
  • Replace cross-component .components-item selectors with private NavigationItem module classes.
  • Identify generated WP Build mount elements by their existing page-specific ID instead of boot-layout-container.
  • Remove obsolete Boot classes, dead selectors, and the cross-package save-panel styling dependency.
  • Document that consumers can style their mount element and route content, but not Boot's private shell.

Testing Instructions

  • Run both init() and initSinglePage().
  • Check LTR and RTL desktop/mobile layouts, navigation states, dropdown and drilldown flows, canvas/stage/inspector surfaces, the mobile drawer, save modal, notices, and error boundaries.
  • Check reduced-motion and view-transition behavior, including Safari-specific stage, inspector, and canvas captures.
  • Register the stylesheet in an editor iframe and confirm document-global transition rules remain guarded.

Testing Instructions for Keyboard

Navigate the sidebar, dropdowns, drilldown screens, save controls, and modal using only Tab, Shift+Tab, Enter, Space, Escape, and arrow keys. Focus indicators, activation, and focus return should behave as before.

Removed global class audit

An exact-name search across the current Gutenberg source tree confirms that these 27 Boot-owned global class names have zero remaining usages:

Area Removed global class names
Root and layout boot-layout-container, boot-layout, boot-layout--single-page, boot-layout__canvas, boot-layout__inspector, boot-layout__mobile-sidebar-drawer, boot-layout__sidebar, boot-layout__sidebar-backdrop, boot-layout__stage, boot-layout__surfaces, boot-notices__snackbar
Navigation boot-dropdown-item, boot-dropdown-item__chevron, boot-dropdown-item__children, boot-navigation-item, boot-navigation-screen, boot-navigation-screen__actions, boot-navigation-screen__description, boot-navigation-screen__title, boot-navigation-screen__title-icon
Leaf components boot-error-boundary, boot-save-button, boot-save-button__shortcut, boot-sidebar__back-button, boot-sidebar__content, boot-sidebar__footer, boot-sidebar__scrollable

Most were replaced by private CSS Module classes. WP Build now identifies its generated mount by the existing page-specific ID. boot-navigation-screen__description had no associated selector, so it needed no replacement.

Other Boot styling hooks:

  • Boot no longer relies on global .active; active state uses existing aria-current="page" and data-status="active" attributes.
  • Dead Boot selectors .with-suffix and canvas .is-full-canvas were removed.
  • Layout state classes (is-mobile, is-up, has-canvas, has-full-canvas, and has-mobile-drawer) are now private CSS Module classes. These generic names can still occur elsewhere in Gutenberg, so they are not repository-wide removals.

edit-site-save-panel__modal remains owned and used by Edit Site. This PR only removes Boot's dependency on it.

These classes are internal implementation details rather than supported styling APIs. Repository search cannot verify whether third-party npm consumers rely on them.

Verification

Passed focused JavaScript and CSS linting, dependency and package validation, npm run lint:tsconfig, npm run typecheck, the full npm run build, transformed CSS inspection, generated PHP syntax checks, the exact removed-class search, and commit hooks. Browser and iframe matrix testing remains manual.

Use of AI Tools

Codex assisted with implementation, CI diagnosis, adversarial review, and local verification. The author reviewed and accepted the resulting changes.

@ciampo
ciampo force-pushed the codex/boot-css-modules-local branch from 10042bc to 3c73ea3 Compare August 17, 2026 13:33
@github-actions

github-actions Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

Size Change: +2.29 kB (+0.03%)

Total Size: 7.74 MB

📦 View Changed
Filename Size Change
build/modules/boot/index.min.js 58.2 kB +2.29 kB (+4.1%)

compressed-size-action

@github-actions

Copy link
Copy Markdown

Flaky tests detected in 3c73ea3.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/32035765970
📝 Reported tests:

should open the inspector panel in /test/e2e/specs/widgets/customizing-widgets.spec.js, passed after 1 failed attempt.
Error: apiRequestContext.fetch: socket hang up
Call log:
  - → GET http://localhost:8889/wp-json/wp/v2/sidebars
    - user-agent: Playwright/1.62.1 (x64; ubuntu 24.04) node/20.20 CI/1
    - accept: */*
    - accept-encoding: gzip,deflate,br
    - X-WP-Nonce: 5ac44ef170
    - cookie: wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_23778236db82f19306f247e20a353a99=admin%7C1787146786%7CZILqCHXhqjviVvTPkF1pUHdt0yVAuQ65ibJ4TXqhaCu%7C8f277d933e6825f84e55a27adf135ddf86701f7b5a347b98aa499d07000a8f9e; wp-settings-time-1=1786974809

    at RequestUtils.rest (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/rest.ts:112:39)
    at RequestUtils.deleteAllWidgets (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/widgets.js:9:8)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/widgets/customizing-widgets.spec.js:34:22

@jsnajdr jsnajdr 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.

This full migration is much better than the superficial :global one. Let's abandon the other PR and merge all the changes into this one, and base it on trunk.

key={ index }
to={ item.to }
shouldShowPlaceholder={ false }
isNested

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.

Is this really the best way how we can declare that NavigationItem children of DropdownItem have a smaller min-height, grid-unit-30 instead of the default grid-unit-40?

@ciampo ciampo Aug 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed the isNested prop, moved those overrides to target directly :global(.components-item) when the item in inside the dropdown, ie. nested (since other item styles are already applied that way, anyway)

@ciampo

ciampo commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor Author

@jsnajdr the idea was to have a 2-phased migration for ease of reviewing, I definitely agree that this PR represents the end state we'd like to reach

I'd like to test GitHub stacks, should we try to keep the two PRs separate (as now) but merge them the whole stack at once?

@jsnajdr

jsnajdr commented Aug 19, 2026

Copy link
Copy Markdown
Member

Personally I'd prefer to review one all-in-one diff, but if you want to test stacks, why not, it's not a big deal 🙂

@ciampo
ciampo force-pushed the codex/boot-css-modules-local branch from 3c73ea3 to 7dd7d3d Compare August 19, 2026 08:00
@github-actions github-actions Bot added [Package] Element /packages/element [Package] Date /packages/date [Package] Data /packages/data [Package] Server Side Render /packages/server-side-render [Package] A11y /packages/a11y [Package] Autop /packages/autop [Package] DOM ready /packages/dom-ready [Package] i18n /packages/i18n [Package] Url /packages/url [Package] Word count /packages/wordcount [Package] Deprecated packages/deprecated [Package] Compose /packages/compose [Package] Core data /packages/core-data [Package] API fetch /packages/api-fetch [Package] HTML entities /packages/html-entities [Package] Viewport /packages/viewport [Package] DOM /packages/dom [Package] Keycodes /packages/keycodes [Package] Plugins /packages/plugins [Package] Components /packages/components [Package] Blocks /packages/blocks [Package] Editor /packages/editor labels Aug 19, 2026
@ciampo ciampo removed [Package] HTML entities /packages/html-entities [Package] Viewport /packages/viewport [Package] DOM /packages/dom [Package] Keycodes /packages/keycodes labels Aug 19, 2026
@ciampo

ciampo commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@jsnajdr merged all changes in this PR. We can test stacks another time

Comment thread packages/boot/src/components/navigation/dropdown-item/style.module.scss Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

cc @youknowriad to make sure this change is correct (I'm basically trying to remove the need for a global .boot-layout-container classname

Comment thread packages/boot/src/view-transitions.scss Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved to packages/boot/src/components/root/_view-transitions.module.scss

@youknowriad

Copy link
Copy Markdown
Contributor

This is working well in my tests, can't spot issues. I think the biggest changes seems to be around menu navigation, I didn't try but maybe we should register some drill down menus to check. Regardless though, we have no use-case for this at the moment.

Comment thread packages/boot/src/components/root/_view-transitions.module.scss Outdated
Comment thread packages/boot/CHANGELOG.md Outdated

@jsnajdr jsnajdr 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.

Looks good to me now 👍 Thanks for doing this migration so quickly and for addressing all feedback.

@simison simison mentioned this pull request Aug 21, 2026
@t-hamano

Copy link
Copy Markdown
Contributor

This PR causes the notification UI to be left-aligned and hidden.

snackbar

.layout .notices-snackbar {
@include mixins.snackbar-container();
}

The cause is that the snackbar-container mixin internally uses classes for external components.

@mixin snackbar-container() {
	// ...
	.components-snackbar {
		margin-inline: auto;
	}
}

There are probably many ways to solve this, but what do you think about centering the Snackbar component itself? Perhaps we shouldn't have created this mixin in the first place.

@ciampo

ciampo commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

There are probably many ways to solve this, but what do you think about centering the Snackbar component itself? Perhaps we shouldn't have created this mixin in the first place.

Opened #81995 with a quick fix.

Re. centering the Snackbar, I don't have enough context on it to recommend the change. We should probably open a separate issue to discuss it and involve the folks with the most expertise on it

);
?>
</div>
<div id="{{PAGE_SLUG}}-wp-admin-app" class="boot-layout-container"></div>

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.

Dropping boot-layout-container from the element also removed the only class Core's boot stylesheet uses to give the admin page viewport height. Now there's an odd gap below, which is a regression.

The ID-based :not() selector is fine on its own. The fix is either to keep the class on the div, or to move those height rules in @wordpress/boot to .boot-layout so they no longer depend on an ancestor class the template controls.

Screenshot

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Boot /packages/boot [Package] wp-build /packages/wp-build [Type] Code Quality Issues or PRs that relate to code quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants