Files
sofa/app/(pages)/titles/[id]/_components/trailer-dialog.tsx
T
jakeandClaude Opus 4.6 fc982bee1b Comprehensive Web Interface Guidelines audit: accessibility, motion, and typography
- 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 <noreply@anthropic.com>
2026-03-05 18:20:25 -05:00

68 lines
2.2 KiB
TypeScript

"use client";
import { IconPlayerPlayFilled } from "@tabler/icons-react";
import dynamic from "next/dynamic";
import { useState } from "react";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
const YoutubeVideo = dynamic(() => import("youtube-video-element/react"), {
ssr: false,
});
const MediaThemeSutro = dynamic(() => import("@player.style/sutro/react"), {
ssr: false,
});
export function TrailerDialog({
videoKey,
variant = "badge",
}: {
videoKey: string;
variant?: "badge" | "backdrop";
}) {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
{variant === "backdrop" ? (
<button
type="button"
aria-label="Play trailer"
onClick={() => setOpen(true)}
className="group flex size-12 items-center justify-center rounded-full bg-white/10 ring-1 ring-white/20 backdrop-blur-md transition-[transform,background-color,box-shadow] duration-300 hover:scale-105 hover:bg-white/20 hover:ring-white/30 active:scale-100 sm:size-16"
>
<IconPlayerPlayFilled className="size-6 text-white drop-shadow-lg sm:size-8" />
</button>
) : (
<button
type="button"
onClick={() => setOpen(true)}
className="inline-flex h-5 items-center gap-1 rounded border border-border/50 px-2 text-xs text-muted-foreground transition-colors hover:border-border hover:text-foreground"
>
<IconPlayerPlayFilled aria-hidden={true} className="h-2.5 w-2.5" />
Trailer
</button>
)}
<DialogContent className="sm:max-w-4xl p-0 overflow-hidden bg-black border-white/10">
<DialogTitle className="sr-only">Trailer</DialogTitle>
<div className="aspect-video w-full">
<MediaThemeSutro
style={
{
width: "100%",
height: "100%",
} as React.CSSProperties
}
>
<YoutubeVideo
slot="media"
src={`https://www.youtube-nocookie.com/watch?v=${videoKey}`}
playsInline
crossOrigin="anonymous"
/>
</MediaThemeSutro>
</div>
</DialogContent>
</Dialog>
);
}