Building Responsive Dark-Mode Components with Tailwind JIT and Arbitrary Variants
Learn how Tailwind CSS JIT mode and arbitrary variants let you build responsive, dark‑mode‑aware components without writing custom CSS, with a concrete card example and practical verification steps.
05 Sept 2026, 23:17 UTC

The problem: styling child elements on hover without extra CSS
Utility‑first frameworks shine when every style lives in the markup. Yet a common headache appears when a parent’s hover state must affect a descendant — think a card that highlights its title and description together. Before Tailwind 3 you would write a custom CSS rule or reach for a JavaScript toggle, both of which break the utility‑only workflow.
JIT mode and arbitrary variants explained
Tailwind 3 ships with Just‑In‑Time (JIT) compilation enabled by default. The compiler scans your template files, generates only the classes you actually use, and does it fast enough for a hot‑reload dev loop. Because the engine knows every class at build time, it can also understand arbitrary variants — syntax like hover:[>p]:text-red-500 that targets a child selector on the fly. No extra configuration, no separate stylesheet.
The group utility pairs with group-hover and group-focus to propagate state from an ancestor to any descendant that opts in. Combined with arbitrary variants you can write complex interactions while staying in HTML.
Worked example: a responsive, dark‑mode‑aware card
1. Tailwind configuration
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{html,js,ts,jsx,tsx}',
'./index.html'
],
darkMode: 'class', // enables class‑based dark mode
theme: {
extend: {}
},
plugins: []
};
2. Minimal HTML component
<div class="group max-w-sm mx-auto p-6 bg-white dark:bg-gray-800 rounded-lg shadow"
role="article"
aria-labelledby="card-title">
<h3 id="card-title" class="text-xl font-semibold text-gray-900 dark:text-gray-100"
>Card Title</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300"
>Hover the card to see the title and description change color.</p>
<!-- Arbitrary variant: when the group is hovered, style the direct child -->
<div class="hover:[&>p]:text-red-500 dark:hover:[&>p]:text-red-400"
>
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400"
>This paragraph turns red on card hover.</p>
</div>
</div>
3. What happens
groupon the outerdivmakes the card a state source.- The inner wrapper uses
hover:[&>p]:text-red-500— an arbitrary variant that says “when this element is hovered, find a directpchild and applytext-red-500”. dark:hover:[&>p]:text-red-400provides a darker‑mode friendly red.- Because
darkMode: 'class'is set, addingclass="dark"to thehtmlelement (via a tiny toggle script) switches alldark:prefixed utilities instantly.
Trade‑offs: consistency vs. flexibility
Arbitrary variants are powerful, but each unique value spawns a new CSS rule. If you sprinkle hover:[&>p]:text-#ff6b6b across dozens of components, the generated stylesheet grows and the design system drifts. Prefer theme tokens (text-red-500, text-primary-600) for repeated colors and reserve arbitrary values for truly one‑off cases.
Another gotcha: dynamically constructed class strings such as `bg-${color}-500` are invisible to the content scanner. You must either keep the full class literal in the source or safelist the pattern in safelist inside tailwind.config.js.
Actionable next steps
- Run
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watchand openoutput.css. Verify that only the classes used in the example appear. - Add a tiny dark‑mode toggle script:
// toggle-dark.js const btn = document.getElementById('theme-toggle'); btn?.addEventListener('click', () => document.documentElement.classList.toggle('dark') ); - Open the page, hover the card, then click the toggle. Confirm the paragraph turns red in both light and dark modes.
- Audit your codebase for template‑string class constructions and add a safelist entry if needed.
With JIT and arbitrary variants you can keep complex interaction logic in the markup, maintain a single source of truth for dark mode, and still ship a tiny CSS payload. Use the checklist above to validate the setup before moving to production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.