Button
Trigger an action. The only button in the library. Adjust looks by appending Tailwind classes via className.
Preview
Install
npx @joinclass/axiom-ui add button Copies button.tsx into ./src/components/. You own the code after that.
Props
| Prop | Description |
|---|---|
| ...native | Every ComponentPropsWithRef<'button'> attribute passes through unchanged (onClick, disabled, aria-*, form attributes, ref, ...). |
| type | "button" | "submit" | "reset" — defaults to "button" so it never submits a form by accident. |
| className | Escape hatch: Tailwind classes appended after the defaults. Both sets land in the generated CSS, so prefix with `!` (e.g. `!p-0`, `!w-auto`) when overriding a conflicting default utility. |
Examples
Default
<Button onClick={save}>Save</Button> Disabled
<Button disabled>Save</Button> Full width via escape hatch
<Button className="w-full">Save</Button> Submit inside a form
<Button type="submit">Send</Button> Accessibility
- Renders a native <button>: keyboard activation (Enter/Space) and focus semantics come for free.
- Visible focus-visible outline for keyboard users.
- disabled uses the native attribute, so it is announced by screen readers and removed from tab order.
States
idlehoverfocus-visibleactivedisabled
Source
The full source of button.tsx — exactly what the CLI copies into your repo.
/**
* Trigger an action. The only button — one style, no variants.
* Accepts every native <button> attribute; `type` defaults to "button"
* so it never submits a form by accident.
*/
import type { ComponentPropsWithRef } from "react";
export type ButtonProps = ComponentPropsWithRef<"button">;
export function Button({ className, type = "button", ...props }: ButtonProps) {
return (
<button
type={type}
className={
"inline-flex cursor-pointer items-center justify-center rounded-md bg-zinc-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-700 active:bg-zinc-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-900 disabled:pointer-events-none disabled:opacity-50" +
(className ? " " + className : "")
}
{...props}
/>
);
}
export const manifest = {
intent:
"Trigger an action. The only button in the library. Adjust looks by appending Tailwind classes via className.",
props: {
"...native":
"Every ComponentPropsWithRef<'button'> attribute passes through unchanged (onClick, disabled, aria-*, form attributes, ref, ...).",
type: '"button" | "submit" | "reset" — defaults to "button" so it never submits a form by accident.',
className:
"Escape hatch: Tailwind classes appended after the defaults. Both sets land in the generated CSS, so prefix with `!` (e.g. `!p-0`, `!w-auto`) when overriding a conflicting default utility.",
},
states: ["idle", "hover", "focus-visible", "active", "disabled"],
a11y: [
"Renders a native <button>: keyboard activation (Enter/Space) and focus semantics come for free.",
"Visible focus-visible outline for keyboard users.",
"disabled uses the native attribute, so it is announced by screen readers and removed from tab order.",
],
examples: [
{ title: "Default", code: "<Button onClick={save}>Save</Button>" },
{ title: "Disabled", code: "<Button disabled>Save</Button>" },
{
title: "Full width via escape hatch",
code: '<Button className="w-full">Save</Button>',
},
{ title: "Submit inside a form", code: '<Button type="submit">Send</Button>' },
],
} as const;