Scoped Surface CSS Plan
Scoped Surface And CSS Migration Plan
Purpose
This note captures the next frontend architecture step for generator:
- stop growing island-prefixed variables like
--header_*,--footer_*,--aside_* - move toward one shared variable vocabulary scoped by the cascade
- generalize the section system so
banner,spotlight, andwrapperbecome presets over a smaller shared model - migrate from SCSS to authored CSS where that is realistic, without forcing a risky flag-day rewrite
This is intentionally separate from the editor plan. It is about the site styling contract first, then editor implications.
Goals
- Keep one shared framework for all sites.
- Let websites vary primarily through Hugo-emitted variables and tokens, not ad hoc override stylesheets.
- Keep Hugo as the source of truth for tokens and scoped surfaces.
- Make the section model easier for the editor to expose and mutate.
- Move new and simple styling to CSS where possible.
Non-goals
- Do not remove SCSS in one pass.
- Do not break existing site palettes or layouts while introducing the new contract.
- Do not make
banner,spotlight, andwrapperdisappear immediately from authored content. - Do not require site-specific CSS files for ordinary stylistic differences.
Current problems
1. Too many island-prefixed variables
Today the framework still emits and consumes many variables like:
--header_bg--footer_fg--aside_border--article_nav_accent
That makes the vocabulary larger than it needs to be and encourages component logic to depend on the island name.
2. Section families are doing too much
banner, spotlight, and wrapper currently bundle together several concerns:
- palette/surface
- text alignment
- content width
- media placement
- motion
- prose vs split layout
That makes the authored API and the future editor harder to reason about.
3. SCSS is still the generator layer for too much of the system
Some files are effectively already plain CSS in .scss clothing, while others still genuinely benefit from Sass generation.
The current setup makes it easy to keep adding to SCSS even where plain CSS would now be cleaner.
Target model
Shared variable vocabulary
The framework should use one shared vocabulary, for example:
--bg--bg-alt--fg--fg-bold--fg-light--border--border-alt--accent--hover-accent--icon--content-max-width--section-gap--section-padding-x--section-padding-y--media-width
These names should mean the same thing everywhere.
Scoped surfaces
Those same names should be rebound by scope:
:root {
--bg: ...;
--fg: ...;
--content-max-width: ...;
}
header {
--bg: ...;
--fg: ...;
--content-max-width: ...;
}
main {
--content-max-width: ...;
}
footer {
--bg: ...;
--fg: ...;
}Then sections and blocks can override the same names more locally:
.wrapper {
--content-max-width: var(--content-max-width-prose);
}
.wrapper.width-wide {
--content-max-width: var(--content-max-width-wide);
}The important point is:
- same variable names
- different scopes
- no need for a separate variable family per island
Widgets are scoped subsystems
Widgets should follow the same scoped-surface rule instead of behaving like exceptions to the site theme.
The architectural pattern is:
- the site theme owns the base tokens
- the widget root aliases those tokens into local
--widget-*variables - the widget root resets global component leakage such as shared button styles
- the widget rebuilds its own controls explicitly with flex/grid and explicit spacing
- widget internals consume only the local aliases
That gives the framework one consistent CSS model:
- sites brand widgets automatically through the shared token cascade
- widgets remain internally stable even when shared component rules evolve
- per-site widget customisation can happen through local alias overrides instead of deep selector forks
Treat the widget as a scoped subsystem inside the generator, not as a special-case island outside the shared surface contract.
Compatibility bridge
We should not delete prefixed island variables immediately. The correct transition is:
- Hugo emits the new scoped generic variables.
- Hugo continues emitting legacy prefixed variables for compatibility.
- SCSS/CSS consumers are migrated to the generic names.
- Prefixed variables become compatibility-only.
- Eventually they can be removed.
The first shared migration slice now lives in assets/css/variables.css:
:rootstill emits the legacy prefixed palette families for compatibilityheader,footer,aside, and#article-navnow also rebind the shared names--bg,--fg,--fg-bold,--fg-light,--border,--border-alt,--border-bg,--accent,--hover-accent,--icon, and--fig-bg- consumers should prefer those shared scoped names first and only fall back to the prefixed variables while older SCSS is still being migrated
Section model generalization
Yes: banner, spotlight, and wrapper should be generalized.
Not by deleting them outright, but by treating them as presets over a smaller section contract.
Better section contract
Every section should be understandable in terms of a few orthogonal concerns:
surfacepalette and skinwidthprose / medium / wide / fullflowprose / split / stack / media-ledmedianone / left / right / fit / contain / coveralignmentleft / center / rightmotionnone / onload / onscroll
The first live bridge toward that model is now in place in Hugo:
content_onlyno section mediafigure_ledsection media exists, but uses figure-led layouts like.fitsplitsection media participates in split banner/spotlight layout
That shared flow distinction now decides whether split-screen tokens like halfscreen and orient-* survive section-class resolution. Explicit section attrs still own section semantics: authored banner / spotlight sections with orient-left or orient-right stay on the split-flow path even when the figure carries .fit.
The next bridge is now in place too: Hugo emits a neutral section-axis contract on the rendered DOM
and in #editorData, without removing any legacy classes yet. The first axis set is:
flowmedia_sidesizecontent_alignsurfacelayout_family
These currently surface as data-ql-* hooks on each rendered <section>, so the editor and later
SCSS migration can read explicit semantics first and keep banner / spotlight / wrapper as
compatibility presets instead of the only source of truth.
The editor-side write split now matches that direction:
- section-frame semantics write to the leading section attr block
- content width and block layout presets keep writing to the trailing content attr block
So the next SCSS migration can treat the emitted data-ql-* hooks as the semantic read surface
without introducing a second competing write authority.
That means:
wrapperbecomes mostly a prose/stack surface presetspotlightbecomes a split-flow presetbannerbecomes a media-led preset
They can remain as legacy class names for authored content, but the framework and editor should think in the more general model above.
Why this is better
- easier authored API
- easier editor UI
- fewer hardcoded section species
- style differences become tokens and modifiers, not distinct layout regimes
Width model
The width contract should follow the same scoped-variable principle.
Recommended shared width names:
--content-max-width-prose--content-max-width-medium--content-max-width-narrow--content-max-width-wide--content-max-width-full
Then scoped usage:
main { --content-max-width: var(--content-max-width-prose); }header { --content-max-width: ...; }footer { --content-max-width: ...; }
And section-level overrides:
.width-prose.width-medium.width-narrow.width-wide.width-full
This is better than inventing:
--header-content-max-width--footer-content-max-width--wrapper-content-max-width
unless those are only temporary bridge names.
SCSS to CSS migration
Bottom line
A large share of the codebase can move to plain authored CSS now, but not all of it.
The realistic path is:
- CSS-first for new/simple surfaces
- keep SCSS temporarily for breakpoint/grid/generator-heavy layout core
- deliberately shrink that core
Good CSS-first candidates now
These are already close to plain CSS:
assets/scss/components/_regions.scssassets/scss/components/_button.scssassets/scss/components/_modal.scssassets/scss/components/_actions.scssassets/scss/components/_list.scssassets/scss/base/_typography.scssassets/scss/layout/_wrapper.scss
These should be the first migration candidates.
Keep in SCSS for now
These still materially benefit from Sass:
assets/scss/libs/_breakpoints.scssassets/scss/libs/_html-grid.scssassets/scss/components/_row.scssassets/scss/components/_banner.scssassets/scss/components/_spotlight.scssassets/scss/components/_items.scssassets/scss/components/_figure.scssassets/scss/main.scss
Reasons:
- breakpoint DSL is pervasive
- grid utilities are generated
- several layout families still rely on pattern generation and repeated media/layout rules
Migration order
Phase 1. Freeze Sass growth
- Prefer plain CSS for new simple rules.
- Only add new SCSS when generation or shared breakpoint DSL is genuinely needed.
Phase 2. Introduce the scoped generic surface vocabulary
- Emit generic scoped variables for
header,main,footer, andaside. - Keep emitting prefixed island variables for compatibility.
- Start documenting the generic names as canonical.
Phase 3. Migrate consumers off island-prefixed vars
Start with:
assets/scss/layout/_nav.scssassets/scss/layout/_footer.scssassets/scss/components/_button.scss
Goal:
- these should consume generic names like
--bg,--fg,--accent,--border,--content-max-widthfrom scope - not hardcoded
--header_*,--footer_*, etc
Phase 4. Move CSS-like SCSS partials into CSS
Move the low-risk partials listed above into authored CSS files or flatten them enough that Sass is no longer adding value.
Phase 5. Generalize the section model
- keep
banner,spotlight, andwrapperworking - redefine them internally as presets over:
- surface
- width
- flow
- media
- alignment
- motion
This is the point where the editor should also stop thinking in terms of the old section species.
Phase 6. Shrink the SCSS generator core
After the width/palette/section contracts are stable:
- reduce Sass dependence in breakpoint-heavy components
- decide whether the breakpoint/grid system should itself be replaced or kept as the last SCSS island
Editor implications
This architecture is better for the editor because it exposes smaller shared controls:
SurfaceWidthFlowMediaAlignmentMotion
Instead of presenting users with:
bannerspotlightwrapperstyle1style2style3
The editor can still write the legacy classes where needed during migration, but its conceptual model should target the smaller shared contract.
Editor should edit the scoped variables directly
Yes: the editor should ultimately read and write the shared scoped variables themselves.
That is the right way to let designers and clients change:
- colors
- margins and padding
- content widths
- section spacing
- media widths
- typography scale
without inventing a second styling system parallel to Hugo and the theme.
Editor scopes
The editor should expose a small number of styling scopes:
siteheadermainfooterasidepage-typepagesectionblock
These should all use the same shared variable vocabulary.
Example:
sitesets the default--content-max-widthhomepageoverrides it to be widerfooteroverrides--bgand--fg- one section overrides
--content-max-widthagain to becomewide
Page-type scope
Page-type scope should be first-class in the editor.
The old minimum split was:
homepagelistsingle
But the better long-term model is:
homepagelistlandingdetail
Where:
landingfirst-level singleton pages such as/about/,/contact/,/bookings/,/gallery/detaildeeper nested pages with more document/article-like rhythm
This is a better editor and framework model than single-root / single-nested.
This lets the editor set sensible defaults for each page family, such as:
- content width
- section spacing
- prose rhythm
- lead max-width
- lead-section behavior
- brochure-like vs document-like density
Why this matters:
- wider default content widths for top-level pages
- stronger lead-section behavior on landing pages
- different section spacing rhythm
- more brochure-like presentation on first-level pages
- more document/article-like presentation on nested pages
The shared scope shape should therefore become:
body.ql-page-type-homepagebody.ql-page-type-listbody.ql-page-type-landingbody.ql-page-type-detail
and then rebind main through the same shared variables, for example:
main { --content-max-width: ... }main { --section-gap: ... }main { --lead-max-width: ... }
Current implementation status:
bodynow emits:ql-page-type-homepageql-page-type-listql-page-type-singleql-page-type-landingql-page-type-detail
mainnow mirrors the primary semantic scope asdata-ql-page-type="homepage|list|landing|detail"mainrebinds:--main-content-max-width--surface-content-max-width--surface-section-padding-y--surface-section-gap-y--lead-max-width--surface-structured-gap--surface-compact-padding-y--surface-intro-card-gap--surface-intro-card-padding-y--surface-intro-card-padding-x--surface-intro-card-max-width--surface-intro-card-mobile-max-widthfrom the shared page-type scope
- content-only
wrapper,banner, andspotlightsections now consume that shared surface width by default instead of hardcoding prose-only caps - core
wrapper.style1/wrapper.style2andbanner.style1/spotlight.style1now consume the shared surface section padding token on desktop - wrapper breakpoint padding now stays on the shared section/surface token path instead of
dropping straight to raw
size_*fallbacks - media-bearing
banner.style1/spotlight.style1content caps now resolve through semantic content-width tokens before the legacysize_innerfallback - remaining
wrapper/banner/spotlightpreset breakpoint geometry still uses legacy size math where the contract is asymmetric; that needs an explicit responsive token model before another migration pass wrapper,banner, andspotlightnow also consume shared section rhythm vars for outer spacing and inner padding- legacy
.squeeze/.padvariants are compatibility aliases over the shared spacing vars - automatic section rhythm now comes from page scope + section flow + semantic adjacency first
- shared SCSS now tightens common transitions such as:
- heading/lead -> grid/note/buttons
- grid -> note/buttons
- note -> grid
- figure-led media -> content
- shared SCSS now also compacts common short sections such as:
- a single note / facts block / buttons row
- a heading plus one semantic module
- short update sections like heading + paragraph or heading + note
- media-bearing
banner/spotlightsections still keep their preset split geometry - heading scale
- card/grid density
Next page-type migration step:
- continue moving remaining rhythm/layout consumers onto shared scoped tokens
- extend the page-type contract beyond width/padding/lead into section density and component spacing
- keep reducing reliance on preset-specific SCSS branches
Current implementation now covers:
- page-scope width defaults
- page-scope section padding and section gap defaults
- page-scope structured-block adjacency density
- page-scope compact-section padding
- page-scope intro-card sizing and density
- page-scope intro-card visual controls:
- radius
- card background / border / shadow / blur
- glow opacity
- kicker colors
- title font / size / weight
- lead max width
- ink accent color
- page-scope brochure component density:
- grid/card spacing
- note padding
- chip/facts density
- action row gap
- team/contact card density
- fixed-height embed caps
- page-scope typography and nav density:
- body/heading line-height
- paragraph and heading rhythm
- nav row/column gap
- nav link size and padding
- a neutral section shell:
- all rendered sections expose
.ql-section - shared content-only width/fullscreen and intro-card scaffolding now come from shared section mixins instead of separate
banner/spotlightcopies figure_ledsections preserve promoted/base layout families rather than collapsing back towrapper- explicit section layout/orientation beats figure-led inference, so
.fitno longer downgrades authored split sections intowrapper
- all rendered sections expose
- editor bridge alignment:
- the local markdown bridge now exposes a primary
surfacetoken group - the bridge also exposes a site-level
chromegroup for header / aside / article-nav / footer shell spacing - global edits map to
params.style.tokens.surface.*andparams.style.tokens.chrome.* - site + page-type
surfaceemissions now land after generated--main-*defaults so authored overrides actually win in the cascade - local edits map to
--surface-*and--lead-max-widthoverrides on markdown attr styles - legacy
section/regiontoken groups remain compatibility-only
- the local markdown bridge now exposes a primary
- chrome shell spacing now runs through shared applied vars:
--surface-shell-padding--surface-shell-margin
- scope mappings preserve compatibility with:
--size_header_multiplier--size_aside_multiplier--size_article_nav_multiplier--size_footer_multiplier- legacy
--*_marginvars when present
without forcing those changes onto every page individually.
Margin and width editing
Margins and widths should be driven by shared variables where possible.
Preferred variables:
--content-max-width--section-gap--section-padding-x--section-padding-y--media-width--figure-width
Shared rhythm should also use:
--section-gap-before--section-gap-after--section-padding-top--section-padding-bottom
Automatic adjacency tightening should live in shared SCSS and be driven by semantic DOM hooks, not JavaScript-only logic.
The intended authored rule is:
- let shared SCSS handle ordinary rhythm automatically
- use
.space-*/.pad-*only for real exceptions - keep
.squeeze*/.pad*only as migration aliases
The editor can label these more clearly as:
Content widthSection spacingSection paddingMedia widthFigure width
Immediate implementation priorities
- Emit generic scoped variables alongside current island-prefixed ones.
- Move width handling onto the shared scoped model.
- Migrate
nav,footer, and button consumers off prefixed island vars.
Concrete migration sequence
This is the recommended implementation order for the current generator state.
It is intentionally conservative:
- keep Hugo render authority intact
- keep the editor writing markdown/front matter/local attrs, not CSS files
- keep legacy variables alive until their consumers are gone
0. Treat the current token layer as the public API
Use the canonical token emission in assets/css/variables.css as the stable style API:
:rootowns the global vocabularymainrebinds the shared surface variables- page-type scopes rebind those same variables
- page front matter writes page-local token overrides
- section/block attrs write local overrides
For implementation purposes, the current public token layer is:
- global/shared:
colorwidthsurfacespaceradiusshadowtype
- compatibility-only:
sectionregion
Rule:
- do not add new editor-facing token families outside that set unless there is a clear cross-site need
Acceptance:
- new styling work targets the canonical token groups first
sectionandregionare treated as migration shims, not the long-term editor vocabulary- legacy Hugo palette vars with underscores, such as
--fg_bold, should continue to emit hyphen aliases like--fg-boldso older site configs remain compatible with the newer surface token layer
1. Freeze token naming and reduce vocabulary drift
The next migration work should use one preferred semantic language:
surfacewidthspacingdensityalignmentmediatype
Implementation rule:
- if a new variable is really just a scoped surface decision, prefer a
--surface-*,--color-*,--content-*,--space-*, or--radius-*name - do not introduce new island-prefixed families such as
--header_*or component-private editor vocabularies unless they are genuinely compatibility-only
Acceptance:
- new code does not introduce fresh island-specific token families
- editor labels can be expressed in semantic terms rather than section-species or preset names
2. Consumer migration wave A: layout chrome
Move the obvious outer-shell consumers onto the shared scoped names first.
Primary files:
assets/scss/layout/_nav.scssassets/scss/layout/_footer.scssassets/scss/components/_button.scss
Goal:
- consume scoped
--bg,--fg,--accent,--border,--surface-*, and shared width tokens from the cascade - keep prefixed variables only as fallback bridges where needed
Why this wave comes first:
- these files define the visible site chrome
- they are easier to reason about than banner/spotlight internals
- they reduce the amount of header/footer-specific vocabulary exposed to the editor
Acceptance:
- those consumers prefer shared scoped names first
- no new direct dependency on
--header_*,--footer_*,--aside_*, or--article_nav_*is added in migrated code - visual output stays unchanged for existing sites
3. Consumer migration wave B: section shell and brochure surface
After layout chrome, continue with the section shell that the editor most directly manipulates.
Primary files:
assets/scss/components/_wrapper.scssassets/scss/components/_banner.scssassets/scss/components/_spotlight.scssassets/scss/main.scss
Goal:
- make section width, spacing, padding, density, and lead sizing flow primarily through the shared surface vocabulary
- keep
banner,spotlight, andwrapperas compatibility presets over the smaller shared contract
Implementation rule:
- if a rule is really about section rhythm or content width, it should resolve through shared section/surface variables before any preset-specific fallback
- preset classes should increasingly choose defaults, not define separate style systems
Acceptance:
- section rhythm comes from shared scoped vars first
- width presets resolve through shared width tokens
banner,spotlight, andwrapperstill work for authored content, but the implementation thinks in shared section properties
4. Consumer migration wave C: repeated brochure blocks
Once the section shell is stable, move structured brochure blocks onto the same density and spacing language.
Primary targets:
- note/card/grid spacing
- actions/chips/facts/team/contact density
- media-width-like block decisions
Goal:
- repeated brochure components read from the same page/section surface tokens instead of bespoke spacing knobs
Acceptance:
- structured blocks on the same page family share density and spacing behavior by default
- block-level overrides remain local and markdown-backed
5. Keep editor scope support aligned with actual consumers
The bridge and token inspector already support:
sitepage_typepagesectionblock
The focused Nowtype section controls should resolve onto that same model rather than opening a
separate layout-only UI. The current bridge for that is an explicit shared-inspector section
context, so Layout and Style can still target section scope even when the user is editing a
focused markdown section rather than selecting a live page node.
Current guardrail:
page_typeandpageglobal writes should stay primarilysurface-focused until more consumers actually read sharedcolor,type,space, andwidthtokens consistently
This avoids exposing controls the site does not yet honor uniformly.
Implementation rule:
- widen editable scope groups only after the corresponding SCSS consumers have been migrated
Acceptance:
- editor controls match real framework behavior
- no inspector control claims to be page-wide or page-type-wide unless the cascade actually honors it across the surface
6. Turn the inspector from token-shaped to semantic
After enough consumer migration, the editor should stop presenting raw token groups as the primary mental model.
Preferred inspector groups:
SurfaceWidthSpacingDensityAlignmentMediaTypography
The runtime can still map those controls onto:
- shared token writes
- page front matter token writes
- section/block local attr writes
- compatibility class aliases during migration
Acceptance:
- common edits do not require thinking in raw class names
- preset names become secondary descriptions, not primary controls
7. Define the cleanup threshold explicitly
Legacy variables should only be removed after both conditions are true:
- the shared consumer path is stable across
header,main,footer,aside, and common section/block surfaces - the editor uses the shared semantic/token vocabulary without relying on legacy-prefixed names
Before that point, legacy variables are compatibility infrastructure, not technical debt to delete aggressively.
Exit criteria for this migration:
- new styling work uses the shared token vocabulary by default
- page-type and page scopes are meaningful and consistent
- section editing is expressed as semantic controls over shared variables plus small local overrides
- site-specific styling differences no longer require per-site theme forks or ad hoc style override files
Migration checkpoints:
- Add scoped variable surfaces for
homepage,list, andsingle. - Make the editor capable of reading and writing shared scoped variables at site/page-type/page/section/block scope.
- Introduce width/flow/surface modifiers as the canonical section editing API.
- Treat
banner,spotlight, andwrapperas legacy presets over that contract.
Current editor bridge status:
- the local bridge now exposes a first-class
surfacetoken group - the token inspector now orders
surfacefirst and pushes legacysection/regiongroups to the end as compatibility - scope targeting is now landing in the editor, so the same shared tokens can be written intentionally at:
Current section-axis migration status:
- Hugo now emits neutral section axes on rendered section roots and in
#editorData - the shared inspector now edits those axes first and writes section-frame semantics back to the leading inline section attr block
- the old standalone Nowtype section-layout popover has been removed, so section layout now routes only through the shared inspector path
- the first additive SCSS slice now consumes:
data-ql-flow="figure_led"data-ql-size="full" | "half"data-ql-section-surface="intro_card"
- legacy
banner/spotlight/wrapperclasses still remain authoritative for the rest of the geometry until more selector families have been migrated- site
- page type
- page
- section
- block
- the current implemented non-local scopes are:
sitepage typepagewith local scopes now behaving explicitly as:section: authored section ordinal (s0,s1, …) backed by the leading section attr blockblock: section media first (s0:media), then structured fenced blocks by kind + ordinal (team:0,gallery:0,contact:0,testimonials:0,buttons:0), then explicit attr/fence-backed block ids when present
- page-type and page scopes currently target the shared
surfacetoken family only, which is intentional: the CSS contract already consumes those tokens consistently across width, spacing, density, typography, and nav rhythm
Editor-mode implication:
- the shared token model should serve all three editor surfaces:
- page WYSIWYG
- single-page PDF
- double-page spread PDF
- those are different editing presentations over the same Hugo + markdown + scoped-token contract, not separate style systems
Editor-Grade Scope Architecture
This is the architecture needed to make the editor feel closer to SquareSpace while preserving Hugo, markdown, and the shared theme as the only styling authority.
Scope stack
The scope stack should be:
sitechrome regionheader,main,footer,aside,article-navpage_typepagesectionblock- widget-local alias scope inside the rendered widget root
The important rule is:
- the editor should not invent a second token vocabulary for these scopes
- each scope reuses the same shared vocabulary and relies on the cascade
- widgets stay on the same system by aliasing shared site tokens into local
--widget-*variables
Bridge contract
The current token bridge in scripts/local-markdown-bridge.mjs
already exposes:
- token catalog reads
- global token reads/writes
- local token reads/writes
The next step is to make reads inheritance-aware, not just value-aware.
Each token read should eventually return:
- requested scope
- requested token group/key
- effective value
- declared value at this scope, if any
- source scope currently supplying the effective value
- source path or node id when relevant
- fallback chain
- writable target metadata
Without that metadata, the editor can write scoped tokens but cannot explain inheritance clearly, which is the main remaining gap versus a professional website builder.
Inspector contract
The token inspector in cdn/custom/toggleMarkdown.js
should evolve from a raw token panel into an inheritance-aware scoped inspector.
It should show, for each field:
- effective value
- inherited vs overridden state
- source scope
- reset action
- optional preview state across desktop, tablet, and phone
The primary visible scopes should become:
SiteHeaderMainFooterAsideArticle NavPage TypePageSectionBlock
The primary visible control groups should become semantic first:
SurfaceWidthSpacingDensityAlignmentMediaTypography
The raw token group structure can remain the implementation layer underneath that UI.
Consumer migration rule
The editor may only advertise a scope/token control as broad if the cascade actually honors it broadly.
That means:
page_typeandpageshould remain primarilysurface-focused until more consumers read sharedcolor,type,space,width,radius, andshadowtokens consistently- chrome region scopes should not be exposed in the editor until shared chrome consumers actually prefer shared scoped names over legacy island-prefixed ones
- widget-specific controls should only appear when they are backed by documented widget-local alias layers rather than deep selector overrides
Current implementation gap
Architecturally, the framework is already close:
- shared vocabulary exists
- scoped token reads and writes exist
site,page_type,page,section, andblockare already modeled in the editor- the bridge already exposes a first-class
surfacegroup
But the editor is not yet editor-grade because:
- chrome scopes are not yet first-class in the inspector
- global reads now expose effective value, declared value, source scope, and fallback metadata, but local reads and the live inspector still need broader semantic polish
- too many consumers still depend on legacy or bespoke variables
- the inspector is still more token-shaped than semantic
Implementation checklist
- Extend the inspector scope model from
site/page_type/page/section/blockto includeheader,main,footer,aside, andarticle-nav. - Upgrade bridge reads so the editor can render effective value, declared value, source scope, source target, and fallback chain.
- Treat the token catalog as the editor field schema, including type, scope support, and reset behavior.
- Keep
page_typeandpageediting surface-first until shared consumers honor broader token groups consistently. - Continue migrating broad framework consumers onto shared scoped names before widening editor claims.
- Keep widgets on the same scope model by exposing widget-local alias layers rather than raw selector-specific style knobs.
- Keep page WYSIWYG, single-page PDF, and spread PDF as different views over the same scoped-token contract.
Definition of done
This part of the architecture is complete when:
- a token changed at
site,chrome region,page_type,page,section, orblockreliably affects the expected rendered scope - the editor can show whether a value is inherited or overridden and from where
- chrome region styling no longer depends on legacy-prefixed variables for ordinary theme decisions
- common sections and brochure components primarily resolve through shared
--surface-*and shared scoped tokens - widgets inherit brand defaults through shared tokens and expose only documented local alias overrides
- the same scope model behaves consistently across page WYSIWYG and PDF editing surfaces
Recommendation
Yes:
- generalize
banner,spotlight, andwrapper - move toward scoped generic variables instead of island-prefixed ones
- migrate from SCSS to CSS incrementally
No:
- do not try to remove SCSS all at once
- do not keep solving site differences with per-site override CSS when shared tokens and scoped variables can express them cleanly