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>
);
}