Tailwind CSS Core Concepts: Utility Classes and Prefix System
How Utility Classes Work
The core of Tailwind CSS is Utility Classes, where each class corresponds to a specific CSS property. By combining multiple utility classes, you can build complex styles directly in HTML.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Utility Class Mechanics</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
<div class="bg-white rounded-lg shadow-md p-6 max-w-md mx-auto">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Utility Class Demo</h2>
<p class="text-gray-600 leading-relaxed mb-4">
Each class name corresponds to one CSS property
</p>
<button class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition-colors">
Click Me
</button>
</div>
</body>
</html>
What each class does in the code above:
| Tailwind Class | Corresponding CSS |
|---|---|
bg-white |
background-color: #fff |
rounded-lg |
border-radius: 0.5rem |
shadow-md |
box-shadow: ... |
p-6 |
padding: 1.5rem |
text-2xl |
font-size: 1.5rem |
font-bold |
font-weight: 700 |
Traditional CSS vs Tailwind The traditional approach requires creating semantic class names (e.g.,
.card) and defining styles in a CSS file, whereas Tailwind uses atomic utility classes directly, eliminating the need to write and maintain CSS files.
Hover/Focus State Prefixes
Tailwind uses state prefixes to handle interaction states, such as hover:, focus:, active:, etc. The syntax is intuitive and easy to use.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>State Prefix Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-50 flex items-center justify-center gap-8">
<!-- Hover Effect -->
<button class="bg-blue-500 text-white px-6 py-3 rounded-lg
hover:bg-blue-700 hover:shadow-lg hover:scale-105
transition-all duration-300">
Hover Effect
</button>
<!-- Focus Effect -->
<input type="text" placeholder="Click the input to see the effect"
class="px-4 py-3 border-2 border-gray-300 rounded-lg
focus:border-blue-500 focus:ring-2 focus:ring-blue-200
focus:outline-none transition-all">
<!-- Combined States -->
<button class="bg-green-500 text-white px-6 py-3 rounded-lg
hover:bg-green-600
focus:ring-2 focus:ring-green-300
active:bg-green-700
disabled:opacity-50 disabled:cursor-not-allowed">
Combined States
</button>
</body>
</html>
Traditional CSS vs Tailwind The traditional approach requires writing pseudo-class selectors like
.button:hoverand.button:focus, whereas Tailwind simply addshover:orfocus:prefixes to class names.
hover: (hover), focus: (focus), active: (active), disabled: (disabled), first: (first element), last: (last element)
Responsive Prefixes
Tailwind follows a mobile-first strategy, using responsive prefixes to set styles for different screen sizes: sm:, md:, lg:, xl:, 2xl:.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Prefix Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-4">
<div class="max-w-6xl mx-auto">
<h1 class="text-xl md:text-2xl lg:text-4xl font-bold text-center mb-8">
Responsive Heading
</h1>
<!-- Responsive Grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<div class="bg-white p-4 rounded-lg shadow">Card 1</div>
<div class="bg-white p-4 rounded-lg shadow">Card 2</div>
<div class="bg-white p-4 rounded-lg shadow">Card 3</div>
<div class="bg-white p-4 rounded-lg shadow">Card 4</div>
<div class="bg-white p-4 rounded-lg shadow">Card 5</div>
<div class="bg-white p-4 rounded-lg shadow">Card 6</div>
<div class="bg-white p-4 rounded-lg shadow">Card 7</div>
<div class="bg-white p-4 rounded-lg shadow">Card 8</div>
</div>
<!-- Responsive Show/Hide -->
<div class="mt-8">
<p class="block md:hidden text-center text-red-500">Only visible on small screens</p>
<p class="hidden md:block lg:hidden text-center text-blue-500">Only visible on medium screens</p>
<p class="hidden lg:block text-center text-green-500">Only visible on large screens</p>
</div>
</div>
</body>
</html>
Breakpoint reference:
| Prefix | Min Width | Typical Devices |
|---|---|---|
sm: |
640px | Large phones, small tablets |
md: |
768px | Tablets |
lg: |
1024px | Laptops |
xl: |
1280px | Desktop monitors |
2xl: |
1536px | Large displays |
Traditional CSS vs Tailwind The traditional approach requires writing numerous responsive rules inside
@mediaqueries, whereas Tailwind declares responsive behavior directly in HTML through prefixes.
Dark Mode Prefix
The dark: prefix makes dark mode adaptation effortless — simply define different styles for dark mode.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-white dark:bg-gray-900 transition-colors">
<div class="max-w-2xl mx-auto p-8">
<h1 class="text-3xl font-bold text-gray-800 dark:text-white mb-6">
Dark Mode Example
</h1>
<div class="bg-gray-100 dark:bg-gray-800 rounded-xl p-6 mb-6">
<p class="text-gray-600 dark:text-gray-300 leading-relaxed">
This text appears dark gray in light mode and light gray in dark mode.
The background color switches accordingly.
</p>
</div>
<div class="flex gap-4">
<button class="bg-blue-500 dark:bg-blue-600 text-white px-6 py-3 rounded-lg
hover:bg-blue-600 dark:hover:bg-blue-700 transition-colors">
Primary Button
</button>
<button class="border border-gray-300 dark:border-gray-600
text-gray-700 dark:text-gray-300 px-6 py-3 rounded-lg
hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
Secondary Button
</button>
</div>
</div>
<script>
// Example code to toggle dark mode
function toggleDarkMode() {
document.documentElement.classList.toggle('dark');
}
</script>
</body>
</html>
Traditional CSS vs Tailwind The traditional approach requires writing separate CSS classes for dark mode or using
[data-theme="dark"]selectors, whereas Tailwind simply adds adark:prefix to class names.
darkMode: 'class' in the Tailwind configuration, or add the dark class to the <html> tag.
Arbitrary Value Syntax
Tailwind's arbitrary value syntax allows you to insert any CSS value using square brackets [], providing maximum flexibility.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arbitrary Value Syntax Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
<div class="max-w-4xl mx-auto space-y-8">
<!-- Custom Width and Height -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-semibold mb-4">Custom Size</h3>
<div class="w-[300px] h-[200px] bg-blue-200 rounded flex items-center justify-center">
300px × 200px
</div>
</div>
<!-- Custom Colors -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-semibold mb-4">Custom Colors</h3>
<div class="flex gap-4">
<div class="w-20 h-20 bg-[#ff6b6b] rounded-lg"></div>
<div class="w-20 h-20 bg-[#4ecdc4] rounded-lg"></div>
<div class="w-20 h-20 bg-[rgb(102,51,153)] rounded-lg"></div>
<div class="w-20 h-20 bg-[hsl(207,90%,54%)] rounded-lg"></div>
</div>
</div>
<!-- Custom Spacing -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-semibold mb-4">Custom Spacing</h3>
<div class="space-y-[15px]">
<div class="bg-gray-200 p-3 rounded">Spacing 15px</div>
<div class="bg-gray-200 p-3 rounded">Spacing 15px</div>
<div class="bg-gray-200 p-3 rounded">Spacing 15px</div>
</div>
</div>
<!-- Custom Font Size -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-semibold mb-4">Custom Font</h3>
<p class="text-[22px] leading-[32px] text-[#333]">
Custom 22px font size, 32px line height
</p>
</div>
</div>
</body>
</html>
Supported arbitrary value formats:
| Syntax | Example | Description |
|---|---|---|
| Fixed value | w-[100px] |
Pixel value |
| Color value | bg-[#ff0000] |
Hexadecimal color |
| RGB | text-[rgb(255,0,0)] |
RGB color |
| CSS variable | p-[var(--spacing)] |
Using CSS variables |
| Calculated value | w-[calc(100%-2rem)] |
calc expression |
Traditional CSS vs Tailwind The traditional approach requires creating custom CSS rules for special values, whereas Tailwind's arbitrary value syntax lets you use any CSS value directly in a class name without additional configuration.
grid-cols-[1fr_2fr_1fr] represents grid-template-columns: 1fr 2fr 1fr.
❓ FAQ
sm: and above take effect at the corresponding breakpoint and up.darkMode: 'selector' configuration and toggle the dark class on <html> via JavaScript. Combine this with the prefers-color-scheme media query for automatic detection.📖 Summary
- Utility Classes are the core of Tailwind, with each class corresponding to a specific CSS property
- State prefixes (
hover:,focus:,active:) handle interaction states without writing pseudo-class selectors - Responsive prefixes (
sm:through2xl:) follow a mobile-first strategy for easy multi-device adaptation - The
dark:prefix simplifies dark mode implementation — just define styles that differ for the dark theme - The arbitrary value syntax
[]allows using any CSS value, providing maximum flexibility
📝 Exercises
-
⭐ Create a button group with normal, hover, focus, and disabled states, demonstrating style changes across different states
-
⭐⭐ Create a responsive card list that shows 1 column on mobile, 2 on tablet, 3 on desktop, and 4 on large screens, with light/dark mode toggle
-
⭐⭐⭐ Use arbitrary value syntax to create a custom-sized grid layout with irregular column width distribution (e.g., a 1:2:1 ratio) and custom spacing



