There are several ways to handle dark and light mode (or any other styling condition) with Code.Movie animations.
The long list of CSS variables can be set conditionally. Nothing stops you from changing certain settings dependent on media queries...
@media (prefers-color-scheme: light) {
:root {
--cm-scene-background: #fff;
}
}
@media (prefers-color-scheme: dark) {
:root {
--cm-scene-background: #000;
}
}
1234567891011@media (prefers-color-scheme: light) {
:root {
--cm-scene-background: #fff;
}
}
@media (prefers-color-scheme: dark) {
:root {
--cm-scene-background: #000;
}
}
... or parent classes:
:root.isLight {
--cm-scene-background: #fff;
}
:root.isDark {
--cm-scene-background: #000;
}
1234567:root.isLight {
--cm-scene-background: #fff;
}
:root.isDark {
--cm-scene-background: #000;
}
If you don't want to micro-manage your CSS variables, you can also alternatively use conditional themes.
Themes are objects that Code.Movie turns in CSS. This process supports more than one theme if the theme objects come with a condition. To make use of this feature, pass a list of themes with conditions instead of a single theme object to animateHTML()
or toAnimationHTML()
:
import {
toAnimationHTML,
monokaiLight,
monokaiDark,
} from "@codemovie/code-movie";
// Example: use dark or light theme dependent on a media query
const html = toAnimationHTML(sceneData, {
// Pass a list of themes with conditions instead of a single theme object
theme: [
{ theme: monokaiLight, condition: "@media (prefers-color-scheme: light)" },
{ theme: monokaiDark, condition: "@media (prefers-color-scheme: dark)" },
],
});
1234567891011121314import {
toAnimationHTML,
monokaiLight,
monokaiDark,
} from "@codemovie/code-movie";
// Example: use dark or light theme dependent on a media query
const html = toAnimationHTML(sceneData, {
// Pass a list of themes with conditions instead of a single theme object
theme: [
{ theme: monokaiLight, condition: "@media (prefers-color-scheme: light)" },
{ theme: monokaiDark, condition: "@media (prefers-color-scheme: dark)" },
],
});
condition
can be a selector or the "header" of any relevant block at-rule, such as @media
and @supports
. The CSS generator will then wrap the CSS it creates for each theme in a new CSS block and prefix it with the string in condition
. The CSS for the above example will therefore look something like this:
/* CSS that is independent of themes */
@media (prefers-color-scheme: light) {
/* CSS generated from monokaiLight */
}
@media (prefers-color-scheme: dark) {
/* CSS generated from monokaiDark */
}
1234567/* CSS that is independent of themes */
@media (prefers-color-scheme: light) {
/* CSS generated from monokaiLight */
}
@media (prefers-color-scheme: dark) {
/* CSS generated from monokaiDark */
}
Because the CSS generator mindlessly wraps any theme with a condition into a new block and CSS nesting is an old hat by now, condition
can just as well be a selector:
import {
toAnimationHTML,
monokaiLight,
monokaiDark,
} from "@codemovie/code-movie";
// Example: use dark or light theme dependent on a parent selector
const html = toAnimationHTML(sceneData, {
theme: [
{ theme: monokaiLight, condition: ":root.isLight" },
{ theme: monokaiDark, condition: ":root.isDark" },
],
});
12345678910111213import {
toAnimationHTML,
monokaiLight,
monokaiDark,
} from "@codemovie/code-movie";
// Example: use dark or light theme dependent on a parent selector
const html = toAnimationHTML(sceneData, {
theme: [
{ theme: monokaiLight, condition: ":root.isLight" },
{ theme: monokaiDark, condition: ":root.isDark" },
],
});
Resulting CSS:
/* CSS that is independent of themes */
:root.isLight {
/* CSS generated from monokaiLight */
}
:root.isDark {
/* CSS generated from monokaiDark */
}
1234567/* CSS that is independent of themes */
:root.isLight {
/* CSS generated from monokaiLight */
}
:root.isDark {
/* CSS generated from monokaiDark */
}
To support both media queries and classes, simply pass more items in the theme list with the relevant conditions:
import {
toAnimationHTML,
monokaiLight,
monokaiDark,
} from "@codemovie/code-movie";
// Example: use dark or light theme dependent on a parent selector with media
// queries as fallbacks
const html = toAnimationHTML(sceneData, {
theme: [
// Fallback items in case neither "isLight" nor "isDark" are set
{ theme: monokaiLight, condition: "@media (prefers-color-scheme: light)" },
{ theme: monokaiDark, condition: "@media (prefers-color-scheme: dark)" },
// Overrides the media queries if either class is set
{ theme: monokaiLight, condition: ":root.isLight" },
{ theme: monokaiDark, condition: ":root.isDark" },
],
});
123456789101112131415161718import {
toAnimationHTML,
monokaiLight,
monokaiDark,
} from "@codemovie/code-movie";
// Example: use dark or light theme dependent on a parent selector with media
// queries as fallbacks
const html = toAnimationHTML(sceneData, {
theme: [
// Fallback items in case neither "isLight" nor "isDark" are set
{ theme: monokaiLight, condition: "@media (prefers-color-scheme: light)" },
{ theme: monokaiDark, condition: "@media (prefers-color-scheme: dark)" },
// Overrides the media queries if either class is set
{ theme: monokaiLight, condition: ":root.isLight" },
{ theme: monokaiDark, condition: ":root.isDark" },
],
});
This will duplicate the CSS generated for each theme, but this is only a small inefficiency - themes don't generate that much CSS compared to the rest of the animation.