How can I group several Tailwind CSS utilities under one modifier?

2.5k Views Asked by At

I'm using Tailwind's default breakpoint values in a TypeScript + React project compiled with Vite. But I noticed that in my project and on a section of their documentation that both repeated instances of the same modifiers within one element. In the doc's case md::

<img class="h-48 w-full object-cover md:h-full md:w-48" src="/img/building.jpg" alt="Modern building architecture">

Is there a way to group h-full and w-48 under one md: modifier to make certain styles more readable and easier to locate?

My Attempted Solution

Using Tailwind's default color palette and default breakpoint values, I made "Hello World" take on an orange background and the heaviest available font weight when the screen size is equal to or greater than sm's default minimum width of 640px:

<h1 className="sm:bg-orange-500 sm:font-black">Hello World</h1>

To reproduce the same result using one instance of the sm modifier within the same <h1> element, I tried adding curly braces around utility classes grouped together with commas:

<h1 className="sm:{bg-orange-500, font-black}">Hello World</h1>
2

There are 2 best solutions below

0
sabist On

It is NOT possible to group several Tailwind classes under a single breakpoint prefix, such as md: or lg:. If you need to customize your classes in this way, you may want to consider using an alternative tool, such as WindiCSS or UnoCSS. These tools offer similar functionality to Tailwind, but with additional features and customization options.

0
fedoraname1 On

i did exactly it i can do text-[red,md:[green,hover:pink,2xl],xl] same for bg import my code into the shortcut

otherwise with unocss you can do lg:(bg-green text-red text-2xl)

my full config : unocss.config.ts

[
        /^(font|bg|border|stroke|outline|ring|divide|text)-\[(.*)\]$/,
        ([, category, stringElement]) => {
            type Category = 'font' | 'bg' | 'border' | 'stroke' | 'outline' | 'text' | 'ring' | 'divide'
            type MediaQuery = 'sm' | 'md' | 'lg' | 'xl' | '2xl'
            const categories: readonly Category[] = ['font', 'bg', 'border', 'stroke', 'outline', 'text', 'ring', 'divide']
            const mediaQuery: readonly MediaQuery[] = ['sm', 'md', 'lg', 'xl', '2xl']
            const rulesForBrakets: Record<'open' | 'close', string> = {
                open: '[',
                close: ']'
            }

            if (!categories.includes(category as Category)) {
                throw new Error(`category in not in unocss list config=> ${category}`)
            }

            function splitString(str: string): Set<string> {
                const result = new Set<string>()
                let currentElement = ''
                let parenthesesCount = true
                for (const char of str) {
                    if (char === rulesForBrakets.open) {
                        parenthesesCount = false
                    } else if (char === rulesForBrakets.close) {
                        parenthesesCount = true
                    }
                    if (char === ',' && parenthesesCount === true) {
                        result.add(currentElement.toLowerCase().trim())
                        currentElement = ''
                    } else {
                        currentElement += char.trim()
                    }
                }
                if (currentElement.trim() !== '') {
                    result.add(currentElement.toLowerCase().trim())
                }
                return result
            }

            const arraySet = splitString(stringElement)

            const regexAtribuffy = new RegExp(`([^:]+):\\${rulesForBrakets.open}([^\\]]+)\\${rulesForBrakets.close}$`)
            const mycustomSet = new Set<string>()

            for (const v of arraySet) {
                if (v.includes(':')) {
                    if (v.match(regexAtribuffy)) {
                        const match = v.match(regexAtribuffy)

                        if (match) {
                            const [, md, rest] = match
                            if (!mediaQuery.includes(md as MediaQuery)) {
                                throw new Error('bad media querie')
                            }
                            const [breakpoint] = md.trim().split(':')

                            for (const e of rest.split(',')) {
                                if (e.includes(':')) {
                                    const index: number = e.lastIndexOf(':')
                                    const state = e.slice(0, index)
                                    const css = e.slice(index + 1)
                                    const result = `${breakpoint}:${state}:${category}-${css.trim()}`
                                    mycustomSet.add(result)
                                } else {
                                    mycustomSet.add(`${breakpoint}:${category}-${e.trim()}`)
                                }
                            }
                        }
                    } else {
                        const index = v.lastIndexOf(':')
                        const breakpointORstate = v.slice(0, index)
                        const css = v.slice(index + 1)
                        const value = `${breakpointORstate}:${category}-${css.trim()}`
                        mycustomSet.add(value.trim())
                    }
                } else {
                    mycustomSet.add(`${category}-${v.trim()}`)
                }
            }
            return Array.from(mycustomSet).join(' ')
        }
    ],