Button
ActionsThe three-weight button set — a solid primary, an outlined secondary and the clay accent — sized to sit on a line of text.
Preview
Controls
Notes
Three variants is the whole set on purpose. A fourth almost always means the page is asking the reader to make a decision it should have made for them.
The outline variant borders on --control-border, not --rule. Rules are decorative hairlines; anything you can click owes 3:1 contrast against the page under WCAG SC 1.4.11, and --rule does not clear it.
There is no disabled-with-tooltip pattern here. A disabled control that will not say why it is disabled is a dead end — prefer leaving it enabled and explaining the failure after the click.
Source
ShowHide
import { cn } from "@/lib/cn";
const base =
"inline-flex items-center justify-center gap-2 rounded-full font-medium transition-all disabled:pointer-events-none disabled:opacity-45";
const variants = {
primary: "bg-ink text-paper hover:opacity-85",
// Borders on --control-border, not --rule: anything clickable owes 3:1
// against the page (SC 1.4.11) and the decorative hairline does not clear it.
secondary: "border border-control-border text-ink hover:border-ink hover:bg-paper-sunken",
accent: "bg-accent text-on-accent hover:bg-accent-hover",
} as const;
const sizes = {
sm: "px-4 py-2 text-[13px]",
md: "px-5 py-2.5 text-sm",
lg: "px-6 py-3 text-base",
} as const;
export type ButtonVariant = keyof typeof variants;
export type ButtonSize = keyof typeof sizes;
export function Button({
variant = "primary",
size = "md",
className,
children,
...rest
}: {
variant?: ButtonVariant;
size?: ButtonSize;
} & React.ComponentProps<"button">) {
return (
<button className={cn(base, variants[variant], sizes[size], className)} {...rest}>
{children}
</button>
);
}
/** Trailing arrow. Decorative — the label already says where you are going. */
export function Arrow({ className }: { className?: string }) {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
aria-hidden="true"
className={className}
>
<path
d="M3 11L11 3M11 3H5M11 3v6"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default function Demo({
variant = "primary",
size = "md",
label = "Start a conversation",
arrow = true,
disabled = false,
}: {
variant?: ButtonVariant;
size?: ButtonSize;
label?: string;
arrow?: boolean;
disabled?: boolean;
}) {
return (
<Button variant={variant} size={size} disabled={disabled}>
{label}
{arrow && <Arrow />}
</Button>
);
}
Next
FieldNext step
Tell me where the business is losing time.
No pitch deck and no discovery-call funnel. Describe the bottleneck and I will tell you plainly whether it is worth building something for, and roughly what that would take.