Skip to content

web UI: style filters and favorites screens #40

Closed

Description

@TSVRN9

What to build

Style src/routes/filters/+page.svelte (allergen/diet preferences) and src/routes/favorites/+page.svelte (binary dish/hall favorites list). Reuse #35's tokens.

Filters are toggle/checkbox-heavy — give them clear on/off states. Favorites is a simple list view, similar treatment to other list screens in the app for consistency.

Acceptance criteria

Blocked by

#35 (Tailwind + design tokens + shared layout)

Activity

  1. TSVRN9 commented on Aug 19, 2026

    @TSVRN9
    OwnerAuthor

    Closes #40

    What changed

    Restyled web/src/routes/filters/+page.svelte and web/src/routes/favorites/+page.svelte using only the landed component classes/tokens from web/src/app.css (.page-title, .section-title, .label-rule, .card, .empty-state, .btn/.btn-primary/.btn-ghost/.btn-sm, .badge) plus Tailwind utilities built from the existing @theme tokens. No app.css edits, no new colors.

    • Filters: page-title header, section-title'd "Avoid allergens"/"Require diet tags" sections, .empty-state for the no-data case, .btn-primary Save, .badge for the "Saved" status. Each allergen/diet-tag checkbox is wrapped in a chip-style <label> that fills maroon (bg-maroon-600 text-paper-50) when checked vs. an outlined border-ink-900/25 chip when not — the issue calls out that filters need "clear on/off states," so this is a real visual, not just decoration on the same 1px checkbox.
    • Favorites: page-title header, section-title'd "Dishes"/"Dining Halls", .card rows matching the treatment already established on / and /halls/[slug] (same list-row pattern), .empty-state for empty lists, .btn-ghost btn-sm Remove (per app.css's own stated intent: "low-stakes/destructive-lite").

    Native checkbox/button roles and accessible names are unchanged: getByRole("checkbox", { name: "Milk" }), the favorite star's glyph-only text-content contract on the hall/home pages, and the Remove button's accessible name all still resolve exactly as before.

    Both are device-only per CLAUDE.md's data-residency table — FoodPreferences in localStorage, Favorites in IndexedDB via IndexedDbFavoritesStorage. No Supabase calls added, no auth gate.

    Red/green evidence (strict TDD)

    Extended web/e2e/filters.spec.ts and web/e2e/favorites.spec.ts write-first with new assertions for the new visual behavior, using toHaveCSS (not class-name matching, so the test verifies an actual rendered difference, not a string in an attribute).

    Red — ran PORT=4303 npx playwright test filters.spec.ts favorites.spec.ts against the unstyled pages, before any .svelte changes:

    1) favorites.spec.ts › favoriting a dish ... Error: expect(received).not.toBe(expected)
       Expected: not "rgba(0, 0, 0, 0)"
       > 62 | 	expect(rowBg).not.toBe("rgba(0, 0, 0, 0)");
    
    2) filters.spec.ts › setting an allergen filter ... Error: expect(locator).not.toHaveCSS(expected) failed
       Locator:  getByRole('checkbox', { name: 'Milk' }).locator('..')
       Expected: not "rgba(0, 0, 0, 0)"
       Received: "rgba(0, 0, 0, 0)"
       > 84 | 	await expect(milkLabel).not.toHaveCSS("background-color", milkBgBeforeCheck);
    
    2 failed
    

    Green — after restyling both pages, same command:

    Running 2 tests using 2 workers
      2 passed (8.6s)
    

    Verification

    • PORT=4303 npx playwright test (full suite, all 17 specs, including sibling-owned shell.spec.ts, halls-menu.spec.ts, rank.spec.ts, vertical-slice.spec.ts): 17 passed, confirming this restyle didn't regress shell.spec.ts's proveHydrated() (which also depends on the favorite star's text-content contract) or any concurrently-landing sibling work.
    • pnpm --filter web check: 0 errors, 0 warnings (396 files).
    • pnpm --filter web build (after cp web/.env.example web/.env): succeeds; only the expected pre-existing adapter-auto "could not detect a supported production environment" notice, not an error.
    • Eyeballed both routes live against the dev server (PORT=4303 vite dev, screenshotted via Playwright's own chromium since no system Chrome was available in this sandbox): confirmed the maroon page-title/label-rule header, chip-style checkboxes with a visibly filled checked state, and the dashed .empty-state panel on /favorites all render as intended.

    Acceptance criteria (from #40)

    • Filters and favorites screens fully restyled using web UI: install Tailwind CSS + design tokens + styled shared layout #35's tokens
    • npx playwright test passes unchanged — extended, not modified in place; all pre-existing assertions (checkbox accessible names, Saved status text, star button text-content contract, Remove button name) still pass as written
    • pnpm --filter web check clean, CI passes (checked locally; CI to confirm)

    Scope

    Touched only: web/src/routes/filters/+page.svelte, web/src/routes/favorites/+page.svelte, web/e2e/filters.spec.ts, web/e2e/favorites.spec.ts. No app.css, no +layout.svelte, no other route dirs, no other e2e specs, no halls/ files.

    Not merging — leaving for review per dispatch instructions.

  2. TSVRN9 commented on Aug 19, 2026

    @TSVRN9
    OwnerAuthor

    PR: #57 (branch ux/filters-favorites, not merged — left for review).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions