fix(web): preset cards use the design-system animated Card (motion + material)

The preset options were raw <button>s — flat, no motion/material — unlike the rest of
the console. They now render as the `interactive` AnimatedCard (motion hover + specular
material, consistent with every other card), keyboard-accessible (role=button + Enter/
Space), with a 2px primary ring for the active one and a proper disabled state for
gaming-rig.

web tsc + vite build + biome-lint green; deployed on .21.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 15:18:24 +00:00
parent 2e43fcc27c
commit c1acfe8b85
+24 -13
View File
@@ -27,6 +27,7 @@ import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
import { m } from "@/paraglide/messages"; import { m } from "@/paraglide/messages";
/** /**
@@ -162,20 +163,30 @@ const DisplayForm: FC<{
const summary = id === "custom" ? m.display_custom_desc() : p?.summary; const summary = id === "custom" ? m.display_custom_desc() : p?.summary;
const selected = preset === id; const selected = preset === id;
const soon = DISABLED_PRESETS.has(id); const soon = DISABLED_PRESETS.has(id);
const cls = [ const disabled = busy || soon;
"flex h-full flex-col rounded-lg border p-4 text-left transition-colors", const pick = () => {
selected if (!disabled) pickPreset(id);
? "border-primary ring-1 ring-primary" };
: "hover:border-primary/40 hover:bg-muted/50",
soon ? "opacity-60" : "",
].join(" ");
return ( return (
<button <Card
key={id} key={id}
type="button" interactive
disabled={busy || soon} role="button"
onClick={() => pickPreset(id)} tabIndex={disabled ? -1 : 0}
className={cls} aria-pressed={selected}
aria-disabled={disabled || undefined}
onClick={pick}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
pick();
}
}}
className={cn(
"flex h-full flex-col p-4",
disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer",
selected && "ring-2 ring-primary",
)}
> >
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<span className="text-base font-semibold"> <span className="text-base font-semibold">
@@ -201,7 +212,7 @@ const DisplayForm: FC<{
<Badge variant="outline">{tr(IDENTITY_LABEL, fields.identity)}</Badge> <Badge variant="outline">{tr(IDENTITY_LABEL, fields.identity)}</Badge>
</div> </div>
)} )}
</button> </Card>
); );
})} })}
</div> </div>