a post-component library for building user-interfaces on the web.
1const class_names = new WeakMap()
2const adopted = new WeakSet()
3const stylesheet = new CSSStyleSheet()
4let next_id = 0
5const cache = new Map()
6
7/**
8 * @param {TemplateStringsArray} strings
9 * @param {unknown[]} dynamics
10 * @returns {import('dhtml/client').Directive}
11 */
12export function css(strings, ...dynamics) {
13 let class_name = class_names.get(strings)
14 if (!class_name) {
15 class_names.set(strings, (class_name = `gen-${next_id++}`))
16 stylesheet.insertRule(
17 `.${class_name}{${strings.reduce((acc, value, index) => acc + `var(--${class_name}-${index - 1})` + value)}}`,
18 )
19 }
20
21 const cache_key = `${class_name}\0${dynamics.map(v => String(v)).join('\0')}`
22 const cached = cache.get(cache_key)
23 if (cached) return cached
24
25 /** @type {import('dhtml/client').Directive} */
26 const directive = element => {
27 const root = /** @type {Document | ShadowRoot} */ (element.getRootNode())
28 if (!adopted.has(root)) {
29 root.adoptedStyleSheets.push(stylesheet)
30 adopted.add(root)
31 }
32
33 const { style, classList } = /** @type {HTMLElement} */ (element)
34
35 classList.add(class_name)
36 for (let i = 0; i < dynamics.length; i++) {
37 style.setProperty(`--${class_name}-${i}`, String(dynamics[i]))
38 }
39
40 return () => {
41 classList.remove(class_name)
42 for (let i = 0; i < dynamics.length; i++) {
43 style.setProperty(`--${class_name}-${i}`, null)
44 }
45 }
46 }
47
48 cache.set(cache_key, directive)
49 return directive
50}