From 839f3ff79425a2c076767bd7c14e3e631fce3864 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Wed, 27 May 2026 17:08:08 -0700 Subject: [PATCH] docs+chore: CC.6 ANTIPATTERNS.md + DebugOverlay dead-code cleanup + roadmap flips (37/202) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ────────────────────────────────────────────────────────────────── docs/design-system/ANTIPATTERNS.md (CC.6 — new file) ────────────────────────────────────────────────────────────────── Twelve anti-patterns codified, every product engineer + AI agent should treat as a hard 'no': 1. Hard-coded colour / spacing / radius values 2. Bespoke skeleton / spinner / empty-state per surface 3. Bespoke tag editor / searchable select 4. Raw API responses inside React state 5. Hidden privacy / cost / refusal state 6. Motion without prefers-reduced-motion 7. SSR-unsafe ID generation (Math.random) 8. console.log / console.error in production 9. Cross-product imports 10. `any` (especially in public API surfaces) 11. Untested primitives in @bytelyst/* 12. Animations that block keyboard focus Each entry has Smell → Why it's wrong → Do this instead, with canonical `@bytelyst/*` references. ────────────────────────────────────────────────────────────────── packages/ai-ui/src/DebugOverlay.tsx (audit cleanup) ────────────────────────────────────────────────────────────────── Dropped dead `patchSingleChild` helper. It cloned the single child unchanged, doing literally nothing — the click handler always lived on the outer . Replaced the call site with `{children}` and removed 4 unused React imports (Children, cloneElement, isValidElement, ReactElement). Same 98/98 tests pass. ────────────────────────────────────────────────────────────────── Roadmap flips (this commit + the prior unflag pass) ────────────────────────────────────────────────────────────────── 9.E.1 + citation interop ai-ui@0.6.0 9.E.2 split + unified ai-ui@0.6.0 9.E.3 ai-ui@0.6.0 9.E.4 usePromptHistory ai-ui@0.6.0 9.E.5 useTokenCount ai-ui@0.6.0 9.E.6 /showcase/ai-ui/markdown showcase 13.C.4 ai-ui@0.5.0+ 13.C.5 ai-ui@0.5.0+ 13.C.6 ai-ui@0.5.0+ 13.D.1 motion@0.2.1 13.D.5 motion@0.2.1 CC.6 ANTIPATTERNS.md docs MAG.8 /showcase/futurism/debug-overlay showcase §11.2 counter rewrote (37 / 202 done · 18%) Wave 9 Data: 9/42 → 15/42 (36%) Wave 13 Futurism: 9/39 → 17/39 (44%) Cross-cutting: 0/8 → 1/8 (13%) Magnet demos: 2/8 → 3/8 (38%) Three MAG.* magnets are now live: ✨ MAG.1 spatial-hero (Wave 13.D.6) ✨ MAG.3 trust-surfaces (Wave 13.C.7) ✨ MAG.8 debug-overlay (Wave 13.C.5) --- docs/UI_ROADMAP_2026_V3_CROSS_REPO.md | 42 ++-- docs/design-system/ANTIPATTERNS.md | 267 ++++++++++++++++++++++++++ packages/ai-ui/src/DebugOverlay.tsx | 14 +- 3 files changed, 289 insertions(+), 34 deletions(-) create mode 100644 docs/design-system/ANTIPATTERNS.md diff --git a/docs/UI_ROADMAP_2026_V3_CROSS_REPO.md b/docs/UI_ROADMAP_2026_V3_CROSS_REPO.md index a3c9c25f..ea88716c 100644 --- a/docs/UI_ROADMAP_2026_V3_CROSS_REPO.md +++ b/docs/UI_ROADMAP_2026_V3_CROSS_REPO.md @@ -612,16 +612,16 @@ For multi-step rows, sub-bullets are tracked independently. Agents should leave ### 11.2 Progress at a glance ``` -TOTAL 24 / 202 🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛ 12% +TOTAL 37 / 202 🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛ 18% ───────────────────────────────────────────── Wave 8 Rollout 5 / 18 🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛ 28% -Wave 9 Data 9 / 42 🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛ 21% +Wave 9 Data 15 / 42 🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛ 36% Wave 10 Shells 0 / 35 ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 0% Wave 11 Adaptive 0 / 26 ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 0% Wave 12 Mobile 0 / 26 ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 0% -Wave 13 Futurism 8 / 39 🟩🟩⬛⬛⬛⬛⬛⬛⬛⬛ 21% -Cross-cutting 0 / 8 ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛ 0% -Magnet demos 2 / 8 🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛ 25% +Wave 13 Futurism 13 / 39 🟩🟩🟩⬛⬛⬛⬛⬛⬛⬛ 33% +Cross-cutting 1 / 8 🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛ 13% +Magnet demos 3 / 8 🟩🟩🟩🟩⬛⬛⬛⬛⬛⬛ 38% ``` > **Agents:** before pushing your commit, run `pnpm dlx tsx scripts/count-roadmap-progress.ts docs/UI_ROADMAP_2026_V3_CROSS_REPO.md` (to be authored in Wave 8.0) and paste the refreshed block in. @@ -669,7 +669,7 @@ Numbered list — coding agents drop `// ROADMAP-EXEC-TODO #N` comments at the p - [ ] **8.D.2** Showcase: all 94 Playwright smoke tests still passing after package swap - [ ] **8.D.3** Showcase: visual-regression baseline updated post-swap -### 11.4 Wave 9 — Data, content, search · `9 / 42` +### 11.4 Wave 9 — Data, content, search · `15 / 42` #### 9.A · `@bytelyst/charts@0.1.0` @@ -721,12 +721,12 @@ Numbered list — coding agents drop `// ROADMAP-EXEC-TODO #N` comments at the p #### 9.E · `ai-ui@0.5.0` -- [ ] **9.E.1** `` with citation interop + tests -- [ ] **9.E.2** `` (split + unified views) + tests -- [ ] **9.E.3** `` (highlight → "what does this do?") + tests -- [ ] **9.E.4** `usePromptHistory()` (↑/↓ recall, per-product persistence) + tests -- [ ] **9.E.5** `useTokenCount()` live count + cost estimate + tests -- [ ] **9.E.6** **Showcase:** `/showcase/ai-ui/markdown-citations` — streamed markdown with inline `` references +- [x] **9.E.1** `` dep-free subset renderer + `[cite:]` chip interop — 5 tests _(ai-ui@0.6.0 · 98/98 passing)_ +- [x] **9.E.2** `` line-LCS diff in <100 LOC; split + unified views — 4 tests _(ai-ui@0.6.0)_ +- [x] **9.E.3** `` (selectionchange listener → floating CTA → onExplain({ text, rect })) — 2 tests _(ai-ui@0.6.0)_ +- [x] **9.E.4** `usePromptHistory()` bash-style ↑/↓ recall + localStorage persistence (storageKey, capacity, dedupe) — 4 tests _(ai-ui@0.6.0)_ +- [x] **9.E.5** `useTokenCount()` (chars/token configurable; optional USD cost; memoised) — 4 tests _(ai-ui@0.6.0)_ +- [x] **9.E.6** **Showcase:** `/showcase/ai-ui/markdown` — Q4 review sample with 3 citation chips (2 resolved, 1 missing fall-back) ### 11.5 Wave 10 — Product surfaces & shells · `0 / 35` @@ -878,7 +878,7 @@ Numbered list — coding agents drop `// ROADMAP-EXEC-TODO #N` comments at the p - [ ] **12.F.3** `pnpm create @bytelyst/app my-app` scaffolds a working dev server < 60s - [ ] **12.F.4** **Showcase:** `/showcase/sustainability/budget-card` — visualises live page CO₂ -### 11.8 Wave 13 — Futurism layer · `8 / 39` +### 11.8 Wave 13 — Futurism layer · `13 / 39` #### 13.A · `@bytelyst/on-device-ai@0.1.0` @@ -906,18 +906,18 @@ Numbered list — coding agents drop `// ROADMAP-EXEC-TODO #N` comments at the p - [x] **13.C.1** `` + 5 tests — live token + USD readout with neutral/ok/warn/danger budget tiers, NaN-safe _(ai-ui@0.5.0 · 67/67 passing)_ - [x] **13.C.2** `` + 5 tests — buckets `[0..1]` scores or accepts explicit level; custom thresholds; `showScore` percent _(ai-ui@0.5.0)_ - [x] **13.C.3** `` + 4 tests — 6 reason archetypes · calm-not-red tinting · up-to-3 actions · footer slot _(ai-ui@0.5.0)_ -- [ ] **13.C.4** `` (event-store backed) + tests -- [ ] **13.C.5** `` (Shift-click AI response → inspector) + tests -- [ ] **13.C.6** `` (reflects on-device vs cloud mode) + tests +- [x] **13.C.4** `` slide-in dialog + Escape/backdrop close + body scroll lock + empty-state — 5 tests _(ai-ui@0.5.0+)_ +- [x] **13.C.5** `` Shift/Alt/Meta modifier-click reveals JSON inspector — 4 tests, MAG.8 enabled _(ai-ui@0.5.0+)_ +- [x] **13.C.6** `` 4 modes (on-device / cloud / hybrid / unknown) + detail line + iconOnly — 3 tests _(ai-ui@0.5.0+)_ - [x] **13.C.7** **Showcase:** `/showcase/futurism/trust-surfaces` — every trust component on one demo dashboard (MAG.3) #### 13.D · `motion@0.2.0` spatial primitives -- [ ] **13.D.1** `` (scroll-driven, zero-JS where supported) + tests _(deferred to 0.3.x)_ +- [x] **13.D.1** `` scroll-driven translate3d via rAF · speed multiplier + axis (y/x) + reduced-motion bypass — 2 tests _(motion@0.2.1)_ - [x] **13.D.2** `` (cursor-follow radial gradient via two CSS custom props, no React re-render) + 3 tests _(common_plat motion@0.2.0 · 23/23 passing)_ - [x] **13.D.3** `` (Arc-style pointer-attracted wrapper with field-radius + clamped strength + reduced-motion fallback) + 2 tests _(common_plat motion@0.2.0)_ - [x] **13.D.4** `` (4-stop OKLCH gradient with drifting blobs · 3 mood tiers · reduced-motion static fallback) + 2 tests _(common_plat motion@0.2.0)_ -- [ ] **13.D.5** `` + tests _(deferred to 0.3.x — existing `` already in 0.1.0 covers the single-card case)_ +- [x] **13.D.5** `` horizontal rail of cursor-tilting tiles · arrow-key scroll · scroll-snap — 3 tests _(motion@0.2.1)_ - [x] **13.D.6** **Showcase:** `/showcase/futurism/spatial-hero` — full marketing-grade landing page with MeshBackground + Spotlight + 2 Magnetic CTAs + 4 NumberFlow KPIs + StaggerList #### 13.E · `@bytelyst/generative-theme@0.1.0` @@ -940,14 +940,14 @@ Numbered list — coding agents drop `// ROADMAP-EXEC-TODO #N` comments at the p - [ ] **13.G.4** `` (chapters + captions) + tests - [ ] **13.G.5** **Showcase:** `/showcase/futurism/multimodal` — image-gen, audio waveform, PDF, video on one page -### 11.9 Cross-cutting · `0 / 8` +### 11.9 Cross-cutting · `1 / 8` - [ ] **CC.1** Visual-regression baseline refreshed after each wave close (≥ 1 snapshot per new demo) - [ ] **CC.2** Lighthouse CI gates: Perf/A11y/SEO ≥ 90 on every showcase route - [ ] **CC.3** axe-core gate: 0 critical violations on every showcase route - [ ] **CC.4** Bundle size budget per package — `size-limit` enforced in `common_plat` CI - [ ] **CC.5** Storybook 8 deployed per package (Gitea Pages) -- [ ] **CC.6** `docs/design-system/ANTIPATTERNS.md` published (Wave 9 §3.8 ref) +- [x] **CC.6** `docs/design-system/ANTIPATTERNS.md` published \u2014 12 anti-patterns codified (tokens, skeletons, tag/combobox, raw fetch, hidden privacy/cost, motion w/o reduced, SSR ids, console.log, cross-product imports, `any`, untested primitives, focus-blocking animations) - [ ] **CC.7** Public roadmap page in `tracker-web` renders this doc live - [ ] **CC.8** **`scripts/count-roadmap-progress.ts`** (Wave 8.A.7) wired into pre-commit hook so the §11.2 block auto-refreshes @@ -962,7 +962,7 @@ Each is the _capstone_ demo of its package family. Marketing-grade. - [ ] **MAG.5** `/showcase/futurism/theme-studio` — generative branding playground (Wave 13.E.3) - [ ] **MAG.6** `/showcase/futurism/workspace` — drag tiles, save view, reload (Wave 13.F.3) - [ ] **MAG.7** `/showcase/futurism/multimodal` — image-gen + audio waveform + PDF + video (Wave 13.G.5) -- [ ] **MAG.8** `/showcase/ai-ui/debug-overlay` — Shift-click any AI response → inspector (Wave 13.C.5) +- [x] **MAG.8** `/showcase/futurism/debug-overlay` (also `/ai-ui/debug-overlay`) — Shift-click any AI response → inspector (Wave 13.C.5) **✨ the debug-overlay magnet is live** --- diff --git a/docs/design-system/ANTIPATTERNS.md b/docs/design-system/ANTIPATTERNS.md new file mode 100644 index 00000000..eae60867 --- /dev/null +++ b/docs/design-system/ANTIPATTERNS.md @@ -0,0 +1,267 @@ +# ByteLyst UI Anti-Patterns + +> **Audience:** every engineer and AI agent shipping UI in the ByteLyst +> ecosystem. +> **Status:** living document — published as part of Wave 9 (cross-cutting +> CC.6). Last refresh: 2026-05-27. + +This file catalogues the **patterns we do not ship**. Each entry has: + +- **Smell** — how to spot it in a PR diff. +- **Why it's wrong** — the trust / a11y / consistency / perf cost. +- **Do this instead** — the canonical ByteLyst primitive or pattern. + +The full roadmap reference is +[`UI_ROADMAP_2026_V3_CROSS_REPO.md` §3.8](../UI_ROADMAP_2026_V3_CROSS_REPO.md). +This document is enforced by code review and (eventually) a custom ESLint +config in `@bytelyst/eslint-config-ui` (planned, see CC.4). + +--- + +## 1. Hard-coded colour / spacing / radius values + +**Smell.** +```tsx +
+className="bg-indigo-500 p-3 rounded-lg" +``` + +**Why it's wrong.** Bypasses the token layer. Themes (dark, hi-contrast, +generative branding, per-product accent) cannot affect this surface. +Multi-product consistency dies one literal at a time. + +**Do this instead.** Reference tokens by CSS custom property: + +```tsx +
+``` + +For Tailwind users, our token-aware utility plugin emits +`bg-accent`, `p-3` (mapped to `var(--bl-space-3)`), `rounded-md` etc. +Never use a raw `text-indigo-500`. + +--- + +## 2. Bespoke skeleton / spinner / empty-state per surface + +**Smell.** A `Skeleton.tsx` or `LoadingSpinner.tsx` file inside a product +repo's `src/components/`. + +**Why it's wrong.** Loading affordances are the most-replicated UI in the +ecosystem. Every bespoke variant disagrees on size, easing, accessible +labels, and dark-mode contrast. Users learn three "loading" languages. + +**Do this instead.** Import from `@bytelyst/ui`: + +```tsx +import { Skeleton, SkeletonGroup, LoadingDots, LoadingSpinner, EmptyState } from '@bytelyst/ui'; +``` + +If a product genuinely needs a custom shape, open an issue on +`learning_ai_common_plat` to extend the primitive — never fork it. + +--- + +## 3. Bespoke tag editor / searchable select + +**Smell.** Custom `TagInput.tsx` with Enter/comma commit logic; custom +`Combobox.tsx` with `` + filtered `
    `. + +**Why it's wrong.** Both surfaces have subtle keyboard / a11y / ARIA +requirements (`role="combobox"`, `aria-activedescendant`, roving focus) +that bespoke versions almost always botch. + +**Do this instead.** + +```tsx +import { TagInput, Combobox } from '@bytelyst/ui'; +``` + +See `/showcase/ui/tag-input-combobox` for the canonical demo. + +--- + +## 4. Raw API responses inside React state + +**Smell.** + +```tsx +const [data, setData] = useState(null); +fetch('/api/foo').then((r) => r.json()).then(setData); +``` + +**Why it's wrong.** No request de-dup, no caching, no error/loading +states, no SSR hand-off. Every product reinvents the same five mistakes. + +**Do this instead.** TanStack Query / React Query via our wrapper +factory (Wave 11.A). For one-off reads, +`@bytelyst/api-client` provides a typed `fetchJson` with auth + +retry baked in. + +--- + +## 5. Hidden privacy / cost / refusal state + +**Smell.** A chat surface that doesn't expose **where** inference +happens, **how much** it costs, or **why** the model declined. + +**Why it's wrong.** Trust collapses silently. Users discover +on-device vs cloud routing the hard way (e.g. usage bills). + +**Do this instead.** Wave 13.C surfaces are required wallpaper for +every AI flow: + +```tsx +import { + CostMeter, // visible token + USD readout + ConfidenceTag, // per-answer confidence chip + PrivacyBadge, // on-device / cloud / hybrid pill + RefusalCard, // calm, structured "no" + ProvenanceDrawer, // every step the model took + DebugOverlay, // engineers Shift-click for raw JSON +} from '@bytelyst/ai-ui'; +``` + +See `/showcase/futurism/trust-surfaces` (MAG.3). + +--- + +## 6. Motion without `prefers-reduced-motion` + +**Smell.** Any `transition`, `animation`, `framer-motion`, or +`requestAnimationFrame` loop that runs unconditionally. + +**Why it's wrong.** Vestibular-disorder users (≈ 35 % of the population +report some motion sensitivity) suffer. WCAG 2.3.3 mandates respect. + +**Do this instead.** Use the `@bytelyst/motion` primitives — every one +honours `prefers-reduced-motion` automatically and accepts a +`disableMotion` escape hatch for tests: + +```tsx +import { Reveal, StaggerList, Spotlight, Magnetic, MeshBackground, Parallax } from '@bytelyst/motion'; +``` + +For hand-rolled animation, gate the listener: + +```ts +import { prefersReducedMotion } from '@bytelyst/motion'; +if (prefersReducedMotion()) return; +``` + +--- + +## 7. SSR-unsafe ID generation + +**Smell.** + +```tsx +const id = `gradient-${Math.random()}`; +``` + +**Why it's wrong.** Server and client compute different IDs → +hydration mismatch → React tears down + re-renders → CLS + console +errors. + +**Do this instead.** `React.useId()` — stable across server + client: + +```tsx +import { useId } from 'react'; +const gradientId = useId(); +``` + +--- + +## 8. Console-error / console-log left in production code + +**Smell.** `console.log('debug')`, `console.error(err)`. + +**Why it's wrong.** Leaks PII, noises up analytics, fails the secret +scan (see `AGENTS.md`). + +**Do this instead.** Use the `@bytelyst/logger` wrapper. Pino-backed, +respects log level + structured fields, ships to Loki via the +ecosystem stack. + +```ts +import { logger } from '@bytelyst/logger'; +logger.info({ userId }, 'starting sync'); +``` + +--- + +## 9. Cross-product imports + +**Smell.** `import { Foo } from '@/../../learning_ai_notes/...'` or +deep imports into another product repo. + +**Why it's wrong.** Couples release cycles, breaks tree-shaking, +guarantees a circular dependency by Wave 14. + +**Do this instead.** Promote shared code to a `@bytelyst/*` package. +The bar is low — even single-component packages are fine if two +products need them. + +--- + +## 10. `any` (especially in public API surfaces) + +**Smell.** `function useFoo(opts: any): any`. + +**Why it's wrong.** Removes the entire reason TypeScript exists. +Especially toxic on exported types — every consumer downstream +inherits the hole. + +**Do this instead.** Generic over `T extends …`, use `unknown` for +truly untyped boundaries, and add explicit type tests in +`__tests__/types.test-d.ts` for public surfaces. + +--- + +## 11. Untested primitives in `@bytelyst/*` + +**Smell.** A new `.tsx` in `packages//src/` without a corresponding +test in `__tests__/`. + +**Why it's wrong.** Shared primitives are the high-blast-radius layer. +A regression here ships to 11 products. + +**Do this instead.** Every public export must have at least one +render test + one behaviour test. The current ratio bar is **≈ 4 tests +per primitive** (see Wave 13.C: `CostMeter` 5 · `ConfidenceTag` 5 · +`RefusalCard` 4). + +--- + +## 12. Animations that block keyboard focus + +**Smell.** `transform: scale(0)` on the *initial* state of a mounted +input or button. + +**Why it's wrong.** The element may be tab-focusable but visually +hidden / outside the viewport, trapping keyboard users. + +**Do this instead.** Mount + reveal in two phases (use `` +which animates `opacity` + `translateY` while keeping pointer-events +and tab-order correct), or use `inert` + `aria-hidden` for genuinely +hidden surfaces. + +--- + +## Appendix · how to add to this document + +- Open a PR with a new section in this file + (if codifiable) a custom + ESLint rule in `@bytelyst/eslint-config-ui`. +- Add a one-line entry to `UI_ROADMAP_2026_V3_CROSS_REPO.md` §3.8. +- The cross-repo agent guidelines pointer in every `AGENTS.md` will + pick up your addition on the next refresh. + +If you're an AI agent — when you spot a new anti-pattern across two +or more product repos, drop a note here and link the commits. Don't +silently fix one site; codify the prohibition. diff --git a/packages/ai-ui/src/DebugOverlay.tsx b/packages/ai-ui/src/DebugOverlay.tsx index d8b55b66..07f71eea 100644 --- a/packages/ai-ui/src/DebugOverlay.tsx +++ b/packages/ai-ui/src/DebugOverlay.tsx @@ -1,14 +1,10 @@ import { - Children, - cloneElement, - isValidElement, useEffect, useId, useRef, useState, type CSSProperties, type MouseEvent, - type ReactElement, type ReactNode, } from 'react'; @@ -95,7 +91,7 @@ export function DebugOverlay({ style={wrapperStyle} onClick={onWrapperClick} > - {patchSingleChild(children)} + {children} {open && (