JSON with Variables-ish: A Variable Theming Prototype

I’ve been coding Home Base, an Übersicht widget, for the last couple of weeks, and it’s been really fun. With Übersicht, I can create macOS widgets using JSX, and, for me, customizing my computer’s desktop has been strangely rewarding.

It’s not perfect though; Übersicht doesn’t seem to support CSS files. Instead, you style components using JavaScript objects. Not my favourite way of doing things — I like to keep styling definitions separate from code — but I got around the problem by writing “CSS” in a JSON file (which I called styles.json) and importing it at runtime as a JavaScript object.

Further along the widget’s development, I decided that I wanted to support themes. I wanted to declare properties in a JSON file and use them to determine styling choices (e.g. which font to use). For readability, I wanted to handle all of the styling in styles.json, but how do you handle variables? Huh…


Let’s say we have the following code:

// themes.json
{
  "red": {
    "textColor": "#f00"
  }
}
// styles.json
{
  "emphasis": {}
}
import stylesJSON from "styles.json";
import themesJSON from "themes.json";

const theme = "red";

return (
  <div style={stylesJSON.emphasis}>
    Using the <q>{theme}</q> theme!
  </div>
);

How can we style our div using the definitions in themes.json? Here’s my solution:

// themes.json
{
  "red": {
    "textColor": "#f00"
  }
}
// styles.json
{
  "emphasis": {
    "color": "<textColor>"
  }
}
import stylesJSON from "styles.json";
import themesJSON from "themes.json";

const makeStyles = (theme) => {
  const pattern = /<[^>]*>/; // matches with <EXPRESSION>
  const t = themesJSON[theme];

  // Find all matches of `pattern` in `styleData` and replace them with their evaluated counterparts.
  let s = JSON.stringify(stylesJSON);
  while (s.match(pattern)) {
    const match = s.match(pattern)[0];
    s = s.replace(match, eval("t." + match.slice(1, -1)));
  }

  return JSON.parse(s);
};

const theme = "red";
const styles = makeStyles(theme);

return (
  <div style={styles.emphasis}>
    Using the <q>{theme}</q> theme!
  </div>
);

Here’s a demo of my idea in action:

Pretty simple, right? It’s not the most efficient idea, but for my use case, it does the job well.


Could I have avoided the issue entirely by storing the styles in a .js file instead of a .json file? Well, yeah, but where’s the fun in that?


Comments