Skip to content

[6.x] Introduce a shared z-index ladder for the CP - #19501

Draft
brianjhanson wants to merge 1 commit into
6.xfrom
feature/tame-z-index
Draft

[6.x] Introduce a shared z-index ladder for the CP#19501
brianjhanson wants to merge 1 commit into
6.xfrom
feature/tame-z-index

Conversation

@brianjhanson

Copy link
Copy Markdown
Contributor

Description

Every z-index in the CP was an independent guess. The modern stack alone spanned 1, 2, 3, 10, 99, 100, 1000, 1001, 10000, 10001, 10002, with numbers picked defensively against each other — Modal.vue carried a @TODO make this less fragile/weird, the slideout shade sat at 99 only to be "one below" a 100 chosen elsewhere, and two pairs collided outright (CpSidebar tied with legacy .prompt at 1001; the element-editor's sticky header tied with legacy .progressbar at 1000).

This adds a named ladder and moves the modern stack onto it.

The ladder is declared in packages/craftcms-ui/src/styles/shared/z-layers.css as --c-z-* custom properties and mirrored in packages/craftcms-ui/src/constants/z-layers.ts as ZLayer for the places that need a number in JS. A unit test asserts the two can't drift. There are two bands: local (behind -1sticky 10) for stacking inside a component's own stacking context, and page-level (page-header 2000debug 9000) for surfaces that compete with the rest of the CP. Rungs are spaced by 1000 so a new layer can be slotted in without a renumber.

The page-level band starts at 2000 deliberately: the legacy CP bundle still uses raw numbers topping out at 1001, so every rung clears legacy without legacy having to be renumbered first — which matters because the two stacks share a page on any CpScreenResponse screen. Legacy SCSS is intentionally untouched; docs/z-layers.md carries a mapping table and says to port a legacy surface onto the ladder when you port the surface itself.

Two things turned up along the way that the docs now record:

  • Lion's OverlayController defaults to zIndex: 9999 and writes it inline on a wrapping <dialog>, so craft-popover, craft-action-menu, craft-tooltip, craft-select-rich, and craft-combobox were floating above everything by default rather than by design. Each now passes its rung through _defineOverlayConfig().
  • craft-dialog is a Lion modal dialog, so it opens via showModal() and lands in the browser's top layer — above every z-index regardless of value. No rung can order against it.
  • markdown-field.css's ::part(dialog) { z-index: 10001 } was dead code: craft-popover only exposes part="popup", and an inline style beats ::part anyway. Removed.

Garnish is standalone and can't import @craftcms/ui, so Drag's helperBaseZindex default moves 10003000 to match --c-z-drag, with a pointer comment; its docs and two assertions are updated to match.

Two intentional behavior changes worth knowing: the element-editor header now clears legacy .progressbar, and the Vue sidebar now sits above legacy .prompt/login rather than tying with them.

Verified with the @craftcms/ui (670) and Garnish (375) unit suites, the new sync test, vp fmt --check, and oxlint. No PHP changed, so composer ci wasn't run. The Storybook browser suite hasn't been run against this yet and is worth a pass before merge.

@github-actions

Copy link
Copy Markdown

📚 Storybook previews

@craftcms/uiopen Storybook

Changed components:

resources/jsopen Storybook

No changed components detected in this Storybook.

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

Labels

None yet

1 participant