Skip to content

Instantly share code, notes, and snippets.

@hasenj
Created April 13, 2024 19:56
Show Gist options
  • Save hasenj/e691039f8187ef83360dd73543ba607d to your computer and use it in GitHub Desktop.
Save hasenj/e691039f8187ef83360dd73543ba607d to your computer and use it in GitHub Desktop.
tiny css-in-js "library"
import type * as csstype from "csstype";
class Sheet {
domSheet: CSSStyleSheet | null;
rulesCount = 0;
constructor() {
let el = document.createElement("style");
el = document.head.appendChild(el);
if (!el.sheet) {
console.error("failed to created style element");
}
this.domSheet = el.sheet;
}
insertRule(rule: string) {
this.domSheet!.insertRule(rule, this.rulesCount);
this.rulesCount++;
}
}
let sheet = new Sheet()
export type RuleDefinition = csstype.StandardProperties<string, string> & {
[key: string]: string | number | RuleDefinition;
}
function combineSelectorForNestedRule(selector: string, child: string): string {
let result: string[] = [];
for (let p of selector.split(',')) {
p = p.trim();
for (let c of child.split(',')) {
c = c.trim();
if (c.startsWith(':')) {
result.push(p + c);
} else if (c.startsWith("&")) {
result.push(selector + c.substring(1))
} else if (c.startsWith('@')) {
result.push(c + ' ' + p)
} else {
result.push(p + ' ' + c)
}
}
}
return result.join(', ');
}
export function block(body: string) {
sheet.insertRule(body);
}
export function rule(sel: string, body: RuleDefinition) {
let subrules: [string, RuleDefinition][] = [];
let bodyLines: string[] = [];
for (const [key, value] of Object.entries(body)) {
if (typeof value === 'object') {
subrules.push([combineSelectorForNestedRule(sel, key), value]);
continue;
} else {
let property = key.replace(/[A-Z]/g, c => '-' + c.toLowerCase());
bodyLines.push(`${property}: ${value}`);
}
}
sheet.insertRule(sel + "{" + bodyLines.join(";") + "}\n");
for (let [sel1, body1] of subrules) {
rule(sel1, body1);
}
}
const usedNames = new Set<string>();
export function cls(name: string, body: RuleDefinition) {
if (usedNames.has(name)) {
// find a new name
for (let i = 0; i < 100; i++) {
let newName = name + '-' + i;
if (!usedNames.has(newName)) {
name = newName;
break;
}
}
}
usedNames.add(name);
rule("." + name, body);
return name;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment