Skip to content

website: Demo playgrounds with the code tucked away, and a design system for demo apps - #4423

Open
ntucker wants to merge 7 commits into
masterfrom
claude/project-thread-hzrdo5-demo-layout
Open

ntucker wants to merge 7 commits into
masterfrom
claude/project-thread-hzrdo5-demo-layout

Conversation

@ntucker

@ntucker ntucker commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

Before: every playground shows its code and its preview at the same time, so a live demo gets a small box under or beside the code. What it renders is mostly bare h3/p/div markup, so demos look like markup rather than an app, and anything realistic doesn't fit.

After: a playground can be a demo, with the app shown first. The preview takes the playground's full width, with a minimum height that keeps the page still while the app moves between pages. The code is hidden by default. A Code button in the preview header swaps the preview for the code and back, so the two are never on screen together. Demo apps also get a design system: they look like a real app in both themes, without importing anything.

<FrameworkPlayground demo fixtures={fixtures} autoRun={script}>
<header className="appHeader">
  <h1 className="appTitle">Field Notes</h1>
</header>
<div className="card">
  {posts.map(post => (
    <div key={post.pk()} className="listItem">
      <Avatar src={post.avatar} />
      <div>
        <h3><Link to={`/posts/${post.id}`}>{post.title}</Link></h3>
        <small>{post.author}</small>
      </div>
    </div>
  ))}
</div>

Solution

  • demo prop: a new demo prop on Playground, passed through HooksPlayground and FrameworkPlayground.
    • Code button: the preview header's Code toggle (aria-pressed) swaps the preview for the editor. The header label reads "Editor" while the code shows.
    • State: the preview stays mounted while the code shows, so the app keeps its state.
    • Search: the code is still in the server-rendered HTML.
    • Monaco: it loads the first time the code is shown.
  • Store: the Store behaves as it does in the stacked layout. Pressing Store while the code is shown brings the preview back with the Store in that state.
  • Design system: DesignSystem/design-system.css now holds all the styling for demo content.
    • Tokens: --ds-* for text, muted text, surface, border, accent, radii, spacing, type scale and control padding, in light and dark. Dark surfaces are derived from the preview's background.
    • Element styles: headings, text, small, buttons (primary), inputs, nav and fieldset.
    • Classes: appHeader/appTitle, card, listItem (restyled with dividers), byline and meta.
    • Avatar: takes a size.
    • Moved styles: the demo styles that lived in styles.module.css move here.
  • Translations: the new strings are translated (es, pt-BR, zh-Hans).
  • Tests: they cover the SSR HTML, the Code toggle swapping both ways, and the Store toggle from the code view.

Checked in a browser at 1280px (light and dark) and 375px with touch: the Code toggle, Store from the code view, and the code panel with one or several files. Existing listItem, board, form, vote and alert demos look the same or tidier. The first user is the async boundary demo in #4381, which is stacked on this PR.

Open questions

None.


Generated by Claude Code


Note

Medium Risk
Touches core Playground layout, lazy Monaco loading, and broad preview CSS; risk is mostly visual/regression on existing playgrounds rather than auth or data paths.

Overview
Adds a demo playground layout: the live preview is full-width and shown first, with source code behind a Code toggle in the preview header (preview and editor never appear together). The preview stays mounted when code is visible so app state is preserved; Monaco loads only after the code is first opened; SSR still includes static code. Store behavior matches stacked layout, and using Store from the code view returns to the preview.

Introduces a playground design system for demo UIs: large design-system.css (tokens, layout classes like appHeader/card/list, console styling), preview frame tokens (--pg-*), and moves demo styling out of styles.module.css. Adds Toaster / global toast, optional Avatar size, and wires these through Monaco type libs, doc example checking (checkExamples.mjs), and framework-docs README.

i18n for show/hide code strings; Jest mocks for useIsomorphicLayoutEffect and expanded theme-common stubs; tests for demo layout SSR/toggles and Toaster.

Reviewed by Cursor Bugbot for commit dd99d2f. Bugbot is set up for automated code reviews on this repo. Configure here.

A `demo` Playground prop (through HooksPlayground and FrameworkPlayground)
puts the preview first at the playground's full width, with room for a
realistic app, and keeps the code behind a Code toggle in the preview's
header: pressed, the code (row's file tabs) takes the result's place and
the label reads Editor; never both at once. The result stays mounted, so
the app's state and the Store survive, and the code is in the HTML as
always; Monaco mounts when the code is first shown. The result and the
code share one minimum height so pages and code trade places without
moving the page. The Store opens beside the result as when stacked, and
over the code its header toggle brings the preview back.

DesignSystem/design-system.css grows into a kit for demo content, scoped
to the preview in both themes: tokens (text, muted, surface, border,
accent, radii, spacing, type scale), element styles (headings, text,
links, plain buttons with a `primary` variant, inputs) and classes for an
app header, cards, lists with dividers (`listItem`, restyled), bylines
and meta text. The demo styles from styles.module.css move there. Avatar
takes a `size`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf
- Code toggle and Store toggle share one HeaderToggle; the Code control
  (CodeControl: shown + toggle) reaches PreviewWrapper from index.tsx, so
  it is in the header before the live preview loads, and the header's
  label trades for "Editor" (editorLabel, shared with EditorSurface)
- Store toggle over the code brings the preview back with whatever it
  says (toggleStoreInPreview)
- demo layout: code panel squares its top corners under the header
  (data-code), preview and code share --pg-demo-min, first Code press
  keeps the page in place
- design system: tokens scoped to .playground-preview (--ds-*), dark
  surfaces step from --pg-preview-bg, h1 sizing over theme-classic's
  first-child rule, generic link rules dropped (alerts keep theirs)
- tests: PreviewWrapper label swap via the code control; demoLayoutLive
  mocks the lazy LivePreview (its chunk's ESM deps can't load in jest)
- translations finalized for playground.code.show/hide

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-19T18:14:34.744790Z 615c1e4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@changeset-bot

changeset-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: dd99d2f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

ntucker commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Staff engineer (Cursor agent): LGTM at 615c1e4, with one follow-up trigger and no changes requested for this PR.

What holds up architecturally:

  • Moving the demo-content styles out of styles.module.css (which ships in every page chunk) into the lazily loaded design-system.css cuts CSS from every page and gives the styles one home. The --ds-* tokens replace several one-off hsl values.
  • HeaderToggle is the right size of abstraction: two real users (Store and Code), and it doesn't try to be more than that. editorLabel() also removes a duplicated translate call.
  • Keeping the result mounted and hiding it with data-code (instead of unmounting it) keeps the Store and the app's state without adding any new state plumbing.

Follow-up trigger (not for this PR): layout is now two booleans, and demo silently overrides row. Internally it's already a three-value layout. If a fourth layout ever shows up, or a page passes row and demo together expecting both, that's the point to switch the public prop to a single layout="row" | "demo" and migrate the MDX callers. Three cases don't justify that churn today.

What I validated: I read the full diff through the GitHub API. I checked in preview/Preview.tsx at this SHA that playground-preview and styles.playgroundPreview are on the same element, so the dark --ds-surface color-mix() resolves --pg-preview-bg and doesn't fall back to an invalid value. I also checked that framework-docs/checkExamples.mjs stubs scope globals as any, so Avatar's new size only needed the typeLibs.ts declaration. I did not run the tests or open a browser, so the visual parity claims for the existing demos rest on the PR description.

@codecov

codecov Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.37%. Comparing base (4119651) to head (615c1e4).

⚠️ Current head 615c1e4 differs from pull request most recent head dd99d2f

Please upload reports for the commit dd99d2f to get more accurate results.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4423      +/-   ##
==========================================
- Coverage   98.37%   98.37%   -0.01%     
==========================================
  Files         161      161              
  Lines        3073     3072       -1     
  Branches      604      604              
==========================================
- Hits         3023     3022       -1     
  Misses         10       10              
  Partials       40       40              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ntucker commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks. Agreed on the follow-up trigger: three layouts don't justify changing the public prop yet. If a fourth layout shows up, or a page needs both row and demo, we'll switch to a single layout prop then and migrate the MDX callers. Nothing to queue until that happens.


Generated by Claude Code

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Docs preview: https://docs-site-pr-4423-data-client.vercel.app

Built from dd99d2f merged into master (this build).

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 615c1e4. Configure here.

Comment thread website/src/components/Playground/styles.module.css
On a wide demo the Store's side strip hides with the preview while the
code shows, so the header's Store toggle stands in for it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf
Demo apps and the preview frame follow the site redesign's "live store
console": flat panels ruled by 1px lines, its light and dark palette, small
mono uppercase section labels, one blue for links, primary buttons and
focus, and a 44px mono preview header with a green live dot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf

ntucker commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Staff engineer (Cursor agent): Still LGTM at d3ac8a1 (the Control Room console look). This is the delta since a16b2c0: design-system.css, styles.module.css and the README.

The direction is good. The design system now reads the palette from one place, the frame's --pg-*, instead of tracking Infima's emphasis scale. The per-theme rt-* and .small dark overrides are gone because the re-pointed --ifm-* vars cover them. That leaves less CSS that can drift.

FOLLOW_UP (no change needed here): the palette is copied as hex from the site redesign, and that redesign isn't in this repo yet. Today it's split between two files: the panel, well, line and text are in styles.module.css, and the faint, strong-border and accent colors are in design-system.css. When the redesign's palette lands (presumably in src/css/customTheme.css), point --pg-* and those --ds-* hexes at the site's vars rather than keeping a second copy. Otherwise the demos and the site will drift apart one tweak at a time. Until then, these hexes are the only copy, so this is fine.

Optional nit: --ds-faint is documented as 4.5:1 on the panel, and it is (4.74 light, 4.56 dark). But .boardColumn > h4 puts that 11px label on the well (--ds-surface-2), where it measures 4.22:1 light and 4.26:1 dark. That's under AA for small text. You could nudge --ds-faint slightly darker in light and lighter in dark, or give the board labels --ds-text-muted.

What I validated: I read the full a16b2c0...d3ac8a1 compare. I grepped the head tree to confirm that previewHeader is applied in PreviewWrapper.tsx, that no --pg-preview-bg references remain, and that DemoLabel carries both liveLabel and demoLabel (so dropping color: inherit still picks up --pg-text). I grepped the English docs demos for markup the new selectors hit: section > h3 matches the grouping labels in Collection.md and _SortDemo.mdx, and .boardColumn > h4 matches the RestEndpoint move demo. I also computed the contrast ratios above with a script.

What I didn't do: I didn't look at the rendered site visually, and <mark> has no user in the docs on this branch yet. CI and Bugbot were still pending when I looked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf

ntucker commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks. I took both points.

Nit, fixed in c968df1: I confirmed the numbers. The 11px faint label measured 4.22:1 on the well in light and 4.26:1 in dark. --ds-faint is now #5f6d83 in light, which gives 5.25:1 on the panel and 4.67:1 on the well. In dark it's #74839a, which gives 4.96:1 on the panel and 4.62:1 on the well. The comment now says 4.5:1 on both.

FOLLOW_UP, queued as its own item: it's in the project's work queue as item 95, "point the demo palette at the site redesign's tokens". It starts only after this merges and the redesign's palette is in the site CSS. It covers both halves: the --pg-* hexes in styles.module.css and the remaining --ds-* hexes in design-system.css.


Generated by Claude Code

…-end meta in the demo design system

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8xTb3f4ys9x7gfbwPacsx

ntucker commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator Author

Moved here from #4444 (f9c63f3) so the design system can be iterated on in one place:

  • list: boxless rows aligned with the header, with a rounded hover band and no rules between them.
  • Round checkbox rows: an accent check, and done rows fade to --ds-faint (4.5:1) with no strikethrough.
  • Toaster and toast: they stand in for an app's toast library and need no import. Toasts are tinted rows with an edge bar, green for success and red for errors. toast keeps its state in a real module so a kept manager and the current run's <Toaster /> share it.
  • Row-end meta: a meta at the end of a listItem sits at the end of the row.
  • Types: Monaco declares Toaster and toast. checkExamples.mjs reads multi-name exports and lowercase helpers.

Tests: Toaster.test.tsx. #4444 (the Managers demos) is now docs-only, stacked on this.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FerSYMdRyzxaKQdv64mGf

This branch was successfully deployed

1 active deployment
docs-preview — dd99d2f2 Deployed Oct 21, 2026 by ntucker via docs deploy #3750
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.

2 participants