Components

Button

Use buttons to trigger actions throughout your interface. Buttons are promises of what happens next.View source →

Use buttons to trigger actions throughout your interface. Buttons are promises of what happens next. Design them to build user confidence and guide decision-making naturally.

This is an enhanced version of the Button component from Radix Themes. For the original API reference, see the Radix Themes Button documentation.

Playground

Installation

shell
npm install @kushagradhawan/kookie-ui

Usage

tsx
import { Button } from '@kushagradhawan/kookie-ui';
 
export function MyComponent() {
  return <Button>Click me</Button>;
}

Props

PropTypeDescription
variant'classic' | 'solid' | 'soft' | 'surface' | 'outline' | 'ghost'Button style variant: classic for elevated, solid for primary, outline for secondary, ghost for utility, soft/surface for content-heavy UIs
size'1' | '2' | '3' | '4'Button density: 1 (24px) for toolbars, 2 (32px) standard, 3 (40px) for important actions, 4 (48px) for hero sections. Supports responsive objects
colorAccentColorSemantic color to communicate action intent. Use highContrast for maximum visibility on complex backgrounds
highContrastbooleanMaximum visibility mode, especially useful on complex or translucent backgrounds
fullWidthbooleanMakes button fill its container width. Useful for mobile layouts and form submissions
flushbooleanRemoves padding for seamless text integration. Use with ghost variant to blend with text while staying interactive
radius'none' | 'small' | 'medium' | 'large' | 'full'Corner radius scale: none for sharp edges, small/medium/large for progressive rounding, full for pill-shaped
material'solid' | 'translucent'Background appearance: solid for opaque backgrounds, translucent for depth over images or dynamic backgrounds
panelBackground'solid' | 'translucent'Deprecated: Use material prop instead. Controls panel background appearance
loadingbooleanReplaces all content with a centered spinner and disables button automatically. For visible text during loading, use Spinner component around icons instead
disabledbooleanPrevents interaction when action is unavailable, such as during form validation or when authentication is required
asElementTypeRenders button as different HTML element while maintaining button styling. Useful for navigation links or routing integration
asChildbooleanMerges button props with child element using Radix Slot pattern. Ideal for composition with routing libraries like Next.js Link
tooltipstring | ReactNodeContent for tooltip. Appears on hover and focus for accessibility
tooltipSide'top' | 'right' | 'bottom' | 'left'Position of tooltip relative to button
tooltipAlign'start' | 'center' | 'end'Alignment of tooltip

Variants

Use the variant prop to set button style.

Classic

Elevated style with subtle shadow for maximum visual impact and primary actions.

tsx
<Button size="3" variant="classic">
  Button
</Button>

Solid

Filled background for primary actions and important CTAs.

tsx
<Button size="3" variant="solid">
  Button
</Button>

Soft

Subtle background for secondary actions and content-heavy interfaces.

tsx
<Button size="3" variant="soft">
  Button
</Button>

Outline

Bordered style for secondary actions and form controls.

tsx
<Button size="3" variant="outline">
  Button
</Button>

Surface

Elevated surface for content-heavy UIs and card-based layouts.

tsx
<Button size="3" variant="surface">
  Button
</Button>

Ghost

Minimal style for utility actions and inline text integration.

tsx
<Button size="3" variant="ghost">
  Button
</Button>

Sizes

Set size for button density: 1 (24px), 2 (32px), 3 (40px), 4 (48px). Use 3 or 4 for mobile touch targets. Supports responsive objects like { initial: '1', sm: '2', md: '3', lg: '4' }.

Size 1

For toolbars and dense interfaces.

tsx
<Button size="1">Button</Button>

Size 2

For standard interface contexts.

tsx
<Button size="2">Button</Button>

Size 3

For important actions and mobile touch targets.

tsx
<Button size="3">Button</Button>

Size 4

For hero sections and maximum impact.

tsx
<Button size="4">Button</Button>

Colors

Use the color prop with semantic colors to communicate the action's intent to users. Use highContrast for maximum visibility, especially on complex or translucent backgrounds.

tsx
<Button size="3" color="crimson" variant="solid">
  Button
</Button>
<Button size="3" color="crimson" variant="solid" highContrast>
  Button
</Button>

Material

Use the material prop to set button appearance. Choose solid for opaque backgrounds, or translucent for depth and separation over images or dynamic backgrounds. Avoid outline and ghost variants with translucent material, as they may lack contrast.

Theme

Buttons automatically inherit the theme's material setting. Wrap your interface in a Theme component to control material globally:

tsx
<Theme material="translucent">
  <Button size="3" color="gray" highContrast variant="soft">
    Button
  </Button>
</Theme>

Custom

Override the theme's material for a button when you need a specific effect. Translucent effects only show over backgrounds—use highContrast for readability.

tsx
<Theme material="solid">
  <Button
    material="translucent"
    size="3"
    color="gray"
    highContrast
    variant="soft"
  >
    Button
  </Button>
</Theme>

States

Loading

Set loading to true to replace all content with a centered spinner and disable the button automatically. No need to set disabled manually.

tsx
<Button
  size="3"
  color="gray"
  highContrast
  loading
>
  Submit
</Button>

For a more sophisticated loading pattern where text remains visible, wrap your icon in a Spinner component:

tsx
<Button disabled={isLoading}>
  <Spinner loading={isLoading}>
    <BookmarkIcon />
  </Spinner>
  Bookmark
</Button>

Disabled

Set the disabled prop to true to prevent interaction when an action is unavailable, such as during form validation or when authentication is required.

tsx
<Button
  size="3"
  disabled
  tooltip="Please fill all required fields"
>
  Save
</Button>

Interactive

Buttons handle focus for keyboard navigation. For complex patterns, use data-state for custom visuals.

tsx
<Button size="3" variant="classic" data-state="open">
  Open
</Button>
<Button size="3" variant="classic" data-state="on">
  On
</Button>

Layout

Full Width

Set the fullWidth prop to true to make the button fill its container. This is useful for mobile layouts and form submissions.

tsx
<Flex direction="column" gap="2" width="300px">
  <Button size="3" variant="soft" color="gray" highContrast fullWidth>
    Complete
  </Button>
</Flex>

Flush

Use the flush prop with the ghost variant to remove padding, making the button blend with text while staying interactive.

tsx
<Flex gap="2" align="center">
  <Button size="3" variant="ghost" flush>
    Learn
  </Button>{' '}
  about us.
</Flex>

Margin

Use margin props for spacing around buttons. All margin props are responsive and accept design tokens or CSS values.

tsx
<Button size="3" variant="soft" mx="2">
  Button
</Button>
<Button size="3" variant="soft" mx="2">
  Button
</Button>

Tooltips

Use the tooltip prop to add context to button actions, especially for icon buttons or unclear actions. Tooltips appear on hover and focus for accessibility. Always provide a tooltip for icon-only buttons.

tsx
<Button
  variant="soft"
  size="3"
  tooltip="Save your current progress"
>
  Button
</Button>

Icons

Place icons directly inside buttons; they'll be sized automatically based on the button's size.

tsx
import { Check } from 'lucide-react';
 
<Button size="3">
  <Check />
  Save
</Button>

For icon-only buttons, use the IconButton component for proper accessibility and touch targets.

tsx
import { ChevronDown } from 'lucide-react';
 
<IconButton
  size="3"
  variant="soft"
  aria-label="More options"
>
  <ChevronDown />
</IconButton>

Polymorphism

As

Use the as prop to render buttons as different HTML elements while maintaining button styling. This is useful for navigation links, routing integration, or custom interactive elements that need button appearance.

tsx
<Button size="3" as="a" href="/home">
  Go Home
</Button>

AsChild

Use the asChild prop to merge button props with a child element using the Radix Slot pattern. This is ideal for composition with routing libraries like Next.js Link or React Router, as it applies button styling without creating an additional wrapper element.

tsx
<Button size="3" asChild>
  <a href="/dashboard">Go to Dashboard</a>
</Button>

Responsive

Use responsive objects with the size prop to adapt button sizing across different breakpoints. The component uses a mobile-first approach where each breakpoint applies styles at its minimum width and above.

tsx
<Button size={{ initial: '1', sm: '2', md: '3' }}>Button</Button>

Form

Use standard HTML form attributes to integrate buttons with forms. The component supports all form attributes for comprehensive form integration, including loading states for async operations and validation-based disabled states.

tsx
<Button type="submit" size="3">
  Submit
</Button>

Button also supports customizable tooltip props for content, position, timing, and behavior. Additionally, all margin props (m, mx, my, mt, mr, mb, ml) are supported with responsive values.

Accessibility

Kookie UI's Button extends Radix's accessibility foundation with enhanced loading states and built-in tooltip support.

Enhanced Loading States

Loading states automatically apply comprehensive ARIA attributes:

  • aria-busy="true" - announces processing state
  • aria-disabled="true" - prevents interaction
  • aria-describedby - links to loading announcement
  • aria-label - dynamically includes "(loading)" status

Loading announcements include: "[Button text] is loading, please wait..."

Keyboard Navigation

  • Full keyboard support with Enter and Space keys
  • Visible focus indicators for all variants
  • Enhanced focus management for Windows High Contrast mode

Screen Readers

  • Semantic button elements with proper roles
  • Loading state announcements
  • Tooltip associations via aria-describedby
  • Disabled state announcements

Enhancements

Kookie UI extends Radix Themes Button with practical improvements:

Built-in Tooltip Support

Native tooltip prop eliminates need for wrapper components. Tooltips automatically provide accessibility through aria-describedby associations and support positioning via tooltipSide and tooltipAlign props.

Override Styles

Advanced overrideStyles prop enables per-state style customization using design tokens. Define styles for normal, hover, active, pressed, open, disabled, and focus states without writing CSS.

Enhanced Loading State

Loading state replaces all button content with a centered spinner and disables interaction. Screen readers announce loading context with proper aria-busy and aria-disabled attributes. For patterns where text should remain visible during loading, wrap icons in the Spinner component instead.

Flush Mode

flush prop removes padding for seamless text integration, especially with ghost variant. Creates inline button behavior while maintaining interactive touch targets.

Changelog

Added

  • Enhanced loading state accessibility with aria-busy, aria-disabled, and aria-describedby attributes
  • Built-in tooltip support through tooltip, tooltipSide, tooltipAlign props
  • New material prop for solid/translucent theme contexts
  • flush prop for seamless text integration with ghost variants
  • Full margin prop system with responsive support
  • Enhanced form integration with complete HTML form attribute support
  • RTL (Right-to-Left) layout support for international applications
  • Windows High Contrast mode support via forced-colors media query
  • Enhanced reduced motion support for users with vestibular disorders
  • Improved loading state announcements with contextual button text
  • Tooltip accessibility integration with proper aria-describedby associations

Changed

  • Improved 3D shadow system for classic variant with better depth perception
  • Enhanced active state feedback with subtle padding adjustments
  • Better contrast relationships in both light and dark themes
  • Comprehensive transition token system with duration and easing tokens
  • Automatic reduced motion support via prefers-reduced-motion

Deprecated

  • panelBackground prop in favor of material prop

Fixed

  • Better color saturation and brightness in high contrast mode
  • Enhanced focus indicators for improved accessibility
  • Consistent contrast ratios across all variants and states
  • Full TypeScript support for polymorphic as prop with better type inference
  • Loading state accessibility with proper button text context in announcements
  • Focus management with enhanced forced-colors support for Windows High Contrast
  • Spinner animations now respect prefers-reduced-motion user preference
  • Tooltip associations properly linked to buttons via unique IDs
© 2026 Kushagra Dhawan. Licensed under MIT. GitHub.

Theme

Accent color

Gray color

Appearance

Radius

Scaling

Panel background