Setup
How to setup your project to use Foundations.
Prerequisites
- A React 19 project.
- Tailwind 4 installed and configured.
We are using React 19. If you still on 18, please check the React 19 migration guide.
A few common changes are:
refis now a prop like the others, so we don't need to useforwardRef.<Context>as a provider: You can now render<Context>as a provider instead of<Context.Provider>.usecan be used instead ofuseContext.MutableRefObjectis nowRefObject.It's possible to use these components in React 18, but you will have to adapt the code as needed.
Installation
Install the core dependencies:
npm install cva@beta tailwind-mergeWe are using CVA 1.0.0 (beta). Check what's new in their docs.
CSS Setup
@import "tailwindcss";
@theme {
/* Typography */
--text-2xs: 0.625rem;
/* Colors */
--color-background: light-dark(oklch(100% 0 0), oklch(12% 0 0));
--color-background-secondary: light-dark(oklch(96% 0 0), oklch(22% 0 0));
--color-foreground: light-dark(oklch(0% 0 0), oklch(95% 0 0));
--color-foreground-secondary: light-dark(oklch(65% 0 0), oklch(53% 0 0));
--color-accent: var(--color-foreground);
/* Foreground at the end means it is used on top of the accent color */
--color-accent-foreground: var(--color-background);
--color-border: light-dark(oklch(94% 0 0), oklch(26% 0 0));
--color-ring: light-dark(
color-mix(in oklab, var(--color-accent) 30%, transparent),
color-mix(in oklab, var(--color-accent) 30%, transparent)
);
/* Shadows */
--shadow-*: initial;
--shadow-xs: 0 1px 2px oklch(0 0 0 / 0.03);
--shadow-sm: 0 1px 2px oklch(0 0 0 / 0.02), 0 2px 4px oklch(0 0 0 / 0.02);
--shadow-md: 0 2px 3px oklch(0 0 0 / 0.02), 0 3px 6px oklch(0 0 0 / 0.02);
--shadow-lg:
0 2px 4px oklch(0 0 0 / 0.02), 0 4px 8px oklch(0 0 0 / 0.02),
0 8px 16px oklch(0 0 0 / 0.02);
/* easings */
--ease-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15);
--ease-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
}
@media (prefers-color-scheme: dark) {
body {
color-scheme: dark;
}
}
/* prettier-ignore */
@utility mix-with-* {
--color-mix-with: --value(--color-*, [*]);
}
/* prettier-ignore */
@utility border-mix-* {
border-color: color-mix(
in oklab,
--value(--color-*, [*]),
var(--color-mix-with, var(--color-foreground)) calc(--modifier(integer) * 1%)
);
}
/* prettier-ignore */
@utility bg-mix-* {
background-color: color-mix(
in oklab,
--value(--color-*, [*]),
var(--color-mix-with, var(--color-foreground)) calc(--modifier(integer) * 1%)
);
}
body {
font-family: var(--font-sans);
background-color: var(--color-background);
color: var(--color-foreground);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body:has(dialog[open]) {
overflow: hidden;
scrollbar-gutter: stable;
}@import "tailwindcss";
@variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
@theme {
/* Typography */
--text-2xs: 0.625rem;
/* Colors */
--color-background: light-dark(oklch(100% 0 0), oklch(12% 0 0));
--color-background-secondary: light-dark(oklch(96% 0 0), oklch(22% 0 0));
--color-foreground: light-dark(oklch(0% 0 0), oklch(95% 0 0));
--color-foreground-secondary: light-dark(oklch(65% 0 0), oklch(53% 0 0));
--color-accent: var(--color-foreground);
/* Foreground at the end means it is used on top of the accent color */
--color-accent-foreground: var(--color-background);
--color-border: light-dark(oklch(94% 0 0), oklch(26% 0 0));
--color-ring: light-dark(
color-mix(in oklab, var(--color-accent) 30%, transparent),
color-mix(in oklab, var(--color-accent) 30%, transparent)
);
/* Shadows */
--shadow-*: initial;
--shadow-xs: 0 1px 2px oklch(0 0 0 / 0.03);
--shadow-sm: 0 1px 2px oklch(0 0 0 / 0.02), 0 2px 4px oklch(0 0 0 / 0.02);
--shadow-md: 0 2px 3px oklch(0 0 0 / 0.02), 0 3px 6px oklch(0 0 0 / 0.02);
--shadow-lg:
0 2px 4px oklch(0 0 0 / 0.02), 0 4px 8px oklch(0 0 0 / 0.02),
0 8px 16px oklch(0 0 0 / 0.02);
/* easings */
--ease-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15);
--ease-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
}
[data-theme="dark"] {
color-scheme: dark;
}
/* prettier-ignore */
@utility mix-with-* {
--color-mix-with: --value(--color-*, [*]);
}
/* prettier-ignore */
@utility border-mix-* {
border-color: color-mix(
in oklab,
--value(--color-*, [*]),
var(--color-mix-with, var(--color-foreground)) calc(--modifier(integer) * 1%)
);
}
/* prettier-ignore */
@utility bg-mix-* {
background-color: color-mix(
in oklab,
--value(--color-*, [*]),
var(--color-mix-with, var(--color-foreground)) calc(--modifier(integer) * 1%)
);
}
body {
font-family: var(--font-sans);
background-color: var(--color-background);
color: var(--color-foreground);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body:has(dialog[open]) {
overflow: hidden;
scrollbar-gutter: stable;
}Utilities Setup
Create a lib/utils.ts file with our utility functions:
import { defineConfig } from "cva";
import { twMerge } from "tailwind-merge";
export const {
cva,
cx: cn,
compose,
} = defineConfig({
hooks: {
onComplete: (className) => twMerge(className),
},
});Icons Setup
We use Phosphor Icons by default, but you can use any icon library of your choice:
npm install @phosphor-icons/react