Quickstart
Add quadratic/ui to your Next.js app.
Create a Next.js app with TypeScript and Tailwind CSS. I recommend using create-t3-app with pnpm. Check out my detailed guide on create-t3-app. Otherwise, you can follow the Next.js installation instructions.
Add the following dependencies to your project.
pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge
Add the Lucide icon library to your project.
pnpm add lucide-react
Make the following changes to your tailwind.config.ts
file.
Add the darkMode
property to the root of your config.
export default {
darkMode: ["class"],
...
} satisfies Config;
Add the borderRadius
, fontSize
, lineHeight
, and screens
object to theme
.
export default {
...
theme: {
borderRadius: {
none: "0",
px: "0.0625rem",
"0.5": "0.125rem",
1: "0.25rem",
"1.5": "0.375rem",
2: "0.5rem",
"2.5": "0.625rem",
3: "0.75rem",
"3.5": "0.875rem",
4: "1rem",
5: "1.25rem",
6: "1.5rem",
7: "1.75rem",
8: "2rem",
9: "2.25rem",
10: "2.5rem",
11: "2.75rem",
12: "3rem",
13: "3.25rem",
14: "3.5rem",
15: "3.75rem",
16: "4rem",
full: "9999px",
},
fontSize: {
2.5: ["0.625rem", "0.75rem"],
3: ["0.75rem", "1rem"],
3.5: ["0.875rem", "1.25rem"],
4: ["1rem", "1.5rem"],
4.5: ["1.125rem", "1.75rem"],
5: ["1.25rem", "1.75rem"],
6: ["1.5rem", "2rem"],
7: ["1.75rem", "2.25rem"],
8: ["2rem", "2.5rem"],
9: ["2.25rem", "2.5rem"],
10: ["2.5rem", "2.75rem"],
11: ["2.75rem", "1"],
12: ["3rem", "1"],
13: ["3.25rem", "1"],
14: ["3.5rem", "1"],
15: ["3.75rem", "1"],
16: ["4rem", "1"],
},
lineHeight: {
5: "1.25rem",
5.5: "1.375rem",
6: "1.5rem",
7: "1.75rem",
8: "2rem",
9: "2.25rem",
10: "2.5rem",
11: "2.75rem",
12: "3rem",
13: "3.25rem",
14: "3.5rem",
15: "3.75rem",
16: "4rem",
},
screens: {
"3xs": "300px",
"2xs": "360px",
xs: "480px",
sm: "640px",
md: "768px",
lg: "840px",
xl: "1024px",
"2xl": "1280px",
"3xl": "1400px",
"4xl": "1600px",
"5xl": "1800px",
},
...
},
...
} satisfies Config;
Under theme.extend
, add this colors
object.
export default {
...
theme: {
...
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
border: "hsl(var(--destructive-border))",
},
warning: {
DEFAULT: "hsl(var(--warning))",
foreground: "hsl(var(--warning-foreground))",
border: "hsl(var(--warning-border))",
},
success: {
DEFAULT: "hsl(var(--success))",
foreground: "hsl(var(--success-foreground))",
border: "hsl(var(--success-border))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
highlight: {
DEFAULT: "hsl(var(--highlight))",
foreground: "hsl(var(--highlight-foreground))",
},
},
},
},
} satisfies Config;
Add the tailwindcss-animate
plugin to root of the config.
export default {
...
plugins: [require("tailwindcss-animate")],
} satisfies Config;
Add the following colors and styles to your styles/globals.css
file.
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--highlight: 234 100% 98%;
--highlight-foreground: 205 61% 28%;
--background: 0 0% 100%;
--foreground: 240 10% 4%;
--primary: 240 6% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 5% 96%;
--secondary-foreground: 240 6% 10%;
--muted: 240 5% 96%;
--muted-foreground: 240 4% 46%;
--accent: 240 5% 96%;
--accent-foreground: 240 6% 10%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 4%;
--card: 0 0% 100%;
--card-foreground: 240 10% 4%;
--destructive: 0 93% 94%;
--destructive-foreground: 0 70% 35%;
--destructive-border: 0 91% 71%;
--warning: 55 97% 88%;
--warning-foreground: 32 81% 29%;
--warning-border: 48 96% 53%;
--success: 141 84% 93%;
--success-foreground: 143 64% 24%;
--success-border: 142 69% 58%;
--border: 240 6% 90%;
--input: 240 6% 90%;
--ring: 240 5% 65%;
}
.dark {
--highlight: 205 18% 15%;
--highlight-foreground: 205 62% 86%;
--background: 240 10% 4%;
--foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 6% 10%;
--secondary: 240 4% 16%;
--secondary-foreground: 0 0% 98%;
--muted: 240 4% 16%;
--muted-foreground: 240 5% 65%;
--accent: 240 6% 10%;
--accent-foreground: 0 0% 98%;
--popover: 240 10% 4%;
--popover-foreground: 0 0% 98%;
--card: 240 10% 4%;
--card-foreground: 0 0% 98%;
--destructive: 0 70% 35%;
--destructive-foreground: 0 93% 94%;
--destructive-border: 0 91% 71%;
--warning: 32 81% 29%;
--warning-foreground: 55 97% 88%;
--warning-border: 48 96% 53%;
--success: 143 64% 24%;
--success-foreground: 141 84% 93%;
--success-border: 142 69% 58%;
--border: 240 4% 16%;
--input: 240 4% 16%;
--ring: 240 5% 84%;
}
}
@layer base {
* {
@apply box-border border-border;
}
body {
@apply bg-background font-sans text-foreground;
font-feature-settings:
"rlig" 1,
"calt" 1;
}
}
Add a cn
helper to utils/tailwind.ts
. This makes it easier to add conditional Tailwind CSS classes.
import { clsx, type ClassValue } from "clsx";
import { extendTailwindMerge } from "tailwind-merge";
import config from "../../tailwind.config";
const twMerge = extendTailwindMerge({
extend: {
classGroups: {
"font-size": Object.keys(config.theme.fontSize).map(
(key) => `text-${key}`,
),
"text-color": Object.keys(config.theme.extend.colors).map(
(key) => `text-${key}`,
),
rounded: Object.keys(config.theme.borderRadius).map(
(key) => `rounded-${key}`,
),
"border-color": Object.keys(config.theme.extend.colors).map(
(key) => `border-${key}`,
),
},
},
});
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}