Button

Buttons allow users to perform actions and make choices with a single tap.

Storybook React Aria Source

Usage

Start by importing the Button component.

import { Button } from '@vibrant-ui/components';

Then use it in your JSX.

<Button>Save</Button>

Add an onPress handler to make it interactive.

'use client';

import { Button } from '@vibrant-ui/components';

export function ButtonOnPressExample() {
  return <Button onPress={() => alert('Button pressed!')}>Save</Button>;
}

Styling

Buttons come in four variants: soft, solid, outlined, and transparent. The default is soft.

import { Button, buttonVariants, HStack } from '@vibrant-ui/components';

export function ButtonVariantsExample() {
  return (
    <HStack align="start" gap="sm">
      {buttonVariants.map((variant) => (
        <Button key={variant} variant={variant}>
          {variant.charAt(0).toUpperCase() + variant.slice(1)}
        </Button>
      ))}
    </HStack>
  );
}

Colors

The color prop sets the button's color. It supports all tokens, and defaults to neutral.

import { Button, buttonVariants, HStack, VStack } from '@vibrant-ui/components';
import { colors } from '@vibrant-ui/styles';

export function ButtonColorsExample() {
  return (
    <VStack gap="sm">
      {buttonVariants.map((variant) => (
        <HStack key={variant} gap="sm">
          {colors.map((color) => (
            <Button key={color} color={color} variant={variant}>
              {color.charAt(0).toUpperCase() + color.slice(1)}
            </Button>
          ))}
        </HStack>
      ))}
    </VStack>
  );
}

Semantic colors

Semantic tokens provide clarity and allow for easier theming.

import { Button, buttonVariants, HStack, VStack } from '@vibrant-ui/components';

export function SemanticButtonColorsExample() {
  return (
    <VStack gap="sm">
      {buttonVariants.map((variant) => (
        <HStack key={variant} gap="sm">
          {(['primary', 'accent', 'info', 'notice', 'positive', 'negative'] as const).map(
            (color) => (
              <Button key={color} color={color} variant={variant}>
                {color.charAt(0).toUpperCase() + color.slice(1)}
              </Button>
            ),
          )}
        </HStack>
      ))}
    </VStack>
  );
}

Sizes

The size option controls a button's overall dimensions.

import { Button, buttonSizes, buttonVariants, HStack, VStack } from '@vibrant-ui/components';

export function ButtonSizesExample() {
  return (
    <VStack gap="sm">
      {buttonVariants.map((variant) => (
        <HStack key={variant} gap="sm" align="center">
          {buttonSizes.map((size) => (
            <Button key={size} size={size} variant={variant}>
              {size.charAt(0).toUpperCase() + size.slice(1)}
            </Button>
          ))}
        </HStack>
      ))}
    </VStack>
  );
}

Full Width

Use stretch to make a button fill its container.

import { Button } from '@vibrant-ui/components';

export function FullWidthButtonExample() {
  return <Button stretch>Save</Button>;
}

Radius

+Set the radius prop to adjust the border radius.

import { Button, buttonVariants, HStack, VStack } from '@vibrant-ui/components';
import { type RadiusScale, radiusScales } from '@vibrant-ui/styles';

export function ButtonRadiiExample() {
  return (
    <VStack gap="sm">
      {buttonVariants.map((variant) => (
        <HStack key={variant} gap="sm">
          {(Object.keys(radiusScales) as RadiusScale[]).map((radius) => (
            <Button key={radius} radius={radius} variant={variant}>
              {radius.charAt(0).toUpperCase() + radius.slice(1)}
            </Button>
          ))}
        </HStack>
      ))}
    </VStack>
  );
}

Shadows

Use the shadow prop to add elevation and highlight a button.

import { Button, buttonVariants, HStack, VStack } from '@vibrant-ui/components';
import { type ShadowToken, tokens } from '@vibrant-ui/tokens';

export function ButtonShadowsExample() {
  return (
    <VStack gap="sm">
      {buttonVariants.map((variant) => (
        <HStack key={variant} gap="sm">
          {(Object.keys(tokens.shadows) as ShadowToken[]).map((shadow) => (
            <Button key={shadow} shadow={shadow} variant={variant}>
              {shadow.charAt(0).toUpperCase() + shadow.slice(1)}
            </Button>
          ))}
        </HStack>
      ))}
    </VStack>
  );
}

States

Communicate availability and progress with clear states.

Disabled

The isDisabled prop visually dims a button and prevents interaction.

import { Button, buttonVariants, HStack } from '@vibrant-ui/components';

export function DisabledButtonExample() {
  return (
    <HStack gap="sm">
      {buttonVariants.map((variant) => (
        <Button key={variant} isDisabled variant={variant}>
          Save
        </Button>
      ))}
    </HStack>
  );
}

Pending

The isPending prop shows a loading spinner and disables interaction.

import { Button, buttonVariants, HStack } from '@vibrant-ui/components';

export function PendingButtonExample() {
  return (
    <HStack gap="sm">
      {buttonVariants.map((variant) => (
        <Button key={variant} isPending variant={variant}>
          Save
        </Button>
      ))}
    </HStack>
  );
}

Label

Composition

Buttons can contain complex labels with icons and multiple elements.

import { faSave } from '@fortawesome/pro-regular-svg-icons';
import { Badge, Button, Icon, Text } from '@vibrant-ui/components';

export function ButtonWithComplexLabelExample() {
  return (
    <Button>
      <Icon icon={faSave} />
      <Text>Save</Text>
      <Badge color="red" variant="solid" size="2xs">
        9
      </Badge>
    </Button>
  );
}

Multiline

Longer labels wrap cleanly to preserve layout and readability.

import { Button, Container } from '@vibrant-ui/components';

export function MultilineButtonExample() {
  return (
    <Container maxWidth="5xl">
      <Button>This is a somewhat long label</Button>
    </Container>
  );
}

Hit Slop

Increase the interactive touch area without changing layout size to improve usability on touch devices.

import { Button, HStack, Shape } from '@vibrant-ui/components';
import { vars } from '@vibrant-ui/styles';

export function ButtonWithHitSlopExample() {
  return (
    <HStack gap="md" align="center">
      {(['3xs', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'] as const).map((token) => (
        <Shape
          key={token}
          border={{ width: 'regular', style: 'dashed', color: 'neutral' }}
          variant="transparent"
          style={{ padding: vars.tokens.spacings[token] }}
        >
          <Button hitSlop={token}>Save</Button>
        </Shape>
      ))}
    </HStack>
  );
}

Icon Buttons

Placing only an <Icon> inside a button renders a square icon button.

import { faStar } from '@fortawesome/pro-regular-svg-icons';
import { Button, buttonSizes, buttonVariants, HStack, Icon, VStack } from '@vibrant-ui/components';

export function IconButtonExample() {
  return (
    <VStack gap="sm">
      {buttonVariants.map((variant) => (
        <HStack key={variant} gap="sm" align="center">
          {buttonSizes.map((size) => (
            <Button key={size} aria-label="Favorite" size={size} variant={variant}>
              <Icon icon={faStar} />
            </Button>
          ))}
        </HStack>
      ))}
    </VStack>
  );
}

Accessibility

Always provide an accessible label with the aria-label prop when a button has no visible text.

Use the dedicated LinkButton component for links that look like buttons.

import { LinkButton } from '@vibrant-ui/components';

export function LinkButtonExample() {
  return (
    <LinkButton href="https://nettbureau.com" target="_blank" color="blue">
      Go to nettbureau.com
    </LinkButton>
  );
}

Button Groups

Use the Group component to group related buttons together.

import { Button, Group, type GroupProps } from '@vibrant-ui/components';

export function ButtonGroupExample(props: GroupProps) {
  return (
    <Group {...props}>
      <Button>One</Button>
      <Button>Two</Button>
      <Button>Three</Button>
    </Group>
  );
}

A common use case for button groups is combining a button with a dropdown menu.

import { faAngleDown } from '@fortawesome/pro-regular-svg-icons';
import { Button, Group, Icon } from '@vibrant-ui/components';

export function MergeButtonGroupExample() {
  return (
    <Group>
      <Button size="lg" color="green" variant="solid">
        Merge pull request
      </Button>
      <Button size="lg" color="green" variant="solid" isPending={false}>
        <Icon icon={faAngleDown} />
      </Button>
    </Group>
  );
}

Props

Prop

Type

Prop

Type