Last active
October 15, 2023 14:43
-
-
Save jacobrask/9f84754f6c2a3f31639c940fd1dc110e to your computer and use it in GitHub Desktop.
CSS variable TypeScript proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* PoC using recursive proxies to maintain a typed interface for your CSS variable themes. | |
* | |
* See https://www.joshwcomeau.com/css/css-variables-for-react-devs/ for an excellent intro to CSS variables. | |
* | |
* Using CSS variables as plain strings in TypeScript is not great though. | |
* It's prone to typos, difficult to refactor and you can't easily deprecate properties or add aliases. | |
* One solution is to create a JavaScript object of your theme, with the CSS variable as the value, but | |
* if users are already downloading your theme as CSS variables, why make them download it again as a JS object? | |
*/ | |
// The theme interface can be written manually or generated by a tool like | |
// Style Dictionary that can generate both your CSS and theme interface from style tokens | |
interface Theme { | |
color: { | |
foreground: { | |
primary: string; | |
secondary: string; | |
action: string; | |
}; | |
background: { | |
primary: string; | |
} | |
}; | |
font: { | |
size1: string; | |
size2: string; | |
} | |
}; | |
let empty = Object.create(null); | |
function createProxy(prefixes: string[]): Theme { | |
return new Proxy(empty, { | |
get(_target, key: string) { | |
if (typeof key === 'symbol' || key === 'toString') { | |
return () => `var(--${prefixes.join('-')})`; | |
} else { | |
return createProxy([...prefixes, key]); | |
} | |
}, | |
}); | |
} | |
const theme = createProxy([]); | |
console.assert(theme.font.size1.toString() === 'var(--font-size1)'); | |
console.assert(theme.color.foreground.primary.toString() === 'var(--color-foreground-primary)'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.theme { | |
--color-foreground-primary: #111; | |
--color-foreground-secondary: #6B818C; | |
--color-foreground-action: #08415C; | |
--color-background-primary: #fff; | |
--font-size1: 3rem; | |
--font-size2: 2rem; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I really like this--thanks for sharing! For my own purposes (since I don't use Style Dictionary), I think I might define the theme as a value and let TypeScript infer the type for me. That way, I could use it to generate the CSS as well as for type information.
e.g.