Skip to content

docs: replace FAB and Switch screenshots with live interactive examples - #5069

Open
hristototov wants to merge 14 commits into
mainfrom
hristototov/revisit-docs
Open

hristototov wants to merge 14 commits into
mainfrom
hristototov/revisit-docs

Conversation

@hristototov

Copy link
Copy Markdown
Collaborator

Motivation

Parts of the documentation have drifted out of sync with the v6 modernisation effort. The FAB and Switch pages were still illustrated with screenshots captured before those components were reworked for the latest Material Design 3 spec, so they showed outdated naming and token references:

  • FAB had an "all modes" screenshot tab, but the component no longer has a mode prop — it's variant (six role-color presets) and size (three spec sizes) now.
  • Switch was documented with a four-way Android (enabled/disabled) / iOS (enabled/disabled) screenshot split, which no longer reflects reality: the modernised component renders the same MD3 visuals on both platforms.

Static images also can't show what these components actually do - the Switch handle animation, the Extended FAB expand/collapse transition - and they silently rot every time a component changes.

This PR drops those screenshots and inlines the real components instead, so the docs render live from

Related issue

Closes #4991

Implementation notes

A general mechanism rather than a one-off, so the remaining outdated pages can follow:

  • docs/src/data/liveExamples.ts - new registry mapping a component title to the example module and named exports to render. Adding an entry here automatically removes that component's screenshot tabs, so a component is documented either with screenshots or with live examples, never both. The invariant is enforced in one place in generatePageMDX.ts.
  • docs/src/components/InteractiveExample.tsx - shared shell for embedded demos. Paper components reach browser APIs through react-native-web, so the tree mounts client-only behind BrowserOnly with a same-sized placeholder during SSR to avoid layout shift, and the Paper theme is kept in sync with the active docs color mode. Follows the existing pattern in BannerExample.tsx.
  • FABExample.tsx / SwitchExample.tsx - the actual demos. The FAB variant and size lists mirror the Variant and Size unions in src/components/FAB/tokens.ts in order, so a spec change surfaces as a type error rather than a stale image.

Also includes one small runtime fix surfaced while building the demos: Switch's absoluteFill style was missing alignItems / justifyContent, so the unselected icon which is smaller than the SELECTED_ICON-sized iconWrap it sits in - was pinned to the top-left instead of centered. Snapshots updated accordingly.

Images

switch-after switch-before fab-after fab-before fab-extended-after fab-extended-before

@hristototov hristototov self-assigned this Aug 24, 2026
@hristototov hristototov changed the title Hristototov/revisit docs docs: revisit documentation structure and organization Aug 24, 2026
@hristototov hristototov changed the title docs: revisit documentation structure and organization docs: replace FAB and Switch screenshots with live interactive examples Aug 24, 2026
@hristototov
hristototov requested a review from satya164 August 24, 2026 12:02

@JKobrynski JKobrynski left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

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

Please avoid emdashes and claudism in text. Using LLM is fine but we shouldn't make our documentation look AI generated without human attention.

Comment thread docs/src/components/FABExample.tsx Outdated
Comment thread docs/src/components/FABExample.tsx Outdated
'tonalPrimary',
'tonalSecondary',
'tonalTertiary',
] as const;

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.

Remove the comment. Annotate the VARIANTS with a type based on the Variant so it actually stays in sync instead of the comment saying it does

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

Comment thread docs/src/components/FABExample.tsx Outdated
Comment on lines +20 to +23
/**
* Every spec size accepted by the `size` prop.
*/
const SIZES = ['default', 'medium', 'large'] as const;

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.

Same as above

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

Comment thread docs/src/components/FABExample.tsx Outdated
Comment thread docs/src/components/FABExample.tsx Outdated
Comment thread docs/src/components/InteractiveExample.tsx Outdated
Comment on lines +60 to +63
* Paper components depend on browser APIs through `react-native-web`, so the
* tree is only mounted on the client and a same-sized placeholder is rendered
* during SSR to avoid layout shift. The Paper theme is kept in sync with the
* active docs color mode so demos match the surrounding page.

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.

The comment is wrong for the component's content. There's nothing related to SSR here.

But also why are we only rendering these on client and skipping on SSR? We need to render components both on SSR and client.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

  • Dropped BrowserOnly and the Placeholder, then collapsed Frame + the pass-through InteractiveExample into a single component with no gate; the split had no purpose
  • Removed the now-dead .paper-interactive-example--loading class
  • Rewrote the comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

  • Dropped BrowserOnly and the Placeholder, then collapsed Frame + the pass-through InteractiveExample into a single component with no gate; the split had no purpose
  • Removed the now-dead .paper-interactive-example--loading class
  • Rewrote the comment

Comment thread docs/src/css/custom.css Outdated
Comment thread docs/src/data/liveExamples.ts Outdated
@hristototov
hristototov requested a review from satya164 August 27, 2026 14:42
Comment thread docs/src/components/SwitchExample.tsx Outdated
@hristototov
hristototov requested a review from JKobrynski August 28, 2026 13:21
const isDarkTheme = useColorMode().colorMode === 'dark';

return (
<Provider theme={isDarkTheme ? DarkTheme : LightTheme}>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you checked these in dark mode? I'm afraid they render with light-theme colors, so the caption under each switch ends up dark on dark and unreadable.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I have checked dark mode (even the screenshots are in dark mode) however, you're right about the text color - i didn't notice this. Fixed now 👍

cr-1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Captions read fine now, thanks. The switches still come up light-themed on a first dark-mode load, though. They only turn dark after navigating away and back. Try a hard reload with a dark OS theme and nothing stored.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch - the theme lookup was fine, but Paper bakes colors into inline styles and hydration keeps whatever the server painted, which is always light. Remounting the Provider on the client forces a repaint.

The recording below is the built output with a dark OS theme and empty storage:

dark-mode-walkthrough.webm


return (
<InteractiveExample title="Extended">
<FAB.Extended

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you checked the extended FAB on the built page?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, checked it on the built output (yarn build + yarn preview), it renders correctly, and the interaction is as expected
cr-2

@satya164
satya164 requested a balanced review from Copilot September 15, 2026 20:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@satya164

Copy link
Copy Markdown
Member

Review by Claude

Web

  1. The Switch captions and accessible labels go stale after a toggle.

    • Steps: open the Switch page. In the "Enabled" example, click the switch captioned "selected", then the one captioned "unselected".
    • What happens: the first switch is now off and the second is on, but the captions still read "selected" and "unselected". The aria-label values also stay "Selected" and "Unselected", so a screen reader announces "Selected, switch, off". The "With icons in the handle" example has the same problem.
    • Expected: the caption matches the current state, and the label does not contradict aria-checked.

    Web: Switch captions after toggling both switches

    (via Claude, verify if it makes sense)

  2. The FAB size captions sit at three different heights.

    • Steps: open the FAB page and look at the "Sizes" example.
    • What happens: the row centers each item vertically, so the "default", "medium" and "large" captions are on three different lines.
    • Expected: the captions line up on one line.

    Web: FAB Sizes captions at different heights

    (via Claude, verify if it makes sense)

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

Review by Claude

See the inline comments. (via Claude, verify if it makes sense)

Comment thread docs/src/components/SwitchExample.tsx Outdated
Comment on lines +14 to +18
<Labelled label="selected">
<Switch value={on} onValueChange={setOn} aria-label="Selected" />
</Labelled>
<Labelled label="unselected">
<Switch value={off} onValueChange={setOff} aria-label="Unselected" />

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.

The captions and aria-label values are fixed, but both switches toggle. After one click, the switch captioned "selected" is off, and a screen reader announces "Selected, switch, off".

Web: Switch captions after toggling both switches

Derive the caption from the value, and keep state words out of aria-label, because aria-checked already announces the state:

<Labelled label={first ? 'selected' : 'unselected'}>
  <Switch value={first} onValueChange={setFirst} aria-label="First switch" />
</Labelled>

The same applies to SwitchIconsExample. The disabled example can keep fixed captions, because those switches cannot change. (via Claude, verify if it makes sense)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

Comment on lines +44 to +55
// The server always renders light colors, so remount to repaint in dark mode.
const [mounted, setMounted] = React.useState(false);

React.useEffect(() => {
setMounted(true);
}, []);

return (
<Provider
key={String(mounted)}
theme={isDarkTheme ? DarkTheme : LightTheme}
>

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.

The key remount hides a hydration mismatch instead of avoiding it. Rspress reads matchMedia and the stored appearance in its initial client state, so useColorMode() already returns dark during hydration. The first client render then uses DarkTheme while the server HTML uses LightTheme. React keeps the server attributes in that case, and the key change throws away the hydrated tree and mounts every example a second time, also in light mode where nothing changed.

Use the light theme until after mount. Hydration then matches the server, and the switch to dark is a normal update:

Suggested change
// The server always renders light colors, so remount to repaint in dark mode.
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
return (
<Provider
key={String(mounted)}
theme={isDarkTheme ? DarkTheme : LightTheme}
>
// The server always renders light colors, so use them until hydration is done.
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
return (
<Provider theme={mounted && isDarkTheme ? DarkTheme : LightTheme}>

(via Claude, verify if it makes sense)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

row: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',

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.

With center, each item is centered on the tallest item in the row. In the Sizes example the three FABs have different heights, so the "default", "medium" and "large" captions sit on three different lines.

Web: FAB Sizes captions at different heights

flex-end puts the captions on one line and does not change rows where all items have the same height:

Suggested change
alignItems: 'center',
alignItems: 'flex-end',

(via Claude, verify if it makes sense)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

updated

@hristototov
hristototov requested a review from satya164 October 24, 2026 08:44

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: revisit the documentation and update outdated sections

4 participants