Postcss plugin for handling prefers-color-scheme
, plus tailwind support
Input
.my-class {
color: black;
@dark {
color: white;
}
}
Output
.my-class {
color: black;
}
html[data-color-scheme="dark"] .my-class {
color: white;
}
@media (prefers-color-scheme: dark) {
html:not([data-color-scheme="light"]) .my-class {
color: white;
}
}
npm install --save-dev postcss postcss-color-scheme
Add to your postcss config
module.exports = {
plugins: [
+ require('postcss-color-scheme'),
],
};
You might have noticed a couple of opinionated code at the top of this document. These are extracted from my daily work, and currently serve my use cases very well. Should you have concerns, suggestions for improvements, or solution for making this more generic, feel free to open an issue. Thanks!
-
Rely on
data-color-scheme
for explicit theme settings. This requires settingdata-color-scheme
on the root html element. -
Provide fallback when user has not explicitly select a theme. Let's refer to the demo above, with rules enumerated:
/* (1) */ .my-class { color: black; } /* (2) */ html[data-color-scheme="dark"] .my-class { color: white; } /* (3) */ @media (prefers-color-scheme: dark) { html:not([data-color-scheme="light"]) .my-class { color: white; } }
Imagine your system provides 3 options:
dark
,light
, andsystem
(default, auto, i.e respect system preferences). There are 4 possible scenarios.-
User has not explicitly selected a theme (theme =
system
), and the system preferslight
(prefers-color-scheme
=light
):--> (1) applies.
-
User has not explicitly selected a theme (theme =
system
), and the system prefersdark
(prefers-color-scheme
=dark
):--> (1) & (3) applies, (3) takes precedence because of its higher specificity.
-
User selected
dark
(data-color-theme
set todark
on root html) :--> (1) & (2) applies, (2) takes precedence because of its higher specificity.
-
User selected
light
(data-color-theme
set tolight
on root html) :--> (1) applies.
flowchart TD A[Has user explicitly selected theme?] -->|Yes| B[Which mode?] B --> Light B --> Dark A -->|No| C[prefers-color-scheme?] C -->Light C -->Dark
-
At Rule | Description |
---|---|
@dark |
apply rules for dark color scheme |
@light |
apply rules for light color scheme |
postcss-color-scheme
supports the :global
syntax, often seen in css modules and similar systems.
Input
.my-class {
@dark global {
color: white;
}
}
:global(html[data-color-scheme="dark"]) .my-class {
color: white;
}
@media (prefers-color-scheme: dark) {
:global(html:not([data-color-scheme="light"])) .my-class {
color: white;
}
}
The following table lists test cases covered by this plugin, please refer to tests for details and to tests' input css as examples
Test Case | Description | Input | Output |
---|---|---|---|
in media queries | @media (min-width: 768px) { .my-class { @dark { color: blue } } } |
input | output |
with combined selector | .my-class, .others { @dark { color: blue } } |
input | output |
with postcss-nesting | .my-class { & .nested { @dark { color: blue } } } |
input | output |
with postcss-nested | .my-class { .nested { @dark { color: blue } } } |
input | output |
inside :global |
:global(.my-class) { @dark global { color: blue } } |
input | output |
with selector at html |
html { @dark { color: blue } } |
input | output |
has child rules | ... |
input | output |
Tailwind Support
Make sure you have installed and configured tailwindcss
.
npm install --save-dev tailwindcss
Add postcss-color-scheme
to your tailwind config as a plugin, and turn off the default darkMode
handling.
/** @type {import("tailwindcss").Config } */
module.exports = {
// negate default Tailwind darkMode declaration
+ darkMode: '',
+ plugins: [require('postcss-color-scheme/tailwind')],
};
Now, the following is available:
<input class="text-white dark:text-black light:border-gray-500">
Note that this tailwind
plugin can be used in conjunction with the postcss
plugin. They are complementary and not exclusive.
postcss
plugin provides@dark
and@light
css at-rule syntax,tailwind
plugin providesdark:
andlight:
classes in html.