Nuri Kim
Nuri Kim

Reputation: 211

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

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>

Upvotes: 3

Views: 5170

Answers (3)

Marcos Guerrero
Marcos Guerrero

Reputation: 388

You can use tailwindcss/plugin to apply multiple rules if the breakpoint matches

first you need to add the following code to your tailwind.config.js file

/** @type {import('tailwindcss').Config} */

export default {
  content: [
    "./index.html"
    "./src/**/*.{js,ts,jsx,tsx}"
  ],
  theme: {
    //...
  },
  plugins: [
    // add the following lines
    require('tailwindcss/plugin')(({ matchUtilities }) => {
      matchUtilities({
        'x': (value) => ({
          [`@apply ${value.replaceAll(',', ' ')}`]: {}
        })
      })
    })
  ]
}

and now you can apply multiple rules that matches with the breakpoint/state/etc e.g.:

<!-- then -->
<section className='text-[#f00] text-[25px] font-semibold md:text-[#0f0] md:text-[36px] md:font-bold'>
  {...}
</section>

<!-- now -->
<section className='text-[#f00] text-[25px] font-semibold md:x-[text-[#0f0],text-[36px],font-bold]'>
  {...}
</section>

Upvotes: 2

fedoraname1
fedoraname1

Reputation: 661

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(' ')
        }
    ],

Upvotes: 1

sabist
sabist

Reputation: 41

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.

Upvotes: 3

Related Questions