Hi everyone ( first post on a forum, sorry for mistakes ), I'm building a library for handle my custom design system logic; I've searched a lot but it seems like a tailwind limitation, I have many components like TypographyDisplay that has props like size or fontColor that needs to be dynamically rendered basing on how the component is instantiated. I'm using Qwik framework but I found that problem in Next.JS too and I'm going to have a terrible headhache: I don't want to send all the class possibile combination in the css bundle, if I instantiate like this "<TypographyDisplay fontColor = "primary">Something</TypographyDisplay>" i only want to send .text-primary not all the theme fontColor combinations in bundle :( I'm using SSR to avoid those things, I've seen that I can purge files but I don't know how to purge SSR generated HTML...Qwik is also a resumable framework so it sends pure HTML to client, I find that TailwindCSS behaviour so frustrating, it should simply understand what class are going to be rendered in that occasion and make bundle on-the-fly...is not the JIT mode (that should be used as default) do something like this? Have I to switch to another CSS technology or use some meta-utils like twin.macro? thanks for the time spent.
package.json
{ "name": "my-qwik-basic-starter", "description": "App with Routing built-in (recommended)", "engines": { "node": ">=15.0.0" }, "private": true, "scripts": { "build": "qwik build", "build.client": "vite build", "build.preview": "vite build --ssr src/entry.preview.tsx", "build.types": "tsc --incremental --noEmit", "deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'", "dev": "vite --mode ssr", "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force", "fmt": "prettier --write .", "fmt.check": "prettier --check .", "lint": "eslint \"src/**/*.ts*\"", "preview": "qwik build preview && vite preview --host", "start": "vite --host --mode ssr", "qwik": "qwik" }, "devDependencies": { "@builder.io/qwik": "0.17.5", "@builder.io/qwik-city": "0.1.0", "@types/eslint": "8.21.0", "@types/node": "^18.11.19", "@types/node-fetch": "latest", "@typescript-eslint/eslint-plugin": "5.51.0", "@typescript-eslint/parser": "5.51.0", "autoprefixer": "10.4.11", "eslint": "8.33.0", "eslint-plugin-qwik": "0.17.5", "node-fetch": "3.3.0", "postcss": "^8.4.16", "prettier": "2.8.3", "tailwindcss": "^3.1.8", "typescript": "4.9.5", "undici": "5.18.0", "vite": "4.1.1", "vite-tsconfig-paths": "3.5.0" }, "dependencies": { "@builder.io/sdk-qwik": "^0.1.7", "dotenv": "^16.0.3" } } tailwind.config.js
/** @type {import('tailwindcss').Config} */ const MDSysTypefaceDisplayLarge = require( "./src/designTokens/materialDesign/md.sys.typescale.display-large" ) ; const MDSysTypefaceDisplayMedium = require( "./src/designTokens/materialDesign/md.sys.typescale.display-medium" ) ; const MDSysTypefaceDisplaySmall = require( "./src/designTokens/materialDesign/md.sys.typescale.display-small" ) ; // etc. module.exports = { content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], theme: { fontFamily : { "display-large" : MDSysTypefaceDisplayLarge.font , "display-medium" : MDSysTypefaceDisplayMedium.font , "display-small" : MDSysTypefaceDisplaySmall.font , // etc. } , fontSize : { "display-large" : [ MDSysTypefaceDisplayLarge.size + "px" , { lineHeight : MDSysTypefaceDisplayLarge.lineHeight + "px" , letterSpacing : MDSysTypefaceDisplayLarge.tracking + "px" , fontWeight : MDSysTypefaceDisplayLarge.weight } ] , "display-medium" : [ MDSysTypefaceDisplayMedium.size + "px" , { lineHeight : MDSysTypefaceDisplayMedium.lineHeight + "px" , letterSpacing : MDSysTypefaceDisplayMedium.tracking + "px" , fontWeight : MDSysTypefaceDisplayMedium.weight } ] , "display-small" : [ MDSysTypefaceDisplaySmall.size + "px" , { lineHeight : MDSysTypefaceDisplaySmall.lineHeight + "px" , letterSpacing : MDSysTypefaceDisplaySmall.tracking + "px" , fontWeight : MDSysTypefaceDisplaySmall.weight } ] , // etc. } , extend: {}, }, plugins: [], }; UITypographyHeadingDisplay.tailwindcss.ts
import type { IUITypographyHeadingDisplay } from "../.." ; import { EUIComponentColors, EUIComponentSizes } from "~/components/enums" ; import { UITypographyHeadingTailwindCSSStylingStrategyHandler } from "~/components/ui/typography/headings" ; export interface IUITypographyHeadingDisplayTailwindCSSStylingStrategyProps extends IUITypographyHeadingDisplay {} export type IUITypographyTailwindCSSStylingStrategy = ( args : IUITypographyHeadingDisplayTailwindCSSStylingStrategyProps ) => string[] ; export const UITypographyHeadingDisplayTailwindCSSStylingStrategyHandler : IUITypographyTailwindCSSStylingStrategy = ( { size = EUIComponentSizes.MEDIUM , fontColor = EUIComponentColors.ON_SURFACE , isBold = false , isItalic = false , } ) => { let _classNames : string[] = UITypographyHeadingTailwindCSSStylingStrategyHandler( { fontColor , isBold , isItalic , } ) ; const fontFaceClassName = size === EUIComponentSizes.LARGE ? "font-display-large" : size === EUIComponentSizes.MEDIUM ? "font-display-medium" : size === EUIComponentSizes.SMALL ? "font-display-small" : "" ; ; if ( fontFaceClassName !== "" ) _classNames = [ ..._classNames , fontFaceClassName , ] ; const fontSizeClassName = size === EUIComponentSizes.LARGE ? "text-display-large" : size === EUIComponentSizes.MEDIUM ? "text-display-medium" : size === EUIComponentSizes.SMALL ? "text-display-small" : "" ; if ( fontSizeClassName !== "" ) _classNames = [ ..._classNames , fontSizeClassName ] ; return _classNames ; } ; UITypographyDisplay.tsx
import { component$ } from "@builder.io/qwik" ; import type { IUITypographyHeadingDisplay } from "../" ; import { UITypographyHeadingDisplayTailwindCSSStylingStrategyHandler } from "../" ; import { QwikHTMLTypographyHeading } from "~/components/html/typography/concretes/heading" ; import { QwikHTMLTypographyBoldDecorator } from "~/components/html/typography/decorators/bold" ; import { QwikHTMLTypographyItalicDecorator } from "~/components/html/typography/decorators/italic" ; import { EUIComponentSizes , EUIComponentColors , } from "~/components/enums" ; import { EUITypographyHeadingLevels } from "~/components/enums/typography/headings" ; export interface IUITypographyHeadingDisplayQwikComponentProps extends IUITypographyHeadingDisplay {} export const QwikUITypographyHeadingDisplay = component$( ( { id = undefined , onClick = undefined , onChange = undefined , size = EUIComponentSizes.MEDIUM , fontColor = EUIComponentColors.ON_SURFACE , isBold = false , isItalic = false , HeadingLevel = EUITypographyHeadingLevels.H1 , copy = "" , } : IUITypographyHeadingDisplayQwikComponentProps ) => { const _classNames = UITypographyHeadingDisplayTailwindCSSStylingStrategyHandler( { size , fontColor , isBold , isItalic } ) ; const _QwikHTMLTypographyHeadingProps = { id , onClick , onChange , classNames : _classNames.join( " " ) , HeadingLevel , } ; return ( <QwikHTMLTypographyBoldDecorator isBold = { isBold } > <QwikHTMLTypographyItalicDecorator isItalic = { isItalic } > <QwikHTMLTypographyHeading { ..._QwikHTMLTypographyHeadingProps } > { copy } </QwikHTMLTypographyHeading> </QwikHTMLTypographyItalicDecorator> </QwikHTMLTypographyBoldDecorator> ) ; } ) ; QwikHTMLTypographyHeading
import { component$ , Slot , } from "@builder.io/qwik" ; import type { IHTMLTypographyHeading } from ".." ; import { EUITypographyHeadingLevels } from "~/components/enums/typography/headings" ; export interface IHTMLTypographyHeadingQwikComponentProps extends IHTMLTypographyHeading {} export const QwikHTMLTypographyHeading = component$( ( { classNames = undefined , HeadingLevel = EUITypographyHeadingLevels.H1 , } : IHTMLTypographyHeadingQwikComponentProps ) => { return ( <HeadingLevel class = { classNames } > <Slot /> </HeadingLevel> ) ; } ) ; tried to set mode:"jit" but obv nothing changes
class = text-${color} was the first attempt and obv not working without commenting classnames or adding them to safelist, far i've understand to use full classnames but laaarge bundle size if someone use a complex design system
ps: I've tried to extrapolate classNames into an external object and that object in an external file too and accessing it like colorStyles[color] but same result with dirty enumerator usage, I've tried to put the logic into QwikHTMLTypographyHeading class property but same result...
I want only to compile css classes with ssr pros like knowing the classname of a used component to have correct bundle size...