/** * The design system's component vocabulary, in one place. Every other file * composes these rather than re-spelling the class strings. */ import type { ReactNode } from "react"; /** * Every control in the app is this tall. Width still grows with the label — * only the height is fixed, so a row of mixed buttons lines up. */ export const CONTROL_H = "h-[30px]"; /** Square version, for icon-only buttons. */ export const ICON_BTN = `grid ${CONTROL_H} w-[30px] shrink-0 place-items-center rounded-lg cursor-pointer ` + "disabled:cursor-not-allowed disabled:opacity-40"; export const HEADING = "text-[11px] font-bold uppercase tracking-widest text-slate-500 dark:text-slate-400"; export const LABEL = "text-[12px] text-slate-500 dark:text-slate-400"; export const HELP = "text-[11px] leading-snug text-slate-500 dark:text-slate-400"; export const PANEL = "bg-white dark:bg-slate-900 border-slate-300 dark:border-slate-800"; export const SECTION = "border-b border-slate-200 px-4 py-4 dark:border-slate-800"; export const INPUT = `w-full ${CONTROL_H} rounded-lg border border-slate-300 bg-white px-3 text-[12px] outline-none ` + "placeholder:text-slate-400 dark:border-slate-700 dark:bg-slate-800 dark:placeholder:text-slate-500"; export const SUBPANEL = "rounded-lg bg-slate-50 p-3 dark:bg-slate-800/50"; const BTN_BASE = `inline-flex ${CONTROL_H} items-center justify-center rounded-lg text-[12px] ` + "disabled:cursor-not-allowed"; export const BTN = `${BTN_BASE} border border-slate-300 px-2.5 font-medium ` + "hover:border-sky-500 hover:text-sky-600 disabled:opacity-40 " + "dark:border-slate-700 dark:hover:border-sky-500 dark:hover:text-sky-400"; export const BTN_PRIMARY = `${BTN_BASE} bg-sky-500 px-3 font-semibold text-white ` + "hover:bg-sky-400 disabled:opacity-40"; export const BTN_DANGER = `${BTN_BASE} bg-red-600 px-3 font-semibold text-white hover:bg-red-500`; /** Header actions: quieter than a secondary button, still a real target. */ export const BTN_CHROME = `inline-flex ${CONTROL_H} items-center rounded-lg px-2 text-[12px] font-medium text-slate-500 ` + "hover:bg-slate-100 hover:text-slate-900 disabled:opacity-40 disabled:hover:bg-transparent " + "dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-white"; /** Reads as a link; sits at the edge of a group. */ export const BTN_QUIET = "text-[11px] text-slate-500 underline underline-offset-2 " + "hover:text-sky-600 dark:text-slate-400 dark:hover:text-sky-400"; /** The only spinning thing in the app; used where a wait has no known length. */ export function Spinner({ className = "size-4" }: { className?: string }) { return ( ); } export function SectionHeading({ step, children, }: { step?: number; children: ReactNode; }) { return (