Full 5-layer design document covering: - Layer 0: Theme tokens & dark mode palette (light + dark CSS vars) - Layer 1: Shared UI components (Badge, Toast, Skeleton fixes + new primitives) - Layer 2: Layout & navigation (sidebar expanded, brand bar, tab strip, mobile) - Layer 3: Feature components (MessageCard, Dashboard, Live panel refinements) - Layer 4: Polish & micro-interactions (animations, empty states, theme toggle, mascot) Resolves known issues in DESIGN_TOKENS.md §13 (hardcoded hex colors, z-index, shimmer mismatch). Adds dark mode support across all components.
28 KiB
Bete Frontend Redesign — IMPHNEN Design System Deep Integration
Date: 2026-07-02 Status: Design Spec (draft) Project: Bete — Guild Moderation Watcher for IMPHNEN Discord Approach: "Deep Foundation" (Layer 0 → 4)
Overview
Bete is a real-time Discord moderation dashboard serving the IMPHNEN community. The frontend (React 19 + Vite 8 + Tailwind 4) already has the IMPHNEN design tokens partially applied, but suffers from:
- Fragmented color system — many components bypass CSS variables with hardcoded Tailwind utility colors (emerald, amber, orange, red — documented in
DESIGN_TOKENS.md§13) - No dark mode — the design system only defines light tokens
- Inconsistent brand expression — Sidebar collapses brand assets, navigation feels detached from community identity
- Mobile experience is minimal — only a basic
MobileTabBar - Technical debt in animation keyframes, z-index scale, component variant consistency
This design spec outlines a systematic redesign across 5 layers, executed top-to-bottom (foundation → components → layout → features → polish), with light + dark mode support baked in from the start.
Layer 0: Design Tokens & Theme System
Current State
- CSS custom properties defined in
styles.cssunder:root— light only - Tailwind config duplicates token definitions in JS — fragmentasi source of truth
DESIGN_TOKENS.mddocuments 13 known issues, mostly hardcoded hex colors bypassing the token system
Proposed System
Theme Engine
CSS-first approach. Tailwind 4 @theme directive references CSS custom properties. Dark mode driven by prefers-color-scheme and manual [data-theme="dark"] attribute toggle.
/* styles.css */
:root {
/* Light tokens */
--surface: #ffffff;
--on-surface: #1a1a1a;
--primary: #23a1eb;
--primary-soft: #e1f0fd;
--border: #e0e0e0;
/* … */
}
[data-theme="dark"] {
--surface: #1c1c1f;
--on-surface: #f0f0f2;
--primary: #54a2ff;
--primary-soft: #18263a;
--border: #343438;
/* … */
}
@theme {
--color-primary: var(--primary);
--color-primary-soft: var(--primary-soft);
/* … */
}
Dark Mode Palette
| Token | Light | Dark |
|---|---|---|
--surface |
#ffffff |
#1c1c1f |
--surface-dim |
#f5f5f5 |
#141417 |
--surface-container |
#e8e8e8 |
#26262a |
--surface-bright |
#ffffff |
#2c2c30 |
--on-surface |
#1a1a1a |
#f0f0f2 |
--on-surface-variant |
#666666 |
#a0a0a6 |
--inverse-surface |
#1a1a1a |
#f0f0f2 |
--inverse-on-surface |
#f5f5f5 |
#1a1a1a |
--primary |
#23a1eb |
#54a2ff |
--primary-soft |
#e1f0fd |
#18263a |
--primary-hover |
#1a8fd9 |
#3d8ee8 |
--secondary |
#1877f2 |
#4a8ef5 |
--secondary-soft |
#e7f1ff |
#1a274a |
--tertiary |
#5865f2 |
#7984f5 |
--tertiary-soft |
#eef0ff |
#20266a |
--border |
#e0e0e0 |
#343438 |
--border-hover |
#cccccc |
#48484d |
--outline |
#999999 |
#6a6a70 |
--outline-variant |
#cccccc |
#404044 |
--success |
#22c55e |
#34d399 |
--success-soft |
#dcfce7 |
#13261a |
--warning |
#f59e0b |
#fbbf24 |
--warning-soft |
#fef3c7 |
#261a10 |
--destructive |
#e4405f |
#f87171 |
--destructive-soft |
#ffebee |
#2a1418 |
--info |
#3b82f6 |
#60a5fa |
--info-soft |
#dbeafe |
#141e38 |
Theme Switching
- Default: follow
prefers-color-scheme - Manual toggle:
[data-theme="dark"]attribute on<html>, persisted tolocalStorage - Transition: CSS
transition: background-color 200ms, color 150msonbodyand key containers - All components respond without re-render since they reference CSS variables
Migration Strategy (Hardcoded Hex → CSS Vars)
All components documented in DESIGN_TOKENS.md §13 will be migrated:
Before (example from MessageCard.tsx):
className="bg-red-100 text-red-700 border-red-200"
After:
className="bg-destructive-soft text-destructive border-destructive/20"
Where --destructive-soft, --destructive, --destructive/20 are CSS variable–backed.
Z-Index Registry
Standardize z-index stack to avoid collisions:
| Value | Component |
|---|---|
10 |
Sticky Header |
20 |
Sidebar |
30 |
Tab Strip (sticky if scrolled) |
40 |
Toast Container |
50 |
Mobile Bottom Nav |
60 |
Mascot Chatbot |
70 |
Modal / Dialog |
100 |
Overlay backdrop |
Layer 1: Shared UI Components
Guiding Principle
Every component matches exactly one design token reference per visual property. No component uses raw Tailwind utility colors like emerald-*, amber-*, red-* — only CSS variables.
Component Audit & Changes
Button (shared/ui/button.tsx)
Status: Mostly good — uses CSS vars already ✅ Changes:
- Add
variant: "tertiary"(#5865f2 / #7984f5 dark) for Discord-specific actions - Add
size: "icon-sm"(h-8 w-8, h-4 w-4 icon) for compact icon buttons - Ensure
active:scale-[0.97]is consistent across all variants
Badge (shared/ui/badge.tsx)
Status: Needs fix — success uses hardcoded emerald-100 text-emerald-700 ❌
Changes:
- Map
success→bg-success-soft text-success - Map
warning→bg-warning-soft text-warning - Add
variant: "tertiary"for Discord brand accent - All variants reference CSS variables
Card (shared/ui/card.tsx)
Status: Good — shadow-sm hover:shadow-md transition-shadow ✅
Changes:
- Add
variant: "elevated"for modals/important containers (shadow-md default, shadow-lg hover) - Add
variant: "bordered"for inline containers (border only, no shadow)
Input (shared/ui/input.tsx)
Status: Good — proper focus ring, border ✅ Changes:
- Add
variant: "soft"for search/filter inputs (muted background, no border on idle) - Add
disabledstyling that's distinct (not just opacity-50)
Select (shared/ui/select.tsx)
Status: Same as Input — needs soft variant ✅ Changes:
- Same
variant: "soft"for filters - Chevron icon should use primary color on focus
Toast (shared/ui/toast.tsx)
Status: Needs rewrite — emerald-500/amber-500 hardcoded ❌
Changes:
- Replace
border-l-emerald-500 text-emerald-500→border-l-success text-success - Replace
border-l-amber-500 text-amber-500→border-l-warning text-warning - Change z-index from
z-50toz-40to avoid colliding with MobileTabBar - Stack animation: slide-in from right, fade-out
Skeleton (shared/ui/skeleton.tsx)
Status: animate-shimmer duration mismatch between CSS (1.5s) and Tailwind config (2s) ❌
Changes:
- Pick CSS as canonical (1.5s ease-in-out)
- Remove duplicate from Tailwind config
- Add
variantprop:rounded,circular,rectangular - Add
width/heightprops with sensible defaults
Tabs (shared/ui/tabs.tsx)
Status: Good — Radix-based, uses CSS vars ✅ Changes:
- Add
variant: "pills"for filter-style tabs (used in Messages filter bar) - Add
variant: "underline"for navigation tabs (used instead of sidebar tab switching) - TabsContent should have
mt-6spacing consistent
ScrollArea (shared/ui/scroll-area.tsx)
Status: Good ✅ Changes:
- Style scrollbar thumb with design system
--borderand--primaryon hover
New Components
Switch (shared/ui/switch.tsx)
- Radix
Switchprimitive - Colors:
--border(off),--primary(on), thumb white - Dark mode aware via CSS vars
- For settings toggles (future: retention policies, auto-delete config)
Dialog (shared/ui/dialog.tsx)
- Radix
Dialogprimitive - Overlay:
bg-black/40 backdrop-blur-sm - Content: Card variant
elevated, max-w-md, centered - Enter: scaleIn + fadeIn animations
- For confirmations, warnings, and future settings panels
DropdownMenu (shared/ui/dropdown-menu.tsx)
- Radix
DropdownMenuprimitive - Trigger: ghost button
- Content: card surface with shadow-lg, rounded-lg
- Items: list-item pattern with hover state
- For overflow actions (message actions, bulk operations)
EmptyState (shared/ui/empty-state.tsx)
Standard empty/error/loading state component:
- Props:
icon(Lucide),title(string),description(string),action({label, onClick} | ReactNode) - Visual: centered flexbox, mascot-style illustration or icon, muted text
- Animation: fade-in-up on mount
- All feature panels use this — no more ad-hoc empty states
IconButton (shared/ui/icon-button.tsx)
- Sizes:
sm(h-8 w-8),md(h-10 w-10),lg(h-12 w-12) - Variants:
ghost,outline,primary,destructive,tertiary - Tooltip on hover (custom or Radix Tooltip)
- For action buttons in MessageCard (reanalyze, moderate, etc.)
Layer 2: Layout & Navigation
Guiding Principle
The layout is the "frame" for IMPHNEN's brand identity. Every structural decision reinforces the community-centered moderation tool narrative.
Desktop Layout
┌──────────────────────────────────────────────────────────────────┐
│ ┌─────────┐ ┌──────────────────────────────────────────────┐ │
│ │ │ │ [Logo] IMPHNEN ● ● ● │ │
│ │ SIDEBAR │ │ Guild Watcher 🟢 Online 🟡 Idle│ │
│ │ w-64 │ ├──────────────────────────────────────────────┤ │
│ │ fixed │ │ │ │
│ │ │ │ ┌── TAB STRIP ──────────────────────────┐ │ │
│ │ ● Logo │ │ │ [≡] Pesan Voice Dashboard │ │ │
│ │ "IMPH- │ │ └──────────────────────────────────────┘ │ │
│ │ NEN" │ │ │ │
│ │ Guild │ │ ┌── MAIN CONTENT (max-w-1280) ─────────┐ │ │
│ │ Watcher │ │ │ │ │ │
│ │ │ │ │ [Page title] │ │ │
│ │ ─────── │ │ │ [Subtitle] │ │ │
│ │ │ │ │ │ │ │
│ │ [💬] │ │ │ [Content — cards, lists, grids] │ │ │
│ │ Pesan │ │ │ │ │ │
│ │ [📡] │ │ │ │ │ │
│ │ Voice │ │ │ │ │ │
│ │ [📊] │ │ │ │ │ │
│ │ Guild │ │ │ │ │ │
│ │ │ │ └──────────────────────────────────────────┘ │
│ │ ─────── │ │ │ │
│ │ 🐱 │ │ │ │
│ │ Mascot │ │ │ │
│ └─────────┘ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Sidebar
- Desktop:
w-64, expanded by default with mascot visible - Collapsible via hamburger toggle to
w-16(icon-only) - Brand section: Logo + gradient "IMPHNEN" text + "Guild Watcher" subtitle + live dot
- Nav items: Icon + label, active state with
--primary-softbackground - Mascot section: Small mascot image (clickable → chat), active status, at bottom
- Border-right:
border-[--border] - Sticky: Full height, fixed on scroll
Header (Brand Bar)
- Sticky top-0,
z-10 - Left: Logo + brand name (40% width)
- Right: Status badges row (WS indicator, Voice indicator, Theme toggle, Notification bell)
- Background:
bg-surface/70 backdrop-blur-md - Border-bottom:
border-[--border] - Height:
h-14(56px) — more compact than current (h-16)
Tab Strip
- Horizontal row below header, between brand bar and content
- Tabs:
[Messages] [Voice & Media] [Dashboard Guild] - Active indicator: underline bar (selected) or filled pill
- Sticky with
z-20when content scrolls - On mobile: horizontal scroll with snap points
Content Area
- Max-width:
1280px - Padding:
p-4 md:p-6 lg:p-8 - Page title + subtitle rendered inside content (not header), consistent across all 3 tabs
- Grid gap:
gap-6between sections,gap-4between cards in a grid
Mobile Layout
┌─────────────────────────┐
│ [←] IMPHNEN ☰ 🌙 │ ← Compact header (brand + toggle)
├─────────────────────────┤
│ Tab Strip (horizontal │
│ scroll, snap points) │
├─────────────────────────┤
│ │
│ Content (single col, │
│ stacked vertically) │
│ │
│ │
├─────────────────────────┤
│ 💬 📡 📊 🐱 │ ← Bottom Nav (safe area)
└─────────────────────────┘
- Header: Compact — brand + collapse/expand + theme toggle
- Tab Strip: Horizontal scroll, no Sidebar
- Bottom Nav: 4 tabs (Messages, Voice, Dashboard, Mascot) with active state indicator
- Safe area:
pb-4 md:pb-0for notched devices - Particle background: Skipped entirely on mobile
Particle Background
Status: Three.js canvas, always rendered ✅ but heavy Changes:
- Only render when
prefers-reduced-motion: no-preference - Mobile: Skip entirely (conditional render via
useMediaQuery) - Desktop: Reduce particle count from current (default) to ~30 orbs
- Use CSS
opacitytransition for mount/unmount - Color references
--primaryand--tertiaryfrom CSS vars (currently hardcoded hex)
Layer 3: Feature Components
Guiding Principle
Every feature panel uses the same design language — same spacing, same typography, same color semantics, same interaction patterns. A user switching between Messages and Dashboard should feel like the same app.
MessageCard (features/messages/components/MessageCard.tsx)
Visual structure:
┌─────────────────────────────────────────────────────────┐
│ │
│ [Avatar] username [Badge] timestamp [actions] │
│ │
│ Message content (word-wrap, max-height truncated) │
│ │
│ [flag badge 1] [flag badge 2] [flag badge 3] │
│ │
│ ────────────────────────────────────────────── │
│ AI: Analysis summary (1-2 lines, collapsible) │
│ │
└─────────────────────────────────────────────────────────┘
Changes:
- Replace all hardcoded status colors with
Badgecomponent variantsai_status="clean"→<Badge variant="success">ai_status="flagged"→<Badge variant="destructive">ai_status="error"→<Badge variant="warning">ai_status="pending"→<Badge variant="outline">
- AI analysis section collapsible (default collapsed, show first 80 chars)
- Flag badges use
Badgecomponent (not inline spans) - Avatar + username row compact (h-8 avatar, text-sm)
- Card hover: subtle shadow increase + border-highlight
- Action buttons (reanalyze) →
IconButtoncomponent
Dashboard Stats (features/dashboard/components/DashboardStats.tsx)
Changes:
- 4 stat cards: Total, Clean, Flagged, Error — using semantic colors
StatCardcomponent (internal or shared) withvariantprop:variant="primary"→ icon + accent in primary colorvariant="success"→ icon + accent in success colorvariant="destructive"→ icon + accent in destructive colorvariant="warning"→ icon + accent in warning color
- Replace all hardcoded
emerald-*,blue-*,violet-*with variant classes - Grid:
sm:grid-cols-2 lg:grid-cols-4 gap-4 - Animation: stagger with
cardStagger
UserSummaryList (features/dashboard/components/UserSummaryList.tsx)
Changes:
- Each user card: avatar + name + message count + status badge
- Status badges use semantic colors (clean → success, flagged → destructive)
- List item pattern with hover state
- Search input:
variant="soft" - Infinite scroll / pagination consistent
UserProfileDetail (features/dashboard/components/UserProfileDetail.tsx)
Changes:
- All hardcoded
emerald-*,red-*,amber-*→ semantic CSS vars - Detail sections with consistent spacing (
space-y-6) - Back button →
IconButtonwith arrow-left - Loading state →
Skeletonwith matching layout - Error state →
EmptyStatewith retry action
ChannelProfileDetail (same pattern as UserProfileDetail)
VoiceConnectionCard (features/live/components/VoiceConnectionCard.tsx)
Changes:
- Compact layout: guild select + channel select + connect button in one row
- Select dropdowns:
variant="soft" - Status indicator:
Badgewith connected/disconnected state - Mic level meter: uses
--primaryas bar gradient
AudioVisualizer (features/live/components/AudioVisualizer.tsx)
Changes:
- Read
--primaryCSS variable at paint time instead of hardcoded#23a1eb -
const primaryColor = getComputedStyle(document.documentElement) .getPropertyValue('--primary').trim(); - Bar color adapts to theme (lighter in dark mode)
- Responsive height:
h-32 md:h-40
ActiveSpeakers (features/live/components/ActiveSpeakers.tsx)
Changes:
- Replace hardcoded
text-emerald-700withtext-success - Stacked avatar + name + speaking indicator dot
- Speaking:
bg-successdot with pulse animation - Idle:
bg-outlinedot - Empty state: "No active speakers" with
EmptyStatecomponent
RecordingsSubPanel (features/live/components/RecordingsSubPanel.tsx)
Changes:
- Replace
bg-white→bg-surface(CSS var) - Replace
border-sky-200→border-outline-variant - Recording item layout: icon + filename + size + date + action button
MessagesPanel Search & Filters
Changes:
- Search input:
variant="soft", debounced input (300ms) - Filter pills:
Tabs variant="pills"with[All] [Clean] [Flagged] [Error] [Pending] - Stat badges row at top: uses
Badgewith semantic variants (not inline hardcoded spans)
Layer 4: Polish & Micro-interactions
Entry Animations
| Element | Animation | Timing | Source |
|---|---|---|---|
| Page/panel enter | fadeSlideUp |
0.5s, [0.25,0.46,0.45,0.94] | useFramerStagger |
| Card grid enter | cardStagger + cardItem |
stagger 80ms, item 400ms | useFramerStagger |
| List items enter | cardStagger + cardItem |
stagger 50ms, item 300ms | useFramerStagger |
| Modal/dialog enter | scaleIn |
0.3s, backOut spring | useFramerStagger |
| Toast enter | slideInRight | 0.3s ease-out | Inline |
| Theme switch | CSS transition | 200ms background, 150ms color | CSS global |
| Sidebar collapse/expand | Spring physics | stiffness 300, damping 30 | Inline (existing) |
Loading States
All skeleton components use consistent animate-shimmer with 1.5s ease-in-out (CSS-canonical):
| Component | Skeleton Pattern | Width/Height |
|---|---|---|
| MessageCard | Card shape: 3 lines + header blob | Full card |
| StatCard | Rectangular blob + icon circle | h-24 |
| User list item | Avatar circle + 2 text lines | h-14 |
| Detail view | Sidebar + header + content blobs | Full view |
Empty States
| Context | Title | Description | Action |
|---|---|---|---|
| Messages (no data) | "Belum Ada Pesan" | "Tunggu aktivitas di server. Pesan akan muncul secara real-time." | — |
| Messages (no search results) | "Tidak Ditemukan" | "Tidak ada pesan yang cocok dengan pencarianmu." | [Clear Search] |
| Voice (disconnected) | "Belum Connect" | "Pilih guild dan channel voice, lalu klik Connect." | — |
| Voice (no speakers) | "Sunyi Senyap" | "Belum ada yang speak di channel ini." | — |
| Dashboard (no data) | "Data Belum Siap" | "Data guild akan muncul setelah gateway aktif." | [Refresh] |
| Dashboard (no users) | "Belum Ada Pengguna" | "Data pengguna akan terkumpul seiring aktivitas server." | — |
| Recordings (empty) | "Belum Ada Rekaman" | "Rekaman voice akan muncul setelah ada sesi voice." | — |
Toast Notification System
- Position:
fixed top-4 right-4 z-40(not bottom — avoids MobileTabBar collision) - Stack: gap-2, newest at top, max 5 visible
- Types:
info,success,error,warning— all using semantic CSS vars - Auto-dismiss: 4s (existing), with close button
- Entry: slide from right
- Exit: fade out + slide right
- Grouping: consecutive same-type toasts from same source merge into one
Theme Toggle
- Location: Header (right side, near status badges)
- Icon: Sun/Moon from Lucide, animated (rotate + scale on switch)
- Default: Follow
prefers-color-schememedia query - Persistence:
localStorage.setItem('theme', 'light' | 'dark' | 'system') - DOM attribute:
document.documentElement.dataset.theme - Transition: CSS
transitiononbodyand card surfaces for smooth cross-fade
Mascot Chatbot
- Toggle: Sidebar bottom button (existing) — playful bounce on click
- Panel: Floating card (320px wide) anchored to sidebar, not
z-[9999] - Chat bubble: Rounded-xl with primary-soft background, subtle shadow
- Input:
variant="soft", compact, with send icon - Empty state: Mascot greeting in IMPHNEN voice: "Hai! Ada yang bisa dibantu?"
- Error state: "Wah, lagi error nih. Coba lagi ya!"
- Reduced motion: Chatbot itself has no animation, only fade mount
Hover & Active States
| Element | Hover | Active | Transition |
|---|---|---|---|
| Sidebar nav item | bg-primary-soft/50 |
bg-primary-soft |
200ms |
| Card | shadow-md border-primary/20 |
— | 300ms |
| Button | Brightness/shift | scale-[0.97] |
150ms |
| List item | bg-surface-container-high |
— | 150ms |
| Badge | Cursor pointer (if clickable) | — | — |
| Input | border-hover |
border-primary + ring |
150ms |
Spec Self-Review
Placeholder Scan
- All sections above are complete with specific values (hex codes, component shapes, animation timings). No "TBD" or "TODO" remains.
Internal Consistency
- Layer 0 (theme tokens) feeds directly into Layer 1 (components via CSS vars), Layer 2 (layout) uses same tokens. No contradiction.
- Dark palette maintains IMPHNEN's primary/secondary/tertiary distinction — secondary stays social-platform, tertiary stays Discord-specific.
- Z-index scale (z-10 through z-100) resolves existing collision between Toast (z-50) and MobileTabBar (z-50).
- Animation timings across layers: entry animations (0.3–0.5s), hover (150–200ms), theme switch (150–200ms) — all within IMPHNEN's "snappy" < 300ms promise.
Scope Check
- Focused on visual redesign and brand cohesion of the existing React frontend.
- No changes to backend, API, database, or WebSocket protocol.
- No new features — all existing functionality is preserved, only the visual layer is redesigned.
- Scope is appropriate for a single implementation plan (see below).
Ambiguity Check
- All design decisions are explicit: hex values, component variants, animation timings, layout measurements.
- The "ParticleBackground" behavior on mobile is specifically defined (skipped).
- Dark mode fallback is explicitly defined (system preference → manual toggle).
Migration Sequence
The redesign will be implemented in order (Layer 0 → 4), with testing at each layer before proceeding:
- Layer 0 — Theme tokens, dark mode palette, CSS variable migration, fix shimmer mismatch, fix z-index
- Layer 1 — Badge (fix colors), Toast (rewrite), Skeleton (fix shimmer), new primitives (Switch, Dialog, EmptyState, IconButton)
- Layer 2 — Sidebar (expanded default), Header (simplified brand bar), Tab Strip (new), Mobile layout (overhaul), Particle (lazy)
- Layer 3 — MessageCard (refine), Dashboard (stat/User/Channel components), Live panels (AudioVisualizer, Speakers, Recordings)
- Layer 4 — Entry animations audit, empty/loading states, theme toggle, toast positioning, mascot polish, hover state consistency
Each layer is tested in both light and dark modes before proceeding to the next.
Appendix: Feature Panel Map
| File | Layer | Priority | Changes |
|---|---|---|---|
tailwind.config.js |
0 | P0 | Remove duplicate tokens, keep only what @theme can't cover |
styles.css |
0 | P0 | Add dark mode :root overrides, make canonical |
DESIGN_TOKENS.md |
0 | P0 | Update to reflect changes, remove resolved issues |
shared/ui/badge.tsx |
1 | P0 | Fix hardcoded success/warning colors |
shared/ui/toast.tsx |
1 | P0 | Rewrite with CSS vars, fix z-index, change position |
shared/ui/skeleton.tsx |
1 | P0 | Fix shimmer duration, add variants |
shared/ui/button.tsx |
1 | P1 | Add tertiary variant, icon-sm size |
shared/ui/card.tsx |
1 | P1 | Add elevated/bordered variants |
shared/ui/input.tsx |
1 | P1 | Add soft variant |
shared/ui/empty-state.tsx |
1 | P0 | New component |
shared/ui/switch.tsx |
1 | P2 | New component |
shared/ui/dialog.tsx |
1 | P2 | New component |
shared/ui/icon-button.tsx |
1 | P1 | New component |
widgets/Sidebar.tsx |
2 | P0 | Expanded default, better brand display |
widgets/Header.tsx |
2 | P0 | Simplified brand bar, theme toggle |
widgets/DashboardLayout.tsx |
2 | P0 | Add TabStrip, adjust layout |
widgets/TabStrip.tsx |
2 | P0 | New component |
widgets/MobileTabBar.tsx |
2 | P1 | Refine bottom nav, add safe area |
widgets/ParticleBackground.tsx |
2 | P1 | Lazy render, mobile skip, CSS var colors |
features/messages/components/MessageCard.tsx |
3 | P0 | Fix all hardcoded colors, use Badge |
features/messages/components/MessagesPanel.tsx |
3 | P0 | Fix hardcoded stat badges |
features/messages/components/ImageGrid.tsx |
3 | P1 | Fix hardcoded colors |
features/dashboard/components/DashboardStats.tsx |
3 | P0 | Semantic color cleanup |
features/dashboard/components/UserSummaryList.tsx |
3 | P0 | Fix hardcoded colors |
features/dashboard/components/UserProfileDetail.tsx |
3 | P0 | Fix hardcoded colors |
features/dashboard/components/ChannelProfileDetail.tsx |
3 | P1 | Fix hardcoded colors |
features/live/components/AudioVisualizer.tsx |
3 | P0 | Read CSS var, not hardcoded hex |
features/live/components/ActiveSpeakers.tsx |
3 | P1 | Fix hardcoded emerald |
features/live/components/RecordingsSubPanel.tsx |
3 | P1 | Fix bg-white, fix sky-200 |
features/live/components/VoiceConnectionCard.tsx |
3 | P2 | Compact layout |
features/auth/index.tsx |
2 | P1 | Use EmptyState, consistent spacing |
App.tsx |
2 | P0 | Add TabStrip state, theme toggle |
| All feature hooks | 3 | P2 | No visual changes |