Buttons
Common buttons prompt most actions in a UI
Usage
Buttons communicate actions that users can take. They are typically placed throughout your UI, in places like: Dialogs, Modal windows, Forms, Cards, Toolbars.
Instalation
Run the following command if is not already installed:
npm i @radix-ui/react-slot
Copy and paste the following code into your project.
import * as React from 'react'
import { Slot, Slottable } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
'flex items-center z-0 justify-center relative no-underline select-none group/button w-fit overflow-hidden text-label-lg font-medium rounded-2xl transition outline-none disabled:pointer-events-none disabled:text-onSurface/38 disabled:shadow-none active:scale-[0.98]',
{
variants: {
size: {
default: 'h-10 px-6',
small: 'h-8 px-4',
},
variant: {
filled:
'bg-primary text-onPrimary hover:shadow-sm focus-visible:shadow-none active:shadow-none disabled:bg-onSurface/12',
tonal:
'bg-secondaryContainer text-onSecondaryContainer disabled:bg-onSurface/12',
elevated:
'bg-surfaceContainerLow shadow-sm text-primary hover:shadow-md focus-visible:shadow-sm active:shadow-sm disabled:bg-onSurface/12',
outlined:
'border border-outline text-primary focus-visible:border-primary active:border-outline disabled:border-onSurface/12',
text: 'px-3 text-primary',
destructive:
'bg-error text-onError hover:shadow-sm focus-visible:shadow-none active:shadow-none disabled:bg-onSurface/12',
},
},
defaultVariants: {
size: 'default',
variant: 'filled',
},
}
)
const stateLayerVariants = cva(
'transition-opacity absolute inset-0 z-[-1] opacity-0 group-hover/button:opacity-8 group-focus/button:opacity-12 group-active/button:opacity-12',
{
variants: {
variant: {
filled: 'bg-onPrimary',
tonal: 'bg-onSecondaryContainer',
elevated: 'bg-primary',
outlined: 'bg-primary',
text: 'bg-primary',
destructive: 'bg-onError',
},
},
defaultVariants: {
variant: 'filled',
},
}
)
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
disableStateLayer?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(
{
className,
variant,
size,
asChild,
disableStateLayer,
children,
...props
},
ref
) => {
const Comp = asChild ? Slot : 'button'
return (
<Comp
ref={ref}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
>
<Slottable>{children}</Slottable>
{!disableStateLayer && (
<span className={stateLayerVariants({ variant })} />
)}
</Comp>
)
}
)
Button.displayName = 'Button'
export { Button, buttonVariants, stateLayerVariants }
export type { ButtonProps }
Update the import paths to match your project setup.
Examples
Filled buttons
Filled buttons have the most visual impact after the FAB, and should be used for important, final actions that complete a flow, like Save, Join now, or Confirm.
import { Button } from '@/components/ui/button'
export const FilledButton = () => {
return <Button>Make payment</Button>
}
Tonal buttons
A filled tonal button is an alternative middle ground between filled and outlined buttons. They’re useful in contexts where a lower-priority button requires slightly more emphasis than an outline would give, such as “Next” in an onboarding flow. Tonal buttons use the secondary color mapping.
import { Button } from '@/components/ui/button'
export const TonalButton = () => {
return <Button variant="tonal">Visit site</Button>
}
Elevated buttons
Elevated buttons are essentially filled tonal buttons with a shadow. To prevent shadow creep, only use them when absolutely necessary, such as when the button requires visual separation from a patterned background.
import { Button } from '@/components/ui/button'
export const ElevatedButton = () => {
return <Button variant="elevated">Explore</Button>
}
Outlined buttons
Outlined buttons are medium-emphasis buttons. They contain actions that are important, but aren’t the primary action in an app.
Outlined buttons pair well with filled buttons to indicate an alternative, secondary action.
import { Button } from '@/components/ui/button'
export const OutlinedButton = () => {
return <Button variant="outlined">Next</Button>
}
Text buttons
Text buttons are used for the lowest priority actions, especially when presenting multiple options.
Text buttons can be placed on a variety of backgrounds. Until the button is interacted with, its container isn’t visible.
Text buttons are often embedded in components like cards, dialogs, and snackbars. Because text buttons don’t have a visible container in their default state, they don’t distract from nearby content.
import { Button } from '@/components/ui/button'
export const TextButton = () => {
return <Button variant="text">Retry</Button>
}
Destructive buttons
Destructive buttons are used to indicate destructive actions, such as deleting an item.
import { Button } from '@/components/ui/button'
export const DestructiveButton = () => {
return <Button variant="destructive">Remove account</Button>
}
With icon
Icons visually communicate the button’s action and help draw attention. They should be placed on the leading side of the button, before the label text.
import { Button } from '@/components/ui/button'
import { Icon } from '@/components/ui/icon'
export const ButtonWithIcon = () => {
return (
<Button className="pl-4">
<Icon
symbol="add"
className="group-hover:font-emphasis mr-2 text-[20px]"
/>
New document
</Button>
)
}
Loading buttons
Loading buttons indicate an action is in progress. The button should be disabled while loading.
import { Button } from '@/components/ui/button'
import { CircularProgress } from '@/components/ui/progress-indicator'
export const LoadingButton = () => {
return (
<Button disabled className="pl-4">
<CircularProgress className="text-onSurface/38 mr-2 h-5 w-5" />
Loading
</Button>
)
}
As child
With asChild prop, you can make a link looks like a button.
import { Button } from '@/components/ui/button'
export const AsChildButton = () => {
return (
<Button asChild className="pl-4">
<Link href="/components/buttons">Go to Buttons</Link>
</Button>
)
}