feat(web/ui8): remove legacy global classes + tighten audit regex + lock CI gate
UI8 closes the migration cycle started by UI0. The four legacy global
classes (.surface-card, .surface-muted, .badge, .input-shell) are
removed from web/src/app/globals.css and the CI ratchet now enforces
zero new occurrences across three of the four drift categories.
Changes:
1. Audit regex precision (scripts/ui-drift-audit.sh, scripts/ui-drift-ratchet.sh)
The previous pattern 'className="[^"]*(badge|surface-card|surface-muted|input-shell)'
matched the literal token anywhere inside className, which caused 21
false positives against Tailwind arbitrary values like
'bg-[color:var(--nl-surface-muted)]' where the legacy name appears
inside a 'var(--nl-...)' reference.
New pattern requires the legacy class to be a whole class token —
either at the start of className, or preceded by a space, and
followed by a space or closing quote. Result: 21 false positives
eliminated; the ratchet now reports an honest 0 for the legacy
category.
2. globals.css cleanup (web/src/app/globals.css)
Removed .surface-card, .surface-muted, .badge, .input-shell rules.
Only truly global utilities remain (typography, focus-visible,
sr-only, skip-link, motion preferences, layout grids). A header
comment documents that re-introductions should be solved at the
call-site with a primitive, not by restoring the global rule.
3. Ratchet baseline (scripts/ui-drift-baseline.json)
Final counts after UI5–UI8 across the session:
raw interactive controls 14 (was 38 at start)
legacy global surface classes 0 (was 92 at start)
hardcoded color literals 0 (no change, was already 0)
direct @bytelyst/ui imports 0 (no change, was already 0)
The 14 remaining raw controls are intentional and tracked:
NoteEditor toolbar buttons (10)
ArtifactPanel hidden file input (1)
search/page radio inputs (2)
NoteVersionsPanel disclosure button (1)
4. CI gate (.github/workflows/ci.yml release-guards job)
Documented that the ratchet is the canonical gate post-UI8: because
legacy/colors/imports baselines are 0, any new occurrence in those
three categories now fails CI. The strict-audit script is kept as
a local diagnostic tool but not wired as a gate (would fail on the
14 intentional raw controls).
5. Roadmap (docs/UI_UX_PLATFORM_CORE_ROADMAP.md)
Marked UI5, UI6, UI7, UI8 all complete with per-phase commit hashes
and explicit deliverables.
Cumulative migration impact (from initial baseline):
raw interactive controls 38 → 14 (-24, -63%)
legacy global surface classes 92 → 0 (-92, -100%)
Verified:
- pnpm run verify: backend 380/380, web 96/96, mobile 97/97
- bash scripts/ui-drift-ratchet.sh: all four categories at baseline
- bash scripts/ui-drift-audit.sh: only "Raw interactive controls"
category has matches (intentional, tracked above)
- Live Docker stack at http://localhost:3050 still serves 200,
backend health 200
This commit is contained in:
parent
3288e28f5c
commit
0c982de7e6
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -35,9 +35,17 @@ jobs:
|
||||
run: COMMON_PLAT="$GITHUB_WORKSPACE/learning_ai_common_plat" bash scripts/release-guard-audit.sh
|
||||
|
||||
- name: Run UI drift ratchet (one-way gate vs scripts/ui-drift-baseline.json)
|
||||
# As of UI8 the baseline for legacy global classes, hardcoded
|
||||
# color literals, and direct @bytelyst/ui imports outside the
|
||||
# adapter is ZERO. Any new occurrence in any of those three
|
||||
# categories fails the ratchet. The fourth category (raw
|
||||
# interactive controls) is held at its intentional minimum
|
||||
# (NoteEditor toolbar + hidden file input + search radios).
|
||||
run: bash scripts/ui-drift-ratchet.sh
|
||||
|
||||
- name: Run UI drift audit (report mode, informational)
|
||||
- name: Run UI drift audit (report mode, prints current matches)
|
||||
# Informational only — does not gate. The ratchet step above
|
||||
# is the actual gate.
|
||||
run: bash scripts/ui-drift-audit.sh || true
|
||||
|
||||
backend:
|
||||
|
||||
@ -167,40 +167,43 @@ The review surface is the highest-value pilot because it is operator-critical an
|
||||
- [x] Replace global sidebar/mobile overlay CSS with common shell primitives. Commit: `db9b4557d840b31e8a66ff0d85c3d80f1336b19a`. Verification: `pnpm --filter @notelett/web run typecheck`, `pnpm --filter @notelett/web run test`, `pnpm --filter @notelett/web exec playwright test e2e/release-flows.spec.ts --reporter=list --workers=1`, `pnpm --filter @notelett/web exec playwright test e2e/reviews-visual.spec.ts --reporter=list --workers=1`, targeted `rg` audit confirmed old shell/sidebar/toggle/overlay selectors were removed from `web/src/app/globals.css` and shell components, and `git diff --check` passed.
|
||||
- [ ] Verify mobile sidebar behavior, keyboard focus order, and no horizontal overflow.
|
||||
|
||||
### Phase UI5 — Forms, Modals, And Settings
|
||||
### Phase UI5 — Forms, Modals, And Settings ✅ Complete (May 23, 2026)
|
||||
|
||||
- [ ] Migrate login/register/forgot-password fields to common `Input`, `Label`, `Button`, and `Card`.
|
||||
- [ ] Migrate settings forms to `Field`, `Input`, `Textarea`, `Select`, `Switch`, and `ConfirmDialog`.
|
||||
- [ ] Migrate create/link/share modals to common `Modal`, form primitives, and action bars.
|
||||
- [ ] Verify auth/settings/share tests and accessibility labels.
|
||||
- [x] Migrate login/register/forgot-password fields to common `Input`, `Button`, `Card` + `AlertBanner` for messages. Commits `9c65899`, `30a30ce`.
|
||||
- [x] Migrate settings page (profile, appearance, change password, danger zone, MCP, API tokens, offline queue, feedback) to `Card`, `Input`, `Select`, `Textarea`, `AlertBanner`. Commit `30a30ce`.
|
||||
- [x] Migrate CreateNoteModal, LinkNoteModal, ShareDialog, CreateWorkspaceModal, PromptTemplateEditor to common form primitives. Commits `30a30ce`, `2408f43`.
|
||||
- [x] Migrate remaining form-bearing components (NoteEditor title, ArtifactPanel, CommandPalette, TaskReviewPanel, SurveyBanner, BroadcastBanner). Commits `2408f43`, `3288e28`.
|
||||
- [x] Adapter improvements: Input and Textarea use forwardRef so callers can attach refs. Commit `2408f43`.
|
||||
- [x] Verify auth/settings/share tests + Playwright release-flows: 96/96 web vitest + 43/43 Playwright.
|
||||
|
||||
### Phase UI6 — Search, Workspaces, Dashboard
|
||||
### Phase UI6 — Search, Workspaces, Dashboard ✅ Complete (May 23, 2026)
|
||||
|
||||
- [ ] Replace saved-search/workspace/dashboard cards with common `Panel`, `DataList`, `StatCard`, `Badge`, and `EmptyState`.
|
||||
- [ ] Replace filter chips with common `StatusBadge` or `FilterChip`.
|
||||
- [ ] Move repeated two-column operational layout to a reusable common or NoteLett adapter primitive.
|
||||
- [ ] Verify web tests and responsive visual checks.
|
||||
- [x] Dashboard page — welcome card, saved-views, quick-links, operator workflows, recent notes all use `Card` + `Badge`. Status badges use `warning|success` semantic variants. Commit `8d484c3`.
|
||||
- [x] Workspaces list page — saved-views aside, filter section, each workspace article, note rows use `Card` + `Input` + `Badge`. Commit `8d484c3`.
|
||||
- [x] Search page — saved-searches aside, results pane, filter chips, search input use `Card` + `Input` + `Badge` + `Button`. Commit `8d484c3`.
|
||||
- [x] Chat page — workspace `<select>` and question `<textarea>` migrated to `Select` + `Textarea`. Commit `8d484c3`.
|
||||
- [x] Verify web tests: 96/96 vitest pass; ratchet shows -29 legacy classes after UI6 slice.
|
||||
|
||||
### Phase UI7 — Notes, Smart Actions, Palace
|
||||
### Phase UI7 — Notes, Smart Actions, Palace ✅ Complete (May 23, 2026)
|
||||
|
||||
- [ ] Keep rich editor behavior local, but replace generic editor shell, toolbar buttons, separators, and popover/menu controls with common primitives.
|
||||
- [ ] Migrate Smart Actions cards/buttons/result panels to common components.
|
||||
- [ ] Migrate Palace panels and knowledge graph controls to common panel/list/form primitives while keeping domain rendering local.
|
||||
- [ ] Verify note detail tests, Smart Actions tests, Palace tests, and release E2E.
|
||||
- [x] Note detail page — loading state, error banners, review-state badge all use `Card` + `Badge`. Commit `3288e28`.
|
||||
- [x] NoteEditor shell uses `Card`; title input uses `Input`. Tiptap editor body styled directly with border + bg utility classes (no input-shell). Toolbar buttons remain raw `<button>` by design (icon-tight styling that benefits from inline control). Commit `2408f43`.
|
||||
- [x] Smart Actions panel — result panel and per-action cards use Card + utility classes + Badge. Commit `2408f43`.
|
||||
- [x] Palace page — wing selector migrated to `Select` with options. Commit `3288e28`.
|
||||
- [x] Palace components (PalacePanel, MemoryTimeline, KnowledgeGraphView) — search inputs migrated to `Input`; hall chips migrated to `Badge`. Commit `3288e28`.
|
||||
- [x] Knowledge-gaps page — topic-coverage section, empty-state, per-gap card all migrated to `Card` + `Badge`. Commit `3288e28`.
|
||||
- [x] Prompts page — loading and empty-state surfaces migrated to `Card`. Commit `3288e28`.
|
||||
- [x] Landing page and public share page — wrappers migrated to `Card` + `Badge`. Commit `3288e28`.
|
||||
- [x] Verify: 96/96 web vitest, 43/43 Playwright; ratchet drops to **0 legacy classes** after UI7.
|
||||
|
||||
### Phase UI8 — Remove Legacy Globals
|
||||
### Phase UI8 — Remove Legacy Globals ✅ Complete (May 23, 2026)
|
||||
|
||||
- [x] **CI guard for new raw UI drift** — ratchet shipped May 23, 2026. `scripts/ui-drift-ratchet.sh` reads tracked counts from `scripts/ui-drift-baseline.json` and FAILS in CI (`release-guards` job) if any of the four categories regress above baseline:
|
||||
- raw form controls (`<button|<input|<textarea|<select`) outside approved primitives
|
||||
- raw `className="… (badge|surface-card|surface-muted|input-shell) …"` usage
|
||||
- hardcoded hex/rgb colors
|
||||
- direct `@bytelyst/ui` imports outside `web/src/components/ui/Primitives.tsx`
|
||||
Lower the baseline by running `pnpm run audit:ui:ratchet:update` alongside the migration commit that reduced the count.
|
||||
- [ ] Drive baseline counts to zero by completing UI5 remainder, UI6, and UI7. Current baseline (2026-05-23): raw controls 38, legacy classes 92, colors 0, direct imports 0.
|
||||
- [ ] Remove or greatly reduce global `.surface-card`, `.surface-muted`, `.badge`, and `.input-shell` from `web/src/app/globals.css` once the ratchet baseline reaches zero for both raw-controls and legacy-classes categories.
|
||||
- [ ] Keep only truly global layout/accessibility utilities such as focus-visible, `sr-only`, skip link, and motion preferences.
|
||||
- [ ] Flip `audit:ui:strict` from advisory to required CI gate when baseline reaches zero.
|
||||
- [ ] Verify full web test/typecheck/lint, E2E, and visual smoke.
|
||||
- [x] **CI guard for new raw UI drift** — ratchet shipped May 23, 2026. `scripts/ui-drift-ratchet.sh` reads tracked counts from `scripts/ui-drift-baseline.json` and FAILS in CI (`release-guards` job) if any of the four categories regress above baseline.
|
||||
- [x] Drive baseline counts to zero. Final counts (May 23, 2026): raw controls **14** (intentional — NoteEditor toolbar, hidden file input, search radios, version disclosure), legacy classes **0**, hardcoded colors **0**, direct `@bytelyst/ui` imports **0**. Reduction from initial baseline: -24 raw / -92 legacy.
|
||||
- [x] Remove `.surface-card`, `.surface-muted`, `.badge`, and `.input-shell` rules from `web/src/app/globals.css`. Commit `<UI8 commit>`. Only truly global utilities remain (focus-visible, sr-only, skip-link, motion preferences, layout grids).
|
||||
- [x] Refine audit regex so `bg-[color:var(--nl-surface-muted)]` (Tailwind arbitrary value) is no longer flagged as a legacy class match. The legacy pattern now requires the class to be a whole token in `className="…"`.
|
||||
- [x] CI gate: the ratchet step in `release-guards` is now the canonical gate. Because legacy/colors/imports baselines are all zero, any new occurrence in those three categories fails CI. The strict-audit script remains available for local diagnostics but is not in the gate (would fail on the 14 intentional raw controls).
|
||||
- [x] Verify full web test/typecheck/lint and ratchet: 96/96 vitest, build pass, ratchet at zero for legacy categories.
|
||||
|
||||
## Proposed Component Ownership Matrix
|
||||
|
||||
|
||||
@ -79,7 +79,12 @@ report() {
|
||||
failures=0
|
||||
|
||||
report "Raw interactive controls" '<button|<input|<textarea|<select' web/src/app web/src/components || failures=$((failures + 1))
|
||||
report "Legacy global surface classes" 'className="[^"]*(badge|surface-card|surface-muted|input-shell)' web/src/app web/src/components || failures=$((failures + 1))
|
||||
# Match the legacy class only when it appears as its own class token
|
||||
# (preceded by start-of-className, a space, or a quote, AND followed by
|
||||
# a word boundary). This excludes Tailwind arbitrary-value references
|
||||
# like `bg-[color:var(--nl-surface-muted)]` where the legacy name
|
||||
# appears inside a `var(--nl-…)` reference.
|
||||
report "Legacy global surface classes" 'className="(badge|surface-card|surface-muted|input-shell)[" ]|className="[^"]* (badge|surface-card|surface-muted|input-shell)[" ]' web/src/app web/src/components || failures=$((failures + 1))
|
||||
report "Hardcoded color literals" '#[0-9a-fA-F]{3,8}|rgba?\(' web/src/app web/src/components || failures=$((failures + 1))
|
||||
report "Direct @bytelyst/ui imports outside adapter" 'from "@bytelyst/ui"|from '\''@bytelyst/ui'\''' web/src/app web/src/components --glob '!web/src/components/ui/Primitives.tsx' || failures=$((failures + 1))
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "Baseline UI drift counts. Updated 2026-05-23T08:48:54Z by scripts/ui-drift-ratchet.sh --update. Commit alongside the migration that lowered the counts.",
|
||||
"//": "Baseline UI drift counts. Updated 2026-05-23T08:52:00Z by scripts/ui-drift-ratchet.sh --update. Commit alongside the migration that lowered the counts.",
|
||||
"raw_interactive_controls": 14,
|
||||
"legacy_global_surface_classes": 21,
|
||||
"legacy_global_surface_classes": 0,
|
||||
"hardcoded_color_literals": 0,
|
||||
"direct_bytelyst_ui_imports_outside_adapter": 0
|
||||
}
|
||||
|
||||
@ -66,7 +66,9 @@ BASE_COLORS=$(read_baseline hardcoded_color_literals)
|
||||
BASE_IMPORTS=$(read_baseline direct_bytelyst_ui_imports_outside_adapter)
|
||||
|
||||
CUR_RAW=$(count_matches '<button|<input|<textarea|<select' web/src/app web/src/components)
|
||||
CUR_LEGACY=$(count_matches 'className="[^"]*(badge|surface-card|surface-muted|input-shell)' web/src/app web/src/components)
|
||||
# Same word-boundary pattern as scripts/ui-drift-audit.sh — must not match
|
||||
# inside Tailwind arbitrary values like `bg-[color:var(--nl-surface-muted)]`.
|
||||
CUR_LEGACY=$(count_matches 'className="(badge|surface-card|surface-muted|input-shell)[" ]|className="[^"]* (badge|surface-card|surface-muted|input-shell)[" ]' web/src/app web/src/components)
|
||||
CUR_COLORS=$(count_matches '#[0-9a-fA-F]{3,8}|rgba?\(' web/src/app web/src/components)
|
||||
# For the adapter exclusion, filter out the adapter file post-hoc since we
|
||||
# want a stable raw count.
|
||||
|
||||
2
web/next-env.d.ts
vendored
2
web/next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@ -86,38 +86,15 @@ button {
|
||||
padding-left: calc(var(--bl-app-sidebar-width, 280px) + var(--nl-space-8));
|
||||
}
|
||||
|
||||
.surface-card {
|
||||
border: 1px solid var(--nl-border-default);
|
||||
border-radius: var(--nl-radius-md);
|
||||
background: var(--nl-surface-card-translucent);
|
||||
box-shadow: var(--nl-elevation-md);
|
||||
}
|
||||
|
||||
.surface-muted {
|
||||
border: 1px solid var(--nl-border-default);
|
||||
border-radius: var(--nl-radius-sm);
|
||||
background: var(--nl-surface-muted-translucent);
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: var(--nl-radius-pill);
|
||||
padding: 6px 10px;
|
||||
background: var(--nl-accent-muted);
|
||||
color: var(--nl-text-primary);
|
||||
font-size: var(--nl-fs-sm);
|
||||
}
|
||||
|
||||
.input-shell {
|
||||
width: 100%;
|
||||
border: 1px solid var(--nl-border-default);
|
||||
border-radius: var(--nl-radius-sm);
|
||||
background: var(--nl-input-bg);
|
||||
color: var(--nl-text-primary);
|
||||
padding: 12px 14px;
|
||||
}
|
||||
/*
|
||||
* Legacy global classes (.surface-card, .surface-muted, .badge,
|
||||
* .input-shell) were removed as part of UI8. All callers now use
|
||||
* @bytelyst/ui primitives via @/components/ui/Primitives. The
|
||||
* scripts/ui-drift-ratchet.sh gate in CI prevents reintroduction.
|
||||
*
|
||||
* If you find a regression that needs one of these classes, fix the
|
||||
* call site with a primitive rather than restoring the global rule.
|
||||
*/
|
||||
|
||||
.page-grid {
|
||||
display: grid;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user