Button
Buttons allow users to perform actions and make choices with a single tap.
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.
Link Buttons
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>
);
}