From fa7869eaf0afface90d8f70b0df69e76f2f34572 Mon Sep 17 00:00:00 2001 From: TSVRN9 <66744142+TSVRN9@users.noreply.github.com> Date: Wed, 19 Aug 2026 09:55:22 -0400 Subject: [PATCH] web UI: style filters and favorites screens (#40) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restyle /filters and /favorites with #35's design tokens: page-title + label-rule headers, section-title for subsections, .card rows on favorites (matching the halls/home list treatment), .empty-state for empty lists, .btn-primary Save and .btn-ghost Remove, and chip-style checkboxes on /filters that fill maroon when checked for a clear on/off state. Extends filters.spec.ts and favorites.spec.ts (write-first, confirmed red against the unstyled pages, green after) to assert the new visual behavior directly via toHaveCSS rather than class names: a checked allergen chip's background actually changes, favorite rows get a card surface distinct from the page background, and the empty state renders with a dashed border. Native checkbox/button roles and accessible names are unchanged, so the existing assertions (getByRole("checkbox", { name: "Milk" }), the favorite star's text-content contract, Remove's accessible name) keep passing as-is. Filters and favorites remain device-only (localStorage/IndexedDB) — no Supabase calls, no auth gate. --- web/e2e/favorites.spec.ts | 14 ++++ web/e2e/filters.spec.ts | 9 +++ web/src/routes/favorites/+page.svelte | 65 ++++++++++++------ web/src/routes/filters/+page.svelte | 97 ++++++++++++++++++--------- 4 files changed, 132 insertions(+), 53 deletions(-) diff --git a/web/e2e/favorites.spec.ts b/web/e2e/favorites.spec.ts index 0f13c6f3..55649948 100644 --- a/web/e2e/favorites.spec.ts +++ b/web/e2e/favorites.spec.ts @@ -54,7 +54,21 @@ test("favoriting a dish from the menu shows it on /favorites, un-favoriting remo const favoriteRow = page.getByRole("listitem").filter({ hasText: "French Toast" }); await expect(favoriteRow).toBeVisible(); + // #40: favorites list rows need the same card treatment as the other list screens (dining + // halls, hall menu) — a non-transparent surface visually distinct from the page background, + // not the page's `
` color leaking straight through into every row. + const pageBg = await page.evaluate(() => getComputedStyle(document.body).backgroundColor); + const rowBg = await favoriteRow.evaluate((el) => getComputedStyle(el).backgroundColor); + expect(rowBg).not.toBe("rgba(0, 0, 0, 0)"); + expect(rowBg).not.toBe(pageBg); + await favoriteRow.getByRole("button", { name: "Remove" }).click(); await expect(page.getByText("No favorite dishes yet.")).toBeVisible(); await expect(favoriteRow).toHaveCount(0); + + // #40: the empty state needs the shared dashed-outline `.empty-state` treatment used elsewhere + // in the app, not a bare unstyled paragraph — checked via the one CSS property that treatment + // actually produces, not by naming the class. + const emptyState = page.getByText("No favorite dishes yet.").locator("xpath=.."); + await expect(emptyState).toHaveCSS("border-style", "dashed"); }); diff --git a/web/e2e/filters.spec.ts b/web/e2e/filters.spec.ts index 32ffef9a..6da05a44 100644 --- a/web/e2e/filters.spec.ts +++ b/web/e2e/filters.spec.ts @@ -73,7 +73,16 @@ test("setting an allergen filter hides a conflicting dish and keeps a non-confli await page.getByRole("link", { name: "Filters" }).click(); + // #40: filters are toggle/checkbox-heavy and need a clear on/off state — a checked filter must + // be visually distinct from an unchecked one, not just carry a different checkbox attribute. + // Scoped through the checkbox's own accessible name (not a listitem text filter) so this stays + // unambiguous even if another allergen option's name contains "Milk" as a substring. + const milkLabel = page.getByRole("checkbox", { name: "Milk" }).locator("xpath=.."); + const milkBgBeforeCheck = await milkLabel.evaluate((el) => getComputedStyle(el).backgroundColor); + await page.getByRole("checkbox", { name: "Milk" }).check(); + await expect(milkLabel).not.toHaveCSS("background-color", milkBgBeforeCheck); + await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByRole("status")).toHaveText("Saved"); diff --git a/web/src/routes/favorites/+page.svelte b/web/src/routes/favorites/+page.svelte index 8dc83cbc..ba695116 100644 --- a/web/src/routes/favorites/+page.svelte +++ b/web/src/routes/favorites/+page.svelte @@ -30,24 +30,47 @@ } -No favorite dishes yet.
-{/if} -No favorite dining halls yet.
-{/if} -No favorite dishes yet.
+No favorite dining halls yet.
+Applied to menus on the dining hall pages. Stored on this device only.
++ Applied to menus on the dining hall pages. Stored on this device only. +
+Saved
{/if} - -No allergen data found on today's menus.
+{#if saved} +Saved
{/if} -No diet-tag data found on today's menus.
-{/if} -No allergen data found on today's menus.
+No diet-tag data found on today's menus.
+