You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Design constraints with css classes and typescript validation and auto-completion
Title is WIP
The talk
One-sentence summary
I'll explain how you can leverage Typescript template literal types to create both run-time and compile time classes enabling your very own Tailwind-like developer experience.
What's the format — is it a case study, a live coding session, a workshop or something else?
case study
Tell us more about the talk
I'll most likely go through the following file and show step by step how Typescript's type system can infer types in various real time scenarios.
// register global stylesdeclare global {interfaceWindow{sfrStyles?: HTMLStyleElement;}}constatomicClasses=(()=>{constbaseClasses={hide: "display: none",block: "display: block","inline-block": "display: inline-block",flex: "display: flex","inline-flex": "display: inline-flex","flex-wrap": "flex-wrap: wrap",flex1: "flex: 1",flex2: "flex: 2",flex3: "flex: 3",flex4: "flex: 4",flex5: "flex: 5",flex6: "flex: 6",capitalize: "text-transform: capitalize",lowercase: "text-transform: lowercase",uppercase: "text-transform: uppercase","margin-0auto": "margin: 0 auto","margin-left-auto": "margin-left: auto","position-relative": "position: relative","background-color-initial": "background-color: initial","background-color-white": "background-color: white","user-select-none": "user-select: none","cursor-pointer": "cursor: pointer","color-white": "color: white","max-width400": "max-width: 400px","max-width720": "max-width: 720px","pointer-events-auto": "pointer-events: auto","white-space-pre-wrap": "white-space: pre-wrap",}asconst;functioncomputeVariations<T1extendsstring,T2extendsstring,V1extendsstring,V2extendsstring>([names,subNames]: readonly[readonlyT1[],readonlyT2[]],values: readonly(readonly[V1,V2])[]){returnnames.flatMap((name)=>[
...values.map(([valueName,value])=>[`${name}${valueName}`,`${name}: ${value}`]asconst),
...subNames.flatMap((subName)=>values.map(([valueName,value])=>[`${name}-${subName}${valueName}`,`${name}-${subName}: ${value}`,]asconst)),]asconst);}constsizes=[["4","4px"],["6","6px"],["8","8px"],["12","12px"],["16","16px"],["18","18px"],["24","24px"],["32","32px"],["48","48px"],["64","64px"],]asconst;constcolors=(["blue","green","red","gray"]asconst).flatMap((color)=>(["1","2","3","4","5","6","7","8","9","10"]asconst).flatMap((step)=>[`${color}-${step}`]asconst));constcolorOptions=([["background","background-color"],["color","color"],]asconst).flatMap(([name,property])=>colors.map((color)=>[`${name}-${color}`,`${property}: var(--${color})`]asconst));constsizeEntries=computeVariations([["width","max-width","min-width","height","max-height","min-height"],[],],[
...sizes,["128","128px"],["100","100%"],["-min","min-content"],["-max","max-content"],]);constspacingEntries=computeVariations([["margin","padding"],["top","bottom","left","right"],],[["0","0"], ...sizes]);constgapEntries=computeVariations([["gap"],[]],[...sizes,["-items","var(--gap-items)"],["-cards","var(--gap-cards)"]]);constsizedEntries=computeVariations([["border-radius"],[]],[["6","6px"],["16","16px"],["100","100%"],]);constborderEntries=computeVariations([["border"],["right"]],[["0","0"],["-none","none"],]);constflexDirectionEntries=computeVariations([["flex-direction"],[]],[["-row","row"],["-column","column"],["-row-reverse","row-reverse"],["-column-reverse","column-reverse"],]);constcontentEntries=computeVariations([["justify-content"],[]],[["-space-between","space-between"],["-space-around","space-around"],["-center","center"],["-end","end"],["-flex-end","flex-end"],]);constitemsEntries=computeVariations([["align-items","align-self","justify-items"],[]],[["-start","start"],["-center","center"],["-stretch","stretch"],["-end","end"],["-flex-end","flex-end"],]);consttextAlignEntries=computeVariations([["text-align"],[]],[["-left","left"],["-center","center"],["-right","right"],]);constoverflowEntries=computeVariations([["overflow"],["x","y"]],[["-visible","visible"],["-hidden","hidden"],["-auto","auto"],]);constfontSizeEntries=computeVariations([["font-size"],[]],sizes);typeNameFromEntries<Textendsreadonly(readonly[string,string])[]>=T[number][0];constexternalClasses=["btn-hidable","btn-icon-right","pagination-hide-steps",]asconst;typeName=|keyoftypeofbaseClasses|NameFromEntries<typeofsizeEntries>|NameFromEntries<typeofcolorOptions>|NameFromEntries<typeofspacingEntries>|NameFromEntries<typeofgapEntries>|NameFromEntries<typeofsizedEntries>|NameFromEntries<typeofflexDirectionEntries>|NameFromEntries<typeofcontentEntries>|NameFromEntries<typeofitemsEntries>|NameFromEntries<typeoftextAlignEntries>|NameFromEntries<typeofoverflowEntries>|NameFromEntries<typeofborderEntries>|NameFromEntries<typeoffontSizeEntries>|typeofexternalClasses[number];constclasses=Object.freeze(Object.assign(Object.create(null)asobject,baseClasses,Object.fromEntries(sizeEntries),Object.fromEntries(colorOptions),Object.fromEntries(spacingEntries),Object.fromEntries(gapEntries),Object.fromEntries(sizedEntries),Object.fromEntries(flexDirectionEntries),Object.fromEntries(contentEntries),Object.fromEntries(itemsEntries),Object.fromEntries(textAlignEntries),Object.fromEntries(overflowEntries),Object.fromEntries(borderEntries),Object.fromEntries(fontSizeEntries),Object.fromEntries(externalClasses.map((name)=>[name,""]asconst)))as{[keyinName]: string});// register stylesconststyleContent=Object.entries(classes).filter(([,style])=>style).map(([className,style])=>`.sfr-${className} {${style} !important;}`).join("\n");consttextNode=document.createTextNode(styleContent);if(window.sfrStyles){window.sfrStyles.firstChild?.remove();window.sfrStyles.appendChild(textNode);}else{window.sfrStyles=document.createElement("style");window.sfrStyles.appendChild(textNode);document.head.appendChild(window.sfrStyles);}returnclasses;})();typeName=keyoftypeofatomicClasses;// let's use arguments directly to prevent webpack from converting it to array/* eslint-disable prefer-rest-params */exportconstcssCommons=functioncssCommons(): string{constparts: string[]=newArray(arguments.length*2);letpartsIdx=0;// those loops aren't recommended because of readability but they are still the fastest way// to iterate over an array// eslint-disable-next-line no-plusplusfor(letargumentsIdx=0;argumentsIdx<arguments.length;argumentsIdx++){// since app wont crash when unknown css classes are used we don't need this check// in production builds and webpack will remove this code when bundlingif(process.env.NODE_ENV!=="production"){if(!((arguments[argumentsIdx]asName)inatomicClasses)){thrownewError(`Unknown atomic class. "${arguments[argumentsIdx]asName}" is not available.`);}
8000
}// parts.push() is a no-go since we preallocate the array/* eslint-disable no-plusplus */parts[partsIdx++]=" sfr-";parts[partsIdx++]=arguments[argumentsIdx];/* eslint-enable no-plusplus */}// apply is faster then "".concat(...parts)returnString.prototype.concat.apply("",parts);}as(...classNames: readonly[Name, ...(readonlyName[])])=>string;/* eslint-enable prefer-rest-params */
And explain how we've got from the code above the following results
<Title of your talk>
Design constraints with css classes and typescript validation and auto-completion
Title is WIP
The talk
One-sentence summary
I'll explain how you can leverage Typescript template literal types to create both run-time and compile time classes enabling your very own Tailwind-like developer experience.
What's the format — is it a case study, a live coding session, a workshop or something else?
case study
Tell us more about the talk
I'll most likely go through the following file and show step by step how Typescript's type system can infer types in various real time scenarios.
And explain how we've got from the code above the following results
You
Co-Funder and CTO at Surfer Local. https://surferlocal.com
A few words about yourself
https://www.linkedin.com/in/mieszko-wawrzyniak/
I'll work up something later if my talk is accepted.
How can we find you on social media?
It's best to email me.
Would you be willing to have a Q/A session after the talk?
Sure
Do you mind if we record the event?
Nope, go ahead
Is there anything we can help you with?
Nope
I was invited to post my proposal by @hasparus
The text was updated successfully, but these errors were encountered: