Introduction
@anxndsgn/ink-ui

ink ui is a React component library built on top of Base UI primitives and styled with Tailwind CSS v4. It provides a cohesive set of accessible, unopinionated UI components with a warm, minimal aesthetic.

Philosophy

  • Built on Base UI — Every component extends a Base UI primitive, inheriting robust accessibility, keyboard navigation, and focus management.
  • Copy and paste — Components live in the registry/ directory as plain source files. Copy them into your project and customize freely.
  • Warm minimalism — A subtle gray and orange palette that feels approachable without being distracting.
  • Tailwind CSS v4 — Uses the new CSS-first configuration with @theme and semantic color tokens.

Prerequisites

Before using ink ui components, ensure your project is set up with:

  • React 19
  • Tailwind CSS 4
  • @base-ui/react

Installation

1. Add Tailwind and Base UI

Follow the Tailwind CSS v4 installation guide and the Base UI quick start for your framework.

2. Copy the theme

Copy the CSS variables into your global stylesheet. These define the warm color palette and semantic tokens used by all components.

@theme {
/* gray */
--color-gray-50: oklch(0.98559 0.002 48.697);
--color-gray-100: oklch(0.95 0.001 48.697);
--color-gray-200: oklch(0.925 0.002 48.697);
--color-gray-300: oklch(0.84598 0.002 48.697);
--color-gray-400: oklch(0.7374 0.002 48.697);
--color-gray-500: oklch(0.585 0.003 48.697);
--color-gray-600: oklch(0.46 0.002 48.697);
--color-gray-700: oklch(0.37 0.002 48.697);
--color-gray-800: oklch(0.31 0.001 48.697);
--color-gray-900: oklch(0.265 0.001 48.697);
--color-gray-950: oklch(0.21 0.002 48.697);

/_ red _/
--color-red-50: oklch(0.96453 0.015 24);
--color-red-100: oklch(0.95036 0.03 24);
--color-red-200: oklch(0.92601 0.045 24);
--color-red-300: oklch(0.8455 0.07 24);
--color-red-400: oklch(0.73813 0.135 24);
--color-red-500: oklch(0.63497 0.18 24);
--color-red-600: oklch(0.50144 0.15 24);
--color-red-700: oklch(0.38998 0.125 24);
--color-red-800: oklch(0.30985 0.095 24);
--color-red-900: oklch(0.26461 0.06 24);
--color-red-950: oklch(0.21016 0.04 24);

/_ orange _/
--color-orange-50: oklch(0.96453 0.02 41.5);
--color-orange-100: oklch(0.95036 0.025 41.5);
--color-orange-200: oklch(0.92601 0.03 41.5);
--color-orange-300: oklch(0.8455 0.07 41.5);
--color-orange-400: oklch(0.73813 0.14 41.5);
--color-orange-500: oklch(0.63497 0.17 41.5);
--color-orange-600: oklch(0.50144 0.135 41.5);
--color-orange-700: oklch(0.38998 0.08 41.5);
--color-orange-800: oklch(0.30985 0.065 41.5);
--color-orange-900: oklch(0.26461 0.055 41.5);
--color-orange-950: oklch(0.21016 0.04 41.5);
}

:root {
--background: oklch(0.98559 0.002 48.697);
--foreground: oklch(0.265 0.001 48.697);
--card: oklch(0.95 0.001 48.697);
--card-foreground: oklch(0.265 0.001 48.697);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.265 0.001 48.697);
--dialog: oklch(0.98559 0.002 48.697);
--dialog-foreground: oklch(0.265 0.001 48.697);
--primary: oklch(0.265 0.001 48.697);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.21 0.002 48.697 / 0.08);
--secondary-foreground: oklch(0.265 0.001 48.697);
--muted: oklch(0.95 0.001 48.697);
--muted-foreground: oklch(0.585 0.003 48.697);
--accent: oklch(0.63497 0.17 41.5);
--accent-foreground: oklch(1 0 0);
--destructive: oklch(0.63497 0.18 24);
--destructive-foreground: oklch(1 0 0);
--border: oklch(0.21 0.002 48.697 / 0.1);
--input: oklch(0.21 0.002 48.697 / 0.1);
--ring: oklch(0.63497 0.17 41.5 / 0.3);
}

[data-mode="dark"] {
--background: oklch(0.21 0.002 48.697);
--foreground: oklch(0.95 0.001 48.697);
--card: oklch(0.21 0.002 48.697);
--card-foreground: oklch(0.95 0.001 48.697);
--popover: oklch(0.265 0.001 48.697);
--popover-foreground: oklch(0.95 0.001 48.697);
--dialog: oklch(0.265 0.001 48.697);
--dialog-foreground: oklch(0.95 0.001 48.697);
--primary: oklch(0.95 0.001 48.697);
--primary-foreground: oklch(0.265 0.001 48.697);
--secondary: oklch(0.98559 0.002 48.697 / 0.08);
--secondary-foreground: oklch(0.95 0.001 48.697);
--muted: oklch(0.31 0.001 48.697);
--muted-foreground: oklch(0.7374 0.002 48.697);
--accent: oklch(0.50144 0.135 41.5);
--accent-foreground: oklch(1 0 0);
--destructive: oklch(0.63497 0.18 24);
--destructive-foreground: oklch(1 0 0);
--border: oklch(0.98559 0.002 48.697 / 0.1);
--input: oklch(0.98559 0.002 48.697 / 0.1);
--ring: oklch(0.63497 0.17 41.5 / 0.3);
}

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-dialog: var(--dialog);
--color-dialog-foreground: var(--dialog-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
}

See the Colors page for a full reference of all available tokens and scales.

3. Add utilities

Copy the cn helper into your project:

import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

4. Copy components

Browse the Components section and copy any component source into your project. Each component is a self-contained file with no hidden dependencies.

Components

ink ui includes the following components:

ComponentDescription
Alert DialogA modal dialog that interrupts the user for critical confirmations
ButtonInteractive button with multiple variants and sizes
CardContainer for grouping related content
CheckboxBinary toggle control
DialogModal window for secondary content
InputText input field with label support
MenuDropdown menu with items and submenus
PopoverFloating content panel anchored to a trigger
RadioSingle-select option from a group
SelectDropdown list for choosing one option
SwitchToggle switch for on/off states
TabsTabbed interface for switching content panels
TagCompact label for categories, statuses, or attributes

Dark mode

Dark mode is toggled by setting the data-mode="dark" attribute on the <html> element. All semantic tokens automatically adapt between light and dark contexts.