← Today I Learned

Dynamically change styles from HTML or JSX

(No, it’s not Tailwind.)

I wanted a button on my website to be “RSS orange” even though that color wasn’t anywhere else in my color scheme. Hardcoding that within the button component itself felt overly specific; the button should mostly be agnostic to what’s inside of it. But the button component encapsulated its own markup and styles, and I didn’t want to break that encapsulation by reaching in from outside to override things.

The solution, surprisingly, was to use inline styles! Not by setting any properties, but by setting a custom property — often known as a CSS variable.

<button className="button" style="--accent: #ff6600">Subscribe via RSS</button>

That sets the custom property --accent to #ff6600 within that specific button element. The corresponding CSS might look something like this:

.button {
  background-color: var(--accent, var(--color-primary));
}

The key is to specify a fallback value when accessing --accent. So by default, everything with the class button would have a background color of --color-primaryexcept when --accent is defined, in which they’ll use --accent.

This pattern pairs particularly well with component-based frameworks like React, where it’s common to change behavior by exposing props. A Button component using this pattern might look like this:

function Button({ accent, children }) {
  // only set `--accent` if it's not an empty string
  // otherwise it'll be set to the literal string "undefined" and everything will break
  const style = accent ? { "--accent": accent } : {};

  return (
    <button className="button" style={style}>
      {children}
    </button>
  );
}

To use it, just pass something to the accent prop:

<Button accent="#ff6600">Subscribe via RSS</Button>