freezmo/components/SegmentedPill.tsx
Martin Dvorak 7da308fa1e Aero button glass + homework report control; reduced-motion sheet glitch
Buttons: the recipe's thin veils let btn-secondary/btn-ghost/glass-pill
blend into the grass and sky - they now carry a stronger pale-glass veil
(the Ask-pill look) over the blurred scene, ghosts plus a rim and
full-contrast text. Homework make/check results gained the same red "!"
report control as the feeds and the guide (a synthetic short-answer
activity maps into the report flow; pinned in phase3).

Reduced motion: the manual toggle only fed framer's MotionConfig, which
strips transform animations - while components branched on the OS-only
useReducedMotion - so translucent sheets still spring-faded their opacity
in over the page (the "ugly visual glitch with the settings panel"). All
16 components now read useAppReducedMotion() (OS or toggle), and the
settings sheet/scrim and the library's phone sheet go instant. Verified
frame by frame: opacity 1.00 from the first frame.

Contrast 0 findings, axe clean; mobile 126/126, chat 102/102, providers
20/20, wizard 95/95, phase3 31/31, bubbles 34/34, i18n 13/13, native
33/33. Version 0.5.4, SW -> freezmo-v76.
2026-10-14 09:19:54 +00:00

37 lines
1,023 B
TypeScript

"use client";
import { motion } from "framer-motion";
import { useAppReducedMotion as useReducedMotion } from "@/lib/motion-pref";
import { springSnappy } from "@/lib/motion";
import { cn } from "@/lib/utils";
/**
* The gliding highlight shared by segmented controls and pickers. One
* `layoutId` per control makes the pill slide between its items; under
* reduced motion it simply appears in place.
*/
export function SegmentedPill({
layoutId,
className,
plain,
}: {
layoutId: string;
className?: string;
/** Skip the ice gradient (for pickers that keep their own selected look). */
plain?: boolean;
}) {
const reduceMotion = useReducedMotion();
const base = plain ? "slide-pill" : "slide-pill grad-ice shadow-[0_6px_16px_-8px_rgb(56_189_248/0.7)]";
if (reduceMotion) {
return <span aria-hidden className={cn(base, className)} />;
}
return (
<motion.span
aria-hidden
layoutId={layoutId}
transition={springSnappy}
className={cn(base, className)}
/>
);
}