From fc982bee1b58a7b778ef3c16bbd9bd381bcd84f0 Mon Sep 17 00:00:00 2001 From: Jake Jarvis Date: Thu, 5 Mar 2026 18:20:25 -0500 Subject: [PATCH] Comprehensive Web Interface Guidelines audit: accessibility, motion, and typography MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add global MotionConfig reducedMotion="user" in root layout - Add color-scheme: dark, theme-color meta, skip-to-main-content link - Add aria-hidden={true} to ~60+ decorative icons across 33 files - Add aria-label to all icon-only buttons (logout, star rating, play, delete, etc.) - Replace transition-all with specific properties (11 files) - Add motion-safe: prefix for CSS hover transforms and animate-pulse - Add prefers-reduced-motion: reduce override in globals.css - Add useReducedMotion guard to status-dot pulsing animation - Fix focus-visible styles on auth form inputs (focus → focus-visible, stronger ring) - Add text-balance to all headings (13 files) - Replace "..." with "…" (U+2026) in all loading/placeholder text - Add autocomplete, spellCheck, role="alert" to auth form - Add aria-label to Switch controls and filmography select - Fix heading hierarchy (h3 → h2 where h2 was skipped) - Add break-words to title overview, overscroll-contain to drawer - Add confirmation dialog for destructive library removal - Add group-focus-within:opacity-100 for keyboard-accessible backup actions - Add role="radio" + aria-checked to star rating buttons - Add aria-label to carousel region, role="img" to TMDB logo SVG - Add text-foreground to native select for dark mode consistency Co-Authored-By: Claude Opus 4.6 --- app/(auth)/register/page.tsx | 4 +- app/(auth)/setup/page.tsx | 26 ++++++----- .../_components/continue-watching-card.tsx | 13 +++--- .../dashboard/_components/feed-section.tsx | 6 ++- .../dashboard/_components/stats-display.tsx | 6 +-- .../dashboard/_components/stats-section.tsx | 4 +- .../dashboard/_components/welcome-header.tsx | 2 +- app/(pages)/explore/filterable-title-row.tsx | 6 ++- app/(pages)/explore/hero-banner.tsx | 11 +++-- app/(pages)/explore/page.tsx | 8 ++-- app/(pages)/explore/title-row.tsx | 4 +- app/(pages)/layout.tsx | 5 ++- .../[id]/_components/filmography-grid.tsx | 9 ++-- .../people/[id]/_components/person-hero.tsx | 6 +-- app/(pages)/people/[id]/not-found.tsx | 6 ++- .../settings/_components/account-section.tsx | 4 +- .../_components/backup-restore-section.tsx | 9 ++-- .../_components/backup-schedule-section.tsx | 21 +++++++-- .../settings/_components/backup-section.tsx | 28 +++++++++--- .../_components/integrations-section.tsx | 5 ++- .../_components/registration-section.tsx | 3 +- .../settings/_components/settings-shell.tsx | 6 ++- .../_components/system-health-section.tsx | 32 +++++++++++--- .../_components/update-check-section.tsx | 6 ++- .../settings/_components/webhook-card.tsx | 44 +++++++++++++++---- app/(pages)/settings/page.tsx | 15 +++++-- .../titles/[id]/_components/cast-carousel.tsx | 9 ++-- .../[id]/_components/recommendations-grid.tsx | 2 +- .../titles/[id]/_components/star-rating.tsx | 3 ++ .../titles/[id]/_components/status-button.tsx | 12 +++-- .../titles/[id]/_components/title-actions.tsx | 2 +- .../[id]/_components/title-availability.tsx | 6 +-- .../titles/[id]/_components/title-hero.tsx | 5 ++- .../titles/[id]/_components/title-seasons.tsx | 15 +++++-- .../[id]/_components/trailer-dialog.tsx | 5 ++- app/globals.css | 7 +++ app/layout.tsx | 15 ++++++- components/auth-form.tsx | 25 +++++++---- components/command-palette.tsx | 34 ++++++++++---- components/landing-page.tsx | 6 +-- components/nav-bar.tsx | 15 ++++--- components/status-dot.tsx | 16 +++++-- components/title-card.tsx | 9 ++-- components/tmdb-logo.tsx | 1 + components/ui/button.tsx | 2 +- components/ui/carousel.tsx | 1 + components/ui/drawer.tsx | 2 +- components/ui/native-select.tsx | 2 +- components/ui/tabs.tsx | 2 +- 49 files changed, 342 insertions(+), 143 deletions(-) diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index 7084a17..bc681ee 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -22,10 +22,10 @@ export default async function RegisterPage() {
- +
-

+

Registration Closed

diff --git a/app/(auth)/setup/page.tsx b/app/(auth)/setup/page.tsx index 38bf302..48a710c 100644 --- a/app/(auth)/setup/page.tsx +++ b/app/(auth)/setup/page.tsx @@ -24,7 +24,7 @@ const steps = [ className="inline-flex items-center gap-1 font-medium text-primary underline decoration-primary/30 underline-offset-2 transition-colors hover:decoration-primary" > themoviedb.org - + {" "} and sign up for a free account. @@ -43,7 +43,7 @@ const steps = [ className="inline-flex items-center gap-1 font-medium text-primary underline decoration-primary/30 underline-offset-2 transition-colors hover:decoration-primary" > Settings → API - + {" "} and request an API key. Choose “Developer” when asked. You need the{" "} @@ -133,10 +133,10 @@ export default function SetupPage() { {/* Header */}

- + Setup required
-

+

Connect to TMDB

@@ -164,7 +164,7 @@ export default function SetupPage() {

-

{step.title}

+

{step.title}

{step.description}

@@ -188,12 +188,15 @@ export default function SetupPage() { > {copiedIdx === idx ? ( <> - + Copied ) : ( <> - + Copy )} @@ -217,14 +220,17 @@ export default function SetupPage() { {configured === true ? (
- +

TMDB API key detected

- Redirecting you to Sofa... + Redirecting you to Sofa…

@@ -247,7 +253,7 @@ export default function SetupPage() { {checking ? ( <> - Checking... + Checking… ) : ( "Check configuration" diff --git a/app/(pages)/dashboard/_components/continue-watching-card.tsx b/app/(pages)/dashboard/_components/continue-watching-card.tsx index 1f71dcd..1b2bf6d 100644 --- a/app/(pages)/dashboard/_components/continue-watching-card.tsx +++ b/app/(pages)/dashboard/_components/continue-watching-card.tsx @@ -44,18 +44,21 @@ export function ContinueWatchingCard({ alt={item.nextEpisode?.name ?? item.title.title} width={500} height={281} - className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" + className="h-full w-full object-cover motion-safe:transition-transform motion-safe:duration-300 motion-safe:group-hover:scale-105" /> ) : (
- +
)}
{item.nextEpisode && (

- + Up next

@@ -76,13 +79,13 @@ export function ContinueWatchingCard({

- +
{progress > 0 && (
diff --git a/app/(pages)/dashboard/_components/feed-section.tsx b/app/(pages)/dashboard/_components/feed-section.tsx index 614bda4..9bdaa38 100644 --- a/app/(pages)/dashboard/_components/feed-section.tsx +++ b/app/(pages)/dashboard/_components/feed-section.tsx @@ -14,8 +14,10 @@ export function FeedSection({ return (
- {icon} -

{title}

+ {icon} +

+ {title} +

{children}
diff --git a/app/(pages)/dashboard/_components/stats-display.tsx b/app/(pages)/dashboard/_components/stats-display.tsx index 98ba36e..517a00d 100644 --- a/app/(pages)/dashboard/_components/stats-display.tsx +++ b/app/(pages)/dashboard/_components/stats-display.tsx @@ -67,7 +67,7 @@ function StatCard({
- +
{label} @@ -75,7 +75,7 @@ function StatCard({

{value}

@@ -98,7 +98,7 @@ function PeriodSelector({ {periodLabels[period]} - +
- +

Your library is empty

@@ -28,7 +28,7 @@ export async function StatsSection({ userId }: { userId: string }) {
Start exploring diff --git a/app/(pages)/dashboard/_components/welcome-header.tsx b/app/(pages)/dashboard/_components/welcome-header.tsx index 3c4691a..c94f8fa 100644 --- a/app/(pages)/dashboard/_components/welcome-header.tsx +++ b/app/(pages)/dashboard/_components/welcome-header.tsx @@ -1,7 +1,7 @@ export function WelcomeHeader({ name }: { name?: string | null }) { return (
-

+

Welcome back{name ? `, ${name}` : ""}

diff --git a/app/(pages)/explore/filterable-title-row.tsx b/app/(pages)/explore/filterable-title-row.tsx index e4c2539..bd51d9a 100644 --- a/app/(pages)/explore/filterable-title-row.tsx +++ b/app/(pages)/explore/filterable-title-row.tsx @@ -106,7 +106,9 @@ function FilterableTitleRowInner({

{icon} -

{heading}

+

+ {heading} +

{/* Genre chips */} @@ -116,7 +118,7 @@ function FilterableTitleRowInner({ key={genre.id} type="button" onClick={() => toggleGenre(genre.id)} - className={`shrink-0 rounded-full border px-3 py-1 text-xs font-medium transition-all ${ + className={`shrink-0 rounded-full border px-3 py-1 text-xs font-medium transition-colors ${ selectedGenre === genre.id ? "border-primary bg-primary/10 text-primary" : "border-border/50 bg-card/50 text-muted-foreground hover:border-primary/20 hover:text-foreground" diff --git a/app/(pages)/explore/hero-banner.tsx b/app/(pages)/explore/hero-banner.tsx index 1f1e723..74836bd 100644 --- a/app/(pages)/explore/hero-banner.tsx +++ b/app/(pages)/explore/hero-banner.tsx @@ -56,7 +56,10 @@ export function HeroBanner({ {voteAverage > 0 && ( - + {voteAverage.toFixed(1)} )} @@ -65,7 +68,7 @@ export function HeroBanner({
-

+

{title}

@@ -74,9 +77,9 @@ export function HeroBanner({

- + Add to Library
diff --git a/app/(pages)/explore/page.tsx b/app/(pages)/explore/page.tsx index c8b1dad..4a067c7 100644 --- a/app/(pages)/explore/page.tsx +++ b/app/(pages)/explore/page.tsx @@ -91,7 +91,7 @@ export default async function ExplorePage() { } + icon={} items={trendingItems.slice(0, 20)} userStatuses={userStatuses} episodeProgress={episodeProgress} @@ -99,7 +99,7 @@ export default async function ExplorePage() { } + icon={} mediaType="movie" defaultItems={popularMovieItems.slice(0, 20)} genres={movieGenres.genres} @@ -109,7 +109,9 @@ export default async function ExplorePage() { } + icon={ + + } mediaType="tv" defaultItems={popularTvItems.slice(0, 20)} genres={tvGenres.genres} diff --git a/app/(pages)/explore/title-row.tsx b/app/(pages)/explore/title-row.tsx index 8ef11cd..13f237b 100644 --- a/app/(pages)/explore/title-row.tsx +++ b/app/(pages)/explore/title-row.tsx @@ -38,7 +38,9 @@ export function TitleRow({
{icon} -

{heading}

+

+ {heading} +

{/* Ambient glow */}
-
+
{children}
diff --git a/app/(pages)/people/[id]/_components/filmography-grid.tsx b/app/(pages)/people/[id]/_components/filmography-grid.tsx index 490a10d..3efef65 100644 --- a/app/(pages)/people/[id]/_components/filmography-grid.tsx +++ b/app/(pages)/people/[id]/_components/filmography-grid.tsx @@ -53,8 +53,10 @@ export function FilmographyGrid({ credits }: FilmographyGridProps) {
- -

Filmography

+ +

+ Filmography +

({filtered.length}) @@ -63,6 +65,7 @@ export function FilmographyGrid({ credits }: FilmographyGridProps) {