Theming with shadcn
Morgan Feeney
6 min read
How I configured themes in ZippyStarter's next.js blog starter
Who likes coming up with naming conventions and standards?
I don't!
I'd rather do the fun stuff, luckily the internet is filled with great people who have done that for you already. This is why I use shadcn/ui, which is where the underlying theme architecture for ZippyStarter's Next.js Blog Starter comes from.
Installation
I added shadcn/ui to an existing project, I ran the shadcn-ui
init CLI command to set up the project.
pnpm dlx shadcn-ui@latest init
I was asked a few questions by the CLI:
Would you like to use TypeScript (recommended)? no / yes
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › › app/globals.css
Do you want to use CSS variables for colors? › no / yes
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › no / yes
I opted for using CSS variables, a.k.a. custom properties.
This enables global styles (for light/dark themes) to be managed in a single file: globals.css
, rather than peppering utility classes like :dark
(don't do this) throughout the codebase.
I'd already defined a folder structure called @/utils
not @/lib/utils
so ended up creating a separate @/lib/utils
dir.
Mainly because when you use the docs it's all about copying and pasting code samples, if you have a different architecture to shadcn/ui you are constantly wasting time reconfiguring all the stuff you copy, it's not wirth the effort!
This is the output from those prompts:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/utils"
}
}
I succumbed to most of the conventions he uses, and I'll probably go all in at some point, if I can say goodbye to PascalCase
component names and go with kebab-case
instead.
Naming conventions
This section isn't about some random face-to-face networking events about naming things, it's about how shadcn/ui names CSS custom properties, something that is really important to understand.
If you misunderstand this part (like I did at first) you're entire codebase will be littered with inappropriate CSS classes that will make everything look wrong and be impossible to manage.
Given the following CSS variables:
/* The background suffix is omitted when the variable is used for the
* background color of the component.
*/
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
The background color of the following component will be hsl(var(--primary))
and the foreground color will be hsl(var(--primary-foreground))
.
<div className="bg-primary text-primary-foreground">Hello</div>
If you misunderstand what primary
& secondary
are for, and use them for things like backgrounds and paragraphs you'll come unstuck, traditionally they are associated with buttons, that's also the case with shadcn/ui.
List of variables
Here's the list of variables available for customization:
/* Default background color of <body />...etc */
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
/* Muted backgrounds such as <TabsList />, <Skeleton /> and <Switch /> */
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
/* Background color for <Card /> */
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
/* Background color for popovers such as <DropdownMenu />, <HoverCard />, <Popover /> */
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
/* Default border color */
--border: 214.3 31.8% 91.4%;
/* Border color for inputs such as <Input />, <Select />, <Textarea /> */
--input: 214.3 31.8% 91.4%;
/* Primary colors for <Button /> */
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
/* Secondary colors for <Button /> */
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
/* Used for destructive actions such as <Button variant="destructive"> */
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
/* Used for focus ring */
--ring: 215 20.2% 65.1%;
/* Border radius for card, input and buttons */
--radius: 0.5rem;
Adding new colors
You can add more colors as you wish, after all they're only custom properties. I added some for the code samples I use, which don't theme switch currently.
To add new colors, you need to add them to your CSS file and to your tailwind.config.js
file.
:root {
--warning: 38 92% 50%;
--warning-foreground: 48 96% 89%;
}
.dark {
--warning: 48 96% 89%;
--warning-foreground: 38 92% 50%;
}
module.exports = {
theme: {
extend: {
colors: {
warning: "hsl(var(--warning))",
"warning-foreground": "hsl(var(--warning-foreground))",
},
},
},
}
You can now use the warning
utility class in your components.
<div className="bg-warning text-warning-foreground" />
Color formats
shadcn/ui recommends using hsl color formats, which I do, you can use other formats if you prefer.