🌴Ocean UI
Base components

Button

A flexible button component that supports multiple variants, sizes, and states. Built with Ark UI factory for accessibility and styled using Ocean UI design tokens.

@ark-ui/react/factory

Example

How to install and use

1

Complete the manual installation setup

If you haven't already completed the first 4 steps of the manual installation guide, please do so before continuing to install these components.

2

Create a button.tsx file and paste the following code into it.

/components/ui/button.tsx
"use client";

import { cva, type VariantProps } from "class-variance-authority";
import { ark } from "@ark-ui/react/factory";
import { Loader2 } from "lucide-react";
import type { ComponentProps, ReactNode } from "react";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-semibold transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        primary:
          "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
        secondary:
          "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
        tertiary: "text-muted-foreground hover:bg-muted hover:text-foreground",
        outline:
          "border border-border bg-transparent text-foreground hover:bg-muted",
        ghost: "text-foreground hover:bg-muted",
        destructive:
          "bg-destructive text-white shadow-xs hover:bg-destructive/90",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        sm: "h-8 px-3 py-2 has-[>svg]:px-2.5",
        md: "h-9 px-3.5 py-2.5 has-[>svg]:px-3",
        lg: "h-10 px-4 py-2.5 has-[>svg]:px-3.5",
        xl: "h-11 px-4.5 py-3 has-[>svg]:px-4",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "md",
    },
  }
);

export interface ButtonProps
  extends ComponentProps<typeof ark.button>,
    VariantProps<typeof buttonVariants> {
  /**
   * Icon or element to display before the button text
   */
  iconLeading?: ReactNode;
  /**
   * Icon or element to display after the button text
   */
  iconTrailing?: ReactNode;
  /**
   * Show loading spinner and disable button
   * @default false
   */
  loading?: boolean;
  /**
   * The text to show while loading
   */
  loadingText?: ReactNode;
  /**
   * Keep text visible during loading state
   * @default false
   */
  showTextWhileLoading?: boolean;
}

function Button({
  variant,
  size,
  className,
  children,
  iconLeading,
  iconTrailing,
  loading = false,
  loadingText,
  showTextWhileLoading = false,
  disabled,
  ...props
}: ButtonProps) {
  const isDisabled = disabled || loading;
  const isIconOnly = (iconLeading || iconTrailing) && !children;

  return (
    <ark.button
      type="button"
      className={cn(
        buttonVariants({ variant, size }),
        loading && !showTextWhileLoading && "relative",
        isIconOnly && size === "sm" && "p-2",
        isIconOnly && size === "md" && "p-2.5",
        isIconOnly && size === "lg" && "p-3",
        isIconOnly && size === "xl" && "p-3.5",
        className
      )}
      disabled={isDisabled}
      aria-busy={loading}
      {...props}
    >
      {loading && (
        <Loader2
          className={cn(
            "animate-spin",
            !showTextWhileLoading &&
              "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
          )}
          aria-hidden="true"
        />
      )}
      {iconLeading && !loading && iconLeading}
      {children && (
        <span className={loading && !showTextWhileLoading ? "invisible" : ""}>
          {loading && loadingText ? loadingText : children}
        </span>
      )}
      {iconTrailing && !loading && iconTrailing}
    </ark.button>
  );
}

export { Button, buttonVariants };
3

Finally, Choose any example you like and add it to your project.
For instance, create a new file at components/shared/{example-component-name}.tsx, paste the example code into that file, and then import and use the component wherever you need it in your application.

The Button component uses Ark UI's factory function (ark.button) for built-in accessibility. It's styled with Tailwind CSS using Ocean UI design tokens from tokens.css.

Overview

The Button component is a fundamental UI element for triggering actions. It supports multiple visual styles, sizes, loading states, and icon placement. Built on top of Ark UI's factory function, it provides native button accessibility out of the box.

Variants

The Button component supports seven visual variants:

  • primary: Main action button with brand color background
  • secondary: Secondary action with subtle background and border
  • tertiary: Tertiary action with tertiary text color and hover background
  • outline: Outlined button with transparent background
  • ghost: Minimal button with no background, only text
  • destructive: For destructive actions (delete, remove)
  • link: Text button styled as a link

Sizes

The Button component supports four sizes:

  • sm: Small button (height: 32px)
  • md: Medium button (height: 36px) - default
  • lg: Large button (height: 40px)
  • xl: Extra large button (height: 44px)

Features

  • Icons: Support for leading and trailing icons
  • Loading State: Built-in loading spinner with optional loading text
  • Disabled State: Proper disabled styling and behavior
  • Accessibility: Full keyboard navigation and ARIA support via Ark UI
  • Customizable: All styles can be overridden via className prop

Select Examples

Primary

Primary variant buttons are used for main actions. They feature a brand color background and are the most prominent buttons in the interface.

Secondary

Secondary variant buttons are used for secondary actions. They have a subtle background and border, providing a less prominent alternative to primary buttons.

Tertiary

Tertiary variant buttons use tertiary text colors and show a background on hover. They're useful for less prominent actions that still need to be visible.

Outline

Outline variant buttons have a transparent background with a border. They're useful for actions that need to be visible but less prominent than primary or secondary buttons.

Ghost

Ghost variant buttons have no background and minimal styling. They're ideal for subtle actions or when you want the button to blend into the interface.

Destructive

Destructive variant buttons are used for dangerous or irreversible actions like deleting or removing content. They use error colors to indicate the severity of the action.

Link variant buttons are styled as text links with underline on hover. They're useful for navigation or less prominent actions.

With Icons

Buttons can include icons before or after the text, or both. Icons help communicate the action more clearly and improve visual hierarchy.

Leading Icon

Trailing Icon

Leading & Trailing Icons

Icon Only

Different Sizes with Icons

Loading

Buttons support loading states with a spinner. You can show the loading spinner alone or keep the text visible while loading.

Basic Loading

Loading with Text Visible

Loading with Custom Text

Loading with Icons

Loading Icon Only

Different Sizes Loading

Sizes

Buttons come in four sizes: small (sm), medium (md), large (lg), and extra large (xl). Choose the size that best fits your interface and action importance.

Primary

Secondary

Outline

Ghost

Destructive

Link

On this page