refactor(frontend): share Tailwind dialog and form controls

This commit is contained in:
2026-10-07 09:55:57 -03:00
parent e9808cb12b
commit 264d7905c6
4 changed files with 235 additions and 109 deletions
+81
View File
@@ -0,0 +1,81 @@
import type { ComponentProps } from "react";
export function Button({
variant = "secondary",
className = "",
type = "button",
...props
}: ComponentProps<"button"> & { variant?: "primary" | "secondary" }) {
return (
<button
{...props}
type={type}
className={`rounded-control border border-solid px-4 py-2.5 text-[14px] font-semibold transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent enabled:cursor-pointer enabled:active:translate-y-px disabled:cursor-not-allowed disabled:opacity-45 compact:text-[13px] ${
variant === "primary"
? "border-foreground bg-foreground text-surface enabled:hover:bg-white white:border-transparent white:bg-linear-to-br white:from-accent white:to-brand-secondary white:text-white white:disabled:opacity-60"
: "border-line-strong bg-surface-overlay text-foreground enabled:hover:border-foreground white:border-slate-900/10 white:enabled:hover:bg-slate-200"
} ${className}`}
/>
);
}
export function IconButton({
className = "",
type = "button",
round = false,
...props
}: ComponentProps<"button"> & { round?: boolean }) {
return (
<button
{...props}
type={type}
className={`relative inline-flex size-10 shrink-0 touch-manipulation items-center justify-center border border-solid border-line bg-surface-raised p-0 text-muted transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent enabled:cursor-pointer enabled:hover:border-line-strong enabled:hover:bg-surface-overlay enabled:hover:text-foreground disabled:cursor-not-allowed disabled:opacity-45 ${round ? "rounded-full" : "rounded-control"} ${className}`}
/>
);
}
export function Field({ className = "", ...props }: ComponentProps<"label">) {
return (
<label
{...props}
className={`flex min-w-0 flex-col gap-1.5 text-[14px] font-medium text-muted compact:text-[13px] [&>span]:text-[13px] [&>span]:font-semibold [&>span]:tracking-[0.04em] [&>span]:text-foreground [&>span]:uppercase compact:[&>span]:text-[12px] ${className}`}
/>
);
}
const inputStyles =
"min-w-0 rounded-control border border-solid border-line bg-surface px-3 py-2.5 font-sans text-[14px] text-foreground outline-none transition-colors focus:border-muted disabled:opacity-60 white:border-slate-900/10 white:bg-surface-raised white:focus:border-brand-secondary white:focus:shadow-[0_0_0_3px_rgb(59_130_246/0.15)]";
export function Input({ className = "", ...props }: ComponentProps<"input">) {
return (
<input
{...props}
className={`${inputStyles} read-only:cursor-default read-only:border-dashed read-only:text-muted ${className}`}
/>
);
}
export function Select({ className = "", ...props }: ComponentProps<"select">) {
return <select {...props} className={`${inputStyles} ${className}`} />;
}
export function Textarea({ className = "", ...props }: ComponentProps<"textarea">) {
return (
<textarea
{...props}
className={`${inputStyles} min-h-[84px] resize-y text-[15px] compact:text-[14px] ${className}`}
/>
);
}
export function FieldHint({ className = "", ...props }: ComponentProps<"small">) {
return <small {...props} className={`text-[13px] leading-[1.4] text-faint compact:text-[12px] ${className}`} />;
}
export function DialogFooter({ className = "", ...props }: ComponentProps<"footer">) {
return <footer {...props} className={`flex justify-between gap-3 pt-1.5 ${className}`} />;
}
export function DialogIntro(props: ComponentProps<"p">) {
return <p {...props} className="m-0 text-[14px] leading-[1.5] text-muted compact:text-[13px]" />;
}