Skip to content

feat(design): a workspace's CSS can restyle a part's type - #858

Closed
mortondev wants to merge 1 commit into
feat/portal-design-v2-phase5-font-uploadsfrom
feat/portal-design-v2-phase5-design-layer
Closed

mortondev wants to merge 1 commit into
feat/portal-design-v2-phase5-font-uploadsfrom
feat/portal-design-v2-phase5-design-layer

Conversation

@mortondev

@mortondev mortondev commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

The twenty-fifth of the stack that builds a new design engine for the portal, and the last of phase 5. A workspace's own CSS can now restyle a part's type, such as its headline's weight and tracking. Behind the portalDesignV2 flag; nothing changes for the admin or for portals on the old theme.

Why

The app's heading rule (h1, h2, h3 { font-weight: 600; letter-spacing: -0.02em }) and the root tracking were unlayered, and so was the design's heading weight, which had to beat them. Unlayered rules beat every layer, the workspace's CSS in tenant included, so a portal's CSS could set a headline's colour or size but not its weight or tracking.

What changes

  • globals.css declares a design layer between utilities and tenant: @layer theme, base, components, utilities, design, tenant;.
  • On a portal drawn by the design engine, the tracking and heading weight rules sit in @layer design. They beat utilities, as the unlayered rules did, and the workspace's CSS beats them. The heading family stays in base, so a utility on a heading still wins, as before.
  • The admin and portals on the old theme keep the unlayered rules, now scoped to :where(:root:not([data-portal-theme])). :where keeps their specificity what it was, so rules such as [data-settings-page] h3 still win over them.

The spec's alternative, moving the app's rules into base for every page, would let any font-* or tracking-* utility on a heading start winning across the admin and old portals. This keeps those pages as they are.

Verification

  • Every heading's computed weight, tracking and family, on seven pages (the portal's feedback, roadmap and changelog; the admin's feedback, roadmap, Settings › Portal and Settings › General), measured on the previous build and this one: no differences, on a portal on the design engine (Juniper) and one on the old theme (Quarry). That covered 60 and 64 headings, including the headline and post titles.
  • On Juniper, with Playwright: Custom CSS [data-qb-part="headline"] { font-weight: 400; letter-spacing: 0.05em; } applied in the preview. After publishing, the live headline drew at 400 with 0.05em tracking (1.5px at 30px), and post titles kept the design's 600 and tight tracking.
  • Tests, each checked by breaking the code it covers: the layer order, the scoped rules with no bare unlayered h1, h2, h3 or :root rule left, and the design's type rules in the design layer.
  • The core bench holds every ceiling, and the reachable suites pass (881 files).

@mortondev
mortondev added this pull request to stack #833 October 31, 2026 08:22
The app's heading and tracking rules were unlayered, so they beat every
layer, the workspace's tenant CSS included: a portal's own CSS could not
change its headline's weight or tracking. On a portal drawn by the design
engine they now sit in a design layer between utilities and tenant. They
still beat utilities, as before, and the workspace's CSS beats them.

The admin and portals on the old theme keep the unlayered rules, scoped
with :where so their specificity is what it was.
@mortondev

Copy link
Copy Markdown
Member Author

Condensed into #879. The stack is now five PRs, #878 to #882; this branch is kept as it was.

@mortondev mortondev closed this Nov 1, 2026
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