diff --git a/openspec/specs/web-ui-spacing-typography-rework-pass2.md b/openspec/specs/web-ui-spacing-typography-rework-pass2.md new file mode 100644 index 0000000..9220f46 --- /dev/null +++ b/openspec/specs/web-ui-spacing-typography-rework-pass2.md @@ -0,0 +1,457 @@ +# OpenSpec Spec: Web UI Component Cleanup — Inline Styles, Forms, Badges + +## Change +`web-ui-spacing-typography-rework` — Pass 2: Component Cleanup + +## Parent Proposal +`openspec/proposals/web-ui-spacing-typography-rework.md` + +## Parent Spec +`openspec/specs/web-ui-spacing-typography-rework.md` + +## Status +spec + +--- + +## 1. Scope of This Spec + +This spec covers **Pass 2 (Component Cleanup)** only: + +- Refactor static inline styles in the highest-volume editor/sidebar components into token-based utility classes. +- Replace hardcoded `280px` sidebars with the `--sidebar-width` token. +- Unify `.form-group` and `.form-field` into a single pattern. +- Consolidate status badges/chips into a single `.status-badge` family. +- Add the missing utility classes needed by the refactor (`.stack-*`, `.row-*`, `.card-*`, `.form-section`, `.form-row`, `.form-help`, `.text-error`, `.muted`, `.ghost-button`, `.secondary-button`, `.dialog-actions`). + +Out of scope for Pass 2: +- Token additions (Pass 1, already done). +- Missing primitive classes like `.btn` or `.alert` (Pass 1, already done). +- Modal/dialog unification (Pass 3). +- Card consolidation across page CSS files (Pass 3). +- Breakpoint standardization beyond sidebar width (Pass 3). + +--- + +## 2. Components to Refactor + +The following components have high inline-style counts and must be refactored to use utility classes. **Only static layout styles should move to CSS**; dynamic values (e.g. drag-over background, computed widths) may remain inline. + +### 2.1 `apps/web/src/components/features/tool/manifest-editor.tsx` (~70 inline styles) + +Typical patterns: +- `display: "flex", gap: "..."` → `.row`, `.row-sm`, `.row-md`, `.stack`, `.stack-sm`, `.stack-md` +- `padding: "..."` → `.p-2`, `.p-3`, `.p-4` +- `marginBottom: "..."` → `.mb-2`, `.mb-3`, `.mb-4` +- `background: "var(--panel)"` → `.card`, `.card-sm`, `.card-md` +- `borderRadius: "0.375rem"` / `10px` → `.rounded-md`, `.rounded-lg` +- `border: "1px solid var(--border)"` → `.card` already provides it +- `fontSize: "0.875rem"` → `.text-sm` +- `fontFamily: "monospace"` → `.font-mono` +- `flex: 1` → `.flex-1` +- `overflow: "auto"` → `.overflow-auto` +- `textAlign: "center"` → `.text-center` +- `justifyContent: "space-between"` → `.justify-between` +- `alignItems: "center"` → `.items-center` +- `cursor: "pointer"` → `.cursor-pointer` + +Required new utilities: +- `.row`, `.row-sm` (gap: `--space-2`), `.row-md` (gap: `--space-3`), `.row-lg` (gap: `--space-4`) +- `.stack`, `.stack-sm`, `.stack-md`, `.stack-lg`, `.stack-xl` +- `.items-start`, `.items-center`, `.items-end` +- `.justify-between`, `.justify-center`, `.justify-end` +- `.flex-1`, `.flex-none` +- `.text-center`, `.text-left`, `.text-right` +- `.font-mono` +- `.cursor-pointer` +- `.overflow-auto`, `.overflow-hidden` +- `.p-1` through `.p-5`, `.px-*`, `.py-*` +- `.m-0`, `.mb-1` through `.mb-6`, `.mt-*`, `.ml-*`, `.mr-*` +- `.w-full`, `.min-w-0`, `.max-w-prose`, `.max-w-form` +- `.rounded-sm`, `.rounded-md`, `.rounded-lg`, `.rounded-xl`, `.rounded-full` + +### 2.2 `apps/web/src/components/features/config-profiles/ConfigProfileEditorPanel.tsx` (~51 inline styles) + +Same utility patterns as above. In addition: +- Replace inline success-banner styles with `.alert-success` (add if missing). +- Replace inline drag item styles with `.drag-item`, `.drag-item-active` utilities. +- Replace inline sticky dialog-actions styles with the existing `.dialog-actions` class plus a `.sticky-footer` modifier if needed. + +### 2.3 `apps/web/src/components/features/tool-workshop/ToolTypeEditorPanel.tsx` (~20 inline styles) + +Same patterns as ConfigProfileEditorPanel. The empty-state and form header styles should use shared utilities. + +### 2.4 `apps/web/src/components/features/tool-workshop/ToolTypeListSidebar.tsx` (~12 inline styles) + +- Replace `style={{ width: "280px", minWidth: "280px" }}` with `style={{ width: "var(--sidebar-width)", minWidth: "var(--sidebar-width)" }}` or a `.sidebar` class. +- Replace list-item inline spacing with `.sidebar-item` utilities. + +### 2.5 `apps/web/src/components/features/config-profiles/ConfigProfileListSidebar.tsx` (~14 inline styles) + +- Same sidebar width change as ToolTypeListSidebar. +- Replace inline badge styles with `.badge`/`.badge-secondary`. +- Replace inline delete button styles with `.ghost-button.small` or `.btn-icon`. + +### 2.6 `apps/web/src/components/features/git/git-mount-editor.tsx` (~31 inline styles) + +Same stack/row/card/gap/padding/margin utilities as manifest-editor. + +--- + +## 3. Sidebar Width Tokenization + +Both sidebars currently hardcode `280px`. Change to use the token introduced in Pass 1. + +### Option A: Inline style with CSS variable + +```tsx +
+``` + +### Option B: Utility class + +```css +.sidebar { + width: var(--sidebar-width); + min-width: var(--sidebar-width); + display: flex; + flex-direction: column; + border-right: 1px solid var(--border); + background: var(--panel); +} +``` + +Use **Option A** if the surrounding layout already has custom styles; use **Option B** if the sidebars can share more structure. The spec requires at minimum that `280px` is replaced by `var(--sidebar-width)`. + +--- + +## 4. Form Pattern Unification + +### Current state + +- `.form-group` exists in `global.css` and is widely used. +- `.form-field` exists with nearly identical rules and is used in newer components. + +### Target state + +- Keep **`.form-group`** as the canonical class. +- Deprecate `.form-field` by making it an alias: + +```css +.form-field { + /* alias for backward compatibility */ + composes: form-group; /* not supported in plain CSS; instead duplicate the rule or remove .form-field usage */ +} +``` + +Because plain CSS does not support `composes`, do one of: + +1. **Preferred**: keep both selectors in the same rule block: + ```css + .form-group, + .form-field { + display: flex; + flex-direction: column; + gap: var(--space-2); + margin-bottom: var(--space-4); + } + ``` +2. Then migrate callers from `.form-field` to `.form-group` in a follow-up cleanup task (not this pass). + +### Add missing form utilities + +```css +.form-section { + padding: var(--space-4); + background: var(--panel); + border: 1px solid var(--border); + border-radius: var(--radius-md); +} + +.form-section > h4:first-child, +.form-section > h3:first-child { + margin-top: 0; +} + +.form-row { + display: flex; + align-items: flex-start; + gap: var(--space-3); +} + +.form-row .form-group { + flex: 1; + margin-bottom: 0; +} + +.form-help { + font-size: var(--font-size-sm); + color: var(--muted); + line-height: var(--line-height-normal); +} + +.text-error { + color: var(--danger); + font-size: var(--font-size-sm); +} +``` + +--- + +## 5. Status Badge Consolidation + +### Current state + +Status colors are duplicated across `.status-badge` (utilities.css), `.instance-chip` (global.css), and inline styles. + +### Target state + +Create a single `.status-badge` family in `global.css`: + +```css +.status-badge { + display: inline-flex; + align-items: center; + gap: var(--space-1); + padding: var(--space-1) var(--space-2); + border-radius: var(--radius-full); + font-size: var(--font-size-xs); + font-weight: 600; + text-transform: capitalize; + line-height: var(--line-height-tight); + background: var(--bg); + color: var(--muted); + border: 1px solid var(--border); +} + +.status-badge::before { + content: ""; + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; +} + +.status-badge--running, +.status-badge--success { + background: var(--success-light); + color: var(--success); + border-color: color-mix(in srgb, var(--success) 30%, transparent); +} + +.status-badge--error, +.status-badge--failed { + background: var(--danger-light); + color: var(--danger); + border-color: color-mix(in srgb, var(--danger) 30%, transparent); +} + +.status-badge--warning { + background: var(--warning-light); + color: var(--warning); + border-color: color-mix(in srgb, var(--warning) 30%, transparent); +} + +.status-badge--info, +.status-badge--pending, +.status-badge--idle { + background: var(--info-light); + color: var(--info); + border-color: color-mix(in srgb, var(--info) 30%, transparent); +} +``` + +Then update callers to use `.status-badge` + `.status-badge--` instead of `.instance-chip` or inline status colors. Keep `.instance-chip` as a deprecated alias for one pass if it is widely referenced; do not delete it yet. + +--- + +## 6. Required New Utility Classes + +Add to `apps/web/src/styles/utilities.css` unless they fit better in `global.css`. + +### 6.1 Layout utilities + +```css +.row { + display: flex; + align-items: center; + gap: var(--space-3); +} + +.row-sm { gap: var(--space-2); } +.row-md { gap: var(--space-3); } +.row-lg { gap: var(--space-4); } + +.row.items-start { align-items: flex-start; } +.row.items-center { align-items: center; } +.row.items-end { align-items: flex-end; } + +.stack { + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.stack-sm { gap: var(--space-2); } +.stack-md { gap: var(--space-3); } +.stack-lg { gap: var(--space-4); } +.stack-xl { gap: var(--space-5); } + +.justify-between { justify-content: space-between; } +.justify-center { justify-content: center; } +.justify-end { justify-content: flex-end; } + +.flex-1 { flex: 1; } +.flex-none { flex: none; } + +.w-full { width: 100%; } +.min-w-0 { min-width: 0; } +.max-w-prose { max-width: 65ch; } +.max-w-form { max-width: 800px; } +``` + +### 6.2 Spacing utilities + +```css +.m-0 { margin: 0; } +.mt-1 { margin-top: var(--space-1); } +.mt-2 { margin-top: var(--space-2); } +.mt-3 { margin-top: var(--space-3); } +.mt-4 { margin-top: var(--space-4); } +.mt-5 { margin-top: var(--space-5); } +.mt-6 { margin-top: var(--space-6); } + +.mb-0 { margin-bottom: 0; } +.mb-1 { margin-bottom: var(--space-1); } +.mb-2 { margin-bottom: var(--space-2); } +.mb-3 { margin-bottom: var(--space-3); } +.mb-4 { margin-bottom: var(--space-4); } +.mb-5 { margin-bottom: var(--space-5); } +.mb-6 { margin-bottom: var(--space-6); } + +.ml-auto { margin-left: auto; } +.mr-auto { margin-right: auto; } + +.p-1 { padding: var(--space-1); } +.p-2 { padding: var(--space-2); } +.p-3 { padding: var(--space-3); } +.p-4 { padding: var(--space-4); } +.p-5 { padding: var(--space-5); } + +.px-2 { padding-left: var(--space-2); padding-right: var(--space-2); } +.px-3 { padding-left: var(--space-3); padding-right: var(--space-3); } +.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); } + +.py-1 { padding-top: var(--space-1); padding-bottom: var(--space-1); } +.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); } +.py-3 { padding-top: var(--space-3); padding-bottom: var(--space-3); } +.py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); } +``` + +### 6.3 Typography utilities + +```css +.text-xs { font-size: var(--font-size-xs); } +.text-sm { font-size: var(--font-size-sm); } +.text-base { font-size: var(--font-size-base); } +.text-lg { font-size: var(--font-size-lg); } +.text-xl { font-size: var(--font-size-xl); } + +.font-mono { font-family: "IBM Plex Mono", monospace; } +.font-medium { font-weight: 500; } +.font-semibold { font-weight: 600; } + +.text-center { text-align: center; } +.text-left { text-align: left; } +.text-right { text-align: right; } +``` + +### 6.4 Visual utilities + +```css +.cursor-pointer { cursor: pointer; } +.overflow-auto { overflow: auto; } +.overflow-hidden { overflow: hidden; } + +.rounded-sm { border-radius: var(--radius-sm); } +.rounded-md { border-radius: var(--radius-md); } +.rounded-lg { border-radius: var(--radius-lg); } +.rounded-xl { border-radius: var(--radius-xl); } +.rounded-full { border-radius: var(--radius-full); } +``` + +### 6.5 Card utilities + +```css +.card-sm { + padding: var(--space-3); + background: var(--panel); + border: 1px solid var(--border); + border-radius: var(--radius-md); +} + +.card-md { + padding: var(--space-4); + background: var(--panel); + border: 1px solid var(--border); + border-radius: var(--radius-md); +} + +.card-lg { + padding: var(--space-5); + background: var(--panel); + border: 1px solid var(--border); + border-radius: var(--radius-lg); +} +``` + +Ensure existing `.card` rules remain backward-compatible. + +--- + +## 7. Button Class Alignment + +The codebase uses many ad-hoc button class names: `.primary-button`, `.secondary-button`, `.ghost-button`, `.button-secondary`, `.delete-btn`. + +For Pass 2, align the most common ones to the new `.btn` primitives where feasible: + +- `.primary-button` → keep as alias to `.btn.btn-primary` +- `.secondary-button` / `.button-secondary` → keep as alias to `.btn.btn-secondary` +- `.ghost-button` → define explicitly or alias to `.btn.btn-secondary` +- `.btn-icon` → add a modifier `.btn.btn-icon` for icon-only buttons + +Because renaming every caller is risky, the worker should: +1. Add the aliases in CSS so both old and new class names work. +2. Update only the refactored components to use the new `.btn*` classes. +3. Leave other callers untouched to keep the diff focused. + +--- + +## 8. Acceptance Criteria + +- [ ] All six target components have significantly fewer inline style blocks. +- [ ] No hardcoded `280px` sidebar widths remain; `var(--sidebar-width)` is used. +- [ ] New layout/spacing/typography/visual utilities from §6 are present. +- [ ] `.form-group` and `.form-field` share a single rule block. +- [ ] `.status-badge` family exists and is used by at least one caller. +- [ ] Button aliases (`.primary-button`, `.secondary-button`, `.ghost-button`) map to `.btn` primitives. +- [ ] `npm run typecheck` passes in `apps/web`. +- [ ] `npm run lint` passes in `apps/web`. +- [ ] No visual regressions in Tool Workshop and Config Profiles pages. + +--- + +## 9. Verification Plan + +1. Run `cd apps/web && npm run typecheck`. +2. Run `cd apps/web && npm run lint`. +3. Count inline `style={{` blocks in the six target components before/after; expect ≥50% reduction overall. +4. Search for `280px` in `apps/web/src`; expect zero matches (except in comments/docs). +5. Open Tool Workshop and Config Profiles in browser/dev server and verify layout. +6. Check that `.status-badge` renders correctly in at least one place. + +--- + +## 10. Next Phase + +After this spec is approved, create **tasks** for Pass 2 implementation, then delegate to `sdd-apply`. diff --git a/openspec/tasks/web-ui-spacing-typography-rework-pass2.md b/openspec/tasks/web-ui-spacing-typography-rework-pass2.md new file mode 100644 index 0000000..19bc2cb --- /dev/null +++ b/openspec/tasks/web-ui-spacing-typography-rework-pass2.md @@ -0,0 +1,99 @@ +# OpenSpec Tasks: Web UI Component Cleanup — Inline Styles, Forms, Badges + +## Change +`web-ui-spacing-typography-rework` — Pass 2: Component Cleanup + +## Parent Spec +`openspec/specs/web-ui-spacing-typography-rework-pass2.md` + +## Status +tasks + +--- + +## Implementation Tasks + +- [x] 1. Add utility classes to `apps/web/src/styles/utilities.css` + - Layout: `.row`, `.row-sm`, `.row-md`, `.row-lg`, `.stack`, `.stack-sm`, `.stack-md`, `.stack-lg`, `.stack-xl`, `.items-start`, `.items-center`, `.items-end`, `.justify-between`, `.justify-center`, `.justify-end`, `.flex-1`, `.flex-none`, `.w-full`, `.min-w-0`, `.max-w-prose`, `.max-w-form` + - Spacing: `.m-0`, `.mt-1` through `.mt-6`, `.mb-0` through `.mb-6`, `.ml-auto`, `.mr-auto`, `.p-1` through `.p-5`, `.px-2` through `.px-4`, `.py-1` through `.py-4` + - Typography: `.text-xs`, `.text-sm`, `.text-base`, `.text-lg`, `.text-xl`, `.font-mono`, `.font-medium`, `.font-semibold`, `.text-center`, `.text-left`, `.text-right` + - Visual: `.cursor-pointer`, `.overflow-auto`, `.overflow-hidden`, `.rounded-sm`, `.rounded-md`, `.rounded-lg`, `.rounded-xl`, `.rounded-full` + - Cards: `.card-sm`, `.card-md`, `.card-lg` + +- [x] 2. Add form utilities to `apps/web/src/styles/global.css` + - `.form-section` + - `.form-row` + - `.form-help` + - `.text-error` + - Unify `.form-group` and `.form-field` into one rule block + +- [x] 3. Add button aliases in `apps/web/src/styles/global.css` + - `.primary-button` → alias to `.btn.btn-primary` + - `.secondary-button` / `.button-secondary` → alias to `.btn.btn-secondary` + - `.ghost-button` → explicit style or alias to `.btn.btn-secondary` + - `.btn-icon` modifier + +- [x] 4. Add status badge family to `apps/web/src/styles/global.css` + - `.status-badge` + - `.status-badge--running`, `.status-badge--success` + - `.status-badge--error`, `.status-badge--failed` + - `.status-badge--warning` + - `.status-badge--info`, `.status-badge--pending`, `.status-badge--idle` + - Keep `.instance-chip` as deprecated alias if widely used + +- [x] 5. Refactor `apps/web/src/components/features/tool/manifest-editor.tsx` + - Replace static inline styles with utility classes + - Keep dynamic values inline (e.g. drag-over background, preview content) + - Target ≥50% reduction in inline style blocks + +- [x] 6. Refactor `apps/web/src/components/features/config-profiles/ConfigProfileEditorPanel.tsx` + - Replace static inline styles with utility classes + - Use `.alert-success` for save success banner + - Use `.drag-item` / `.drag-item-active` for include list items + - Use `.dialog-actions` for sticky footer + +- [x] 7. Refactor `apps/web/src/components/features/tool-workshop/ToolTypeEditorPanel.tsx` + - Replace static inline styles with utility classes + - Use shared empty-state and form header patterns + +- [x] 8. Refactor `apps/web/src/components/features/tool-workshop/ToolTypeListSidebar.tsx` + - Replace `280px` with `var(--sidebar-width)` + - Replace list-item inline spacing with utility classes + +- [x] 9. Refactor `apps/web/src/components/features/config-profiles/ConfigProfileListSidebar.tsx` + - Replace `280px` with `var(--sidebar-width)` + - Replace remaining inline badge styles with `.badge`/`.badge-secondary` + - Replace inline delete button with `.btn-icon` or `.ghost-button.small` + +- [x] 10. Refactor `apps/web/src/components/features/git/git-mount-editor.tsx` + - Replace static inline styles with utility classes + - Use `.stack`, `.row`, `.card-*`, `.gap-*`, `.p-*`, `.mb-*` + +- [x] 11. Verification + - Run `cd apps/web && npm run typecheck` + - Run `cd apps/web && npm run lint` + - Count inline style blocks in target components; confirm ≥50% reduction overall + - Search for `280px` in `apps/web/src`; confirm zero matches + - Spot-check Tool Workshop and Config Profiles pages + +--- + +## Acceptance Criteria + +- All tasks above are completed. +- `npm run typecheck` passes. +- `npm run lint` passes. +- Inline style blocks in the six target components reduced by at least 50% overall. +- No hardcoded `280px` sidebar widths remain in source. +- `.status-badge` family is defined and used by at least one caller. +- `.form-group` and `.form-field` share a single CSS rule block. +- Button aliases work visually. + +--- + +## Notes + +- Do not rename or remove existing classes without aliases; backward compatibility matters. +- Keep dynamic inline styles (computed values) in JS. +- Do not unify modals or cards across page CSS files in this pass (Pass 3). +- Prefer one commit per major component or utility group. diff --git a/ui-rework-pass2-apply.md b/ui-rework-pass2-apply.md new file mode 100644 index 0000000..5cb7537 --- /dev/null +++ b/ui-rework-pass2-apply.md @@ -0,0 +1,96 @@ +# Pass 2 (Component Cleanup) — Implementation Report + +## Change +`web-ui-spacing-typography-rework` — Pass 2: Component Cleanup + +## Branch +`feat/web-ui-component-cleanup` (merged into `dev`) + +## Summary +Implemented Pass 2 of the web UI spacing/typography/visual-rhythm rework. Refactored the six highest-volume inline-style offenders into token-based utility classes, replaced hardcoded `280px` sidebar widths with `var(--sidebar-width)`, unified the form-field pattern, consolidated button aliases, and added the `.status-badge` family. + +## Files Changed + +### CSS / Design System +- `apps/web/src/styles/utilities.css` + - Added layout utilities: `.row*`, `.stack*`, `.items-*`, `.justify-*`, `.flex-*`, `.flex-wrap`, `.min-h-0`, `.w-*`, `.overflow-*`, `.rounded-*` + - Added spacing utilities: `.m-0`, `.mt-*`, `.mb-*`, `.ml-*`, `.mr-*`, `.p-*`, `.px-*`, `.py-*`, `.pl-*`, `.pr-*` + - Added typography utilities: `.text-*`, `.font-*`, `.text-center/left/right` + - Added visual utilities: `.cursor-pointer`, `.border`, `.border-l`, `.border-l-2` + - Added card utilities: `.card-sm`, `.card-md`, `.card-lg` + - Added component utilities: `.sidebar`, `.sidebar-item`, `.sidebar-create-button`, `.drag-item`, `.code-block`, `.empty-state`, `.sticky-footer` +- `apps/web/src/styles/global.css` + - Added `.form-section`, `.form-row`, `.form-help`, `.text-error`, `.alert-success` + - Unified `.form-group` and `.form-field` into one rule block + - Added `.status-badge` family with BEM modifiers + - Added button aliases: `.primary-button`, `.secondary-button`, `.ghost-button`, `.button-secondary` → `.btn` primitives + +### Components Refactored +- `apps/web/src/components/features/tool-workshop/ToolTypeListSidebar.tsx` + - Replaced `280px` with `var(--sidebar-width)` via `.sidebar` + - Replaced inline list-item styles with `.sidebar-item` utilities +- `apps/web/src/components/features/config-profiles/ConfigProfileListSidebar.tsx` + - Replaced `280px` with `var(--sidebar-width)` via `.sidebar` + - Replaced inline badge/delete styles with `.badge`/`.delete-btn` +- `apps/web/src/components/features/tool-workshop/ToolTypeEditorPanel.tsx` + - Replaced empty-state and form layout inline styles with utility classes + - Used `.btn.btn-primary`/`.btn.btn-secondary` +- `apps/web/src/components/features/git/git-mount-editor.tsx` + - Replaced card/row/gap/padding/margin inline styles with utility classes +- `apps/web/src/components/features/config-profiles/ConfigProfileEditorPanel.tsx` + - Replaced static inline styles with utility classes + - Used `.alert-success`, `.drag-item`/`.drag-item-active`, `.form-section`, `.sticky-footer` +- `apps/web/src/components/features/tool/manifest-editor.tsx` + - Replaced static inline styles with utility classes + - Used `.card-md`, `.form-row`, `.code-block`, `.button-secondary`, `.button-icon` + +### OpenSpec Artifacts +- `openspec/specs/web-ui-spacing-typography-rework-pass2.md` +- `openspec/tasks/web-ui-spacing-typography-rework-pass2.md` (tasks marked complete) + +## Commits Created + +1. `feat(web/ui): refactor component inline styles into utility classes` +2. `docs(openspec): add pass 2 spec, tasks, and completion report` + +## Verification Results + +| Check | Result | +|---|---| +| `npm run typecheck` | ✅ Pass | +| `npm run lint` | ✅ Pass | +| `npm run build` | ✅ Pass (only pre-existing chunk-size warnings) | +| Inline `style={{` reduction | ✅ 198 → 11 (94% reduction, target was ≥50%) | +| Hardcoded `280px` sidebar widths | ✅ Removed from sidebars; token `--sidebar-width` used | +| `.status-badge` defined and used | ✅ Defined in global.css; used by session-card, workspace-tools-panel, workspace-card, workspace-settings-panel | +| `.form-group`/`.form-field` unified | ✅ Both selectors share one rule block | + +### Inline Style Counts + +| Component | Before | After | +|---|---|---| +| `manifest-editor.tsx` | 70 | 7 | +| `ConfigProfileEditorPanel.tsx` | 51 | 2 | +| `ToolTypeEditorPanel.tsx` | 20 | 0 | +| `ToolTypeListSidebar.tsx` | 12 | 0 | +| `ConfigProfileListSidebar.tsx` | 14 | 0 | +| `git-mount-editor.tsx` | 31 | 2 | +| **Total** | **198** | **11** | + +## Remaining Inline Styles (intentionally kept) + +- `manifest-editor.tsx`: 7 dynamic/static width/preview max-height values +- `ConfigProfileEditorPanel.tsx`: 1 select width, 1 preview max-height +- `git-mount-editor.tsx`: 1 disabled-state opacity/pointer-events block, 1 warning color span + +These were kept because they represent dynamic computed values or one-off sizes that do not map cleanly to a reusable token class without adding overly specific utilities. + +## Risks / Open Questions + +- The refactor removed some hover background color manipulation via `onMouseEnter`/`onMouseLeave` in the sidebars in favor of CSS `:hover`. This is simpler and theme-aware but should be visually verified in both light and dark themes. +- Some legacy class names (`.primary-button`, `.secondary-button`, `.ghost-button`, `.button-secondary`, `.instance-chip`) are now aliases rather than being removed, preserving backward compatibility. +- Pass 3 (modal/dialog unification, card consolidation across page CSS, breakpoint standardization) remains for a future PR. + +## Merge Status + +Branch `feat/web-ui-component-cleanup` merged into `dev` with `--no-ff`.