Skip to content

feat(AppShell): add variant prop for nav/content background styles - #597

Merged
cixzhang merged 1 commit into
mainfrom
navi/feat/shell-variant
Mar 13, 2026
Merged

feat(AppShell): add variant prop for nav/content background styles#597
cixzhang merged 1 commit into
mainfrom
navi/feat/shell-variant

Conversation

@cixzhang

Copy link
Copy Markdown
Contributor

Summary

Replace background prop with variant on XDSAppShell. Controls how nav areas contrast with content. Part of #565, closes #346.

Variants

Variant Nav areas Content Dividers Border radius
wash wash wash no no
surface surface surface no no
section inherit inherit yes no
elevated wash surface no top-left when both navs present

Changes

  • variant prop replaces background on XDSAppShell
  • --radius-page token (20px) for elevated content corner
  • --color-navbar token removed — nav bg controlled by shell variant
  • SideNav uses backgroundColor: inherit instead of hardcoded surface
  • TopNav removes hardcoded navbar background
  • Elevated uses decorative backdrop sibling with isolation: isolate stacking
  • Storybook Playground updated with variant control

Breaking changes

  • background prop removed → use variant
  • --color-navbar token removed → use variant="surface" or theme override

Replace the `background` prop with a `variant` prop that controls how
nav areas contrast with the content area:

- `wash`: Nav and content both wash, no dividers
- `surface`: Nav and content both surface, no dividers
- `section`: Dividers between nav and content (default)
- `elevated`: Wash nav, surface content with border radius corner

Key changes:
- Remove `--color-navbar` token — nav bg now controlled by shell
- Add `--radius-page` token (20px) for elevated content corner
- SideNav/TopNav inherit background instead of hardcoding
- Elevated variant uses decorative backdrop with isolation stacking
- Storybook Playground updated with variant control

Part of #565. Closes #346.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Mar 13, 2026
@github-actions

github-actions Bot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

Modified Components

AppShell · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A 625B N/A
Lines of Code N/A 383 -
Complexity N/A Very High (47) -
SideNav · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A 711B N/A
Lines of Code N/A 832 -
Complexity N/A Very High (70) -
TopNav · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A 675B N/A
Lines of Code N/A 973 -
Complexity N/A Very High (50) -

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@xds/core 9.3KB 15.0KB 2.4KB

Accessibility Audit

Status: 1 accessibility violation(s) found — 1 serious.

XDSSideNav - 1 issue(s)
  • 🟠 serious: Ensure links have discernible text
    • Rule: link-name · Affects 1/8 stories · Learn more
    • WCAG: 2.4.4, 4.1.2 (Level A)

Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@cixzhang
cixzhang merged commit 094b843 into main Mar 13, 2026
18 of 19 checks passed
cixzhang added a commit that referenced this pull request Mar 13, 2026
- AppShell: update theming visualProps from ['background', 'height'] to
  ['variant', 'height'] to match xdsClassName() call after #597
- AppShell: add missing 'variant' prop entry to .doc.mjs (both EN and ZH)
- PowerSearch: add missing 'endContent' and 'resultCount' props from #593
- PowerSearch: rename XDSPowerSearch.doc.mjs → PowerSearch.doc.mjs for
  CLI convention consistency (all other docs use directory name)
- Tokenizer: add missing 'endContent' prop from #593 (both EN and ZH)

Found during Night Watch doc review.

Co-authored-by: Navi <navi@multimango.com>
cixzhang added a commit that referenced this pull request Mar 13, 2026
- AppShell: update theming visualProps from ['background', 'height'] to
  ['variant', 'height'] to match xdsClassName() call after #597
- AppShell: add missing 'variant' prop entry to .doc.mjs (both EN and ZH)
- PowerSearch: add missing 'endContent' and 'resultCount' props from #593
- PowerSearch: rename XDSPowerSearch.doc.mjs → PowerSearch.doc.mjs for
  CLI convention consistency (all other docs use directory name)
- Tokenizer: add missing 'endContent' prop from #593 (both EN and ZH)

Found during Night Watch doc review.

Co-authored-by: Navi <navi@multimango.com>
@cixzhang cixzhang mentioned this pull request Mar 14, 2026
cixzhang added a commit that referenced this pull request Apr 26, 2026
- AppShell: update theming visualProps from ['background', 'height'] to
  ['variant', 'height'] to match xdsClassName() call after #597
- AppShell: add missing 'variant' prop entry to .doc.mjs (both EN and ZH)
- PowerSearch: add missing 'endContent' and 'resultCount' props from #593
- PowerSearch: rename XDSPowerSearch.doc.mjs → PowerSearch.doc.mjs for
  CLI convention consistency (all other docs use directory name)
- Tokenizer: add missing 'endContent' prop from #593 (both EN and ZH)

Found during Night Watch doc review.

Co-authored-by: Navi <navi@users.noreply.github.com>
ernestt added a commit that referenced this pull request Jun 1, 2026
The elevated variant's root already paints --color-background-body. Both
the header and sidenav should be transparent, letting the root show
through — same as the section variant.

PR #597 (d0d8fca) grouped 'elevated' with 'wash' in the navAreaStyle
condition, giving both an explicit navAreaWash background. This was
unnecessary for elevated (root already has that color) and caused the
header/sidenav to paint on separate layers, which created subtle
rendering inconsistencies between the two nav areas.

Fix: remove 'elevated' from the navAreaStyle condition so both header
and sidenav are transparent. The stickyBgStyle fallback (for auto mode
scroll opacity) now correctly uses navAreaWash for elevated instead of
navAreaSurface.
ernestt added a commit that referenced this pull request Jun 2, 2026
The header wrapper only received a background in auto mode (via
stickyBgStyle for scroll opacity), while the sidenav panel always
received navAreaStyle. In fill mode (the default), the header was
transparent — relying on the root background showing through. This
caused a visible color mismatch between the top nav and side nav
in the elevated variant due to compositing layer differences.

Fix: apply navAreaStyle unconditionally to the header wrapper,
matching the sidenav panel. Both nav areas now paint their own
background layer identically in all height modes.

Introduced in PR #597 (d0d8fca) which added navAreaStyle to the
panel but only added it to the header for auto mode in PR #615.
ernestt added a commit that referenced this pull request Jun 4, 2026
…es (#2464)

* fix(AppShell): remove explicit navAreaStyle from elevated variant

The elevated variant's root already paints --color-background-body. Both
the header and sidenav should be transparent, letting the root show
through — same as the section variant.

PR #597 (d0d8fca) grouped 'elevated' with 'wash' in the navAreaStyle
condition, giving both an explicit navAreaWash background. This was
unnecessary for elevated (root already has that color) and caused the
header/sidenav to paint on separate layers, which created subtle
rendering inconsistencies between the two nav areas.

Fix: remove 'elevated' from the navAreaStyle condition so both header
and sidenav are transparent. The stickyBgStyle fallback (for auto mode
scroll opacity) now correctly uses navAreaWash for elevated instead of
navAreaSurface.

* fix(AppShell): apply navAreaStyle to header wrapper in all height modes

The header wrapper only received a background in auto mode (via
stickyBgStyle for scroll opacity), while the sidenav panel always
received navAreaStyle. In fill mode (the default), the header was
transparent — relying on the root background showing through. This
caused a visible color mismatch between the top nav and side nav
in the elevated variant due to compositing layer differences.

Fix: apply navAreaStyle unconditionally to the header wrapper,
matching the sidenav panel. Both nav areas now paint their own
background layer identically in all height modes.

Introduced in PR #597 (d0d8fca) which added navAreaStyle to the
panel but only added it to the header for auto mode in PR #615.

* fix(docsite): scope frosted top-nav CSS to outermost shell only

The docsite's translucent top-nav effect (backdrop-filter blur + 80%
transparent background) used bare .xds-top-nav and .xds-layout-header
selectors. These leaked into nested AppShells rendered inside component
examples and template previews, causing their top navs to get the
frosted glass treatment unintentionally.

Scope all affected rules with :not(.xds-app-shell .xds-app-shell) so
they only match the outermost (page-level) shell. Nested shells inside
the content area render with their normal opaque backgrounds.

* fix(docsite): correctly scope frosted top-nav to outermost AppShell

The previous scoping put the nested-shell guard on the outer .xds-app-shell
(`.xds-app-shell:not(.xds-app-shell .xds-app-shell) > .xds-layout .xds-top-nav`),
but the trailing .xds-top-nav was an unrestricted descendant — a nested
AppShell's top nav is still a descendant of the outer layout, so the frost
leaked into component examples and templates.

Move the guard onto the styled element itself via
`:where(:not(.xds-app-shell .xds-app-shell *))`. Any nav with two AppShell
ancestors is excluded and keeps its solid navAreaStyle background, staying
seamless with its sidenav. :where() keeps specificity at (0,1,0) so the
home-page, nav-mode, and theme-preview overrides still win unchanged.
cixzhang added a commit that referenced this pull request Jun 21, 2026
- AppShell: update theming visualProps from ['background', 'height'] to
  ['variant', 'height'] to match xdsClassName() call after #597
- AppShell: add missing 'variant' prop entry to .doc.mjs (both EN and ZH)
- PowerSearch: add missing 'endContent' and 'resultCount' props from #593
- PowerSearch: rename XDSPowerSearch.doc.mjs → PowerSearch.doc.mjs for
  CLI convention consistency (all other docs use directory name)
- Tokenizer: add missing 'endContent' prop from #593 (both EN and ZH)

Found during Night Watch doc review.

Co-authored-by: Navi <navi@users.noreply.github.com>
cixzhang pushed a commit that referenced this pull request Jun 21, 2026
…es (#2464)

* fix(AppShell): remove explicit navAreaStyle from elevated variant

The elevated variant's root already paints --color-background-body. Both
the header and sidenav should be transparent, letting the root show
through — same as the section variant.

PR #597 (d78a5d0) grouped 'elevated' with 'wash' in the navAreaStyle
condition, giving both an explicit navAreaWash background. This was
unnecessary for elevated (root already has that color) and caused the
header/sidenav to paint on separate layers, which created subtle
rendering inconsistencies between the two nav areas.

Fix: remove 'elevated' from the navAreaStyle condition so both header
and sidenav are transparent. The stickyBgStyle fallback (for auto mode
scroll opacity) now correctly uses navAreaWash for elevated instead of
navAreaSurface.

* fix(AppShell): apply navAreaStyle to header wrapper in all height modes

The header wrapper only received a background in auto mode (via
stickyBgStyle for scroll opacity), while the sidenav panel always
received navAreaStyle. In fill mode (the default), the header was
transparent — relying on the root background showing through. This
caused a visible color mismatch between the top nav and side nav
in the elevated variant due to compositing layer differences.

Fix: apply navAreaStyle unconditionally to the header wrapper,
matching the sidenav panel. Both nav areas now paint their own
background layer identically in all height modes.

Introduced in PR #597 (d78a5d0) which added navAreaStyle to the
panel but only added it to the header for auto mode in PR #615.

* fix(docsite): scope frosted top-nav CSS to outermost shell only

The docsite's translucent top-nav effect (backdrop-filter blur + 80%
transparent background) used bare .xds-top-nav and .xds-layout-header
selectors. These leaked into nested AppShells rendered inside component
examples and template previews, causing their top navs to get the
frosted glass treatment unintentionally.

Scope all affected rules with :not(.xds-app-shell .xds-app-shell) so
they only match the outermost (page-level) shell. Nested shells inside
the content area render with their normal opaque backgrounds.

* fix(docsite): correctly scope frosted top-nav to outermost AppShell

The previous scoping put the nested-shell guard on the outer .xds-app-shell
(`.xds-app-shell:not(.xds-app-shell .xds-app-shell) > .xds-layout .xds-top-nav`),
but the trailing .xds-top-nav was an unrestricted descendant — a nested
AppShell's top nav is still a descendant of the outer layout, so the frost
leaked into component examples and templates.

Move the guard onto the styled element itself via
`:where(:not(.xds-app-shell .xds-app-shell *))`. Any nav with two AppShell
ancestors is excluded and keeps its solid navAreaStyle background, staying
seamless with its sidenav. :where() keeps specificity at (0,1,0) so the
home-page, nav-mode, and theme-preview overrides still win unchanged.
cixzhang added a commit that referenced this pull request Jun 21, 2026
- AppShell: update theming visualProps from ['background', 'height'] to
  ['variant', 'height'] to match xdsClassName() call after #597
- AppShell: add missing 'variant' prop entry to .doc.mjs (both EN and ZH)
- PowerSearch: add missing 'endContent' and 'resultCount' props from #593
- PowerSearch: rename XDSPowerSearch.doc.mjs → PowerSearch.doc.mjs for
  CLI convention consistency (all other docs use directory name)
- Tokenizer: add missing 'endContent' prop from #593 (both EN and ZH)

Found during Night Watch doc review.
cixzhang pushed a commit that referenced this pull request Jun 21, 2026
…es (#2464)

* fix(AppShell): remove explicit navAreaStyle from elevated variant

The elevated variant's root already paints --color-background-body. Both
the header and sidenav should be transparent, letting the root show
through — same as the section variant.

PR #597 (9237ea5) grouped 'elevated' with 'wash' in the navAreaStyle
condition, giving both an explicit navAreaWash background. This was
unnecessary for elevated (root already has that color) and caused the
header/sidenav to paint on separate layers, which created subtle
rendering inconsistencies between the two nav areas.

Fix: remove 'elevated' from the navAreaStyle condition so both header
and sidenav are transparent. The stickyBgStyle fallback (for auto mode
scroll opacity) now correctly uses navAreaWash for elevated instead of
navAreaSurface.

* fix(AppShell): apply navAreaStyle to header wrapper in all height modes

The header wrapper only received a background in auto mode (via
stickyBgStyle for scroll opacity), while the sidenav panel always
received navAreaStyle. In fill mode (the default), the header was
transparent — relying on the root background showing through. This
caused a visible color mismatch between the top nav and side nav
in the elevated variant due to compositing layer differences.

Fix: apply navAreaStyle unconditionally to the header wrapper,
matching the sidenav panel. Both nav areas now paint their own
background layer identically in all height modes.

Introduced in PR #597 (9237ea5) which added navAreaStyle to the
panel but only added it to the header for auto mode in PR #615.

* fix(docsite): scope frosted top-nav CSS to outermost shell only

The docsite's translucent top-nav effect (backdrop-filter blur + 80%
transparent background) used bare .xds-top-nav and .xds-layout-header
selectors. These leaked into nested AppShells rendered inside component
examples and template previews, causing their top navs to get the
frosted glass treatment unintentionally.

Scope all affected rules with :not(.xds-app-shell .xds-app-shell) so
they only match the outermost (page-level) shell. Nested shells inside
the content area render with their normal opaque backgrounds.

* fix(docsite): correctly scope frosted top-nav to outermost AppShell

The previous scoping put the nested-shell guard on the outer .xds-app-shell
(`.xds-app-shell:not(.xds-app-shell .xds-app-shell) > .xds-layout .xds-top-nav`),
but the trailing .xds-top-nav was an unrestricted descendant — a nested
AppShell's top nav is still a descendant of the outer layout, so the frost
leaked into component examples and templates.

Move the guard onto the styled element itself via
`:where(:not(.xds-app-shell .xds-app-shell *))`. Any nav with two AppShell
ancestors is excluded and keeps its solid navAreaStyle background, staying
seamless with its sidenav. :where() keeps specificity at (0,1,0) so the
home-page, nav-mode, and theme-preview overrides still win unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(AppShell): add navBackground prop for nav/content color contrast

1 participant