actions
Button
Nine visual weights across six tones, six hover effects on a separate axis, and the grouped, toggle, copy and back-to-top patterns built on them.
Examples
Bold styles
Twenty-eight one-off looks built with plain `className` on top of the same Button — neon, glass, neumorphic, a rotating gradient border, a real ripple, a hold-to-confirm fill, a save button that morphs into a checkmark, a confetti burst, and a real progress fill, among others.
Variants
Visual weight, from most to least emphasis. Colour never comes from here.
Expressive variants
Four surfaces beyond the flat set. Every colour in them is the tone you passed — the gradient runs along the tone’s own OKLCH ramp, and the glow is that same tone as a shadow.
Hover effects
Point at each one. Effect is a third axis, independent of variant and tone, so any of the six works on any of the nine variants. All of them are inert under prefers-reduced-motion.
Three axes at once
Variant, tone and effect chosen separately. None of these is a preset — there is no variant named after a use case anywhere in the library.
Grouped, toggled, copied
Three shipped patterns built on Button rather than beside it — ButtonGroup joins buttons into one control, ToggleButton holds a pressed state on aria-pressed, and CopyButton writes to the clipboard and announces it.
Floating buttons
FloatingButton pins one action to a corner — icon-only, extended with its label, in the gradient/glow/soft variants, with a pulse effect, or anchored to a different corner. FloatingMenu is the expandable version, shown here with hidden labels and a custom open icon too — both stack their actions with a staggered entrance, close on Escape or an outside press, and hand focus back to the trigger.
Simple
Extended
Gradient variant
Glow + pulse effect
Top-left placement
Bottom-center placement
Expandable — open it
Icons only — labels hidden
Custom open icon
Back to top
Scroll the panel to watch it work. A passive rAF-throttled listener, visibility rather than opacity so it leaves the tab order while hidden, and focus handed back to the top after the scroll. Pass a container ref and it watches that element instead of the window.
1. Scroll this panel
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
2. Keep going
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
3. Almost there
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
4. Past the threshold
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
5. The button is up now
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
6. Still scrolling
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
7. Nearly the end
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
8. The end
It watches this panel rather than the window, because it was handed a ref to it. Past 160px it fades in; click it and the panel scrolls smoothly back.
The button is a sibling of the scrolling panel, not a child of it — an absolutely positioned child would scroll away with the content. Give it no container and it watches the window instead, which is the usual full-page back-to-top.
Tones
One prop repaints the whole button. Every tone declares the same seven slots, so there is no variant × tone matrix to maintain.
Sizes
Heights and padding derive from --noksha-density; one variable retunes them all.
Shapes
A third box axis beside size — `round` is the pill, `circle` the icon disc. It composes with every variant and every size rather than being a look of its own. Two rows show what that composes into: a notification bell and cart with count badges, an unread dot, a wishlist heart, an avatar; then back, close, a real play/pause toggle, a star rating, a kebab menu, a mic-mute toggle, help and location — all `Button`/`ToggleButton`, no bespoke controls.
circleroundreal-world32moresizesWith icons
Loading
Click Save — the button holds its exact width while the spinner is in. `loadingPlacement="icon"` swaps the indicator into the leading icon’s slot instead, which keeps the label readable, and `loadingIcon` replaces the spinner with any node.
Own the source
Noksha ships as a package and as copy-paste source. Take the files and they are yours to change. These are read from the generated registry, so they are exactly what the library ships — never a paraphrase of it.
Take these files, and spinner, internal helpers along with them — the imports below point at them. Or let the CLI do it: npx @noksha-ui/cli add button writes them, follows the same dependency graph, and fixes up the imports.
API reference
Accepted values
- ButtonVariant
solidsoftoutlineghostlinkgradientglassglowdashed- ButtonEffect
noneliftsheenwipepulsetilt- ButtonSize
xssmmdlgxl- ButtonShape
defaultroundcircle- ButtonLoadingPlacement
overlayicon- FloatingPlacement
bottom-rightbottom-leftbottom-centertop-righttop-lefttop-center
ButtonBaseProps
Also accepts everything from Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'>.
| Prop | Type | Description |
|---|---|---|
| variant | ButtonVariant | Visual weight. |
| tone | ButtonTone | Semantic colour. |
| effect | ButtonEffect | Hover motion. Orthogonal to `variant` and `tone`. |
| size | ButtonSize | — |
| shape | ButtonShape | Corner treatment. `round` is a pill, `circle` is the icon-only disc. |
| fullWidth | boolean | Stretches to the container width. |
| loading | boolean | Shows a spinner and blocks interaction, holding the button's width. |
| loadingLabel | string | Replaces the spinner's default label for screen readers. |
| loadingIcon | React.ReactNode | Replaces the default `Spinner` while loading. |
| loadingPlacement | ButtonLoadingPlacement | `overlay` (default) blanks the button and centres the indicator, holding the width. `icon` swaps it into the leading icon's slot and leaves the label readable. |
| icon | React.ReactNode | — |
| trailingIcon | React.ReactNode | — |
| asChild | boolean | Renders the child element instead of a `<button>`, merging props onto it. |
ButtonGroupProps
Also accepts everything from React.HTMLAttributes<HTMLDivElement>.
| Prop | Type | Description |
|---|---|---|
| orientation | 'horizontal' | 'vertical' | Lay the buttons out in a column instead of a row. |
| attached | boolean | Join the buttons into one control, collapsing the borders between them. |
CopyButtonProps
Also accepts everything from Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'value'>.
| Prop | Type | Description |
|---|---|---|
| value* | string | The text written to the clipboard. |
| label | string | Accessible name, and the label when `withLabel` is set. |
| copiedLabel | string | Replaces `label` while the confirmation is showing. |
| timeout | number | How long the confirmation stays up, in milliseconds. |
| withLabel | boolean | Show the label beside the icon instead of only as an accessible name. |
| variant | ButtonVariant | — |
| tone | ButtonTone | — |
| size | ButtonSize | — |
| onCopied | (value: string) => void | Called after a successful copy, never after a rejected one. |
FloatingAnchorProps
| Prop | Type | Description |
|---|---|---|
| placement | FloatingPlacement | — |
| offset | number | Distance from the two edges it is pinned to, in pixels. |
| container | React.RefObject<HTMLElement | null> | Pins inside this element instead of the viewport. It must be positioned. |
ScrollToTopProps
Also accepts everything from Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'>, FloatingAnchorProps.
| Prop | Type | Description |
|---|---|---|
| showAfter | number | Pixels scrolled before the button appears. |
| label | string | — |
| focusTarget | string | Selector for the element to focus after scrolling. Defaults to the body. |
| variant | ButtonVariant | — |
| size | ButtonSize | — |
| icon | React.ReactNode | — |
FloatingButtonProps
Also accepts everything from Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'>, FloatingAnchorProps.
| Prop | Type | Description |
|---|---|---|
| label* | string | Accessible name, and the visible text when `extended` is set. |
| icon* | React.ReactNode | — |
| extended | boolean | Show the label beside the icon instead of only as an accessible name. |
| variant | ButtonVariant | — |
| tone | ButtonTone | — |
| size | ButtonSize | — |
| effect | ButtonEffect | — |
FloatingAction
| Prop | Type | Description |
|---|---|---|
| id* | string | — |
| label* | string | — |
| icon* | React.ReactNode | — |
| tone | ButtonTone | — |
| onSelect | () => void | — |
FloatingMenuProps
Also accepts everything from Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'>, FloatingAnchorProps.
| Prop | Type | Description |
|---|---|---|
| label* | string | Accessible name for the trigger and for the action stack it opens. |
| actions* | FloatingAction[] | — |
| icon | React.ReactNode | — |
| openIcon | React.ReactNode | Replaces the icon while the stack is open. |
| hideActionLabels | boolean | Hide the label chip beside each action. |
| open | boolean | Present means controlled. |
| defaultOpen | boolean | — |
| onOpenChange | (open: boolean) => void | — |
| variant | ButtonVariant | — |
| tone | ButtonTone | — |
| size | ButtonSize | — |