Add UI audit report

Catalogs systemic UI issues by pattern (root causes), what was already
fixed in this session, and prioritized recommendations for further work.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Devin 2026-05-10 08:27:04 +00:00
parent 343ffb48af
commit 3949062435

238
docs/ui/UI_AUDIT.md Normal file
View File

@ -0,0 +1,238 @@
# Trading Web UI Audit
> **Last updated:** 2026-05-10
> **Scope:** `learning_ai_invt_trdg/web/src` (~13,000 lines of UI code across views/tabs/components)
> **Method:** Pattern-based grep audit + manual inspection of fixed components
This doc catalogs UI issues by **pattern** (so you fix root causes, not symptoms), tells you **which files are affected**, and recommends **concrete fixes**.
---
## 1. Summary — what's the state of the codebase?
| Metric | Count | Note |
|---|---|---|
| Files using `style={{...}}` inline blocks | 25 | A few have 5072 blocks each — major drift source |
| `!important` declarations in `index.css` | 122 | Indicates repeated style-override fights |
| `<Button>` (primitive) usages | 31 files | Has known overlap-with-block-children issue (we hit it in Copilot) |
| `<table>` direct usages | 6 files | Several without scroll containers |
| Largest views | `PositionsTab` 2035, `SimpleView` 1635, `OverviewTab` 1061, `AdminTab` 990, `HomeView` 917 | High UI surface = high bug surface |
The codebase has **pattern bugs**, not just one-off issues. The same root causes recur across files. Fixing them centrally (CSS overrides, lint rules) is much cheaper than file-by-file.
---
## 2. Already fixed in this session
| # | Issue | Fix | Commit |
|---|---|---|---|
| 1 | Plans page: saved-setup cards clipped Edit/Delete buttons; chips truncated with ellipsis instead of wrapping; outer grid forced 380px on saved column | Refactored saved-setup-card to `overflow: visible`, flex-wrap topline, real chip wrap, single-column form below 1440px | `46a357c` |
| 2 | Critical alert banner overflowed at narrow widths; loud red styling | Restructured into wrapper + inner row with min-width:0 text + shrink-0 CTA, max-width 1280px, soft tinted background | `1fa2bcd` |
| 3 | Sidebar logo: "ByteLyst / Trading OS" clipped due to two competing CSS rules (44×44 grid vs 56px flex) | Forced flex layout with min-height 60px, brand text ellipsis-only | `1fa2bcd` |
| 4 | AI Trading Copilot: Quick Action title and prompt overlapped; not responsive | Replaced `<Button>` with native `<button>` with explicit flex-column; responsive 1/2-col grid; line-clamp on prompt | `343ffb4` |
| 5 | DevOps panel tables overflowed | Wrapped in `overflow-x: auto`, table-layout: fixed, break-word on cells | `d2420f5d` |
| 6 | Global: `.dashboard-main { overflow: hidden }` clipped popovers/modals | `overflow: visible !important` | `f463ff6` |
| 7 | Global: right panel covers main on narrow viewports | Collapses to 260px below 1280px, hidden below 1024px | `f463ff6` |
| 8 | Global: tables push layout horizontally | `.dashboard-content table` wrapped with `overflow-x: auto` | `f463ff6` |
| 9 | Global: header search/indices crowd each other | flex-wrap, search shrinks 240360px, indices wrap | `f463ff6` |
All centralized in `web/src/layout-fixes.css` (495 lines). Single file = single review surface.
---
## 3. Issue patterns (root causes)
### Pattern A — `<Button>` primitive ate block children
**Symptom:** Two `<span style={{display:block}}>` inside a `<Button>` overlap each other.
**Root cause:** `<Button>` from `@bytelyst/ui` is `inline-flex` internally; block children get treated as inline-flex items.
**Fix pattern:** Use native `<button>` with explicit `display: flex; flex-direction: column` for cards-as-buttons.
**Where to look:** any `<Button>` containing 2+ stacked text spans/divs.
```bash
grep -rn "<Button" --include="*.tsx" web/src | wc -l # 31 files
```
### Pattern B — Inline styles instead of classes
**Symptom:** Style drift, no responsive breakpoints, can't override via CSS without `!important`.
**Files:** `OverviewTab` (72 blocks), `MyStrategiesTab` (64), `HomeView` (55), `ChatControl` (28), `MembershipTab` (26), `MarketplaceTab` (23), `VisualRuleBuilder` (22).
**Why bad:** Inline styles can't have `@media` queries, can't be tweaked via theme, fight `index.css` for specificity.
**Fix pattern:** Lift repeated patterns into named CSS classes; keep one-off positional styles inline.
### Pattern C — Hardcoded pixel widths in JSX
**Symptom:** Doesn't shrink on narrow viewports → horizontal overflow.
**Findings:**
- `App.tsx:226` `maxWidth: 420` (toast container)
- `Header.tsx:77` `width: 300` (search) — already fixed via CSS override
- `MembershipTab.tsx:48,160` `width: 44`, `maxWidth: 1400`
- `VisualRuleBuilder.tsx:111,326` `width: 70`, `width: 260`
- `RightPanel.tsx:109` `width: 52, height: 44` (image — fine, intentional thumbnail)
- `PositionsTab.tsx` multiple `truncate max-w-[80px/120px/180px/220px/260px]`
**Fix pattern:** Replace fixed widths with flex/grid + `min-width: 0` + `flex: 0 1 <basis>`. For images, keep fixed but add `flex-shrink: 0`.
### Pattern D — `overflow: hidden` on cards that have anchored children
**Symptom:** Buttons, badges, dropdowns clipped.
**Findings (current):**
- `ReconciliationAuditPanel.tsx:447,509,579``<Card className="overflow-hidden rounded-2xl">` × 3
- `AdminTab.tsx:372,608,843` — admin panel sections with overflow-hidden
- `PositionsTab.tsx:1392,1578``table-container ... overflow-hidden` (this one is correct — it's the wrapper for a horizontal-scroll table)
**Fix pattern:** Use `overflow: hidden` only when intentionally clipping (e.g. rounded corners over images). For cards that contain actionable children, prefer `overflow: visible` and let children manage their own bounds.
### Pattern E — Truncation without `title` attribute
**Symptom:** User sees "PKRBKDW7…" with no way to read the full value.
**Findings:** 11 instances of `className="...truncate..."` without a sibling `title=` attribute (a11y + UX regression).
**Fix pattern:** Always pair `truncate` with `title={value}` so hover/focus shows full content.
### Pattern F — Grid templates without `minmax(0, 1fr)`
**Symptom:** Grid children with content wider than `1fr` push past container.
**Findings:**
- `index.css:1230` `grid-template-columns: 2fr 1.2fr 1fr 1.2fr` (no minmax)
- `index.css:1895` `grid-template-columns: repeat(3, 1fr)` (no minmax)
- `index.css:2325` `grid-template-columns: 100px minmax(220px, 1fr) 90px ...` — fine
- `MarketplaceTab.tsx:36,86`, `MembershipTab.tsx:100`, `MyStrategiesTab.tsx:127``gridTemplateColumns: '1fr 1fr'` inline
**Fix pattern:** Always use `minmax(0, 1fr)` not bare `1fr` in grid templates. Default `min-width` is `auto` which equals `min-content` of children; minmax(0,...) lets the column shrink.
### Pattern G — Two competing CSS rules for the same class
**Symptom:** Styles you can't reason about; depends on rule order in 3000-line stylesheet.
**Where seen:** `.trading-sidebar-logo` (44×44 grid block at line 349 + 56px flex block at line 1546). The logo bug we fixed was a direct consequence.
**Likely more:** `index.css` has 122 `!important` declarations — each one suggests a fight between two rules. Worth a future cleanup pass.
### Pattern H — `100vw` / `vw` units inside layouts with a fixed sidebar
**Symptom:** Element widths don't subtract sidebar/scrollbar width → overflow.
**Findings:**
- `index.css:1620` `width: min(42vw, 460px) !important;`
- `index.css:1791,1798,1822,1832``clamp(... vw ...)` for fluid typography (these are fine)
- `ChatControl.tsx:635` `maxWidth: 'calc(100vw - 32px)'` (acceptable — modal portaled to body, no sidebar offset)
- `TradeProfileManager.tsx:908` `max-w-[92vw]` (also portaled side-drawer — acceptable)
**Fix pattern:** Inside the dashboard shell use `100%` not `100vw`. `vw` only safe for elements outside the sidebar layout (modals, banners).
---
## 4. High-impact components still to address
### 4.1 PositionsTab.tsx (2035 lines, Portfolio page)
**Issues:**
- 6+ instances of `truncate max-w-[80260px]` for trade IDs / sub-tags
- `<table className="pro-table">` already has horizontal scroll wrapper, but the inner `pro-table` has hardcoded `min-width: 980px` — fine for desktop, but on narrow viewports the table scrolls within itself (correct behavior, by design)
- 3 separate scroll regions (`.positions-section`, `.orders-section`, `.history-tab`) each with their own min-widths
- Many badges with hardcoded font/padding via inline style
**Recommendation:** Keep horizontal scroll behavior (it's correct for data tables). Add `title={fullValue}` to the 6 truncate instances that don't have it. Audit badge inline styles → lift to `.position-badge` class.
### 4.2 OverviewTab.tsx (1061 lines, dashboard landing)
**Issues:**
- 72 inline `style={{...}}` blocks
- Likely uses many `<Button>` with multiple-span content → potential overlap (Pattern A)
- No responsive breakpoints in inline styles
**Recommendation:** Highest-payoff refactor target. Move colors/spacing into Tailwind utilities or `.overview-*` classes. Estimated 34 hours for a clean pass.
### 4.3 HomeView.tsx (917 lines, root /)
**Issues:**
- 55 inline style blocks
- Hero panel uses `clamp(... vw ...)` for fluid typography — looks intentional
- Action grid likely uses `gridTemplateColumns: '1fr 1fr'` (Pattern F)
**Recommendation:** Same as OverviewTab — extract into classes. Verify mobile breakpoint for action grid (`grid-cols-1 md:grid-cols-2 xl:grid-cols-3`).
### 4.4 TradeProfileManager.tsx (slide-over drawer)
**Issues:**
- Uses `w-[520px] max-w-[92vw]` — fixed width on desktop. May feel cramped for forms with many fields.
- Drawer slides in from right, portaled to body (good).
**Recommendation:** Consider `w-[640px]` on `xl:` breakpoint for more breathing room. Inspect the form layout for the same overlap-with-Button issue we saw in Copilot.
### 4.5 ConfigTab.tsx + AdminTab.tsx tables
**ConfigTab:**
- `<th className="min-w-[200px/300px/400px]">` — guarantees horizontal scroll. Acceptable for a config table; user can scroll.
**AdminTab:**
- `max-h-[400px] overflow-y-auto` × 2 instances. Bounded height for log/audit panels — likely intentional.
- `max-w-[260px]` truncate on token displays — good (these are secrets, full value isn't useful).
**Recommendation:** No urgent fix. Both pages are admin-only and the scroll behavior is appropriate for their content density.
### 4.6 ChatControl.tsx (Copilot, just fixed)
Quick Actions block fixed (commit `343ffb4`). Remaining minor:
- 28 inline-style blocks (background gradients, animations) — keep, they're animation-specific
- Floating button portal styles — fine
- Backdrop styles — fine
**No further action needed.**
---
## 5. Recommendations
### Immediate (1 commit, 1 hour)
1. Add `title=` to the 11 untitled truncate instances. Quick a11y win.
2. Add `minmax(0, 1fr)` to the 5 inline `gridTemplateColumns: '1fr 1fr'` instances in MarketplaceTab/MembershipTab/MyStrategiesTab. Prevents overflow when content gets long.
3. Convert `<Button>` to native `<button>` in any Button that has 2+ block children inside (audit needed).
### Short-term (1 week)
4. **ESLint rules** (preventive):
- `tailwindcss/no-custom-classname` to catch inline arbitrary widths like `w-[80px]`
- Custom rule: `truncate` requires `title` (catches Pattern E)
- Custom rule: flag `gridTemplateColumns` containing bare `1fr` without `minmax`
- Custom rule: warn on inline `style` blocks with > 5 properties (encourage class extraction)
5. **CSS audit:** scan `index.css` for class names with multiple definitions; consolidate. Aim to reduce `!important` count from 122 → < 60.
### Medium-term (2-3 weeks)
6. **Refactor OverviewTab + HomeView** to extract inline styles into a `home.css` / `overview.css` partial. Highest user impact (these are the landing pages).
7. **Component primitives audit:** the `<Button>` overlap issue suggests `@bytelyst/ui` Button needs a documented prop for stacked children, or a separate `<CardButton>` primitive. Worth a discussion with the design system team.
---
## 6. Quick verification commands
```bash
# Check current state
cd /opt/bytelyst/learning_ai_invt_trdg/web/src
# Truncate without title
grep -rn "truncate\b" --include='*.tsx' | grep -v test | grep -v "title=" | wc -l
# Inline style blocks per file (drift indicator)
for f in $(grep -rln "style={{" --include='*.tsx' | grep -v test); do
count=$(grep -c "style={{" "$f")
[ "$count" -gt 20 ] && echo "$count $f"
done | sort -rn
# !important count
grep -c "!important" index.css
# Hardcoded pixel widths in JSX
grep -rEn "(width|minWidth|maxWidth):\s*[0-9]+(px)?[,$]" --include='*.tsx' | grep -v test
```
---
## 7. Files changed in recent UI fix sweep
- `web/src/layout-fixes.css` (new, 495 lines) — all global UI overrides centralized
- `web/src/main.tsx` — imports layout-fixes.css
- `web/src/App.tsx` — refactored alert banner JSX
- `web/src/components/ChatControl.tsx` — refactored Quick Actions
- `web/src/components/layout/AppShell.tsx` — added /devops redirect
The single-file approach (`layout-fixes.css`) is intentional: every global UI fix lands there with a numbered section + comment explaining the issue. Easy to review, easy to revert one section without touching others.