Button
Displays a button or a component that looks like a button.
import { Button } from "~/components/button";
export default function ButtonVariantsDemo() {
return (
<div class="flex flex-wrap gap-4">
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
</div>
);
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add buttonManual
Create the recipe file at src/components/recipes/button.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const buttonVariants = tv({
base: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium transition duration-300 motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0.5 active:duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&>svg]:pointer-events-none [&>svg]:size-4 [&>svg]:shrink-0 [[data-icon]]:pointer-events-none [[data-icon=inline-start]]:-me-0.5 [[data-icon=inline-end]]:-ms-0.5",
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
xs: "h-7 px-2 text-xs",
sm: "h-8 px-2.5 text-sm",
md: "h-9 px-3 py-1.5",
lg: "h-10 px-6",
icon: "size-8",
"icon-xs": "size-7 [&>svg]:size-4",
"icon-sm": "size-8 [&>svg]:size-4",
"icon-md": "size-9 [&>svg]:size-5",
"icon-lg": "size-10 [&>svg]:size-6",
},
},
defaultVariants: {
variant: "default",
size: "sm",
},
});
export type ButtonVariants = VariantProps<typeof buttonVariants>;Create the component file at src/components/button/index.tsx:
import { splitProps, type Component, children } from "solid-js";
import { ButtonVariants, buttonVariants } from "../recipes/button";
import { ark, HTMLArkProps } from "@ark-ui/solid/factory";
type ButtonProps = HTMLArkProps<"button"> & ButtonVariants;
const Button: Component<ButtonProps> = (props) => {
const [local, others] = splitProps(props, ["class", "variant", "size", "children"]);
const resolvedChildren = children(() => local.children);
return (
<ark.button
class={buttonVariants({
variant: local.variant,
size: local.size,
class: local.class,
})}
{...others}
>
{resolvedChildren()}
</ark.button>
);
};
export { Button, buttonVariants };Note: Make sure your project has the Tailwind CSS theme variables set up (
--background,--foreground,--ring,--border, etc.) or override the utility classes to match your design system.
Usage
Basic Usage
import { Button } from "~/components/button";
export default function ButtonBasicDemo() {
return <Button>Click me</Button>;
}Variants
Use the variant prop to change the visual style.
import { Button } from "~/components/button";
export default function ButtonVariantsDemo() {
return (
<div class="flex flex-wrap gap-4">
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
</div>
);
}Sizes
Use the size prop to change the button size.
import { Button } from "~/components/button";
export default function ButtonSizesDemo() {
return (
<div class="flex flex-wrap items-center gap-4">
<Button size="xs">Xs</Button>
<Button size="sm">Sm</Button>
<Button size="md">Md</Button>
<Button size="lg">Lg</Button>
<Button size="icon-xs">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</Button>
<Button size="icon-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</Button>
<Button size="icon-md">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</Button>
<Button size="icon-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</Button>
</div>
);
}Disabled
Add the disabled prop to disable interaction.
import { Button } from "~/components/button";
export default function ButtonDisabledDemo() {
return (
<div class="flex flex-wrap gap-4">
<Button disabled>Default</Button>
<Button variant="secondary" disabled>
Secondary
</Button>
<Button variant="destructive" disabled>
Destructive
</Button>
<Button variant="outline" disabled>
Outline
</Button>
<Button variant="ghost" disabled>
Ghost
</Button>
<Button variant="link" disabled>
Link
</Button>
</div>
);
}With Icon
import { Button } from "~/components/button";
export default function ButtonIconsDemo() {
return (
<div class="flex flex-wrap items-center gap-4">
<Button variant="outline" size="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</Button>
<Button variant="outline">
<svg
data-icon="inline-start"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
New Branch
</Button>
<Button variant="outline">
Fork
<svg
data-icon="inline-end"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</Button>
</div>
);
}Spinner
import { Button, Spinner } from "~/components/button";
export default function ButtonSpinnerDemo() {
return (
<div class="flex flex-wrap items-center gap-4">
<Button disabled variant="outline" size="icon">
<Spinner />
</Button>
<Button disabled variant="outline">
<Spinner data-icon="inline-start" />
Loading...
</Button>
<Button disabled variant="outline">
Saving
<Spinner data-icon="inline-end" />
</Button>
</div>
);
}Link
Use the buttonVariants helper to render a link that looks like a button.
import { buttonVariants } from "~/components/button";
export default function ButtonLinkDemo() {
return (
<div class="flex flex-wrap gap-4">
<a class={buttonVariants({ variant: "outline" })} href="#">
Click here
</a>
</div>
);
}API Reference
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "default" |
| size | "xs" | "sm" | "md" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-md" | "icon-lg" | "sm" |
| disabled | boolean | false |
| class | string | — |