@hex-core/components

59 Radix + CVA components incl. layout primitives + ColorPicker, Tailwind v4 entry.

Install

Version 1.4.0Runtime dependency
pnpm
pnpm add @hex-core/components

What it does

`@hex-core/components` is the runtime React component library. It ships 59 Radix + CVA components, including six layout primitives and an HSL ColorPicker.

The package owns the visual contract. `@hex-core/tokens` decides what colors mean; `@hex-core/components` decides how a Button looks when it's `variant="outline" size="sm"`. Consumers wire the two together via Tailwind v4's `@theme` block in their globals.css.

In Tailwind v4, utility classes inside published bundles are not auto-scanned. A `tailwind.css` entry (since 1.2.1) ships that adds the right `@source` directive — one `@import "@hex-core/components/tailwind.css"` and you're done.

Public API

Button

ts
import { Button, type ButtonProps } from "@hex-core/components";

interface ButtonProps {
	variant?: "default" | "outline" | "secondary" | "ghost" | "link" | "destructive";
	size?: "default" | "sm" | "lg" | "icon";
	asChild?: boolean;
}

Polymorphic button with six variants and four sizes. `asChild` swaps the rendered element for the child component while preserving the variant classes — useful for wrapping `<Link>` or anchor tags without losing the visual contract.

tsx
import { Button } from "@hex-core/components";

export function ConfirmRow() {
	return (
		<div className="flex gap-2">
			<Button variant="outline">Cancel</Button>
			<Button>Confirm</Button>
		</div>
	);
}

Stack / Cluster / Grid / Container / Spacer / AspectRatio

ts
import {
	Stack, Cluster, Grid, Container, Spacer, AspectRatio,
} from "@hex-core/components";

interface StackProps {
	gap?: "0" | "1" | "2" | "3" | "4" | "6" | "8";
	align?: "start" | "center" | "end" | "stretch";
	justify?: "start" | "center" | "end" | "between";
	asChild?: boolean;
}

Layout primitives shipped in 1.2.1. Stack = vertical flex; Cluster = horizontal flex with wrap; Grid = 1/2/3/4/6 columns or `auto-fit` with `minColWidth`; Container = max-width + padding; Spacer = declarative whitespace; AspectRatio = Radix re-export. They all share the same `gap` scale (drawn from `--space-*` tokens), so mixing them never produces uneven seams.

tsx
<Stack gap="4">
	<h2>Account</h2>
	<Cluster gap="2" wrap>
		<Button variant="outline">Edit</Button>
		<Button variant="outline">Reset</Button>
	</Cluster>
</Stack>

ColorPicker

ts
import { ColorPicker, type ColorPickerProps } from "@hex-core/components";

interface ColorPickerProps {
	mode: "hsl";
	value: string; // "240 5.9% 10%" — same triplet @hex-core/tokens emits
	onChange: (next: string) => void;
}

HSL color picker. Round-trips losslessly through the same `<H> <S>% <L>%` triplet `@hex-core/tokens` exports, so binding it to a token override doesn't introduce float drift.

tsx
import { ColorPicker } from "@hex-core/components";
import { useState } from "react";

export function PrimaryEditor() {
	const [hsl, setHsl] = useState("240 5.9% 10%");
	return <ColorPicker mode="hsl" value={hsl} onChange={setHsl} />;
}

Color utilities

ts
import {
	parseHslTriplet, formatHslTriplet,
	hslToRgb, rgbToHsl,
	hslTripletToHex, hexToHslTriplet,
	type HslTriplet, type RgbColor,
} from "@hex-core/components";

Pure conversions between the four canonical color formats the design system speaks: HSL triplets, HSL objects, RGB objects, and hex. Use these instead of hand-rolled regex — they handle the percent-vs-decimal lightness format that bites every consumer.

tsx
import { hexToHslTriplet, hslTripletToHex } from "@hex-core/components";

const triplet = hexToHslTriplet("#FF5733"); // "11 100% 60%"
const hex = hslTripletToHex("240 5.9% 10%"); // "#18181B"

tailwind.css entry

ts
/* @hex-core/components/tailwind.css — exported entry, not a TS API */
@source "./dist/*.js";

Single-line replacement for the manual `@source "../../node_modules/@hex-core/components/dist/*.js"` Tailwind v4 directive. Required for utility classes inside the published bundle to compile in your app. Add the import to your globals.css after `@import "tailwindcss";`.

css
@import "tailwindcss";
@import "@hex-core/components/tailwind.css";

@theme {
	--color-background: hsl(0 0% 100%);
	--color-foreground: hsl(240 10% 3.9%);
	/* ... */
}

Workflows

Add the package to a fresh Next.js + Tailwind v4 app

Three commands and one CSS import. Works the same on Vite, Remix, or any Tailwind v4 host.

bash
pnpm add @hex-core/components @hex-core/tokens
# ensure tailwindcss + @tailwindcss/postcss are already installed

Compose a settings card with layout primitives

Showcases Stack + Cluster + Grid sharing the same gap scale. No hand-rolled flex utilities.

tsx
import {
	Stack, Cluster, Grid, Card, CardHeader, CardContent,
	Button, Input, Label, Switch,
} from "@hex-core/components";

export function NotificationSettings() {
	return (
		<Card>
			<CardHeader><h2>Notifications</h2></CardHeader>
			<CardContent>
				<Stack gap="4">
					<Grid cols={2} gap="3">
						<Stack gap="1">
							<Label>Email</Label>
							<Input type="email" />
						</Stack>
						<Stack gap="1">
							<Label>Push</Label>
							<Switch />
						</Stack>
					</Grid>
					<Cluster gap="2" justify="end">
						<Button variant="outline">Cancel</Button>
						<Button>Save</Button>
					</Cluster>
				</Stack>
			</CardContent>
		</Card>
	);
}

Compatibility

  • Peer deps: React 18 or 19, Tailwind CSS v4.
  • Optional peers: `@tanstack/react-table` 8 or 9 (only for the DataTable component); `react-hook-form` 7 (only for the Form helpers).
  • Bundles `@hex-core/registry@^0.2.1` as a transitive dep — no `workspace:^` republish bug.
  • All components ship as ESM only.

See also