COMPONENTS/ACTIONS
Button.
A small action. A clear intention.
In practice
Use render={<a href="…" />} for links. Variants: default, secondary, outline, ghost, destructive and link. Sizes: default, sm, lg, icon.
Installation
Install Coal once. Components and TypeScript declarations are included. React 19 and React DOM 19 are peer dependencies.
Terminal · local package
npm install http://localhost:3100/downloads/chlohal-coal-ui-0.5.0.tgzThis installs the local package. It is not yet published on npm. See installation for stylesheet setup and integration.
Usage
example.tsx
"use client";
import * as React from "react";
import "@chlohal/coal-ui/styles.css";
import { ArrowUpRight, Plus } from "lucide-react";
import { Button } from "@chlohal/coal-ui";
export default function Example() {
const [message, setMessage] = React.useState("");
const notice = <p role="status">{message}</p>;
const [saved, setSaved] = React.useState(false);
return (<div>
<Button onClick={() => setMessage("Your changes are saved.")}>
Save changes <ArrowUpRight />
</Button>
<Button variant="outline" onClick={() => setMessage("Changes discarded.")}>
Cancel
</Button>
<Button variant="ghost" size="icon" aria-label="Add item" onClick={() => setMessage("Item added.")}>
<Plus />
</Button>
{notice}
</div>);
}
Source
The original Coal implementation. Use the package export to integrate it.
packages/react/src/button.tsx
"use client";
import { Action, cn, type ActionProps } from "./internal.js";
export type ButtonProps = ActionProps & {
loading?: boolean;
variant?:
| "default"
| "destructive"
| "outline"
| "secondary"
| "ghost"
| "link";
size?: "default" | "sm" | "lg" | "icon";
};
export function buttonVariants({
variant = "default",
size = "default",
className,
}: Pick<ButtonProps, "variant" | "size" | "className"> = {}) {
return cn(
"coal-button",
`coal-button--${variant}`,
`coal-button--${size}`,
className,
);
}
export function Button({
variant,
size,
className,
loading = false,
children,
disabled,
...props
}: ButtonProps) {
return (
<Action
className={buttonVariants({ variant, size, className })}
{...props}
{...(disabled !== undefined || loading
? { disabled: disabled || loading }
: {})}
aria-busy={loading || undefined}
>
{loading ? (
<>
<span className="coal-button-loading" aria-hidden="true" />
{children ??
(
props.render?.props as
| { children?: import("react").ReactNode }
| undefined
)?.children}
</>
) : (
children
)}
</Action>
);
}
Accessibility checklist
- Provide an accessible name for controls, including icon-only buttons.
- Check keyboard navigation, visible focus and disabled states in your application.
- Do not rely on color alone to communicate status.