Skip to content

feat(ui): Batch A — hierarchy + payoff surface#23

Merged
simonsangla merged 1 commit intomainfrom
feat/batch-a-ui-hierarchy
Apr 16, 2026
Merged

feat(ui): Batch A — hierarchy + payoff surface#23
simonsangla merged 1 commit intomainfrom
feat/batch-a-ui-hierarchy

Conversation

@simonsangla
Copy link
Copy Markdown
Owner

Summary

Pure UI reorganization + visual polish. No schema, persistence, export-core, or product-boundary changes. All 267 existing tests remain green.

What changed

  • Left rail regroup — Identity (name + presets) → Colors → Typography → Spacing → Advanced (collapsed by default, native `
    `/``).
  • Preview — chrome header strip ("Preview" + theme name) + framed canvas (12px radius, subtle shadow). Body padding 20 → 24px.
  • Widget gallery states — dropped opacity-dim; unselected = neutral ghost hairline; hover = primary hairline; selected = 2px primary ring + check-badge in top-right. `role="switch"` + `aria-checked` preserved.
  • Export panel — chip tabs with label + `.ext` sub-line, active filled primary; per-format one-line description; Download = primary CTA, Copy = ghost. `data-testid="export-output"` preserved.
  • App shell — sidebar 280 → 300px, consistent 12px card radii, canvas gutter 24 → 28px.

Constraints honored

  • `ThemeConfigSchema` + all zod validators: untouched.
  • `src/export/*` formatters: untouched.
  • `src/lib/persistence/*`: untouched.
  • `src/lib/theme/*`: untouched.
  • No new component directories.

Deferred

  • Batch B — 2-color Simple mode + Advanced overrides (deterministic HSL derivation, per-slot override flags persisted). Documented in `AGENT_HANDOFF.md` → Next recommended batch.

Test plan

  • `npm run lint` — 0 errors
  • `npm run typecheck` — pass
  • `npm run test` — 267/267 pass
  • `npm run build` — pass (21.39 kB CSS / 295.78 kB JS)
  • Preview at 1440×2400: Advanced collapsed by default, expands on click; widget badges render on selected cards; export chip tabs render active state + description; topbar + shell contained
  • Interaction smoke: clicked Advanced → expanded Shadows + Radii; clicked Card widget toggle → `aria-checked` flipped

🤖 Generated with Claude Code

…/export-core changes)

Pure UI reorganization. Regroup left rail into Identity → Colors → Typography →
Spacing → Advanced (collapsed by default, native <details>). Frame preview with
chrome header + shadowed canvas. Swap widget-card opacity-dim for neutral ghost
+ primary ring + check badge on selected. Rebuild export panel as payoff: chip
tabs with .ext sub-line, per-format description, Download as primary CTA, Copy
as ghost. Align app shell rhythm: sidebar 300px, 12px radii, consistent cards.

All 267 tests still pass. Schema, persistence, and export formatters untouched.
2-color Simple mode derivation deferred to Batch B.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
theme-forge Ready Ready Preview, Comment Apr 16, 2026 5:30pm

@simonsangla simonsangla merged commit 60247fb into main Apr 16, 2026
3 checks passed
@simonsangla simonsangla deleted the feat/batch-a-ui-hierarchy branch April 16, 2026 17:31
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements a comprehensive UI reorganization and visual polish for the theme-forge application, focusing on layout rhythm, component hierarchy, and accessibility. Key updates include a redesigned export panel with format descriptions, enhanced widget selection states with visual badges, and a regrouped sidebar that introduces a collapsible 'Advanced' section for shadows and radii. However, a high-severity issue was identified in the new Collapsible component: using the 'open' attribute instead of 'defaultOpen' on the native details element makes it a controlled component without state management, causing the section to snap shut during every re-render (e.g., on every keystroke in the editor).

children: ReactNode
}) {
return (
<details className={styles.collapsible} open={defaultOpen}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The Collapsible component uses the open attribute on the <details> element, which makes it a controlled component in React. Since it is being passed a literal false (via defaultOpen={false} on line 372) and there is no state management to update this value when the user interacts with the component, the section will forcefully collapse every time the parent ThemeEditor re-renders. This happens on every keystroke in any of the editor's inputs, making the "Advanced" section practically unusable as it snaps shut while typing.

To fix this while keeping the component uncontrolled, use the defaultOpen attribute instead of open.

Suggested change
<details className={styles.collapsible} open={defaultOpen}>
<details className={styles.collapsible} defaultOpen={defaultOpen}>

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d8d503f5c

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

children: ReactNode
}) {
return (
<details className={styles.collapsible} open={defaultOpen}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve Advanced panel open state across re-renders

Using open={defaultOpen} turns the <details> into a controlled element that is always forced back to false here, so once a user opens Advanced, any subsequent ThemeEditor re-render (for example typing in a shadow textarea or radius input) collapses the panel again. This makes editing advanced fields frustrating and effectively breaks the new collapsible workflow; the component should treat open state as uncontrolled after initial mount or manage it with real state.

Useful? React with 👍 / 👎.

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.

1 participant