Installation
How to install dependencies and structure your app.
Next.js
Create a project
Start by creating a new Next.js project using create-next-app:
npx create-next-app@latest my-app --typescript --tailwind --eslint
Update tailwind.config.ts
import type { Config } from "tailwindcss";
const config = {
darkMode: ["class"],
content: ["./src/**/*.{ts,tsx}"],
prefix: "",
theme: {
extend: {
colors: {
bg: {
DEFAULT: "hsl(var(--color-bg))",
inverse: "hsl(var(--color-bg-inverse))",
surface: "hsl(var(--color-bg-surface))",
muted: "hsl(var(--color-bg-muted))",
disabled: "hsl(var(--color-bg-disabled))",
overlay: "hsl(var(--color-bg-overlay))",
tooltip: "hsl(var(--color-bg-tooltip))",
neutral: {
DEFAULT: "hsl(var(--color-bg-neutral))",
hover: "hsl(var(--color-bg-neutral-hover))",
active: "hsl(var(--color-bg-neutral-active))",
},
primary: {
DEFAULT: "hsl(var(--color-bg-primary))",
hover: "hsl(var(--color-bg-primary-hover))",
active: "hsl(var(--color-bg-primary-active))",
},
success: {
DEFAULT: "hsl(var(--color-bg-success))",
hover: "hsl(var(--color-bg-success-hover))",
active: "hsl(var(--color-bg-success-active))",
muted: {
DEFAULT: "hsl(var(--color-bg-success-muted))",
hover: "hsl(var(--color-bg-success-muted-hover))",
active: "hsl(var(--color-bg-success-muted-active))",
},
},
danger: {
DEFAULT: "hsl(var(--color-bg-danger))",
hover: "hsl(var(--color-bg-danger-hover))",
active: "hsl(var(--color-bg-danger-active))",
muted: {
DEFAULT: "hsl(var(--color-bg-danger-muted))",
hover: "hsl(var(--color-bg-danger-muted-hover))",
active: "hsl(var(--color-bg-danger-muted-active))",
},
},
warning: {
DEFAULT: "hsl(var(--color-bg-warning))",
hover: "hsl(var(--color-bg-warning-hover))",
active: "hsl(var(--color-bg-warning-active))",
muted: {
DEFAULT: "hsl(var(--color-bg-warning-muted))",
hover: "hsl(var(--color-bg-warning-muted-hover))",
active: "hsl(var(--color-bg-warning-muted-active))",
},
},
accent: {
DEFAULT: "hsl(var(--color-bg-accent))",
hover: "hsl(var(--color-bg-accent-hover))",
active: "hsl(var(--color-bg-accent-active))",
muted: {
DEFAULT: "hsl(var(--color-bg-accent-muted))",
hover: "hsl(var(--color-bg-accent-muted-hover))",
active: "hsl(var(--color-bg-accent-muted-active))",
},
},
},
fg: {
DEFAULT: "hsl(var(--color-fg))",
muted: {
DEFAULT: "hsl(var(--color-fg-muted))",
inverse: "hsl(var(--color-fg-muted-inverse))",
},
inverse: "hsl(var(--color-fg-inverse))",
disabled: "hsl(var(--color-fg-disabled))",
link: {
DEFAULT: "hsl(var(--color-fg-link))",
hover: "hsl(var(--color-fg-link-hover))",
active: "hsl(var(--color-fg-link-active))",
visited: "hsl(var(--color-fg-link-visited))",
},
accent: "hsl(var(--color-fg-accent))",
success: "hsl(var(--color-fg-success))",
warning: "hsl(var(--color-fg-warning))",
danger: "hsl(var(--color-fg-danger))",
info: "hsl(var(--color-fg-info))",
onAccent: "hsl(var(--color-fg-onAccent))",
onNeutral: "hsl(var(--color-fg-onNeutral))",
onPrimary: "hsl(var(--color-fg-onPrimary))",
onSuccess: "hsl(var(--color-fg-onSuccess))",
onMutedSuccess: "hsl(var(--color-fg-onMutedSuccess))",
onDanger: "hsl(var(--color-fg-onDanger))",
onMutedDanger: "hsl(var(--color-fg-onMutedDanger))",
onWarning: "hsl(var(--color-fg-onWarning))",
onMutedWarning: "hsl(var(--color-fg-onMutedWarning))",
onTooltip: "hsl(var(--color-fg-onTooltip))",
},
border: {
DEFAULT: "hsl(var(--color-border))",
field: "hsl(var(--color-border-field))",
control: "hsl(var(--color-border-control))",
hover: "hsl(var(--color-border-hover))",
active: "hsl(var(--color-border-active))",
disabled: "hsl(var(--color-border-disabled))",
danger: "hsl(var(--color-border-danger))",
success: "hsl(var(--color-border-success))",
warning: "hsl(var(--color-border-warning))",
info: "hsl(var(--color-border-info))",
secondary: "hsl(var(--color-border-secondary))",
focus: "hsl(var(--color-border-focus))",
inverse: "hsl(var(--color-border-inverse))",
},
},
transitionTimingFunction: {
drawer: "cubic-bezier(0.32,0.72,0,1)",
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"progress-grow": {
"0%": {
transform: "scaleX(0.01)",
},
"20%": {
transform: "scaleX(0.1)",
},
"30%": {
transform: "scaleX(0.6)",
},
"40%,50%": {
transform: "scaleX(0.9)",
},
"100%": {
transform: "scaleX(1)",
},
},
"progress-pulse": {
"0%": {
"mask-position": "200% center",
},
"100%": {
"mask-position": "0% center",
},
},
},
animation: {
"progress-indeterminate":
"progress-grow var(--progress-duration) 1 both normal, progress-pulse 1s ease var(--progress-duration) infinite normal none running",
},
},
},
plugins: [require("tailwindcss-animate"), require("tailwindcss-react-aria-components")],
} satisfies Config;
export default withTV(config);
Update globals.css
Add the following code to the globals.css file:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--color-black: 0 0 0%;
--color-white: 0 0 100%;
--color-neutral-100: 0 0% 98%; /* Background layers (default bg) */
--color-neutral-200: 0 0% 79%; /* Background layers (Bg secondary/muted) */ /* Decorative border */
--color-neutral-300: 0 0% 73%; /* Decorative border */
--color-neutral-400: 0 0% 65%; /* Field border */
--color-neutral-500: 0 0% 54%; /* Disabled Text */
--color-neutral-600: 0 0% 42%; /* control border (switch/checkbox/radio) */
--color-neutral-700: 0 0% 35%; /* Text secondary (muted) */
--color-neutral-800: 0 0% 28%; /* Text */
--color-neutral-900: 0 0% 16%; /* Heading Text */
--color-neutral-1000: 0 0% 12%;
--color-primary-100: 0 0% 87%;
--color-primary-200: 0 0% 79%;
--color-primary-300: 0 0% 73%;
--color-primary-400: 0 0% 65%;
--color-primary-500: 0 0% 54%;
--color-primary-600: 0 0% 42%;
--color-primary-700: 0 0% 35%;
--color-primary-800: 0 0% 28%;
--color-primary-900: 0 0% 16%;
--color-primary-1000: 0 0% 12%;
--color-success-100: 130 34% 83%;
--color-success-200: 131 35% 75%;
--color-success-300: 131 35% 66%;
--color-success-400: 132 35% 56%;
--color-success-500: 131 41% 43%;
--color-success-600: 132 41% 34%;
--color-success-700: 132 41% 28%;
--color-success-800: 131 41% 23%;
--color-success-900: 131 40% 13%;
--color-success-1000: 132 42% 9%;
--color-warning-100: 35 100% 80%;
--color-warning-200: 35 100% 69%;
--color-warning-300: 35 100% 58%;
--color-warning-400: 35 93% 49%;
--color-warning-500: 35 92% 41%;
--color-warning-600: 35 93% 32%;
--color-warning-700: 35 93% 26%;
--color-warning-800: 35 93% 22%;
--color-warning-900: 35 94% 12%;
--color-warning-1000: 36 91% 9%;
--color-danger-100: 358 69% 90%;
--color-danger-200: 358 69% 85%;
--color-danger-300: 358 70% 79%;
--color-danger-400: 358 69% 73%;
--color-danger-500: 358 69% 63%;
--color-danger-600: 358 64% 49%;
--color-danger-700: 358 63% 41%;
--color-danger-800: 358 64% 33%;
--color-danger-900: 357 64% 20%;
--color-danger-1000: 358 65% 15%;
--color-accent-100: 210 100% 88%;
--color-accent-200: 210 100% 81%;
--color-accent-300: 210 100% 74%;
--color-accent-400: 210 100% 67%;
--color-accent-500: 210 64% 55%;
--color-accent-600: 210 51% 44%;
--color-accent-700: 210 51% 36%;
--color-accent-800: 210 52% 29%;
--color-accent-900: 211 52% 17%;
--color-accent-1000: 211 52% 12%;
--radius: 0.5rem;
--color-bg: var(--color-neutral-100);
--color-bg-muted: var(--color-neutral-200);
--color-bg-inverse: var(--color-neutral-1000);
--color-bg-disabled: var(--color-neutral-200);
--color-bg-tooltip: var(--color-neutral-500);
--color-bg-overlay: var(--color-neutral-200);
--color-bg-neutral: var(--color-neutral-300);
--color-bg-neutral-hover: var(--color-neutral-400);
--color-bg-neutral-active: var(--color-neutral-500);
--color-bg-primary: var(--color-neutral-1000);
--color-bg-primary-hover: var(--color-neutral-900);
--color-bg-primary-active: var(--color-neutral-800);
--color-bg-success: var(--color-success-500);
--color-bg-success-hover: var(--color-success-600);
--color-bg-success-active: var(--color-success-700);
--color-bg-success-muted: var(--color-success-300);
--color-bg-success-muted-hover: var(--color-success-300);
--color-bg-success-muted-active: var(--color-success-300);
--color-bg-danger: var(--color-danger-500);
--color-bg-danger-hover: var(--color-danger-600);
--color-bg-danger-active: var(--color-danger-700);
--color-bg-danger-muted: var(--color-danger-300);
--color-bg-danger-muted-hover: var(--color-danger-300);
--color-bg-danger-muted-active: var(--color-danger-300);
--color-bg-warning: var(--color-warning-500);
--color-bg-warning-hover: var(--color-warning-600);
--color-bg-warning-active: var(--color-warning-700);
--color-bg-warning-muted: var(--color-warning-300);
--color-bg-warning-muted-hover: var(--color-warning-300);
--color-bg-warning-muted-active: var(--color-warning-300);
--color-bg-accent: var(--color-accent-500);
--color-bg-accent-hover: var(--color-accent-600);
--color-bg-accent-active: var(--color-accent-700);
--color-bg-accent-muted: var(--color-accent-300);
--color-bg-accent-muted-hover: var(--color-accent-300);
--color-bg-accent-muted-active: var(--color-accent-300);
--color-fg: var(--color-neutral-1000);
--color-fg-muted: var(--color-neutral-800);
--color-fg-inverse: var(--color-neutral-100);
--color-fg-disabled: var(--color-neutral-500);
--color-fg-danger: var(--color-danger-600);
--color-fg-warning: var(--color-warning-600);
--color-fg-success: var(--color-success-600);
--color-fg-accent: var(--color-accent-600);
--color-fg-link: var(--color-neutral-1000);
--color-fg-link-hover: var(--color-neutral-1000);
--color-fg-link-active: var(--color-neutral-1000);
--color-fg-link-visited: var(--color-neutral-1000);
--color-fg-onAccent: var(--color-neutral-1000);
--color-fg-onNeutral: var(--color-neutral-1000);
--color-fg-onPrimary: var(--color-neutral-100);
--color-fg-onSuccess: var(--color-neutral-1000);
--color-fg-onMutedSuccess: var(--color-neutral-1000);
--color-fg-onDanger: var(--color-neutral-1000);
--color-fg-onMutedDanger: var(--color-neutral-1000);
--color-fg-onWarning: var(--color-neutral-1000);
--color-fg-onMutedWarning: var(--color-neutral-1000);
--color-fg-onTooltip: var(--color-neutral-1000);
--color-border: var(--color-neutral-300);
--color-border-field: var(--color-neutral-400);
--color-border-control: var(--color-neutral-700);
--color-border-hover: var(--color-neutral-1000);
--color-border-active: var(--color-neutral-500);
--color-border-disabled: var(--color-neutral-300);
--color-border-success: var(--color-success-400);
--color-border-danger: var(--color-danger-400);
--color-border-warning: var(--color-warning-400);
--color-border-info: var(--color-info-400);
--color-border-accent: var(--color-accent-500);
--color-border-secondary: var(--color-neutral-200);
--color-border-focus: var(--color-accent-500);
--color-border-inverse: var(--color-neutral-500);
}
.dark {
--color-neutral-100: 240 6% 8%;
--color-neutral-200: 240 6% 13%;
--color-neutral-300: 240 6% 19%;
--color-neutral-400: 240 6% 25%;
--color-neutral-500: 240 6% 33%;
--color-neutral-600: 240 6% 47%;
--color-neutral-700: 240 6% 55%;
--color-neutral-800: 240 6% 70%;
--color-neutral-900: 240 6% 85%;
--color-neutral-1000: 240 10% 98%;
--color-success-100: 132 42% 9%;
--color-success-200: 130 41% 14%;
--color-success-300: 131 41% 18%;
--color-success-400: 132 41% 22%;
--color-success-500: 131 41% 44%;
--color-success-600: 131 41% 48%;
--color-success-700: 131 41% 54%;
--color-success-800: 131 34% 70%;
--color-success-900: 131 34% 76%;
--color-success-1000: 130 35% 83%;
--color-warning-100: 34 100% 10%;
--color-warning-200: 34 100% 20%;
--color-warning-300: 35 100% 40%;
--color-warning-400: 35 100% 48%;
--color-warning-500: 35 100% 50%;
--color-warning-600: 35 100% 58%;
--color-warning-700: 35 100% 66%;
--color-warning-800: 35 93% 65%;
--color-warning-900: 35 100% 72%;
--color-warning-1000: 35 100% 80%;
--color-danger-100: 357 64% 14%;
--color-danger-200: 358 64% 21%;
--color-danger-300: 358 64% 26%;
--color-danger-400: 358 64% 32%;
--color-danger-500: 358 64% 42%;
--color-danger-600: 358 69% 51%;
--color-danger-700: 358 69% 60%;
--color-danger-800: 358 69% 70%;
--color-danger-900: 359 70% 86%;
--color-danger-1000: 358 69% 90%;
--color-accent-100: 211 51% 12%;
--color-accent-200: 211 52% 18%;
--color-accent-300: 210 52% 29%;
--color-accent-400: 209 51% 40%;
--color-accent-500: 210 52% 48%;
--color-accent-600: 210 51% 55%;
--color-accent-700: 210 68% 62%;
--color-accent-800: 210 97% 71%;
--color-accent-900: 210 100% 82%;
--color-accent-1000: 210 100% 87%;
--color-fg-onWarning: var(--color-black);
--color-fg-onTooltip: var(--color-white);
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Install the following dependencies:
npm i clsx tailwind-merge tailwind-variants
Add utils/classes.ts
Add utilities for classnames:
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
You're done.
You can now start adding components of your choice