jw-search / docs /APP_CSS_REDUCTION_PLAN.md
jw-tools's picture
deploy: latest main (lazy-ML cold start, durable launcher, web-image search, scene search) + full-app data refresh
7ea1851 verified

App.css reduction plan

This document tracks the work to retire the legacy frontend/src/App.css in favour of the redesign's design tokens (styles/design.css), shared utilities (styles/page-shared.css, styles/clip-grid.css), and per-page CSS files (styles/persons-page.css, styles/download-page.css).

Where we are now

Snapshot Lines Notes
Pre-redesign baseline (main) 6,401 Single mega-file with mixed concerns.
After first orphan sweep (09c416c) 5,892 Removed 78 classes tied to deleted UI (.tab-bar-, .results-, .grid-*, etc.).
After deeper sweep (1c47412) 4,695 145 more classes (details-panel cluster, frame thumbnails, batch forms, etc.).
After persons extraction (be7f4cd) 3,828 144 rule blocks moved to styles/persons-page.css.
After download extraction (3ef5161) 2,183 303 rule blocks moved to styles/download-page.css.
After iterative orphan scrub (497e72c) 2,161 Current. Eight passes of comma-orphan removal until stable.

Net reduction so far: 6,401 β†’ 2,161 lines (βˆ’66 %).

Other CSS files at the same date:

File Lines Purpose
App.css 2,161 Mostly legacy, see breakdown below.
styles/design.css 342 Tokens, topbar, layout, buttons, status pills, frame gradients.
styles/clip-grid.css 421 Search clip grid + inline player + Open Subtitles modal.
styles/page-shared.css 109 page-shell, page-head, card-section, settings rows, error banner.
styles/persons-page.css 878 All PersonsPage-only chrome.
styles/download-page.css 1,625 All DownloadPage-only chrome.

What's left in App.css

Roughly 83 unique base classes remain, falling into four buckets:

1. Legacy SearchPage controls (~28 classes, ~580 lines)

Used only by SearchPage.jsx's in-page Search Mode / Sort By / Filters strip. The redesign already lifted the search input, language picker, and method toggle into the topbar; the page-local controls below it still ride on these legacy class names:

.search-controls, .controls-row, .control-group, .control-label, .control-select, .search-input, .filter-toggle-button, .filter-panel, .filter-row, .filter-group, .filter-group-wide, .filter-label, .filter-date-inputs, .filter-duration-inputs, .filter-input, .filter-separator, .filter-select, .filter-hint, .filter-actions, .filter-clear-button, .image-drop-overlay, .smart-search-info (+ children), .scripture-input (+ children).

2. Legacy SearchPage face / image / scripture / save-person panels (~18 classes, ~350 lines)

Used by the empty-state panels rendered when the corresponding search method is selected. Most of these moved into components/FaceSearchPanel, ImageUploadPanel, ScriptureSearchPanel, SavePersonBanner during the panel extraction, but their CSS still lives in App.css:

.face-drop-zone, .face-drop-hint, .face-search-controls, .face-search-divider, .face-search-hint, .face-quick-actions, .drop-indicator, .upload-image-button, .scripture-search-form, .scripture-search-button, .scripture-hint, .scripture-colon, .scripture-inputs, .save-person-banner, .save-person-form, .save-person-input, .save-person-confirm, .save-person-cancel, .save-person-text, .save-person-button, .person-select, .empty-state.face-drop-zone.dragging.

3. Truly shared utilities (~5 classes, ~60 lines)

Consumed by more than one page or component:

.empty-state (Search/Download/Persons + 3 panel components), .error-message (Search/Download/Persons), .toast and .toast-message (DownloadPage Toast + PersonsPage toast renderer). .app and .main-content are wrappers used by the mockup-viewer branch in App.jsx.

(.face-crop-container was previously listed here but already lives in styles/persons-page.css β€” only PersonsPage references it.)

4. Genuinely orphan / dead (~30 classes, ~350 lines)

Rules with no current consumer; survived prior sweeps because their last selector was paired with a still-active class via comma list. Includes .add-person-button, .assign-button and several .appearance-thumbnail* modifiers that were superseded by persons-page.css equivalents.

Sweep order for the rest

Each step is its own atomic commit, each verified with a 3-agent QC panel (completeness + cross-page safety + runtime regression).

Step 1 β€” Move SearchPage in-page controls into a new styles/search-page.css

Bucket #1 above. The SearchPage component is finally under the 600-line cap and its inner JSX is stable, so an extraction now is low-risk. Expected impact: App.css β‰ˆ βˆ’320 lines β†’ ~1,840 (the controls cluster spans App.css:22–283 plus the scripture sub-cluster near 1011–1037 plus the small drag-overlay rule near 1484).

Step 2 β€” Move SearchPage panel-specific styles into the panel components

Bucket #2 above. Each empty-state panel component (FaceSearchPanel.jsx, ImageUploadPanel.jsx, ScriptureSearchPanel.jsx, SavePersonBanner.jsx) gains its own .css sibling and CSS module import. Expected impact: β‰ˆ βˆ’200 lines β†’ ~1,640 (the face-drop / save-person / image-upload clusters span roughly App.css:858–1037).

Step 3 β€” Promote the .toast, .error-message, .empty-state rules into styles/page-shared.css

Bucket #3. These are the truly shared utilities. Once they're in the shared file, App.css can drop them. Expected impact: β‰ˆ βˆ’60 lines β†’ ~1,580.

Step 4 β€” Sweep the remaining orphans

Bucket #4. Confirm with a per-class JSX grep that every selector in the file has a current consumer before deleting. Expected impact: β‰ˆ βˆ’350 lines β†’ ~1,230.

Step 5 β€” Audit what remains

Whatever survives Step 4 is genuinely shared between the redesign and a legacy page that hasn't been refactored yet. Decide for each remaining rule whether it (a) graduates into design tokens, (b) stays in App.css as a temporary holding area, or (c) is dead and removable.

Ground rules during the sweep

  • Never delete the last selector of a multi-selector list without also moving its rule body somewhere. The bug pattern that reviewer #1 caught on 3ef5161 (orphan trailing-comma silently extending into the next rule) is real β€” every extraction that touches a comma-list must move both the relevant selectors AND the rule body in one operation.
  • Run the iterative orphan-comma sweep after every extraction. Sweeping once isn't always enough β€” removing a trailing-comma orphan can leave the line above it freshly orphaned.
  • Each commit must pass a three-reviewer QC panel (completeness
    • cross-page safety + runtime regression check) before the next step starts.

Final target

App.css removed entirely; everything lives in:

  • styles/design.css β€” tokens + global resets + topbar + buttons
  • styles/clip-grid.css β€” search results layout
  • styles/page-shared.css β€” page-head, card-section, error-banner, toast, empty-state
  • styles/persons-page.css β€” PersonsPage chrome
  • styles/download-page.css β€” DownloadPage chrome
  • styles/search-page.css β€” SearchPage chrome (created in Step 1)
  • components/<Panel>.css β€” panel-specific styles (created in Step 2)

Estimated final state: App.css = 0 lines (file deleted). Cumulative deltas across Steps 1-4: β‰ˆ βˆ’930 lines, leaving ~1,230 lines that need a Step 5 audit before the file can be removed entirely.