Skip to content
50 linesCodeBlameRaw
1import { type VariantProps, cva } from "class-variance-authority";
2import { Slot } from "radix-ui";
3import type { ComponentProps } from "react";
4
5import { cn } from "../../lib/cn";
6
7// shadcn/ui's button, styled with g1t's tokens. `asChild` puts its look on
8// a <Link> or a menu's trigger. The older `Button` in ./index stays until
9// its callers move here.
10//
11// Sizes: `default` 36px, `sm` 32px, `lg` 40px; the icon sizes are squares
12// of the same. `bar` is the top bar's: 32px on a computer, 44px to a finger
13// on a phone, and `icon-bar` its square.
14
15export const buttonVariants = cva(
16 "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap outline-none transition-[color,background-color,border-color,box-shadow] focus-visible:ring-2 focus-visible:ring-accent disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
17 {
18 variants: {
19 variant: {
20 default: "bg-fg text-bg hover:bg-fg-hover",
21 accent: "bg-accent text-bg hover:bg-accent-hover",
22 outline: "border border-line bg-bg text-fg/90 hover:border-line-strong hover:bg-surface hover:text-fg",
23 secondary: "bg-raised text-fg hover:bg-raised/80",
24 ghost: "text-muted hover:bg-raised hover:text-fg data-[state=open]:bg-raised data-[state=open]:text-fg aria-pressed:bg-raised aria-pressed:text-fg",
25 // The accent as a wash: the one tinted control in a row, such as Ask g1t.
26 soft: "bg-accent/10 text-fg ring-1 ring-accent/20 ring-inset hover:bg-accent/15 hover:ring-accent/35",
27 destructive: "border border-danger/40 text-danger hover:border-danger hover:bg-danger/10",
28 link: "text-accent underline-offset-4 hover:underline",
29 },
30 size: {
31 default: "h-9 px-3.5",
32 sm: "h-8 gap-1.5 px-2.5 text-[0.8125rem]",
33 lg: "h-10 px-5",
34 bar: "h-8 gap-1.5 px-2 text-[0.8125rem] max-md:h-11 max-md:min-w-11",
35 icon: "size-9",
36 "icon-sm": "size-8",
37 "icon-lg": "size-10",
38 "icon-bar": "size-8 max-md:size-11",
39 },
40 },
41 defaultVariants: { variant: "default", size: "default" },
42 },
43);
44
45export type ButtonProps = ComponentProps<"button"> & VariantProps<typeof buttonVariants> & { asChild?: boolean };
46
47export function Button({ className, variant, size, asChild = false, ...props }: ButtonProps) {
48 const Comp = asChild ? Slot.Root : "button";
49 return <Comp data-slot="button" className={cn(buttonVariants({ variant, size }), className)} {...props} />;
50}