trails/packages/ui/src/IconButton.tsx
Ullrich Schäfer 1bd5f18a33
feat(ui): topbar primitives — IconButton, SegmentedControl, Avatar
Adds the shared primitives the planner topbar redesign needs, on the
design-system tokens:

- IconButton — icon-only button (undo/redo), ghost/secondary variants,
  requires an accessible label.
- SegmentedControl — generic single-select toggle (color mode
  Plain/Elevation/Surface), radiogroup semantics.
- Avatar — colored initial circle for participants, per-user color with
  an accent fallback.

Co-located jsdom unit tests for each (roles, aria-checked, onChange,
disabled, color fallback). Extends the dev-only /dev/ui gallery with
sections for all three.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:43:14 +02:00

50 lines
1.3 KiB
TypeScript

import type { ButtonHTMLAttributes } from "react";
import { cn } from "./cn.ts";
export type IconButtonVariant = "ghost" | "secondary";
export type IconButtonSize = "sm" | "md";
export interface IconButtonProps
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "aria-label"> {
/** Required accessible name — the button has no visible text. */
label: string;
variant?: IconButtonVariant;
size?: IconButtonSize;
}
const base =
"inline-flex items-center justify-center rounded-md transition-colors " +
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent " +
"focus-visible:ring-offset-1 focus-visible:ring-offset-bg " +
"disabled:pointer-events-none disabled:opacity-30";
const variants: Record<IconButtonVariant, string> = {
ghost: "text-text-md hover:bg-bg-subtle hover:text-text-hi",
secondary:
"border border-border bg-bg-raised text-text-hi hover:bg-bg-subtle",
};
const sizes: Record<IconButtonSize, string> = {
sm: "h-7 w-7",
md: "h-9 w-9",
};
export function IconButton({
label,
variant = "ghost",
size = "md",
className,
type = "button",
title,
...props
}: IconButtonProps) {
return (
<button
type={type}
aria-label={label}
title={title ?? label}
className={cn(base, variants[variant], sizes[size], className)}
{...props}
/>
);
}