ui
ui copied to clipboard
Use CSS variables to simplify creating dark theme and maybe other themes.
My proposition is to develop semantic colors for light and dark theme, write them into CSS variables and set them in TailwindCSS config https://tailwindcss.com/docs/customizing-colors#using-css-variables. This will almost completely automate the creation of a dark theme, since the colors will automatically change from the given variables for the dark and light theme, and another advantage is that it will be possible to add new different themes.
Example:
// tailwind.config.js
module.exports = {
darkMode: ['class'],
theme: {
colors: {
current: 'currentColor',
transparent: 'transparent',
black: '#000',
white: '#fff',
background: 'hsl(var(--background) / 1)',
base: {
50: 'hsl(var(--base) / 0.05)',
100: 'hsl(var(--base) / 0.1)',
200: 'hsl(var(--base) / 0.2)',
300: 'hsl(var(--base) / 0.3)',
400: 'hsl(var(--base) / 0.4)',
500: 'hsl(var(--base) / 0.5)',
600: 'hsl(var(--base) / 0.6)',
700: 'hsl(var(--base) / 0.7)',
800: 'hsl(var(--base) / 0.8)',
900: 'hsl(var(--base) / 1)',
},
primary: {
50: 'hsl(var(--primary-50))',
100: 'hsl(var(--primary-100))',
200: 'hsl(var(--primary-200))',
300: 'hsl(var(--primary-300))',
400: 'hsl(var(--primary-400))',
500: 'hsl(var(--primary-500))',
600: 'hsl(var(--primary-600))',
700: 'hsl(var(--primary-700))',
800: 'hsl(var(--primary-800))',
900: 'hsl(var(--primary-900))',
},
secondary: {
50: 'hsl(var(--secondary-50))',
100: 'hsl(var(--secondary-100))',
200: 'hsl(var(--secondary-200))',
300: 'hsl(var(--secondary-300))',
400: 'hsl(var(--secondary-400))',
500: 'hsl(var(--secondary-500))',
600: 'hsl(var(--secondary-600))',
700: 'hsl(var(--secondary-700))',
800: 'hsl(var(--secondary-800))',
900: 'hsl(var(--secondary-900))',
},
tertiary: {
50: 'hsl(var(--tertiary-50))',
100: 'hsl(var(--tertiary-100))',
200: 'hsl(var(--tertiary-200))',
300: 'hsl(var(--tertiary-300))',
400: 'hsl(var(--tertiary-400))',
500: 'hsl(var(--tertiary-500))',
600: 'hsl(var(--tertiary-600))',
700: 'hsl(var(--tertiary-700))',
800: 'hsl(var(--tertiary-800))',
900: 'hsl(var(--tertiary-900))',
},
success: {
50: 'hsl(var(--success-50))',
100: 'hsl(var(--success-100))',
200: 'hsl(var(--success-200))',
300: 'hsl(var(--success-300))',
400: 'hsl(var(--success-400))',
500: 'hsl(var(--success-500))',
600: 'hsl(var(--success-600))',
700: 'hsl(var(--success-700))',
800: 'hsl(var(--success-800))',
900: 'hsl(var(--success-900))',
},
highlight: {
50: 'hsl(var(--highlight-50))',
100: 'hsl(var(--highlight-100))',
200: 'hsl(var(--highlight-200))',
300: 'hsl(var(--highlight-300))',
400: 'hsl(var(--highlight-400))',
500: 'hsl(var(--highlight-500))',
600: 'hsl(var(--highlight-600))',
700: 'hsl(var(--highlight-700))',
800: 'hsl(var(--highlight-800))',
900: 'hsl(var(--highlight-900))',
},
warning: {
50: 'hsl(var(--warning-50))',
100: 'hsl(var(--warning-100))',
200: 'hsl(var(--warning-200))',
300: 'hsl(var(--warning-300))',
400: 'hsl(var(--warning-400))',
500: 'hsl(var(--warning-500))',
600: 'hsl(var(--warning-600))',
700: 'hsl(var(--warning-700))',
800: 'hsl(var(--warning-800))',
900: 'hsl(var(--warning-900))',
},
},
},
};
/* globals.css */
@layer base {
:root {
--background: 0 0% 100%;
--base: 0 0% 10%;
--primary-50: 216 100% 97%;
--primary-100: 214 94% 93%;
--primary-200: 213 97% 87%;
--primary-300: 212 96% 78%;
--primary-400: 213 94% 68%;
--primary-500: 217 80% 56%;
--primary-600: 221 83% 53%;
--primary-700: 224 76% 48%;
--primary-800: 226 71% 40%;
--primary-900: 224 64% 33%;
--secondary-50: 270 100% 98%;
--secondary-100: 270 100% 95%;
--secondary-200: 269 100% 92%;
--secondary-300: 269 97% 85%;
--secondary-400: 270 95% 75%;
--secondary-500: 271 91% 65%;
--secondary-600: 271 81% 56%;
--secondary-700: 272 72% 47%;
--secondary-800: 273 67% 39%;
--secondary-900: 274 66% 32%;
--tertiary-50: 0 0% 99%;
--tertiary-100: 240 5% 96%;
--tertiary-200: 240 6% 90%;
--tertiary-300: 240 5% 84%;
--tertiary-400: 240 5% 65%;
--tertiary-500: 240 4% 46%;
--tertiary-600: 240 5% 34%;
--tertiary-700: 240 5% 26%;
--tertiary-800: 240 4% 16%;
--tertiary-900: 240 6% 10%;
--success-50: 136 73% 97%;
--success-100: 142 83% 93%;
--success-200: 141 79% 85%;
--success-300: 142 77% 73%;
--success-400: 142 69% 58%;
--success-500: 142 76% 43%;
--success-600: 142 76% 36%;
--success-700: 142 72% 29%;
--success-800: 143 61% 27%;
--success-900: 144 63% 18%;
--highlight-50: 55 92% 95%;
--highlight-100: 55 97% 88%;
--highlight-200: 53 98% 77%;
--highlight-300: 50 98% 64%;
--highlight-400: 48 96% 53%;
--highlight-500: 45 93% 47%;
--highlight-600: 41 96% 40%;
--highlight-700: 35 92% 33%;
--highlight-800: 32 81% 29%;
--highlight-900: 28 73% 26%;
--warning-50: 0 87% 97%;
--warning-100: 0 94% 94%;
--warning-200: 0 96% 89%;
--warning-300: 0 93% 82%;
--warning-400: 0 97% 71%;
--warning-500: 0 84% 60%;
--warning-600: 0 72% 51%;
--warning-700: 0 74% 42%;
--warning-800: 0 70% 35%;
--warning-900: 0 63% 31%;
}
.dark {
--background: 0 0% 8%;
--base: 0 0% 100%;
--primary-50: 230 37% 18%;
--primary-100: 222 87% 29%;
--primary-200: 223 83% 44%;
--primary-300: 221 87% 49%;
--primary-400: 217 81% 56%;
--primary-500: 213 92% 57%;
--primary-600: 212 100% 66%;
--primary-700: 212 100% 74%;
--primary-800: 214 94% 93%;
--primary-900: 216 100% 97%;
--secondary-50: 274 50% 19%;
--secondary-100: 274 59% 28%;
--secondary-200: 272 72% 47%;
--secondary-300: 272 82% 59%;
--secondary-400: 270 100% 67%;
--secondary-500: 278 100% 75%;
--secondary-600: 286 100% 81%;
--secondary-700: 300 100% 87%;
--secondary-800: 300 100% 92%;
--secondary-900: 300 100% 97%;
--tertiary-50: 0 0% 15%;
--tertiary-100: 0 0% 20%;
--tertiary-200: 0 0% 26%;
--tertiary-300: 0 0% 37%;
--tertiary-400: 0 0% 45%;
--tertiary-500: 0 0% 53%;
--tertiary-600: 0 0% 61%;
--tertiary-700: 0 0% 73%;
--tertiary-800: 0 0% 84%;
--tertiary-900: 0 0% 94%;
--success-50: 136 59% 13%;
--success-100: 144 61% 20%;
--success-200: 143 64% 24%;
--success-300: 142 80% 32%;
--success-400: 142 71% 45%;
--success-500: 142 69% 58%;
--success-600: 142 77% 73%;
--success-700: 141 79% 85%;
--success-800: 142 83% 93%;
--success-900: 136 73% 97%;
--highlight-50: 55 47% 17%;
--highlight-100: 32 81% 29%;
--highlight-200: 35 92% 33%;
--highlight-300: 41 96% 40%;
--highlight-400: 45 93% 47%;
--highlight-500: 48 96% 53%;
--highlight-600: 50 98% 64%;
--highlight-700: 53 98% 77%;
--highlight-800: 55 96% 90%;
--highlight-900: 55 92% 95%;
--warning-50: 0 45% 25%;
--warning-100: 0 59% 30%;
--warning-200: 0 46% 47%;
--warning-300: 0 69% 55%;
--warning-400: 0 91% 63%;
--warning-500: 0 100% 69%;
--warning-600: 0 100% 79%;
--warning-700: 0 100% 83%;
--warning-800: 0 100% 87%;
--warning-900: 0 96% 91%;
}
body {
@apply bg-background text-base-900;
}
}
Usage:
export const Demo = () => (
<div className="bg-tertiary-100 p-4 text-tertiary-900">Hello World!</div>
);
Result:
☀️ Light theme:
🌙 Dark theme:
It's nice idea!
[FYI] This library also automatically realize dark mode and may be more suited to Radix UI and Tailwind.
https://github.com/brattonross/windy-radix-palette
For me, this approach leads to a worse tailwind experience, since it's not always clear which two colors are meant by bg-primary-200
. Another problem I sometimes run into is that I might want my border colors to be a little different from my background colors.
Doing this will be harder to maintain due to having declarations in both tailwind.config.js
and the globals.css
file. It also has the a minor inconvenience that the tailwind autocomplete doesn't show the color swatch when selecting. Some shameless plug here but this plugin to create colors solves both of the issues. tailwind-easy-theme
But l also would prefer using semantic colors.
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.