· 6 min read
10 Hidden Tailwind CSS Tricks You Didn’t Know Existed
Discover 10 lesser-known Tailwind CSS features - from arbitrary selector variants and CSS-variable power to matchUtilities and safelisting - that can speed up your workflow and unlock expressive UI patterns.
Introduction
Tailwind CSS is full of powerful, composable utilities - but beyond the well-trodden ground of px-4
and bg-blue-500
there are many lesser-known features that can dramatically expand what you can do without leaving your HTML or writing custom CSS. This article explores 10 “hidden” Tailwind tricks that will make your stylesheets shorter, your components more robust, and your design decisions easier to manage.
For official documentation and deeper reading, see the Tailwind docs: https://tailwindcss.com/docs and the plugin docs: https://tailwindcss.com/docs/plugins. (Where specific pages are mentioned below, they reflect the Tailwind approach to arbitrary values, selectors and plugins.)
- Arbitrary selector variants: target almost any selector
Tailwind’s arbitrary variants let you write variants that map to arbitrary selectors, media queries or attributes. You can target parent states, sibling combinators and even complex :not() selectors.
Example: style an element when a parent has a data attribute or when it’s not the last child.
<div class="[data-state=open]:bg-white [&:not(:last-child)]:mb-4">
<p class="text-sm">I'm styled conditionally</p>
</div>
- [data-state=open]:bg-white applies when an ancestor matches that attribute.
- [&:not(:last-child)]:mb-4 applies margin-bottom to every element except the last one.
Why it matters: You can avoid writing custom CSS for many contextual patterns directly in your markup.
Gotcha: Complex selectors can be hard to read - name your components clearly.
- Arbitrary values and functions: calc(), urls, gradients, and more
Arbitrary values let you pass almost any CSS value inside square brackets: sizes, calc expressions, URLs, gradients, borders, and so on.
<div class="w-[calc(100%-1.5rem)] bg-[url('/images/pattern.svg')] border-[3px]">
Arbitrary value demo
</div>
You can even use colors or alpha with arbitrary values:
<div class="bg-[linear-gradient(90deg,#06b6d4,transparent)]">
Gradient background
</div>
Why it matters: Complex layouts and custom visuals can stay in your HTML util classes instead of separate CSS files.
- Use CSS variables seamlessly with Tailwind classes
Tailwind plays well with CSS variables. Combine them with arbitrary values for themeable components.
<!-- root variables set in a theme switcher or a component -->
<div style="--card-bg: 242 244 246; --accent: 220 38 38;">
<div class="bg-[rgb(var(--card-bg))] text-[rgb(var(--accent))] p-4 rounded">
Card with CSS variables
</div>
</div>
Tip: Using rgb(var(--my-var))
plus Tailwind’s /
alpha notation gives precise control over colors and opacity.
Why it matters: Perfect for design tokens, runtime theme switching, and when you want to keep color logic declarative.
- Pseudo-elements with Tailwind’s content utilities (before/after)
Tailwind supports before:
and after:
plus the content-[]
utility to create pseudo-elements without custom CSS.
<button
class="relative before:content-[''] before:absolute before:-inset-0 before:bg-gradient-to-r before:from-indigo-400 before:to-pink-500 before:opacity-20"
>
Button with decorative gradient overlay
</button>
You can combine before:
/after:
with positioning and transforms to make tooltips, decorative accents, or icons.
Why it matters: Small decorative patterns that previously required CSS files can live with the component markup.
- Advanced peer & group patterns for sibling/parent-driven styles
peer
and group
utilities let one element influence another. Pair them with arbitrary selectors to create nuanced interactions.
<label class="flex items-center space-x-2">
<input type="checkbox" class="peer sr-only" />
<span
class="peer-checked:bg-green-500 peer-checked:text-white px-3 py-1 rounded"
>Toggle</span
>
</label>
Combine with arbitrary selector variants for deeper control:
<div class="peer-focus:[&>svg]:stroke-blue-500">
<svg class="w-6 h-6 stroke-gray-400">...</svg>
</div>
Why it matters: Build accessible UI controls (toggles, tabs, accordions) with minimal JS and no separate CSS.
- Control output order and reuse with @layer + @apply + the ! prefix
Use @layer
in your global CSS to register design components and control whether rules are emitted in base
, components
, or utilities
layers. Combine with @apply
and the !
prefix to add !important
when needed.
styles.css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded;
}
.btn-ghost {
@apply btn-primary bg-transparent text-blue-600;
}
}
You can also use !
to force utilities to be !important
(e.g., !mt-0
). This is handy for overriding third-party components.
Why it matters: Keep small CSS systems organized, create shared components, and avoid specificity wars.
- Arbitrary variants for @supports and custom media queries
Tailwind’s bracketed variant syntax can be used to create @supports
conditions or custom media queries inline.
<!-- Apply a blur only where backdrop-filter is supported -->
<div
class="supports-[backdrop-filter]:backdrop-blur-md supports-[backdrop-filter]:bg-white/30"
>
Card with optional blur
</div>
<!-- Use a custom media query on a per-class basis -->
<div class="[@media(min-width:1200px)]:px-20">Wide-screen padding</div>
Why it matters: Progressive enhancement and precise media queries can be written where they are consumed.
- Safelist dynamic classes (useful when classes are generated at runtime)
When you programmatically build class names (e.g., using template strings), Tailwind’s content extractor can miss them. Use safelist
in tailwind.config.js
to whitelist classes or regex patterns:
// tailwind.config.js
module.exports = {
// ... other config
safelist: ['bg-red-500', { pattern: /bg-(red|green|blue)-(50|100|200|500)/ }],
};
Why it matters: Prevents critical classes from being purged in production when they are produced dynamically (e.g., user colors, CMS content, theme tokens).
- Create custom utilities that accept arbitrary values with matchUtilities (plugin API)
If you need a CSS property that Tailwind doesn’t have first-class support for, matchUtilities
lets you map a utility name to values - including arbitrary values - so authors can use foo-[value]
directly.
// tailwind.config.js
const plugin = require('tailwindcss/plugin');
module.exports = {
// ...
plugins: [
plugin(function ({ matchUtilities, theme }) {
matchUtilities(
{
'text-shadow': value => ({
textShadow: value,
}),
},
{ values: theme('textShadow') }
);
}),
],
};
Then in HTML: <h1 class="text-shadow-[0_2px_6px_rgba(0,0,0,0.25)]">Heading</h1>
Why it matters: Build first-class utilities for properties your design system needs, and still support arbitrary values.
Reference: Tailwind plugin docs - https://tailwindcss.com/docs/plugins
- Use theme() and config-driven tokens to bridge Tailwind and raw CSS
Tailwind’s theme()
function can be used inside your custom CSS to reference theme values, enabling a single source of truth for sizes, colors and spacing.
/* styles.css */
@layer components {
.card {
color: theme('colors.gray.800');
padding: theme('spacing.6');
border-radius: theme('borderRadius.lg');
}
}
You can also export tokens from tailwind.config.js
(like custom --
CSS variable mappings) and reference them across JS and CSS for perfect consistency.
Why it matters: Keep spacing, colors and radii centralized. It helps with design handoffs and ensures your UI remains coherent.
Closing notes & practical tips
- Favor composition: pick a few of the above tricks that map to your team’s workflow and standardize them in your component library.
- Readability matters: arbitrary selectors and values are powerful - but too many can make markup hard to scan. Balance convenience with clarity.
- Use
@layer
and@apply
for shared patterns and keep per-component tweaks in markup.
Further reading
- Tailwind CSS documentation: https://tailwindcss.com/docs
- Tailwind Plugins & API: https://tailwindcss.com/docs/plugins
Which trick will you try first? Whether it’s arbitrary selectors, matchUtilities plugins, or CSS-variable-driven themes, these features let Tailwind users close the gap between utility-first speed and real-world design needs.