Skip to Content
Module 4: Styling & Layout🎯 Mini Project: Landing Page

Module 4.3: Mini Project 3 - Responsive Landing Page (Dark Mode)

A sleek, dual-split 3D isometric illustration of a web landing page. The left half is in Day Mode (bright, clean white/blue UI), and the right half is in Night Mode (elegant dark/neon UI). A central toggle switch connects the two. Minimalist, modern SaaS aesthetic, 8k resolution.

(AI Image Gen): > Prompt: A sleek, dual-split 3D isometric illustration of a web landing page. The left half is in Day Mode (bright, clean white/blue interface), and the right half is in Night Mode (elegant dark/neon interface). A central toggle switch connects the two. Minimalist, modern SaaS aesthetic, 8k resolution.

🛠️ Image Assets Script

Run the following script in your terminal to create the placeholder image files:

mkdir -p images/b04-3 touch images/b04-3/b04-3-banner.png touch images/b04-3/b04-3-dark-mode-diagram.png touch images/b04-3/b04-3-final-result.png

🎯 What You’ll Build

In this hands-on project, we’re going to build a complete landing page for a hypothetical tech product (“SaaS Kit”).

Key skills you’ll master:

  • Responsive Layout: Ensure a beautiful UI across Mobile, Tablet, and Desktop.
  • Dark/Light Mode: Setup manual dark mode using Tailwind CSS dark variant.
  • Component Composition: Reusing Button and Card components.

1. Setting Up Dark Mode

1.1. Tailwind Configuration

Open your tailwind.config.js file. Add the property darkMode: 'class' to enable dark mode when the root element has the dark class.

/** @type {import('tailwindcss').Config} */ export default { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], darkMode: 'class', // Important! theme: { extend: {}, }, plugins: [], };

1.2. Toggle Logic (Custom Hook)

Create a custom hook called useTheme to manage adding/removing the dark class on the html tag.

// src/hooks/useTheme.ts import { useState, useEffect } from 'react'; export default function useTheme() { const [theme, setTheme] = useState<string>(localStorage.getItem('theme') || 'light'); useEffect(() => { const root = window.document.documentElement; if (theme === 'dark') { root.classList.add('dark'); } else { root.classList.remove('dark'); } // Save preference localStorage.setItem('theme', theme); }, [theme]); const toggleTheme = () => { setTheme(theme === 'dark' ? 'light' : 'dark'); }; return { theme, toggleTheme }; }

A conceptual minimalist flowchart showing the Dark Mode mechanism. A 'Toggle Button' sends a signal to the 'HTML Tag'. The HTML tag toggles the class 'dark'. This triggers CSS variables to switch from 'Light Palette' to 'Dark Palette'. Clean lines, white and dark grey contrast.

(AI Image Gen): > Prompt: A conceptual minimalist flowchart showing the Dark Mode mechanism. A ‘Toggle Button’ sends a signal to the ‘HTML Tag’. The HTML tag toggles the class ‘dark’. This triggers CSS variables to switch from ‘Light Palette’ to ‘Dark Palette’. Clean lines, white and dark grey contrast.

2. Building the Header (Navbar)

The Header needs to include a Logo, Navigation Links (hidden on mobile), and the Theme Toggle button.

Requirements: Sticky top, background color change on scroll (optional).

// src/components/Header.tsx import useTheme from '../hooks/useTheme'; export default function Header() { const { theme, toggleTheme } = useTheme(); return ( <header className="sticky top-0 z-50 w-full border-b bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-800 transition-colors duration-300"> <div className="container mx-auto px-4 h-16 flex items-center justify-between"> {/* Logo */} <div className="text-2xl font-bold text-blue-600 dark:text-blue-400">SaaS Kit</div> {/* Nav - Hidden on mobile, flex on md */} <nav className="hidden md:flex gap-6 text-gray-600 dark:text-gray-300"> <a href="#" className="hover:text-blue-600 dark:hover:text-blue-400"> Features </a> <a href="#" className="hover:text-blue-600 dark:hover:text-blue-400"> Pricing </a> <a href="#" className="hover:text-blue-600 dark:hover:text-blue-400"> About </a> </nav> {/* Actions */} <div className="flex items-center gap-4"> <button onClick={toggleTheme} className="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800"> {theme === 'dark' ? '🌞' : '🌙'} </button> <button className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700">Get Started</button> </div> </div> </header> ); }

3. Hero Section

This is your main introductory section.

  • Layout: Content on the left, Illustration on the right (Desktop). Stacked vertically (Mobile).
  • Typography: Large heading (text-4xl or above).
// src/components/Hero.tsx export default function Hero() { return ( <section className="bg-white dark:bg-gray-900 pt-20 pb-32 transition-colors duration-300"> <div className="container mx-auto px-4 grid md:grid-cols-2 gap-12 items-center"> {/* Text Content */} <div> <h1 className="text-4xl md:text-6xl font-extrabold text-gray-900 dark:text-white mb-6"> Build faster with <br /> <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-cyan-500"> React & Tailwind </span> </h1> <p className="text-lg text-gray-600 dark:text-gray-300 mb-8"> The ultimate starting point for your next project. Includes Dark Mode, Responsive Design, and Atomic Components. </p> <div className="flex gap-4"> <button className="px-8 py-3 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition"> Start Building </button> <button className="px-8 py-3 bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-lg font-semibold hover:bg-gray-200 dark:hover:bg-gray-700 transition"> Learn More </button> </div> </div> {/* Image Placeholder */} <div className="relative"> <div className="absolute -inset-1 bg-gradient-to-r from-blue-600 to-cyan-500 rounded-lg blur opacity-30"></div> <div className="relative bg-gray-100 dark:bg-gray-800 rounded-lg p-8 h-80 flex items-center justify-center border border-gray-200 dark:border-gray-700"> <span className="text-gray-400">Hero Image Placeholder</span> </div> </div> </div> </section> ); }

4. Features Section (Grid Layout)

Display features using a 3-column Grid (Desktop).

// src/components/Features.tsx interface Feature { title: string; desc: string; icon: string; } const features: Feature[] = [ { title: 'Atomic Design', desc: 'Built with modular components for maximum reusability.', icon: '⚛️', }, { title: 'Dark Mode', desc: 'Native support for light and dark themes with one click.', icon: '🌙', }, { title: 'Responsive', desc: 'Looks perfect on any device, from mobile to desktop.', icon: '📱', }, ]; export default function Features() { return ( <section className="py-20 bg-gray-50 dark:bg-gray-800 transition-colors duration-300"> <div className="container mx-auto px-4"> <h2 className="text-3xl font-bold text-center text-gray-900 dark:text-white mb-12">Why choose us?</h2> <div className="grid md:grid-cols-3 gap-8"> {features.map((feature, index) => ( <div key={index} className="bg-white dark:bg-gray-900 p-8 rounded-xl shadow-sm hover:shadow-md transition border border-gray-100 dark:border-gray-700" > <div className="text-4xl mb-4">{feature.icon}</div> <h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">{feature.title}</h3> <p className="text-gray-600 dark:text-gray-400">{feature.desc}</p> </div> ))} </div> </div> </section> ); }

5. Bringing It All Together (App.jsx)

Let’s combine all the components.

import Header from './components/Header'; import Hero from './components/Hero'; import Features from './components/Features'; function App() { return ( <div className="min-h-screen bg-white dark:bg-gray-900 font-sans transition-colors duration-300"> <Header /> <main> <Hero /> <Features /> </main> {/* Simple Footer */} <footer className="py-8 text-center text-gray-500 dark:text-gray-400 text-sm"> © 2024 SaaS Kit. Built with React & Tailwind. </footer> </div> ); } export default App;

A full-page screenshot mockup of the completed website displayed on a laptop screen. The design is modern and clean. The Hero section has a large bold headline and a call-to-action button. Below it, a three-column feature grid. The overall color scheme is consistent, demonstrating a polished UI.

(AI Image Gen): > Prompt: A full-page screenshot mockup of the completed website displayed on a laptop screen. The design is modern and clean. The Hero section has a large bold headline and a call-to-action button. Below it, a three-column feature grid. The overall color scheme is consistent, demonstrating a polished UI.

6. Conclusion

In this Mini Project, you’ve gained hands-on experience with:

  1. Tailwind Dark Mode: Utilizing the dark: class and darkMode: 'class' configuration.
  2. Custom Hooks: Separating theme management logic from the UI (useTheme).
  3. Responsive Grid: Smoothly transitioning from a 1-column layout (Mobile) to 2-3 columns (Desktop).
  4. Transitions: Adding transition-colors duration-300 for fluid color change effects.

Congratulations on completing Module 4! Get ready for Module 5, where we’ll dive into useEffect and APIs.

Last updated on