Skip to content

feat(a2ui-playground): theme toggle, preview modes, and UI polish#2556

Merged
Huxpro merged 2 commits into
lynx-family:mainfrom
Huxpro:Huxpro/a2ui-playground-improvements
May 4, 2026
Merged

feat(a2ui-playground): theme toggle, preview modes, and UI polish#2556
Huxpro merged 2 commits into
lynx-family:mainfrom
Huxpro:Huxpro/a2ui-playground-improvements

Conversation

@Huxpro
Copy link
Copy Markdown
Collaborator

@Huxpro Huxpro commented May 3, 2026

Summary

  • Page title: Set to "Lynx A2UI Playground" (both <title> and header brand)
  • Dark/light mode toggle: Added theme switch button in the header; uses data-theme attribute instead of prefers-color-scheme media query
  • Phone frame redesign: Clean bezel with 6px border, no notch/status bar/home indicator
  • Phone/Full preview toggle: Pill switch in the preview panel header to toggle between phone-framed and full-panel iframe preview
  • Header layout fix: Preview panel header wraps properly when chips + toggle compete for space

Test plan

  • Toggle dark/light mode via the header button
  • Switch between Phone and Full preview modes
  • Verify phone frame looks clean in both light and dark mode
  • Verify chips wrap properly in the preview panel header
  • Page title shows "Lynx A2UI Playground" in the browser tab

Summary by CodeRabbit

  • New Features

    • Added light/dark theme toggle with system preference detection and applied theme via document attribute.
    • Added preview mode switcher to toggle between phone-framed and full-panel views.
    • Updated top-bar branding text to "Lynx A2UI Playground".
  • Style

    • Updated dark-theme styling selector and consolidated theme tokens.
    • Refreshed phone preview frame, iframe layout, preview header, and preview mode control styles.

… UI polish

- Set page title to "Lynx A2UI Playground" (html.title + header brand)
- Add dark/light mode toggle in the header
- Simplify phone frame: clean bezel with border, no notch/status bar
- Add Phone/Full preview mode toggle in the preview panel header
- Fix preview header layout to handle chips + toggle without cramping
Copilot AI review requested due to automatic review settings May 3, 2026 23:37
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 3, 2026

⚠️ No Changeset found

Latest commit: aa3bb8b

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6d66d11d-27cc-4b2f-af29-37ded70adedc

📥 Commits

Reviewing files that changed from the base of the PR and between b2e4ae2 and aa3bb8b.

📒 Files selected for processing (2)
  • packages/genui/a2ui-playground/src/App.tsx
  • packages/genui/a2ui-playground/src/styles.css
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/genui/a2ui-playground/src/App.tsx
  • packages/genui/a2ui-playground/src/styles.css

📝 Walkthrough

Walkthrough

Adds client-side light/dark theming (via data-theme) with a header theme toggle, introduces a preview mode switch (phone vs full) for the Lynx preview, simplifies phone preview markup, updates related CSS, and sets the HTML title in the build config.

Changes

Theme & Preview Mode

Layer / File(s) Summary
Build metadata
packages/genui/a2ui-playground/rsbuild.config.ts
Adds source.html.title: 'Lynx A2UI Playground'.
Theme infrastructure
packages/genui/a2ui-playground/src/App.tsx
Adds Theme type, getSystemTheme() helper, theme state, and useLayoutEffect to keep document.documentElement's data-theme in sync; updates header branding and adds a theme toggle button.
Preview mode state & wiring
packages/genui/a2ui-playground/src/pages/DemosPage.tsx
Adds previewMode state (`'phone'
Component markup
packages/genui/a2ui-playground/src/components/MobilePreview.tsx
Removes phone chrome wrapper (phoneScreen, status bar/notch, home indicator), rendering the iframe directly inside phoneFrame.
Styling & layout
packages/genui/a2ui-playground/src/styles.css
Moves dark tokens from @media (prefers-color-scheme: dark) to [data-theme="dark"]; adds .themeToggle, updates .phoneFrame and .phoneIframe, tightens .previewPanelHeader, and adds .previewModeSwitch, .previewModeBtn, .previewPanelBodyFull, and .previewFullIframe for full-mode layout.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Sherry-hue
  • gaoachao
  • HuJean

Poem

🐰 I hopped in code to flip the light,

Dark or bright, both feel just right,
Phone or full, a button’s cheer,
The playground shifts — come see it here! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: theme toggle, preview modes, and UI polish enhancements across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 3/8 reviews remaining, refill in 33 minutes and 32 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds UI theming controls and preview layout options to the a2ui-playground web app, improving the playground’s usability and presentation while keeping the existing render pipeline intact.

Changes:

  • Introduces a header dark/light theme toggle implemented via a data-theme attribute and updates branding/title to “Lynx A2UI Playground”.
  • Adds a Phone vs Full preview mode switch and corresponding full-iframe preview layout.
  • Simplifies and polishes the phone frame visuals and preview panel header wrapping.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/genui/a2ui-playground/src/styles.css Moves dark theme tokens to [data-theme="dark"], adds styling for theme toggle + preview mode switch, updates phone frame + preview layout styles.
packages/genui/a2ui-playground/src/pages/DemosPage.tsx Adds preview mode state, segmented control UI, and conditional rendering for phone-framed vs full iframe preview.
packages/genui/a2ui-playground/src/components/MobilePreview.tsx Simplifies markup to match the redesigned phone frame (no notch/status/home indicator).
packages/genui/a2ui-playground/src/App.tsx Adds theme state + DOM data-theme wiring and updates header brand text + theme toggle button.
packages/genui/a2ui-playground/rsbuild.config.ts Sets the HTML title to “Lynx A2UI Playground”.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +33 to +38
[data-theme="dark"] {
--geist-foreground: #ededed;
--geist-background: #0a0a0a;
--geist-secondary: #888;
--geist-border: #333;
--geist-border-hover: #555;
Comment on lines +378 to +384
<div className='previewModeSwitch'>
<button
type='button'
className={previewMode === 'phone'
? 'previewModeBtn active'
: 'previewModeBtn'}
onClick={() => setPreviewMode('phone')}
Comment on lines +38 to +42
function getSystemTheme(): Theme {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/genui/a2ui-playground/rsbuild.config.ts (1)

11-35: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Prefer a reliably reachable LAN address here.

findLocalIp() just takes the first non-internal IPv4, which can easily be a VPN, Docker, or virtual adapter. In those environments the generated __rspeedy_url points phones at an unreachable host, so native preview QR flows break.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/genui/a2ui-playground/rsbuild.config.ts` around lines 11 - 35, The
current findLocalIp() returns the first non-internal IPv4 which can be a
VPN/virtual adapter; change findLocalIp() to prefer a reliably reachable LAN
address by: scanning all non-internal IPv4 addresses, filtering out interfaces
whose names indicate virtual/VPN/docker adapters (e.g., containing "docker",
"vbox", "vmnet", "veth", "tun", "tap", "vpn", "lo"), then choosing an address in
private LAN ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x) before falling back to
any remaining non-internal IPv4 and finally '127.0.0.1'; keep
buildRspeedyBundleUrl(port) using the returned ip from findLocalIp(). Ensure you
reference the existing function names findLocalIp and buildRspeedyBundleUrl when
making the changes.
🧹 Nitpick comments (1)
packages/genui/a2ui-playground/src/pages/DemosPage.tsx (1)

378-399: ⚡ Quick win

Expose the preview-mode selection to assistive tech.

The active Phone/Full state is only visual right now. Adding aria-pressed to both buttons will make the current mode clear to screen readers without changing the interaction model.

♻️ Minimal fix
 <div className='previewModeSwitch'>
   <button
     type='button'
     className={previewMode === 'phone'
       ? 'previewModeBtn active'
       : 'previewModeBtn'}
     onClick={() => setPreviewMode('phone')}
     title='Phone frame'
+    aria-pressed={previewMode === 'phone'}
   >
     Phone
   </button>
   <button
     type='button'
     className={previewMode === 'full'
       ? 'previewModeBtn active'
       : 'previewModeBtn'}
     onClick={() => setPreviewMode('full')}
     title='Full panel'
+    aria-pressed={previewMode === 'full'}
   >
     Full
   </button>
 </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx` around lines 378 -
399, The Phone/Full buttons only convey state visually; update the two button
elements in DemosPage.tsx (the ones using previewMode and setPreviewMode and
className 'previewModeBtn') to expose state to assistive tech by adding
aria-pressed attributes that evaluate to (previewMode === 'phone') for the Phone
button and (previewMode === 'full') for the Full button so screen readers can
detect the active mode while keeping the same click behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/genui/a2ui-playground/src/App.tsx`:
- Around line 49-53: The theme is being applied in a useEffect which runs after
first paint causing a flash; change the App component to apply the theme before
paint by replacing useEffect with useLayoutEffect (import useLayoutEffect from
React) for the effect that sets
document.documentElement.setAttribute('data-theme', theme), or alternatively set
the data-theme synchronously during initialization using getSystemTheme before
React mounts; update the effect that references theme (and any references to
setTheme/getSystemTheme) so it uses useLayoutEffect to avoid the initial
light-flash.

---

Outside diff comments:
In `@packages/genui/a2ui-playground/rsbuild.config.ts`:
- Around line 11-35: The current findLocalIp() returns the first non-internal
IPv4 which can be a VPN/virtual adapter; change findLocalIp() to prefer a
reliably reachable LAN address by: scanning all non-internal IPv4 addresses,
filtering out interfaces whose names indicate virtual/VPN/docker adapters (e.g.,
containing "docker", "vbox", "vmnet", "veth", "tun", "tap", "vpn", "lo"), then
choosing an address in private LAN ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
before falling back to any remaining non-internal IPv4 and finally '127.0.0.1';
keep buildRspeedyBundleUrl(port) using the returned ip from findLocalIp().
Ensure you reference the existing function names findLocalIp and
buildRspeedyBundleUrl when making the changes.

---

Nitpick comments:
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx`:
- Around line 378-399: The Phone/Full buttons only convey state visually; update
the two button elements in DemosPage.tsx (the ones using previewMode and
setPreviewMode and className 'previewModeBtn') to expose state to assistive tech
by adding aria-pressed attributes that evaluate to (previewMode === 'phone') for
the Phone button and (previewMode === 'full') for the Full button so screen
readers can detect the active mode while keeping the same click behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0156e095-6876-44e0-8e90-71ea3985f37f

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc29ee and b2e4ae2.

📒 Files selected for processing (5)
  • packages/genui/a2ui-playground/rsbuild.config.ts
  • packages/genui/a2ui-playground/src/App.tsx
  • packages/genui/a2ui-playground/src/components/MobilePreview.tsx
  • packages/genui/a2ui-playground/src/pages/DemosPage.tsx
  • packages/genui/a2ui-playground/src/styles.css

Comment thread packages/genui/a2ui-playground/src/App.tsx
@codecov
Copy link
Copy Markdown

codecov Bot commented May 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

- Use useLayoutEffect for theme to prevent dark mode flash
- Guard matchMedia for environments where it's unavailable
- Remove dead .previewPanelMeta and .previewMetaTags CSS
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 4, 2026

Merging this PR will degrade performance by 77.4%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 2 regressed benchmarks
✅ 78 untouched benchmarks
⏩ 26 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
008-many-use-state-destroyBackground 9.5 ms 8 ms +18.84%
basic-performance-large-css 15.3 ms 67.7 ms -77.4%
transform 1000 view elements 40.1 ms 46.9 ms -14.56%

Comparing Huxpro:Huxpro/a2ui-playground-improvements (aa3bb8b) with main (4fc29ee)

Open in CodSpeed

Footnotes

  1. 26 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 4, 2026

React Example (Element Template)

#33 Bundle Size — 198.61KiB (0%).

aa3bb8b(current) vs 4fc29ee main#31(baseline)

Bundle metrics  Change 1 change
                 Current
#33
     Baseline
#31
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 78 78
No change  Duplicate Modules 23 23
Change  Duplicate Code 40.51%(-0.02%) 40.52%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#33
     Baseline
#31
No change  IMG 145.76KiB 145.76KiB
No change  Other 52.85KiB 52.85KiB

Bundle analysis reportBranch Huxpro:Huxpro/a2ui-playground-im...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 4, 2026

React Example

#7765 Bundle Size — 225.52KiB (0%).

aa3bb8b(current) vs 4fc29ee main#7763(baseline)

Bundle metrics  no changes
                 Current
#7765
     Baseline
#7763
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 180 180
No change  Duplicate Modules 69 69
No change  Duplicate Code 44.54% 44.54%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#7765
     Baseline
#7763
No change  IMG 145.76KiB 145.76KiB
No change  Other 79.77KiB 79.77KiB

Bundle analysis reportBranch Huxpro:Huxpro/a2ui-playground-im...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 4, 2026

React MTF Example

#897 Bundle Size — 196.68KiB (0%).

aa3bb8b(current) vs 4fc29ee main#895(baseline)

Bundle metrics  no changes
                 Current
#897
     Baseline
#895
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 174 174
No change  Duplicate Modules 66 66
No change  Duplicate Code 44.05% 44.05%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#897
     Baseline
#895
No change  IMG 111.23KiB 111.23KiB
No change  Other 85.45KiB 85.45KiB

Bundle analysis reportBranch Huxpro:Huxpro/a2ui-playground-im...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 4, 2026

Web Explorer

#9338 Bundle Size — 900.03KiB (0%).

aa3bb8b(current) vs 4fc29ee main#9336(baseline)

Bundle metrics  Change 1 change
                 Current
#9338
     Baseline
#9336
No change  Initial JS 44.46KiB 44.46KiB
No change  Initial CSS 2.22KiB 2.22KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 9 9
No change  Assets 11 11
Change  Modules 230(+0.44%) 229
No change  Duplicate Modules 11 11
No change  Duplicate Code 27.28% 27.28%
No change  Packages 10 10
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#9338
     Baseline
#9336
No change  JS 495.9KiB 495.9KiB
No change  Other 401.92KiB 401.92KiB
No change  CSS 2.22KiB 2.22KiB

Bundle analysis reportBranch Huxpro:Huxpro/a2ui-playground-im...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 4, 2026

React External

#880 Bundle Size — 680.82KiB (0%).

aa3bb8b(current) vs 4fc29ee main#878(baseline)

Bundle metrics  no changes
                 Current
#880
     Baseline
#878
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 17 17
No change  Duplicate Modules 5 5
No change  Duplicate Code 8.59% 8.59%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#880
     Baseline
#878
No change  Other 680.82KiB 680.82KiB

Bundle analysis reportBranch Huxpro:Huxpro/a2ui-playground-im...Project dashboard


Generated by RelativeCIDocumentationReport issue

@Huxpro Huxpro merged commit f4f2d13 into lynx-family:main May 4, 2026
50 of 53 checks passed
Huxpro added a commit that referenced this pull request May 4, 2026
## Summary

Depends on #2556.

On mobile viewports, the Lynx Preview panel has severely limited height
due to the stacked column layout (sidebar + code panel + preview all
compete for space). This makes it impossible to actually enjoy the
rendered output.

**Solution: Fullscreen preview overlay**

- Add an expand button in the preview panel header to toggle fullscreen
mode
- Fullscreen overlay covers the entire viewport with z-index above
everything
- **Auto-expand on mobile**: When rendering starts on a viewport
<=980px, the preview automatically enters fullscreen
- Close via the close button in the header to return to the normal
layout

## Test plan

- [ ] On desktop: click the expand button in preview header — verify
fullscreen, click to close
- [ ] On mobile: select a demo — verify preview auto-enters fullscreen
- [ ] Verify simulation bar and QR section work in fullscreen
- [ ] Verify Phone/Full toggle works in fullscreen

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## New Features
* Added preview mode selector to switch between phone and full-width
layouts.
* Introduced independent fullscreen toggle for expanded preview viewing.
* Added live component stack display showing components rendered over
time.
* Preview panel now intelligently initializes based on viewport size
(phone layout for narrower screens).

## Style
* Updated dark theme styling implementation.
* Enhanced preview control UI with improved buttons and responsive
header layout.
* Refined phone preview frame styling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@coderabbitai coderabbitai Bot mentioned this pull request May 16, 2026
3 tasks
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