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", }; 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?"_"+activeQueryKey:""}`; const check = styles.get(selector); if(!check) { const body = `{ ${propertyName}:${propertyValue}; }`; styles.set(selector, activeQuery ? `{ @media(${activeQuery})${body}}` : body); } 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:false|string = false; let activeQueryKey:false|string = false; type QueryInvoker = (...args:string[])=>string function Query(amount:string, key?:string):QueryInvoker { activeQuery = amount; activeQueryKey = key||amount return (...args)=> { activeQuery = false; activeQueryKey = 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({});