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.75rem"],
10: ["2.5rem", "3rem"],
11: ["2.75rem", "3.5rem"],
12: ["3rem", "4rem"],
13: ["3.25rem", "4.5rem"],
14: ["3.5rem", "5rem"],
15: ["3.75rem", "5.5rem"],
16: ["4rem", "6rem"],
},
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))",
},
info: {
DEFAULT: "hsl(var(--info))",
foreground: "hsl(var(--info-foreground))",
border: "hsl(var(--info-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))",
},
},
},
},
} 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 {
--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 100% 97%;
--destructive-foreground: 0 100% 45%;
--destructive-border: 358 100% 94%;
--warning: 48 100% 97%;
--warning-foreground: 31 92% 45%;
--warning-border: 49 91% 91%;
--success: 145 81% 96%;
--success-foreground: 140 100% 27%;
--success-border: 146 91% 91%;
--info: 208 100% 97%;
--info-foreground: 210 92% 45%;
--info-border: 221 91% 91%;
--border: 240 6% 90%;
--input: 240 6% 90%;
--ring: 240 5% 65%;
--chart-1: 173 58% 39%;
--chart-2: 12 76% 61%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--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: 358 76% 10%;
--destructive-foreground: 358 100% 81%;
--destructive-border: 357 90% 16%;
--warning: 64 100% 6%;
--warning-foreground: 46 87% 65%;
--warning-border: 60 100% 12%;
--success: 149 100% 6%;
--success-foreground: 150 87% 65%;
--success-border: 148 100% 12%;
--info: 215 100% 6%;
--info-foreground: 216 87% 65%;
--info-border: 223 100% 12%;
--border: 240 4% 16%;
--input: 240 4% 16%;
--ring: 240 5% 84%;
--chart-1: 220 70% 50%;
--chart-2: 340 75% 55%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 160 60% 45%;
}
* {
@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
. It's a utility function used to efficiently merge Tailwind CSS classes in JS without having sylte conflicts.
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));
}