// tss.ts type PseudoKeys = ["hover"|"before"|"after"] | ["hover", "after"|"before"]; type ValueSignature = (...args:any[])=>string type EnumDefinition = [property:string, options:Record, values?:Signature]; type EnumBlock = Record>; type RecursiveObject> = { [F in keyof Obj]: Obj[F][2] extends Sig ? ((...args:Parameters)=> RecursiveObject&((...args:PseudoKeys)=>RecursiveObject)&Array>)&{ [E in keyof Obj[F][1]]:RecursiveObject&((...args:PseudoKeys)=>RecursiveObject)&Array> } : { [E in keyof Obj[F][1]]:RecursiveObject&((...args:PseudoKeys)=>RecursiveObject)&Array> } } function Block>(options:Fields):()=>RecursiveObject { return ()=>{ let pseudo = false; let query = false; const list = []; let fieldLookup = {}; const proxyOuter = new Proxy( function(...args) { // OUTER context called as function: if(args.length) // pseudo elements are being declared, capture and return the OUTER context { list.push(`${(pseudo||query) ? "}" : ""} &:${args.join("::")}{`); pseudo = true; query = false; return proxyOuter; } else // css dump requested: { pseudo && list.push("}"); query && list.push("}"); pseudo = false; query = false; return list; } }, { get(_target, propName) { // OUTER context property access: if(parseInt(propName)) // if a number was used as a property, treat that as a mobile-first media query in px units, and return the OUTER context { list.push(`${(query) ? "}" : ""} @media(min-width:${propName}px){`); query = true; return proxyOuter; } // a css property alias was accessed, look up the mapped css property name from the declaration array, push that, and return the OUTER context fieldLookup = options[propName]; if(fieldLookup) { list.push(fieldLookup[0]); } return proxyInner; } } ); const proxyInner = new Proxy( function(...args) { // INNER context called as function // capture the evalued dynamic values and return the OUTER context list.push(`:${fieldLookup[2](...args)};`); return proxyOuter; }, { get(_target, valName) { // INNER context property access try // capture the mapped css value and return the OUTER context { list.push(`:${fieldLookup[1][valName]};`); return proxyOuter; } catch(_e) { console.warn("someone is trying to stringify a style proxy"); return ()=>"[StyleProxy]"; } } } ); return proxyOuter; } } type Measure = "px" | "em" | "rem" | "vh" | "vw" | "%"; type UnitString = `${number}${Measure}`; const funcUnitSingle = (amount:UnitString)=>amount const funcUnitTRBL=(...args:[t:T, r?:T, b?:T, l?:T])=>args.join(" ") const UnitAuto = {Auto:"auto"}; const styles = Block( { Pos:["position", { Abs:"absolute", Rel:"relative", Fix:"fixed", Pin:"sticky", No:"static" }], Dis:["display", { Flex:"flex", Grid:"grid", None:"none", Block:"block", InlineBlock:"inline-block" }], Box:["box-sizing", { Border:"border-box", Content:"content-box" }], T:["top", UnitAuto, funcUnitSingle], R:["right", UnitAuto, funcUnitSingle], B:["bottom", UnitAuto, funcUnitSingle], L:["left", UnitAuto, funcUnitSingle], Pad: ["padding", {}, funcUnitTRBL ], PadT:["padding-top", {}, funcUnitSingle], PadR:["padding-right", {}, funcUnitSingle], PadB:["padding-bottom", {}, funcUnitSingle], PadL:["padding-left", {}, funcUnitSingle], Mar: ["margin", UnitAuto, funcUnitTRBL ], MarT:["margin-top", UnitAuto, funcUnitSingle], MarR:["margin-right", UnitAuto, funcUnitSingle], MarB:["margin-bottom", UnitAuto, funcUnitSingle], MarL:["margin-left", UnitAuto, funcUnitSingle] } ); const masterSheet = {}; const SheetId = { index:-1, getId() { this.index++; return this.index; } }; export function Sheet, css:ReturnType)=>void >>(userClasses:UserClasses):{[Class in keyof UserClasses]:string} { const hash = SheetId.getId(); const writer = globalThis["document"] ? (selector, declaration)=> { const styleBlock = document.createElement("style"); styleBlock.innerText = "."+selector+declaration; document.head.insertAdjacentElement("beforeend", styleBlock) } : (selector, declaration)=>{masterSheet[selector] = declaration}; return new Proxy(function(){}, {get(_target, className){ const lookup = userClasses[className]; if(typeof lookup == "function") { const skewer = styles(); lookup.call(skewer, skewer); const name = `${className}_${hash}`; writer(name, `{ ${skewer().join("")} }`) userClasses[className] = name; return name; } else { return lookup; } }}); } export function Divulge() { return masterSheet; }