AiTechWorlds
AiTechWorlds
Two of the most practical things you need to build are responsive layouts and dark mode. Tailwind makes both straightforward with prefix-based variants — no separate stylesheets, no media query management. Just prefixes.
Tailwind uses a mobile-first approach. Classes without a prefix apply at all screen sizes. Add a breakpoint prefix to override at larger screens:
| Prefix | Min-width | Typical use |
|---|---|---|
| (none) | 0px | Mobile — base styles |
sm: | 640px | Large phones |
md: | 768px | Tablets |
lg: | 1024px | Laptops |
xl: | 1280px | Desktops |
2xl: | 1536px | Wide screens |
Start with mobile styles, then add breakpoint overrides:
<!-- Single column → two columns → three columns -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="bg-white p-6 rounded-xl shadow-sm">Card 1</div>
<div class="bg-white p-6 rounded-xl shadow-sm">Card 2</div>
<div class="bg-white p-6 rounded-xl shadow-sm">Card 3</div>
</div>
<!-- Text size scales up on larger screens -->
<h1 class="text-2xl md:text-4xl lg:text-5xl font-bold">
Build Something Great
</h1>
<!-- Hidden on mobile, visible on desktop -->
<aside class="hidden lg:block w-64 flex-shrink-0">
Sidebar content
</aside>
<!-- Visible on mobile only (hamburger menu) -->
<button class="lg:hidden p-2 text-gray-600">
Menu
</button>
A common layout: stacked on mobile, side-by-side on desktop:
<!-- Hero section -->
<section class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 lg:py-24">
<div class="flex flex-col lg:flex-row items-center gap-12">
<!-- Text content — full width mobile, half on desktop -->
<div class="flex-1 text-center lg:text-left">
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 leading-tight">
Learn to build<br/>
<span class="text-blue-600">real-world apps</span>
</h1>
<p class="mt-4 text-gray-600 text-lg leading-relaxed max-w-xl mx-auto lg:mx-0">
Practical courses that take you from zero to hired.
Join 50,000 developers learning with us.
</p>
<div class="mt-8 flex flex-col sm:flex-row gap-3 justify-center lg:justify-start">
<button class="bg-blue-600 hover:bg-blue-700 text-white font-semibold px-6 py-3 rounded-xl transition-colors">
Start Learning Free
</button>
<button class="border border-gray-300 text-gray-700 hover:bg-gray-50 font-semibold px-6 py-3 rounded-xl transition-colors">
See Courses
</button>
</div>
</div>
<!-- Image — hidden on small mobile, shown on sm+ -->
<div class="hidden sm:block flex-1">
<img src="/hero.png" alt="Dashboard" class="w-full rounded-2xl shadow-2xl"/>
</div>
</div>
</section>
<!-- Responsive stats bar -->
<div class="border-y border-gray-200 py-8">
<div class="max-w-7xl mx-auto px-4 grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
<div>
<p class="text-3xl font-bold text-gray-900">50k+</p>
<p class="text-gray-500 text-sm mt-1">Students</p>
</div>
<div>
<p class="text-3xl font-bold text-gray-900">9</p>
<p class="text-gray-500 text-sm mt-1">Courses</p>
</div>
<div>
<p class="text-3xl font-bold text-gray-900">200+</p>
<p class="text-gray-500 text-sm mt-1">Lessons</p>
</div>
<div>
<p class="text-3xl font-bold text-gray-900">4.9★</p>
<p class="text-gray-500 text-sm mt-1">Rating</p>
</div>
</div>
</div>
Enable dark mode in tailwind.config.js:
export default {
darkMode: "class", // Toggle dark mode by adding class="dark" to <html>
// ...
};
With "class" mode, you control dark mode explicitly by toggling the dark class on <html>. This is the most reliable approach — it respects user preference and allows manual toggle.
The alternative is "media" which always follows prefers-color-scheme and can't be overridden.
Prefix any utility with dark: to apply it only in dark mode:
<!-- Background switches -->
<body class="bg-white dark:bg-gray-950">
<!-- Text color switches -->
<h1 class="text-gray-900 dark:text-gray-100">Title</h1>
<p class="text-gray-600 dark:text-gray-400">Body text</p>
<!-- Border color switches -->
<div class="border border-gray-200 dark:border-gray-800">
<!-- Card with dark mode -->
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl p-6 shadow-sm">
<h2 class="text-gray-900 dark:text-white font-semibold">Card Title</h2>
<p class="text-gray-600 dark:text-gray-400 mt-2 text-sm">Description text</p>
</div>
<!-- Button in dark mode -->
<button class="bg-blue-600 hover:bg-blue-500 dark:bg-blue-500 dark:hover:bg-blue-400 text-white font-semibold px-5 py-2.5 rounded-lg transition-colors">
Get Started
</button>
<!-- Input in dark mode -->
<input
type="text"
class="bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 rounded-lg px-3 py-2.5 focus:border-blue-500 outline-none"
placeholder="Search..."
/>
// Toggle dark mode
function toggleDarkMode() {
document.documentElement.classList.toggle("dark");
// Save preference
const isDark = document.documentElement.classList.contains("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
}
// Restore preference on load (put this before your app renders to avoid flash)
const saved = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
if (saved === "dark" || (!saved && prefersDark)) {
document.documentElement.classList.add("dark");
}
In React:
function ThemeToggle() {
const [isDark, setIsDark] = useState(() => {
const saved = localStorage.getItem("theme");
if (saved) return saved === "dark";
return window.matchMedia("(prefers-color-scheme: dark)").matches;
});
useEffect(() => {
if (isDark) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
} else {
document.documentElement.classList.remove("dark");
localStorage.setItem("theme", "light");
}
}, [isDark]);
return (
<button
onClick={() => setIsDark(prev => !prev)}
className="p-2 rounded-lg text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
aria-label="Toggle dark mode"
>
{isDark ? (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clipRule="evenodd"/>
</svg>
) : (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/>
</svg>
)}
</button>
);
}
A practical component with full light/dark support:
<!-- Pricing card -->
<div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-2xl p-8 shadow-sm hover:shadow-md transition-shadow">
<div class="flex items-center justify-between mb-6">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Pro Plan</h3>
<span class="bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 text-xs font-semibold px-2.5 py-1 rounded-full">
Most Popular
</span>
</div>
<div class="mb-6">
<span class="text-4xl font-bold text-gray-900 dark:text-white">$49</span>
<span class="text-gray-500 dark:text-gray-400">/month</span>
</div>
<ul class="space-y-3 mb-8">
<li class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-300">
<svg class="w-4 h-4 text-green-500 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
All 9 courses
</li>
<li class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-300">
<svg class="w-4 h-4 text-green-500 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
Certificate of completion
</li>
</ul>
<button class="w-full bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400 text-white font-semibold py-3 rounded-xl transition-colors">
Start Free Trial
</button>
</div>
<nav class="bg-white dark:bg-gray-950 border-b border-gray-200 dark:border-gray-800 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6">
<div class="flex items-center justify-between h-16">
<a href="/" class="text-xl font-bold text-gray-900 dark:text-white">
AiTechWorlds
</a>
<div class="hidden md:flex items-center gap-1">
<a href="/courses" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium transition-colors">
Courses
</a>
<a href="/pricing" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white px-3 py-2 rounded-md text-sm font-medium transition-colors">
Pricing
</a>
</div>
<div class="flex items-center gap-3">
<!-- Dark mode toggle -->
<button id="theme-toggle" class="p-2 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors">
<!-- Sun/Moon icon here -->
</button>
<a href="/signup" class="hidden md:block bg-blue-600 hover:bg-blue-700 text-white text-sm font-semibold px-4 py-2 rounded-lg transition-colors">
Get Started
</a>
</div>
</div>
</div>
</nav>
Container queries let components respond to their own width rather than the viewport. Great for reusable components used in different contexts:
// tailwind.config.js — enable the plugin
import containerQueries from "@tailwindcss/container-queries";
export default { plugins: [containerQueries] };
<!-- Mark the container -->
<div class="@container">
<!-- Styles based on the container's width, not the viewport -->
<div class="flex flex-col @md:flex-row gap-4">
<img class="w-full @md:w-32 rounded-lg" src="/img.jpg" alt=""/>
<div>
<h3 class="font-semibold @md:text-lg">Course Title</h3>
<p class="text-gray-500 text-sm">Description</p>
</div>
</div>
</div>
This card stacks vertically when in a narrow sidebar, but goes horizontal when placed in a wide main area — without any global media query.
Next lesson: Next.js setup and routing — moving from React to full-stack development.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises