Add article about theme colors in JS
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
1a8786305b
commit
97c6cd7eff
BIN
content/img/2023-08-10.chartjs.png
(Stored with Git LFS)
Normal file
BIN
content/img/2023-08-10.chartjs.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -0,0 +1,132 @@
|
|||
---
|
||||
title: "Getting theme colors in JavaScript using React with DaisyUI and TailwindCSS"
|
||||
date: 2023-08-10T00:18:27-05:00
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- web
|
||||
- dev
|
||||
- react
|
||||
---
|
||||
|
||||
I've been building a web app using React and TailwindCSS, with DaisyUI. But
|
||||
while working on it I hit a minor snag: I'm trying to use Chart.js, and Chart.js
|
||||
creates a canvas to render charts. But the canvas can't pick up the CSS
|
||||
variables that are defined on the page itself. That means that my charts can't
|
||||
use the colors from my theme, unless I manually copy and paste the theme colors!
|
||||
This is pretty bad for maintainability because if the theme colors are ever
|
||||
changed, or themes are added or removed, you'll have to come back and update the
|
||||
colors for the charts as well. Boo!
|
||||
|
||||
Luckily, I found out that you can read CSS variables from JavaScript. So I added
|
||||
this code:
|
||||
|
||||
```ts
|
||||
function getPropertyValue(name: string) {
|
||||
return getComputedStyle(document.body).getPropertyValue(name);
|
||||
}
|
||||
```
|
||||
|
||||
This returns the value of any CSS variable you pass it. For example
|
||||
`getPropertyValue("--tab-border")` returns `1px` for my theme!
|
||||
|
||||
Next, I just looked through the CSS on the page to figure out what CSS variables
|
||||
DaisyUI sets for themes. I quickly found the most important ones I needed: the
|
||||
primary and secondary colors, and the colors for the text that goes on top of
|
||||
them.
|
||||
|
||||
```ts
|
||||
const primary = getPropertyValue("--p");
|
||||
const secondary = getPropertyValue("--s");
|
||||
const primaryText = getPropertyValue("--pc");
|
||||
const secondaryText = getPropertyValue("--sc");
|
||||
```
|
||||
|
||||
This is all great! But I had one more concern: I needed a way to change these
|
||||
variables and re-render components whenever the user toggles between the light
|
||||
and dark themes.
|
||||
|
||||
I decided to use SWR for this. SWR is mainly meant to be used to fetch data from
|
||||
an API, but there's really nothing stopping you from using it for anything else.
|
||||
In this case, SWR will cache all the colors in a primary place, and allow me to
|
||||
re-render all the components when the colors change using its `mutate` API.
|
||||
Here's how that code looks like:
|
||||
|
||||
```ts
|
||||
export function useThemeColor() {
|
||||
const themeFetcher = useCallback(() => {
|
||||
const primary = getPropertyValue("--p");
|
||||
const primaryText = getPropertyValue("--pc");
|
||||
const secondary = getPropertyValue("--s");
|
||||
const secondaryText = getPropertyValue("--sc");
|
||||
return { primary, primaryText, secondary, secondaryText };
|
||||
}, []);
|
||||
|
||||
// The key "data:theme" could be anything, as long as it's unique in the app
|
||||
const { data: color, mutate } = useSWR("data:theme", themeFetcher);
|
||||
|
||||
return { ...color, mutate };
|
||||
}
|
||||
```
|
||||
|
||||
It's very easy to use it.
|
||||
|
||||
```ts
|
||||
export default function Dashboard() {
|
||||
const { primary, primaryContent } = useThemeColor();
|
||||
|
||||
// ... fetch the data and labels ...
|
||||
|
||||
return (
|
||||
<Line
|
||||
data={{
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
data,
|
||||
// borderColor is the color of the line
|
||||
borderColor: `hsl(${primary})`,
|
||||
// backgroundColor is the color of the dots
|
||||
backgroundColor: `hsl(${primaryContent})`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Here's what that looks like:
|
||||
|
||||
![A screenshot of a web page. At the top there is a dark red colored button labelled "Dashboard". There is a line chart below, which uses the same dark red color as the button.](/img/2023-08-10.chartjs.png)
|
||||
|
||||
To keep the colors changing whenever the user toggles the theme, you then just
|
||||
have to call the `mutate` function inside your toggle button.
|
||||
|
||||
```ts
|
||||
export function ThemeToggle() {
|
||||
const { mutate: mutateTheme } = useThemeColor();
|
||||
const [theme, setTheme] = useState("light");
|
||||
const toggleTheme = useCallback(() => {
|
||||
if (theme === "dark") {
|
||||
document.body.dataset.theme = "autumn";
|
||||
setTheme("light");
|
||||
} else {
|
||||
document.body.dataset.theme = "forest";
|
||||
setTheme("dark");
|
||||
}
|
||||
mutateTheme();
|
||||
}, [theme, mutateTheme, setTheme]);
|
||||
|
||||
return (
|
||||
<div className="btn btn-ghost text-xl" onClick={toggleTheme}>
|
||||
<PiLightbulbBold />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Oh and that's a bonus trick for you. You can swap the DaisyUI theme by just
|
||||
setting `document.body.dataset.theme`, as long as that theme is enabled in the
|
||||
settings.
|
Loading…
Reference in a new issue