const typeface = { sans: "sans serif", serif: "Times New Roman" }; const sizes = { small: "1rem", large: "3rem" }; const colors = { red: "#ff2200", blue: "#0022ff" }; const responsive = { md: "min-width:767px", lg: "min-width:1024px", }; let sheet = {insertRule(_:string){}} as CSSStyleSheet; if(globalThis?.document) { const sheetElement = document.createElement("style"); document.head.setAttribute("data-gale", "true"); document.head.appendChild(sheetElement); sheet = sheetElement.sheet as CSSStyleSheet; } const styles = new Map(); function Mapper>(className:string, propertyName:string, lut:T) { const build =(propertyValue:string, customPropertyName?:string):string=> { const selector = `${className}-${(customPropertyName||propertyValue as string).replace(/[^a-zA-Z0-9]/g, "")}${activeQuery.val?"_"+activeQuery.key:""}`; const check = styles.get(selector); if(!check) { const body = `{ ${propertyName}:${propertyValue}; }`; const rules = activeQuery.val ? `{ @media(${activeQuery.val})${body}}` : body styles.set(selector, rules); sheet.insertRule(selector + " " + rules); } return selector; } type UseCustom = (custom:string)=>string; return new Proxy(((override:string)=>build(override)) as UseCustom, { get(_, prop){ return build(lut[prop as string], prop as string); } } ) as T & UseCustom; } function MapperQuery>(lut:T) { type UseCustom = (custom:string)=>QueryInvoker; return new Proxy(((override:string)=>Query(override, override)) as UseCustom, { get(_, prop:string){ return Query(lut[prop], prop) } } ) as Record & UseCustom; } let activeQuery:ActiveQuery; type ActiveQuery = {key:string|false, val:string|false}; const activeQueryStack:ActiveQuery[] = []; type QueryInvoker = (...args:string[])=>string function Query(amount:string, key?:string):QueryInvoker { activeQuery = {key:key||amount, val:amount}; activeQueryStack.push(activeQuery); return (...args)=> { activeQueryStack.pop() activeQuery = activeQueryStack.at(-1) || {key:false, val:false}; return args.join(" "); } } export function config(obj:T) { const CSS = { Sheet() { const rules = []; for(const [key, value] of styles.entries()) { rules.push(key+value); } return rules.join("\n"); }, Face: Mapper("Face", "font-family", {...typeface, ...obj}), Pad: Mapper("Pad", "padding", sizes), Q: MapperQuery(responsive) }; return CSS; } export default config({});