export const title = "TanStack Start integration"
export const description = "Set up a TanStack Start project with Untitled UI React in seconds. Our TanStack Start starter kit includes providers, routing, and everything you need to get started."
## Installation
The easiest way to get started with Untitled UI in a TanStack Start project is to use our CLI:
Using our CLI, you can initialize a new TanStack Start project with Untitled UI with the following command:
```bash
npx untitledui@latest init untitled-ui --tanstack
```
While running the command, you'll be asked which color to use as the brand color:
```bash
? Which color would you like to use as the brand color? › - Use arrow-keys. Return to submit.
❯ brand
red
orange
amber
yellow
lime
green
...
zinc
neutral
stone
```
This will create a new TanStack Start project in the `untitled-ui` directory with all the necessary configurations and components pre-installed. The choices are Tailwind's built-in palettes; picking one points the `--color-brand-*` variables in `theme.css` at it, and `brand` keeps the default purple. You can change the brand color at any time later (see [Theming](/docs/theming)). If you leave out the directory name, the CLI asks for it first.
Once it's done, start the development server, which serves the app at `http://localhost:3000`:
```bash
cd untitled-ui
npm run dev
```
Great! You're all set to start using Untitled UI components.
If something is missing, you can copy/paste what you need into your project directly from individual [components](/components) pages.
**Need help?** Check our [GitHub repository](https://github.com/untitleduico/react/) for examples, or [open an issue](https://github.com/untitleduico/react/issues/) if you run into any problems. Our community is here to help!
## Manual installation
If you prefer to add Untitled UI to an existing TanStack Start project, you can follow these steps:
The TanStack Start template already includes `tailwindcss` and `@tailwindcss/vite`, so install the remaining packages with your favorite package manager:
```bash
npm install @untitledui/icons react-aria-components tailwindcss-react-aria-components tailwind-merge tailwindcss-animate
```
```bash
yarn add @untitledui/icons react-aria-components tailwindcss-react-aria-components tailwind-merge tailwindcss-animate
```
```bash
bun add @untitledui/icons react-aria-components tailwindcss-react-aria-components tailwind-merge tailwindcss-animate
```
TanStack Start runs on Vite, and the template's `vite.config.ts` already has everything Untitled UI needs:
```ts
import { defineConfig } from 'vite'
import { devtools } from '@tanstack/devtools-vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
const config = defineConfig({
resolve: { tsconfigPaths: true },
plugins: [devtools(), tailwindcss(), tanstackStart(), viteReact()],
})
export default config
```
This configuration does two important things:
1. Adds the Tailwind CSS Vite plugin for efficient processing
2. Resolves the `@/` path alias from your `tsconfig.json`
If your project was set up differently, make sure both are in place.
The template's `tsconfig.json` already maps `@/*` to `./src/*`. Change `lib` from `ES2022` to `ESNext`, because some components use newer JavaScript methods such as `Array.prototype.toReversed()`:
```json
{
"compilerOptions": {
// other options...
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"paths": {
"#/*": ["./src/*"],
"@/*": ["./src/*"]
}
}
}
```
Create a `theme.css` file in `src/styles` and add the following code:
{/* @embed:start file="styles/theme.css" lang="css" */}
```css
@theme {
/* FONT FAMILY */
--font-body: var(--font-inter, "Inter"), -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
--font-display: var(--font-inter, "Inter"), -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
--font-mono: ui-monospace, "Roboto Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* FONT SIZE */
--text-xs: calc(var(--spacing) * 3);
--text-xs--line-height: calc(var(--spacing) * 4.5);
--text-sm: calc(var(--spacing) * 3.5);
--text-sm--line-height: calc(var(--spacing) * 5);
--text-md: calc(var(--spacing) * 4);
--text-md--line-height: calc(var(--spacing) * 6);
--text-lg: calc(var(--spacing) * 4.5);
--text-lg--line-height: calc(var(--spacing) * 7);
--text-xl: calc(var(--spacing) * 5);
--text-xl--line-height: calc(var(--spacing) * 7.5);
--text-display-xs: calc(var(--spacing) * 6);
--text-display-xs--line-height: calc(var(--spacing) * 8);
--text-display-sm: calc(var(--spacing) * 7.5);
--text-display-sm--line-height: calc(var(--spacing) * 9.5);
--text-display-md: calc(var(--spacing) * 9);
--text-display-md--line-height: calc(var(--spacing) * 11);
--text-display-md--letter-spacing: -0.72px;
--text-display-lg: calc(var(--spacing) * 12);
--text-display-lg--line-height: calc(var(--spacing) * 15);
--text-display-lg--letter-spacing: -0.96px;
--text-display-xl: calc(var(--spacing) * 15);
--text-display-xl--line-height: calc(var(--spacing) * 18);
--text-display-xl--letter-spacing: -1.2px;
--text-display-2xl: calc(var(--spacing) * 18);
--text-display-2xl--line-height: calc(var(--spacing) * 22.5);
--text-display-2xl--letter-spacing: -1.44px;
/* MAX WIDTH */
--max-width-container: 1280px;
/* BREAKPOINTS */
--breakpoint-xxs: 320px;
/* This must match the breakpoint in Sonner: https://github.com/emilkowalski/sonner/blob/main/src/styles.css */
--breakpoint-xs: 600px;
/* RADIUS */
--radius-none: 0px;
--radius-xs: 0.125rem;
--radius-sm: 0.25rem;
--radius-DEFAULT: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
--radius-3xl: 1.5rem;
--radius-full: 9999px;
/* SHADOW */
--shadow-xs: 0px 1px 2px rgba(0, 0, 0, 0.05);
--shadow-sm: 0px 1px 3px rgba(0, 0, 0, 0.1), 0px 1px 2px -1px rgba(0, 0, 0, 0.1);
--shadow-md: 0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.06);
--shadow-lg: 0px 12px 16px -4px rgba(0, 0, 0, 0.08), 0px 4px 6px -2px rgba(0, 0, 0, 0.03), 0px 2px 2px -1px rgba(0, 0, 0, 0.04);
--shadow-xl: 0px 20px 24px -4px rgba(0, 0, 0, 0.08), 0px 8px 8px -4px rgba(0, 0, 0, 0.03), 0px 3px 3px -1.5px rgba(0, 0, 0, 0.04);
--shadow-2xl: 0px 24px 48px -12px rgba(0, 0, 0, 0.18), 0px 4px 4px -2px rgba(0, 0, 0, 0.04);
--shadow-3xl: 0px 32px 64px -12px rgba(0, 0, 0, 0.14), 0px 5px 5px -2.5px rgba(0, 0, 0, 0.04);
--shadow-skeuomorphic: 0px 0px 0px 1px rgba(0, 0, 0, 0.18) inset, 0px -2px 0px 0px rgba(0, 0, 0, 0.05) inset;
--shadow-xs-skeuomorphic: var(--shadow-skeuomorphic), var(--shadow-xs);
--shadow-modern-mockup-inner-lg: 0px 0px 3.765px 1.255px rgba(10, 13, 18, 0.08) inset, 0px 0px 2.51px 1.255px rgba(10, 13, 18, 0.03) inset;
--shadow-modern-mockup-inner-md: 0px 0px 1.692px 0.564px rgba(10, 13, 18, 0.08) inset, 0px 0px 1.128px 0.564px rgba(10, 13, 18, 0.03) inset;
--shadow-modern-mockup-inner-sm: 0px 0px 4.48px 1.493px rgba(10, 13, 18, 0.08) inset, 0px 0px 2.987px 1.493px rgba(10, 13, 18, 0.03) inset;
--shadow-modern-mockup-outer-lg:
0px 7.529px 10.039px -2.51px rgba(10, 13, 18, 0.08), 0px 2.51px 3.765px -1.255px rgba(10, 13, 18, 0.03),
0px 1.255px 1.255px -0.627px rgba(10, 13, 18, 0.04);
--shadow-modern-mockup-outer-md:
0px 3.385px 4.513px -1.128px rgba(10, 13, 18, 0.08), 0px 1.128px 1.692px -0.564px rgba(10, 13, 18, 0.03),
0px 0.564px 0.564px -0.282px rgba(10, 13, 18, 0.04);
--drop-shadow-iphone-mockup: 20px 12px 18px rgba(16, 24, 40, 0.2);
/* ANIMATIONS */
--animate-marquee: marquee 60s linear infinite;
--animate-caret-blink: caret-blink 1s infinite;
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
@keyframes caret-blink {
0%,
50% {
opacity: 1;
}
51%,
100% {
opacity: 0;
}
}
/* BASE COLOR */
--color-transparent: rgb(0 0 0 / 0);
--color-white: rgb(255 255 255);
--color-black: rgb(0 0 0);
/* These will be inverted in dark mode. */
--color-alpha-white: rgb(255 255 255);
--color-alpha-black: rgb(0 0 0);
--color-brand-50: rgb(249 245 255);
--color-brand-100: rgb(244 235 255);
--color-brand-200: rgb(233 215 254);
--color-brand-300: rgb(214 187 251);
--color-brand-400: rgb(182 146 246);
--color-brand-500: rgb(158 119 237);
--color-brand-600: rgb(127 86 217);
--color-brand-700: rgb(105 65 198);
--color-brand-800: rgb(83 56 158);
--color-brand-900: rgb(66 48 125);
--color-brand-950: rgb(44 28 95);
/* LIGHT MODE VARIABLES */
--color-alpha-white: rgb(255 255 255);
--color-alpha-black: rgb(0 0 0);
/* UTILITY COLORS */
--color-utility-blue-50: var(--color-blue-50);
--color-utility-blue-100: var(--color-blue-100);
--color-utility-blue-200: var(--color-blue-200);
--color-utility-blue-300: var(--color-blue-300);
--color-utility-blue-400: var(--color-blue-400);
--color-utility-blue-500: var(--color-blue-500);
--color-utility-blue-600: var(--color-blue-600);
--color-utility-blue-700: var(--color-blue-700);
--color-utility-brand-50: var(--color-brand-50);
--color-utility-brand-50_alt: var(--color-brand-50);
--color-utility-brand-100: var(--color-brand-100);
--color-utility-brand-100_alt: var(--color-brand-100);
--color-utility-brand-200: var(--color-brand-200);
--color-utility-brand-200_alt: var(--color-brand-200);
--color-utility-brand-300: var(--color-brand-300);
--color-utility-brand-300_alt: var(--color-brand-300);
--color-utility-brand-400: var(--color-brand-400);
--color-utility-brand-400_alt: var(--color-brand-400);
--color-utility-brand-500: var(--color-brand-500);
--color-utility-brand-500_alt: var(--color-brand-500);
--color-utility-brand-600: var(--color-brand-600);
--color-utility-brand-600_alt: var(--color-brand-600);
--color-utility-brand-700: var(--color-brand-700);
--color-utility-brand-700_alt: var(--color-brand-700);
--color-utility-brand-800: var(--color-brand-800);
--color-utility-brand-800_alt: var(--color-brand-800);
--color-utility-brand-900: var(--color-brand-900);
--color-utility-brand-900_alt: var(--color-brand-900);
--color-utility-neutral-50: var(--color-neutral-50);
--color-utility-neutral-100: var(--color-neutral-100);
--color-utility-neutral-200: var(--color-neutral-200);
--color-utility-neutral-300: var(--color-neutral-300);
--color-utility-neutral-400: var(--color-neutral-400);
--color-utility-neutral-500: var(--color-neutral-500);
--color-utility-neutral-600: var(--color-neutral-600);
--color-utility-neutral-700: var(--color-neutral-700);
--color-utility-neutral-800: var(--color-neutral-800);
--color-utility-neutral-900: var(--color-neutral-900);
--color-utility-red-50: var(--color-red-50);
--color-utility-red-100: var(--color-red-100);
--color-utility-red-200: var(--color-red-200);
--color-utility-red-300: var(--color-red-300);
--color-utility-red-400: var(--color-red-400);
--color-utility-red-500: var(--color-red-500);
--color-utility-red-600: var(--color-red-600);
--color-utility-red-700: var(--color-red-700);
--color-utility-yellow-50: var(--color-yellow-50);
--color-utility-yellow-100: var(--color-yellow-100);
--color-utility-yellow-200: var(--color-yellow-200);
--color-utility-yellow-300: var(--color-yellow-300);
--color-utility-yellow-400: var(--color-yellow-400);
--color-utility-yellow-500: var(--color-yellow-500);
--color-utility-yellow-600: var(--color-yellow-600);
--color-utility-yellow-700: var(--color-yellow-700);
--color-utility-green-50: var(--color-green-50);
--color-utility-green-100: var(--color-green-100);
--color-utility-green-200: var(--color-green-200);
--color-utility-green-300: var(--color-green-300);
--color-utility-green-400: var(--color-green-400);
--color-utility-green-500: var(--color-green-500);
--color-utility-green-600: var(--color-green-600);
--color-utility-green-700: var(--color-green-700);
--color-utility-orange-50: var(--color-orange-50);
--color-utility-orange-100: var(--color-orange-100);
--color-utility-orange-200: var(--color-orange-200);
--color-utility-orange-300: var(--color-orange-300);
--color-utility-orange-400: var(--color-orange-400);
--color-utility-orange-500: var(--color-orange-500);
--color-utility-orange-600: var(--color-orange-600);
--color-utility-orange-700: var(--color-orange-700);
--color-utility-indigo-50: var(--color-indigo-50);
--color-utility-indigo-100: var(--color-indigo-100);
--color-utility-indigo-200: var(--color-indigo-200);
--color-utility-indigo-300: var(--color-indigo-300);
--color-utility-indigo-400: var(--color-indigo-400);
--color-utility-indigo-500: var(--color-indigo-500);
--color-utility-indigo-600: var(--color-indigo-600);
--color-utility-indigo-700: var(--color-indigo-700);
--color-utility-fuchsia-50: var(--color-fuchsia-50);
--color-utility-fuchsia-100: var(--color-fuchsia-100);
--color-utility-fuchsia-200: var(--color-fuchsia-200);
--color-utility-fuchsia-300: var(--color-fuchsia-300);
--color-utility-fuchsia-400: var(--color-fuchsia-400);
--color-utility-fuchsia-500: var(--color-fuchsia-500);
--color-utility-fuchsia-600: var(--color-fuchsia-600);
--color-utility-fuchsia-700: var(--color-fuchsia-700);
--color-utility-pink-50: var(--color-pink-50);
--color-utility-pink-100: var(--color-pink-100);
--color-utility-pink-200: var(--color-pink-200);
--color-utility-pink-300: var(--color-pink-300);
--color-utility-pink-400: var(--color-pink-400);
--color-utility-pink-500: var(--color-pink-500);
--color-utility-pink-600: var(--color-pink-600);
--color-utility-pink-700: var(--color-pink-700);
--color-utility-purple-50: var(--color-purple-50);
--color-utility-purple-100: var(--color-purple-100);
--color-utility-purple-200: var(--color-purple-200);
--color-utility-purple-300: var(--color-purple-300);
--color-utility-purple-400: var(--color-purple-400);
--color-utility-purple-500: var(--color-purple-500);
--color-utility-purple-600: var(--color-purple-600);
--color-utility-purple-700: var(--color-purple-700);
--color-utility-sky-50: var(--color-sky-50);
--color-utility-sky-100: var(--color-sky-100);
--color-utility-sky-200: var(--color-sky-200);
--color-utility-sky-300: var(--color-sky-300);
--color-utility-sky-400: var(--color-sky-400);
--color-utility-sky-500: var(--color-sky-500);
--color-utility-sky-600: var(--color-sky-600);
--color-utility-sky-700: var(--color-sky-700);
--color-utility-slate-50: var(--color-slate-50);
--color-utility-slate-100: var(--color-slate-100);
--color-utility-slate-200: var(--color-slate-200);
--color-utility-slate-300: var(--color-slate-300);
--color-utility-slate-400: var(--color-slate-400);
--color-utility-slate-500: var(--color-slate-500);
--color-utility-slate-600: var(--color-slate-600);
--color-utility-slate-700: var(--color-slate-700);
--color-utility-emerald-50: var(--color-emerald-50);
--color-utility-emerald-100: var(--color-emerald-100);
--color-utility-emerald-200: var(--color-emerald-200);
--color-utility-emerald-300: var(--color-emerald-300);
--color-utility-emerald-400: var(--color-emerald-400);
--color-utility-emerald-500: var(--color-emerald-500);
--color-utility-emerald-600: var(--color-emerald-600);
--color-utility-emerald-700: var(--color-emerald-700);
--color-utility-amber-50: var(--color-amber-50);
--color-utility-amber-100: var(--color-amber-100);
--color-utility-amber-200: var(--color-amber-200);
--color-utility-amber-300: var(--color-amber-300);
--color-utility-amber-400: var(--color-amber-400);
--color-utility-amber-500: var(--color-amber-500);
--color-utility-amber-600: var(--color-amber-600);
--color-utility-amber-700: var(--color-amber-700);
/* TEXT COLORS */
--color-text-primary: var(--color-neutral-900);
--color-text-tertiary: var(--color-neutral-600);
--color-text-error-primary: var(--color-red-600);
--color-text-warning-primary: var(--color-yellow-600);
--color-text-success-primary: var(--color-green-600);
--color-text-white: var(--color-white);
--color-text-secondary: var(--color-neutral-700);
--color-text-secondary_hover: var(--color-neutral-800);
--color-text-tertiary_hover: var(--color-neutral-700);
--color-text-brand-secondary: var(--color-brand-700);
--color-text-placeholder: var(--color-neutral-500);
--color-text-brand-tertiary: var(--color-brand-600);
--color-text-editor-icon-fg: var(--color-neutral-400);
--color-text-editor-icon-fg_active: var(--color-neutral-500);
--color-text-quaternary: var(--color-neutral-500);
--color-text-brand-primary: var(--color-brand-900);
--color-text-primary_on-brand: var(--color-white);
--color-text-secondary_on-brand: var(--color-brand-200);
--color-text-tertiary_on-brand: var(--color-brand-200);
--color-text-quaternary_on-brand: var(--color-brand-300);
--color-text-brand-tertiary_alt: var(--color-brand-600);
--color-text-error-primary_hover: var(--color-red-700);
--color-text-brand-secondary_hover: var(--color-brand-800);
/* BORDER COLORS */
--color-border-primary: var(--color-neutral-300);
--color-border-secondary: var(--color-neutral-200);
--color-border-secondary_alt: rgb(0 0 0 / 0.1);
--color-border-tertiary: var(--color-neutral-100);
--color-border-error: var(--color-red-500);
--color-border-error_subtle: var(--color-red-300);
--color-border-brand: var(--color-brand-500);
--color-border-brand_alt: var(--color-brand-600);
/* FOREGROUND COLORS */
--color-fg-secondary: var(--color-neutral-700);
--color-fg-warning-primary: var(--color-yellow-600);
--color-fg-success-primary: var(--color-green-600);
--color-fg-white: var(--color-white);
--color-fg-success-secondary: var(--color-green-500);
--color-fg-secondary_hover: var(--color-neutral-800);
--color-fg-primary: var(--color-neutral-900);
--color-fg-brand-secondary: var(--color-brand-500);
--color-fg-brand-primary: var(--color-brand-600);
--color-fg-quaternary: var(--color-neutral-400);
--color-fg-quaternary_hover: var(--color-neutral-500);
--color-fg-error-primary: var(--color-red-600);
--color-fg-warning-secondary: var(--color-yellow-500);
--color-fg-error-secondary: var(--color-red-500);
--color-fg-tertiary: var(--color-neutral-600);
--color-fg-tertiary_hover: var(--color-neutral-700);
--color-fg-brand-primary_alt: var(--color-fg-brand-primary);
--color-fg-brand-secondary_alt: var(--color-fg-brand-secondary);
--color-fg-brand-secondary_hover: var(--color-brand-600);
/* BACKGROUND COLORS */
--color-bg-primary: var(--color-white);
--color-bg-tertiary: var(--color-neutral-100);
--color-bg-brand-primary: var(--color-brand-50);
--color-bg-error-secondary: var(--color-red-100);
--color-bg-warning-primary: var(--color-yellow-50);
--color-bg-warning-secondary: var(--color-yellow-100);
--color-bg-success-primary: var(--color-green-50);
--color-bg-success-secondary: var(--color-green-100);
--color-bg-brand-solid: var(--color-brand-600);
--color-bg-secondary-solid: var(--color-neutral-600);
--color-bg-error-solid: var(--color-red-600);
--color-bg-warning-solid: var(--color-yellow-600);
--color-bg-success-solid: var(--color-green-600);
--color-bg-secondary_hover: var(--color-neutral-100);
--color-bg-primary_hover: var(--color-neutral-50);
--color-bg-active: var(--color-neutral-50);
--color-bg-brand-solid_hover: var(--color-brand-700);
--color-bg-error-primary: var(--color-red-50);
--color-bg-brand-secondary: var(--color-brand-100);
--color-bg-secondary: var(--color-neutral-50);
--color-bg-quaternary: var(--color-neutral-200);
--color-bg-primary_alt: var(--color-white);
--color-bg-brand-primary_alt: var(--color-brand-50);
--color-bg-secondary_alt: var(--color-neutral-50);
--color-bg-overlay: var(--color-neutral-950);
--color-bg-brand-section: var(--color-brand-800);
--color-bg-brand-section_subtle: var(--color-brand-700);
--color-bg-primary-solid: var(--color-neutral-950);
--color-bg-error-solid_hover: var(--color-red-700);
/* COMPONENT COLORS */
--color-app-store-badge-border: 166 166 166 1;
--color-avatar-styles-bg-neutral: 224 224 224 1;
--color-featured-icon-light-fg-brand: var(--color-brand-600);
--color-featured-icon-light-fg-error: var(--color-red-600);
--color-featured-icon-light-fg-gray: var(--color-neutral-500);
--color-featured-icon-light-fg-success: var(--color-green-600);
--color-featured-icon-light-fg-warning: var(--color-yellow-600);
--color-focus-ring-error: var(--color-red-500);
--color-focus-ring: var(--color-brand-500);
--color-footer-button-fg: var(--color-brand-200);
--color-footer-button-fg_hover: var(--color-white);
--color-icon-fg-brand: var(--color-brand-600);
--color-icon-fg-brand_on-brand: var(--color-brand-200);
--color-screen-mockup-border: var(--color-neutral-900);
--color-slider-handle-bg: var(--color-white);
--color-slider-handle-border: var(--color-brand-600);
--color-toggle-border: var(--color-neutral-300);
--color-toggle-slim-border_pressed-hover: var(--color-bg-brand-solid_hover);
--color-toggle-slim-border_pressed: var(--color-bg-brand-solid);
--color-tooltip-supporting-text: var(--color-neutral-300);
/* BACKGROUND PROPERTY COLORS */
--background-color-primary: var(--color-bg-primary);
--background-color-tertiary: var(--color-bg-tertiary);
--background-color-brand-primary: var(--color-bg-brand-primary);
--background-color-error-secondary: var(--color-bg-error-secondary);
--background-color-warning-primary: var(--color-bg-warning-primary);
--background-color-warning-secondary: var(--color-bg-warning-secondary);
--background-color-success-primary: var(--color-bg-success-primary);
--background-color-success-secondary: var(--color-bg-success-secondary);
--background-color-brand-solid: var(--color-bg-brand-solid);
--background-color-secondary-solid: var(--color-bg-secondary-solid);
--background-color-error-solid: var(--color-bg-error-solid);
--background-color-warning-solid: var(--color-bg-warning-solid);
--background-color-success-solid: var(--color-bg-success-solid);
--background-color-secondary_hover: var(--color-bg-secondary_hover);
--background-color-primary_hover: var(--color-bg-primary_hover);
--background-color-active: var(--color-bg-active);
--background-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
--background-color-error-primary: var(--color-bg-error-primary);
--background-color-brand-secondary: var(--color-bg-brand-secondary);
--background-color-secondary: var(--color-bg-secondary);
--background-color-quaternary: var(--color-bg-quaternary);
--background-color-primary_alt: var(--color-bg-primary_alt);
--background-color-brand-primary_alt: var(--color-bg-brand-primary_alt);
--background-color-secondary_alt: var(--color-bg-secondary_alt);
--background-color-overlay: var(--color-bg-overlay);
--background-color-brand-section: var(--color-bg-brand-section);
--background-color-brand-section_subtle: var(--color-bg-brand-section_subtle);
--background-color-primary-solid: var(--color-bg-primary-solid);
--background-color-error-solid_hover: var(--color-bg-error-solid_hover);
--background-color-border-brand: var(--color-border-brand);
--background-color-border-brand_alt: var(--color-border-brand_alt);
/* TEXT PROPERTY COLORS */
--color-text-white: var(--color-white);
--text-color-primary: var(--color-text-primary);
--text-color-secondary: var(--color-text-secondary);
--text-color-secondary_hover: var(--color-text-secondary_hover);
--text-color-tertiary: var(--color-text-tertiary);
--text-color-tertiary_hover: var(--color-text-tertiary_hover);
--text-color-error-primary: var(--color-text-error-primary);
--text-color-warning-primary: var(--color-text-warning-primary);
--text-color-success-primary: var(--color-text-success-primary);
--text-color-brand-secondary: var(--color-text-brand-secondary);
--text-color-placeholder: var(--color-text-placeholder);
--text-color-brand-tertiary: var(--color-text-brand-tertiary);
--text-color-editor-icon-fg: var(--color-text-editor-icon-fg);
--text-color-editor-icon-fg_active: var(--color-text-editor-icon-fg_active);
--text-color-quaternary: var(--color-text-quaternary);
--text-color-brand-primary: var(--color-text-brand-primary);
--text-color-primary_on-brand: var(--color-text-primary_on-brand);
--text-color-secondary_on-brand: var(--color-text-secondary_on-brand);
--text-color-tertiary_on-brand: var(--color-text-tertiary_on-brand);
--text-color-quaternary_on-brand: var(--color-text-quaternary_on-brand);
--text-color-brand-tertiary_alt: var(--color-text-brand-tertiary_alt);
--text-color-error-primary_hover: var(--color-text-error-primary_hover);
--text-color-brand-secondary_hover: var(--color-text-brand-secondary_hover);
--text-color-tooltip-supporting-text: var(--color-tooltip-supporting-text);
/* BORDER PROPERTY COLORS */
--border-color-primary: var(--color-border-primary);
--border-color-secondary: var(--color-border-secondary);
--border-color-secondary_alt: var(--color-border-secondary_alt);
--border-color-tertiary: var(--color-border-tertiary);
--border-color-error: var(--color-border-error);
--border-color-error_subtle: var(--color-border-error_subtle);
--border-color-brand: var(--color-border-brand);
--border-color-brand_alt: var(--color-border-brand_alt);
--border-color-brand-solid: var(--color-bg-brand-solid);
--border-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
/* RING PROPERTY COLORS */
--ring-color-primary: var(--color-border-primary);
--ring-color-secondary: var(--color-border-secondary);
--ring-color-secondary_alt: var(--color-border-secondary_alt);
--ring-color-tertiary: var(--color-border-tertiary);
--ring-color-error: var(--color-border-error);
--ring-color-error_subtle: var(--color-border-error_subtle);
--ring-color-brand: var(--color-border-brand);
--ring-color-brand_alt: var(--color-border-brand_alt);
--ring-color-brand-solid: var(--color-bg-brand-solid);
--ring-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
/* OUTLINE PROPERTY COLORS */
--outline-color-primary: var(--color-border-primary);
--outline-color-secondary: var(--color-border-secondary);
--outline-color-secondary_alt: var(--color-border-secondary_alt);
--outline-color-tertiary: var(--color-border-tertiary);
--outline-color-error: var(--color-border-error);
--outline-color-error_subtle: var(--color-border-error_subtle);
--outline-color-brand: var(--color-border-brand);
--outline-color-brand_alt: var(--color-border-brand_alt);
--outline-color-brand-solid: var(--color-bg-brand-solid);
--outline-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
}
@layer base {
/* DARK MODE VARIABLES */
.dark-mode {
--color-alpha-white: rgb(12 14 18);
--color-alpha-black: rgb(255 255 255);
/* UTILITY COLORS */
--color-utility-blue-50: var(--color-blue-950);
--color-utility-blue-100: var(--color-blue-900);
--color-utility-blue-200: var(--color-blue-800);
--color-utility-blue-300: var(--color-blue-700);
--color-utility-blue-400: var(--color-blue-600);
--color-utility-blue-500: var(--color-blue-500);
--color-utility-blue-600: var(--color-blue-400);
--color-utility-blue-700: var(--color-blue-300);
--color-utility-brand-50: var(--color-brand-950);
--color-utility-brand-50_alt: var(--color-utility-neutral-50);
--color-utility-brand-100: var(--color-brand-900);
--color-utility-brand-100_alt: var(--color-utility-neutral-100);
--color-utility-brand-200: var(--color-brand-800);
--color-utility-brand-200_alt: var(--color-utility-neutral-200);
--color-utility-brand-300: var(--color-brand-700);
--color-utility-brand-300_alt: var(--color-utility-neutral-300);
--color-utility-brand-400: var(--color-brand-600);
--color-utility-brand-400_alt: var(--color-utility-neutral-400);
--color-utility-brand-500: var(--color-brand-500);
--color-utility-brand-500_alt: var(--color-utility-neutral-500);
--color-utility-brand-600: var(--color-brand-400);
--color-utility-brand-600_alt: var(--color-utility-neutral-600);
--color-utility-brand-700: var(--color-brand-300);
--color-utility-brand-700_alt: var(--color-utility-neutral-700);
--color-utility-brand-800: var(--color-brand-200);
--color-utility-brand-800_alt: var(--color-utility-neutral-800);
--color-utility-brand-900: var(--color-brand-100);
--color-utility-brand-900_alt: var(--color-utility-neutral-900);
--color-utility-neutral-50: var(--color-neutral-900);
--color-utility-neutral-100: var(--color-neutral-800);
--color-utility-neutral-200: var(--color-neutral-700);
--color-utility-neutral-300: var(--color-neutral-700);
--color-utility-neutral-400: var(--color-neutral-600);
--color-utility-neutral-500: var(--color-neutral-500);
--color-utility-neutral-600: var(--color-neutral-400);
--color-utility-neutral-700: var(--color-neutral-300);
--color-utility-neutral-800: var(--color-neutral-200);
--color-utility-neutral-900: var(--color-neutral-100);
--color-utility-red-50: var(--color-red-950);
--color-utility-red-100: var(--color-red-900);
--color-utility-red-200: var(--color-red-800);
--color-utility-red-300: var(--color-red-700);
--color-utility-red-400: var(--color-red-600);
--color-utility-red-500: var(--color-red-500);
--color-utility-red-600: var(--color-red-400);
--color-utility-red-700: var(--color-red-300);
--color-utility-yellow-50: var(--color-yellow-950);
--color-utility-yellow-100: var(--color-yellow-900);
--color-utility-yellow-200: var(--color-yellow-800);
--color-utility-yellow-300: var(--color-yellow-700);
--color-utility-yellow-400: var(--color-yellow-600);
--color-utility-yellow-500: var(--color-yellow-500);
--color-utility-yellow-600: var(--color-yellow-400);
--color-utility-yellow-700: var(--color-yellow-300);
--color-utility-green-50: var(--color-green-950);
--color-utility-green-100: var(--color-green-900);
--color-utility-green-200: var(--color-green-800);
--color-utility-green-300: var(--color-green-700);
--color-utility-green-400: var(--color-green-600);
--color-utility-green-500: var(--color-green-500);
--color-utility-green-600: var(--color-green-400);
--color-utility-green-700: var(--color-green-300);
--color-utility-orange-50: var(--color-orange-950);
--color-utility-orange-100: var(--color-orange-900);
--color-utility-orange-200: var(--color-orange-800);
--color-utility-orange-300: var(--color-orange-700);
--color-utility-orange-400: var(--color-orange-600);
--color-utility-orange-500: var(--color-orange-500);
--color-utility-orange-600: var(--color-orange-400);
--color-utility-orange-700: var(--color-orange-300);
--color-utility-indigo-50: var(--color-indigo-950);
--color-utility-indigo-100: var(--color-indigo-900);
--color-utility-indigo-200: var(--color-indigo-800);
--color-utility-indigo-300: var(--color-indigo-700);
--color-utility-indigo-400: var(--color-indigo-600);
--color-utility-indigo-500: var(--color-indigo-500);
--color-utility-indigo-600: var(--color-indigo-400);
--color-utility-indigo-700: var(--color-indigo-300);
--color-utility-fuchsia-50: var(--color-fuchsia-950);
--color-utility-fuchsia-100: var(--color-fuchsia-900);
--color-utility-fuchsia-200: var(--color-fuchsia-800);
--color-utility-fuchsia-300: var(--color-fuchsia-700);
--color-utility-fuchsia-400: var(--color-fuchsia-600);
--color-utility-fuchsia-500: var(--color-fuchsia-500);
--color-utility-fuchsia-600: var(--color-fuchsia-400);
--color-utility-fuchsia-700: var(--color-fuchsia-300);
--color-utility-pink-50: var(--color-pink-950);
--color-utility-pink-100: var(--color-pink-900);
--color-utility-pink-200: var(--color-pink-800);
--color-utility-pink-300: var(--color-pink-700);
--color-utility-pink-400: var(--color-pink-600);
--color-utility-pink-500: var(--color-pink-500);
--color-utility-pink-600: var(--color-pink-400);
--color-utility-pink-700: var(--color-pink-300);
--color-utility-purple-50: var(--color-purple-950);
--color-utility-purple-100: var(--color-purple-900);
--color-utility-purple-200: var(--color-purple-800);
--color-utility-purple-300: var(--color-purple-700);
--color-utility-purple-400: var(--color-purple-600);
--color-utility-purple-500: var(--color-purple-500);
--color-utility-purple-600: var(--color-purple-400);
--color-utility-purple-700: var(--color-purple-300);
--color-utility-sky-50: var(--color-sky-950);
--color-utility-sky-100: var(--color-sky-900);
--color-utility-sky-200: var(--color-sky-800);
--color-utility-sky-300: var(--color-sky-700);
--color-utility-sky-400: var(--color-sky-600);
--color-utility-sky-500: var(--color-sky-500);
--color-utility-sky-600: var(--color-sky-400);
--color-utility-sky-700: var(--color-sky-300);
--color-utility-slate-50: var(--color-slate-950);
--color-utility-slate-100: var(--color-slate-900);
--color-utility-slate-200: var(--color-slate-800);
--color-utility-slate-300: var(--color-slate-700);
--color-utility-slate-400: var(--color-slate-600);
--color-utility-slate-500: var(--color-slate-500);
--color-utility-slate-600: var(--color-slate-400);
--color-utility-slate-700: var(--color-slate-300);
--color-utility-emerald-50: var(--color-emerald-950);
--color-utility-emerald-100: var(--color-emerald-900);
--color-utility-emerald-200: var(--color-emerald-800);
--color-utility-emerald-300: var(--color-emerald-700);
--color-utility-emerald-400: var(--color-emerald-600);
--color-utility-emerald-500: var(--color-emerald-500);
--color-utility-emerald-600: var(--color-emerald-400);
--color-utility-emerald-700: var(--color-emerald-300);
--color-utility-amber-50: var(--color-amber-950);
--color-utility-amber-100: var(--color-amber-900);
--color-utility-amber-200: var(--color-amber-800);
--color-utility-amber-300: var(--color-amber-700);
--color-utility-amber-400: var(--color-amber-600);
--color-utility-amber-500: var(--color-amber-500);
--color-utility-amber-600: var(--color-amber-400);
--color-utility-amber-700: var(--color-amber-300);
--color-text-primary: var(--color-neutral-50);
--color-text-tertiary: var(--color-neutral-400);
--color-text-error-primary: var(--color-red-400);
--color-text-warning-primary: var(--color-yellow-400);
--color-text-success-primary: var(--color-green-400);
--color-text-white: var(--color-white);
--color-text-secondary: var(--color-neutral-300);
--color-text-secondary_hover: var(--color-neutral-200);
--color-text-tertiary_hover: var(--color-neutral-300);
--color-text-brand-secondary: var(--color-neutral-300);
--color-text-placeholder: var(--color-neutral-500);
--color-text-brand-tertiary: var(--color-neutral-400);
--color-text-editor-icon-fg: var(--color-neutral-400);
--color-text-editor-icon-fg_active: var(--color-white);
--color-text-quaternary: var(--color-neutral-400);
--color-text-brand-primary: var(--color-neutral-50);
--color-text-primary_on-brand: var(--color-neutral-50);
--color-text-secondary_on-brand: var(--color-neutral-300);
--color-text-tertiary_on-brand: var(--color-neutral-400);
--color-text-quaternary_on-brand: var(--color-neutral-400);
--color-text-brand-tertiary_alt: var(--color-neutral-50);
--color-text-error-primary_hover: var(--color-red-300);
--color-text-brand-secondary_hover: var(--color-neutral-200);
--color-border-secondary: var(--color-neutral-800);
--color-border-error_subtle: var(--color-red-500);
--color-border-primary: var(--color-neutral-700);
--color-border-brand: var(--color-brand-400);
--color-border-error: var(--color-red-400);
--color-border-tertiary: var(--color-neutral-800);
--color-border-brand_alt: var(--color-neutral-700);
--color-border-secondary_alt: var(--color-neutral-800);
--color-fg-secondary: var(--color-neutral-300);
--color-fg-warning-primary: var(--color-yellow-500);
--color-fg-success-primary: var(--color-green-500);
--color-fg-white: var(--color-white);
--color-fg-success-secondary: var(--color-green-400);
--color-fg-secondary_hover: var(--color-neutral-200);
--color-fg-primary: var(--color-white);
--color-fg-brand-secondary: var(--color-brand-500);
--color-fg-brand-primary: var(--color-brand-500);
--color-fg-quaternary: var(--color-neutral-600);
--color-fg-quaternary_hover: var(--color-neutral-500);
--color-fg-error-primary: var(--color-red-500);
--color-fg-warning-secondary: var(--color-yellow-400);
--color-fg-error-secondary: var(--color-red-400);
--color-fg-tertiary: var(--color-neutral-400);
--color-fg-tertiary_hover: var(--color-neutral-300);
--color-fg-brand-primary_alt: var(--color-neutral-300);
--color-fg-brand-secondary_alt: var(--color-neutral-600);
--color-fg-brand-secondary_hover: var(--color-neutral-500);
--color-bg-primary: var(--color-neutral-950);
--color-bg-tertiary: var(--color-neutral-800);
--color-bg-brand-primary: var(--color-brand-500);
--color-bg-error-secondary: var(--color-red-600);
--color-bg-warning-primary: var(--color-yellow-950);
--color-bg-warning-secondary: var(--color-yellow-600);
--color-bg-success-primary: var(--color-green-950);
--color-bg-success-secondary: var(--color-green-600);
--color-bg-brand-solid: var(--color-brand-600);
--color-bg-secondary-solid: var(--color-neutral-600);
--color-bg-error-solid: var(--color-red-600);
--color-bg-warning-solid: var(--color-yellow-600);
--color-bg-success-solid: var(--color-green-600);
--color-bg-secondary_hover: var(--color-neutral-800);
--color-bg-primary_hover: var(--color-neutral-900);
--color-bg-active: var(--color-neutral-800);
--color-bg-brand-solid_hover: var(--color-brand-500);
--color-bg-error-primary: var(--color-red-950);
--color-bg-brand-secondary: var(--color-brand-600);
--color-bg-secondary: var(--color-neutral-900);
--color-bg-quaternary: var(--color-neutral-700);
--color-bg-primary_alt: var(--color-bg-secondary);
--color-bg-brand-primary_alt: var(--color-bg-secondary);
--color-bg-secondary_alt: var(--color-bg-primary);
--color-bg-overlay: var(--color-neutral-800);
--color-bg-brand-section: var(--color-bg-secondary);
--color-bg-brand-section_subtle: var(--color-bg-primary);
--color-bg-primary-solid: var(--color-bg-secondary);
--color-bg-error-solid_hover: var(--color-red-500);
--color-app-store-badge-border: var(--color-white);
--color-avatar-styles-bg-neutral: 224 224 224 1;
--color-featured-icon-light-fg-brand: var(--color-brand-200);
--color-featured-icon-light-fg-error: var(--color-red-200);
--color-featured-icon-light-fg-gray: var(--color-neutral-200);
--color-featured-icon-light-fg-success: var(--color-green-200);
--color-featured-icon-light-fg-warning: var(--color-yellow-200);
--color-focus-ring-error: var(--color-red-500);
--color-focus-ring: var(--color-brand-500);
--color-footer-button-fg: var(--color-neutral-300);
--color-footer-button-fg_hover: var(--color-neutral-100);
--color-icon-fg-brand: var(--color-neutral-400);
--color-icon-fg-brand_on-brand: var(--color-neutral-400);
--color-screen-mockup-border: var(--color-neutral-700);
--color-slider-handle-bg: var(--color-fg-brand-primary);
--color-slider-handle-border: var(--color-bg-primary);
--color-toggle-border: var(--color-transparent);
--color-toggle-slim-border_pressed-hover: var(--color-transparent);
--color-toggle-slim-border_pressed: var(--color-transparent);
--color-tooltip-supporting-text: var(--color-neutral-300);
/* BACKGROUND PROPERTY COLORS */
--background-color-primary: var(--color-bg-primary);
--background-color-tertiary: var(--color-bg-tertiary);
--background-color-brand-primary: var(--color-bg-brand-primary);
--background-color-error-secondary: var(--color-bg-error-secondary);
--background-color-warning-primary: var(--color-bg-warning-primary);
--background-color-warning-secondary: var(--color-bg-warning-secondary);
--background-color-success-primary: var(--color-bg-success-primary);
--background-color-success-secondary: var(--color-bg-success-secondary);
--background-color-brand-solid: var(--color-bg-brand-solid);
--background-color-secondary-solid: var(--color-bg-secondary-solid);
--background-color-error-solid: var(--color-bg-error-solid);
--background-color-warning-solid: var(--color-bg-warning-solid);
--background-color-success-solid: var(--color-bg-success-solid);
--background-color-secondary_hover: var(--color-bg-secondary_hover);
--background-color-primary_hover: var(--color-bg-primary_hover);
--background-color-active: var(--color-bg-active);
--background-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
--background-color-error-primary: var(--color-bg-error-primary);
--background-color-brand-secondary: var(--color-bg-brand-secondary);
--background-color-secondary: var(--color-bg-secondary);
--background-color-quaternary: var(--color-bg-quaternary);
--background-color-primary_alt: var(--color-bg-primary_alt);
--background-color-brand-primary_alt: var(--color-bg-brand-primary_alt);
--background-color-secondary_alt: var(--color-bg-secondary_alt);
--background-color-overlay: var(--color-bg-overlay);
--background-color-brand-section: var(--color-bg-brand-section);
--background-color-brand-section_subtle: var(--color-bg-brand-section_subtle);
--background-color-primary-solid: var(--color-bg-primary-solid);
--background-color-error-solid_hover: var(--color-bg-error-solid_hover);
--background-color-border-brand: var(--color-border-brand);
--background-color-border-tertiary: var(--color-border-tertiary);
--background-color-border-brand_alt: var(--color-border-brand_alt);
/* TEXT PROPERTY COLORS */
--text-color-primary: var(--color-text-primary);
--text-color-tertiary: var(--color-text-tertiary);
--text-color-error-primary: var(--color-text-error-primary);
--text-color-warning-primary: var(--color-text-warning-primary);
--text-color-success-primary: var(--color-text-success-primary);
--text-color-white: var(--color-text-white);
--text-color-secondary: var(--color-text-secondary);
--text-color-secondary_hover: var(--color-text-secondary_hover);
--text-color-tertiary_hover: var(--color-text-tertiary_hover);
--text-color-brand-secondary: var(--color-text-brand-secondary);
--text-color-placeholder: var(--color-text-placeholder);
--text-color-brand-tertiary: var(--color-text-brand-tertiary);
--text-color-editor-icon-fg: var(--color-text-editor-icon-fg);
--text-color-editor-icon-fg_active: var(--color-text-editor-icon-fg_active);
--text-color-quaternary: var(--color-text-quaternary);
--text-color-brand-primary: var(--color-text-brand-primary);
--text-color-primary_on-brand: var(--color-text-primary_on-brand);
--text-color-secondary_on-brand: var(--color-text-secondary_on-brand);
--text-color-tertiary_on-brand: var(--color-text-tertiary_on-brand);
--text-color-quaternary_on-brand: var(--color-text-quaternary_on-brand);
--text-color-brand-tertiary_alt: var(--color-text-brand-tertiary_alt);
--text-color-error-primary_hover: var(--color-text-error-primary_hover);
--text-color-brand-secondary_hover: var(--color-text-brand-secondary_hover);
--text-color-tooltip-supporting-text: var(--color-tooltip-supporting-text);
/* BORDER PROPERTY COLORS */
--border-color-primary: var(--color-border-primary);
--border-color-secondary: var(--color-border-secondary);
--border-color-secondary_alt: var(--color-border-secondary_alt);
--border-color-tertiary: var(--color-border-tertiary);
--border-color-error: var(--color-border-error);
--border-color-error_subtle: var(--color-border-error_subtle);
--border-color-brand: var(--color-border-brand);
--border-color-brand_alt: var(--color-border-brand_alt);
--border-color-brand-solid: var(--color-bg-brand-solid);
--border-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
/* RING PROPERTY COLORS */
--ring-color-primary: var(--color-border-primary);
--ring-color-secondary: var(--color-border-secondary);
--ring-color-secondary_alt: var(--color-border-secondary_alt);
--ring-color-tertiary: var(--color-border-tertiary);
--ring-color-error: var(--color-border-error);
--ring-color-error_subtle: var(--color-border-error_subtle);
--ring-color-brand: var(--color-border-brand);
--ring-color-brand-solid: var(--color-bg-brand-solid);
--ring-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
--ring-color-brand_alt: var(--color-border-brand_alt);
/* OUTLINE PROPERTY COLORS */
--outline-color-primary: var(--color-border-primary);
--outline-color-secondary: var(--color-border-secondary);
--outline-color-secondary_alt: var(--color-border-secondary_alt);
--outline-color-tertiary: var(--color-border-tertiary);
--outline-color-error: var(--color-border-error);
--outline-color-error_subtle: var(--color-border-error_subtle);
--outline-color-brand: var(--color-border-brand);
--outline-color-brand-solid: var(--color-bg-brand-solid);
--outline-color-brand-solid_hover: var(--color-bg-brand-solid_hover);
--outline-color-brand_alt: var(--color-border-brand_alt);
}
}
```
{/* @embed:end */}
Now, create a `globals.css` file next to it that imports the `theme.css` and necessary plugins. It replaces the template's `src/styles.css`, which you can delete:
```javascript
@import "tailwindcss";
@import "./theme.css";
@plugin "tailwindcss-animate";
@plugin "tailwindcss-react-aria-components";
@custom-variant dark (&:where(.dark-mode, .dark-mode *));
@custom-variant label (& [data-label]);
@custom-variant focus-input-within (&:has(input:focus));
@utility scrollbar-hide {
/* For Webkit-based browsers (Chrome, Safari and Opera) */
&::-webkit-scrollbar {
display: none;
-webkit-appearance: none;
}
/* For IE, Edge and Firefox */
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
@utility transition-inherit-all {
transition-property: inherit;
transition-duration: inherit;
transition-timing-function: inherit;
}
html,
body {
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
-webkit-font-kerning: normal;
font-kerning: normal;
}
/* Hide the default expand arrow on Safari. */
details summary::-webkit-details-marker {
display: none;
}
/* Hide default arrows from number inputs. */
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
/* Hide the default clear button (X) from search inputs. */
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/* Hide the default outline on chart elements. */
.recharts-surface,
.recharts-surface g,
.recharts-surface path {
outline: none;
}
```
In order to make the development experience slightly better we have added a few utility functions which we'll use throughout our project. Create these files under the `src/utils` and `src/hooks` directories.
```ts
import { extendTailwindMerge } from "tailwind-merge";
const twMerge = extendTailwindMerge({
extend: {
theme: {
text: ["display-xs", "display-sm", "display-md", "display-lg", "display-xl", "display-2xl"],
},
},
});
/**
* This function is a wrapper around the twMerge function.
* It is used to merge the classes inside style objects.
*/
export const cx = twMerge;
/**
* This function does nothing besides helping us to be able to
* sort the classes inside style objects which is not supported
* by the Tailwind IntelliSense by default.
*/
export function sortCx>>>(classes: T): T {
return classes;
}
```
```ts
/* We cannot use type `unknown` instead of `any` here because it will break the type assertion `isReactComponent` function is providing. */
/* eslint-disable @typescript-eslint/no-explicit-any */
import type React from "react";
type ReactComponent = React.FC | React.ComponentClass;
/**
* Checks if a given value is a function component.
*/
export const isFunctionComponent = (component: any): component is React.FC => {
return typeof component === "function";
};
/**
* Checks if a given value is a class component.
*/
export const isClassComponent = (component: any): component is React.ComponentClass => {
return typeof component === "function" && component.prototype && (!!component.prototype.isReactComponent || !!component.prototype.render);
};
/**
* Checks if a given value is a forward ref component.
*/
export const isForwardRefComponent = (component: any): component is React.ForwardRefExoticComponent => {
return typeof component === "object" && component !== null && component.$$typeof.toString() === "Symbol(react.forward_ref)";
};
/**
* Checks if a given value is a valid React component.
*/
export const isReactComponent = (component: any): component is ReactComponent => {
return isFunctionComponent(component) || isForwardRefComponent(component) || isClassComponent(component);
};
```
```ts
import { useEffect, useState } from "react";
const screens = {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
};
/**
* Checks whether a particular Tailwind CSS viewport size applies.
*
* @param size The size to check, which must either be included in Tailwind CSS's
* list of default screen sizes, or added to the Tailwind CSS config file.
*
* @returns A boolean indicating whether the viewport size applies.
*/
export const useBreakpoint = (size: "sm" | "md" | "lg" | "xl" | "2xl") => {
const [matches, setMatches] = useState(typeof window !== "undefined" ? window.matchMedia(`(min-width: ${screens[size]})`).matches : true);
useEffect(() => {
const breakpoint = window.matchMedia(`(min-width: ${screens[size]})`);
setMatches(breakpoint.matches);
const handleChange = (value: MediaQueryListEvent) => setMatches(value.matches);
breakpoint.addEventListener("change", handleChange);
return () => breakpoint.removeEventListener("change", handleChange);
}, [size]);
return matches;
};
```
```ts
import { useCallback, useState } from "react";
const DEFAULT_TIMEOUT = 2000;
type UseClipboardReturnType = {
/**
* The state indicating whether the text has been copied.
* If a string is provided, it will be used as the identifier for the copied state.
*/
copied: string | boolean;
/**
* Function to copy text to the clipboard using the modern clipboard API.
* Falls back to the fallback function if the modern API fails.
*
* @param {string} text - The text to be copied.
* @param {string} [id] - Optional identifier to set the copied state.
* @returns {Promise
For client-side routing integration with React Aria components, create a `RouteProvider` component in `src/providers/router-provider.tsx` or in your desired location:
```tsx
import type { PropsWithChildren } from "react";
import { useRouter } from "@tanstack/react-router";
import { RouterProvider } from "react-aria-components";
export const RouteProvider = ({ children }: PropsWithChildren) => {
const router = useRouter();
return (
router.navigate({ to: href })} useHref={(href) => router.buildLocation({ to: href }).href}>
{children}
);
};
```
`navigate` hands React Aria links, such as a `Button` with an `href`, to TanStack Router, so they navigate client-side instead of reloading the page, and `useHref` asks the router to build the `href` each link renders.
For dark mode support, add a `ThemeProvider` component in `src/providers/theme-provider.tsx` or in your desired location:
```tsx
import type { ReactNode } from "react";
import { createContext, useContext, useEffect, useState } from "react";
type Theme = "light" | "dark" | "system";
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
}
const ThemeContext = createContext(undefined);
export const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};
interface ThemeProviderProps {
children: ReactNode;
/**
* The class to add to the root element when the theme is dark
* @default "dark-mode"
*/
darkModeClass?: string;
/**
* The default theme to use if no theme is stored in localStorage
* @default "system"
*/
defaultTheme?: Theme;
/**
* The key to use to store the theme in localStorage
* @default "ui-theme"
*/
storageKey?: string;
}
export const ThemeProvider = ({ children, defaultTheme = "system", storageKey = "ui-theme", darkModeClass = "dark-mode" }: ThemeProviderProps) => {
const [theme, setTheme] = useState(() => {
if (typeof window !== "undefined") {
const savedTheme = localStorage.getItem(storageKey) as Theme | null;
return savedTheme || defaultTheme;
}
return defaultTheme;
});
useEffect(() => {
const applyTheme = () => {
const root = window.document.documentElement;
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
root.classList.toggle(darkModeClass, systemTheme === "dark");
localStorage.removeItem(storageKey);
} else {
root.classList.toggle(darkModeClass, theme === "dark");
localStorage.setItem(storageKey, theme);
}
};
applyTheme();
// Listen for system theme changes
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = () => {
if (theme === "system") {
applyTheme();
}
};
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, [theme]);
return {children};
};
```
Update the root route in `src/routes/__root.tsx` to include the providers, the Inter font and the global styles:
```tsx
import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router";
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
import { TanStackDevtools } from "@tanstack/react-devtools";
import { RouteProvider } from "@/providers/router-provider";
import { ThemeProvider } from "@/providers/theme-provider";
import appCss from "@/styles/globals.css?url";
// Applies the saved theme before the page paints, so a dark-mode visitor never sees a light flash.
const themeScript = `
const theme = localStorage.getItem("ui-theme");
if (theme === "dark" || (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark-mode");
}
`;
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "TanStack Start Starter",
},
],
links: [
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
{ rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" },
{ rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400..700;1,14..32,400..700&display=swap" },
{ rel: "stylesheet", href: appCss },
],
scripts: [{ children: themeScript }],
}),
shellComponent: RootDocument,
});
function RootDocument({ children }: { children: React.ReactNode }) {
return (
{children},
},
]}
/>
);
}
```
The inline script runs before React hydrates, so it adds the `dark-mode` class before the first paint; `suppressHydrationWarning` tells React that the class on `` is expected to differ from the server-rendered markup.
Great! You're all set to start using Untitled UI components.
If something is missing, you can copy/paste what you need into your project directly from individual [components](/components) pages.
**Need help?** Check our [GitHub repository](https://github.com/untitleduico/react/) for examples, or [open an issue](https://github.com/untitleduico/react/issues/) if you run into any problems. Our community is here to help!
## Using Untitled UI components
After setting up the providers, you can use Untitled UI components in any route:
```tsx
import { createFileRoute } from "@tanstack/react-router";
import { Button } from "@/components/base/buttons/button";
import { Input } from "@/components/base/input/input";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const name = formData.get("name") as string;
console.log(name);
};
return (
Welcome to Untitled UI + TanStack Start
);
}
```
## Dark mode
Untitled UI supports dark mode out of the box. The implementation uses CSS variables to provide a seamless experience.
### Theme toggle
Create a toggle to switch between light and dark mode:
```tsx
import { Moon01, Sun } from "@untitledui/icons";
import { Button } from "@/components/base/buttons/button";
import { useTheme } from "@/providers/theme-provider";
export function ThemeToggle() {
const { setTheme } = useTheme();
return (