feat: add button press feedback
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
const css = readFileSync("src/index.css", "utf8");
|
||||
const sharedButton = readFileSync("src/components/ui/button.tsx", "utf8");
|
||||
|
||||
describe("button press feedback", () => {
|
||||
test("gives every enabled button a short physical press state", () => {
|
||||
expect(css).toMatch(/button:not\(:disabled\)\s*\{[^}]*transition-duration:\s*140ms;/s);
|
||||
expect(css).toMatch(
|
||||
/button:not\(:disabled\):active\s*\{[^}]*transform:\s*scale\(0\.97\);[^}]*filter:\s*brightness\(0\.94\);/s,
|
||||
);
|
||||
expect(css).toContain("cubic-bezier(0.22, 1, 0.36, 1)");
|
||||
expect(sharedButton).not.toContain("active:not-aria-[haspopup]:translate-y-px");
|
||||
});
|
||||
|
||||
test("keeps non-kinetic feedback when reduced motion is requested", () => {
|
||||
expect(css).toMatch(
|
||||
/@media \(prefers-reduced-motion: reduce\)\s*\{[\s\S]*?button:not\(:disabled\)\s*\{[^}]*transition-duration:\s*0\.01ms;/,
|
||||
);
|
||||
expect(css).toMatch(
|
||||
/@media \(prefers-reduced-motion: reduce\)\s*\{[\s\S]*?button:not\(:disabled\):active\s*\{[^}]*transform:\s*none;/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-semibold tracking-[0.005em] whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 focus-visible:ring-ring/25 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-semibold tracking-[0.005em] whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 focus-visible:ring-ring/25 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -95,6 +95,33 @@
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
|
||||
/* A click should read as a physical state change, not only as a hover ending.
|
||||
Keep the response short and compositor-friendly so repeated actions stay fast. */
|
||||
button:not(:disabled) {
|
||||
transform-origin: center;
|
||||
transition-property: color, background-color, border-color, box-shadow, filter, transform;
|
||||
transition-duration: 140ms;
|
||||
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
button:not(:disabled):active {
|
||||
transform: scale(0.97);
|
||||
filter: brightness(0.94);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
button:not(:disabled) {
|
||||
transition-duration: 0.01ms;
|
||||
}
|
||||
|
||||
button:not(:disabled):active {
|
||||
transform: none;
|
||||
filter: brightness(0.9);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
@apply font-sans;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user