CSS Flexbox in Responsive Web Design

Week 4: Wednesday Morning Session

Understanding Flexbox: A Layout Revolution

CSS Flexbox (Flexible Box Layout) represents one of the most significant advancements in CSS layout since the introduction of CSS itself. Before Flexbox, creating complex layouts required using a combination of floats, positioning, and other hacks that often led to fragile designs and unexpected behavior across different browsers and screen sizes.

Flexbox provides a more efficient way to lay out, align, and distribute space among items in a container, even when their size is unknown or dynamic. Think of Flexbox as a way to give elements superpowers—they can grow to fill available space, shrink to avoid overflow, and align themselves in ways that were previously difficult or impossible.

The true power of Flexbox lies in its ability to create layouts that adapt to different screen sizes without relying heavily on media queries. While media queries remain important for major layout shifts, Flexbox handles many responsive behaviors automatically.

Key Concept: One-Dimensional Layout

Flexbox is designed for one-dimensional layouts—either a row or a column. Unlike CSS Grid (which handles two-dimensional layouts), Flexbox focuses on distributing elements along a single axis, making it perfect for:

  • Navigation bars and menus
  • Card layouts
  • Form elements
  • Equal-height columns
  • Vertical and horizontal centering
  • And much more

Analogy: The Yoga Mat

Think of a Flexbox container as a yoga mat. Items placed on the mat can stretch, shrink, and align themselves in various ways. The mat can be laid horizontally or vertically, and the items on it will organize themselves along that axis. The instructor (you, the developer) gives commands about how the participants should position themselves relative to each other and the mat's edges.

Flexbox Basics: Containers and Items

Flexbox works with a parent-child relationship. The parent element becomes a flex container, and its immediate children become flex items.

Creating a Flex Container

To create a flex container, you simply set the display property to flex or inline-flex:

.container {
    display: flex; /* or inline-flex */
}

This single declaration immediately changes how the container's children behave:

Example: Basic Flex Container

<div class="flex-container">
    <div class="flex-item">Item 1</div>
    <div class="flex-item">Item 2</div>
    <div class="flex-item">Item 3</div>
</div>
.flex-container {
    display: flex;
    background-color: #f0f0f0;
    padding: 10px;
}

.flex-item {
    background-color: #4285f4;
    color: white;
    padding: 20px;
    margin: 10px;
}

With just these few lines of code, we've created a row of items that maintain equal height and adjust to their content. This is already far simpler than what would have been required with traditional CSS methods.

Flex Container Properties

The flex container has several properties that control how flex items are laid out. These properties give you tremendous control over your layout.

flex-direction

The flex-direction property determines the primary axis along which items are placed:

.container {
    flex-direction: row;          /* left to right (default) */
    /* Or alternatively: */
    flex-direction: row-reverse;  /* right to left */
    flex-direction: column;       /* top to bottom */
    flex-direction: column-reverse; /* bottom to top */
}

Example: Column Layout

.flex-container {
    display: flex;
    flex-direction: column;
    height: 400px; /* Height needed for column to have room to grow */
}

/* Media query for larger screens */
@media (min-width: 768px) {
    .flex-container {
        flex-direction: row; /* Switch to horizontal layout on larger screens */
    }
}

This pattern—starting with a column layout on mobile and switching to a row layout on larger screens—is extremely common in responsive design.

flex-wrap

By default, flex items will all try to fit onto one line. The flex-wrap property changes this behavior:

.container {
    flex-wrap: nowrap;     /* Single-line (default) */
    /* Or alternatively: */
    flex-wrap: wrap;       /* Multi-line if needed */
    flex-wrap: wrap-reverse; /* Multi-line, reversed */
}

Example: Wrapping Cards

.card-container {
    display: flex;
    flex-wrap: wrap;
}

.card {
    flex: 0 0 100%; /* Full width on mobile */
}

@media (min-width: 768px) {
    .card {
        flex: 0 0 calc(50% - 20px); /* Two cards per row on tablet */
    }
}

@media (min-width: 1024px) {
    .card {
        flex: 0 0 calc(33.333% - 20px); /* Three cards per row on desktop */
    }
}

flex-wrap: wrap is crucial for responsive designs, as it allows items to flow onto multiple lines as the viewport narrows, rather than shrinking or overflowing.

flex-flow

This is a shorthand property for flex-direction and flex-wrap:

.container {
    flex-flow: row wrap; /* direction wrap */
}

justify-content

The justify-content property aligns items along the main axis (horizontal in a row, vertical in a column):

.container {
    justify-content: flex-start;    /* Items at start (default) */
    /* Or alternatively: */
    justify-content: flex-end;      /* Items at end */
    justify-content: center;        /* Items at center */
    justify-content: space-between; /* Items with space between them */
    justify-content: space-around;  /* Items with space around them */
    justify-content: space-evenly;  /* Items with equal space around them */
}

Example: Responsive Navigation

.navbar {
    display: flex;
    justify-content: center; /* Center nav items on mobile */
    flex-wrap: wrap;
}

@media (min-width: 768px) {
    .navbar {
        justify-content: space-between; /* Space between logo and nav on desktop */
    }
}

Analogy: The Bookshelf

justify-content is like arranging books on a shelf. You might align them all to the left (flex-start), all to the right (flex-end), center them (center), spread them evenly with the largest gaps at the ends (space-around), or spread them evenly with equal gaps between each book and at the ends (space-evenly).

align-items

The align-items property aligns items along the cross axis (vertical in a row, horizontal in a column):

.container {
    align-items: stretch;     /* Items stretch to fill container (default) */
    /* Or alternatively: */
    align-items: flex-start;  /* Items aligned to start of cross axis */
    align-items: flex-end;    /* Items aligned to end of cross axis */
    align-items: center;      /* Items centered on cross axis */
    align-items: baseline;    /* Items aligned to text baseline */
}

Example: Vertically Centered Hero Section

.hero {
    display: flex;
    align-items: center; /* Vertical centering */
    justify-content: center; /* Horizontal centering */
    height: 80vh; /* 80% of viewport height */
    text-align: center;
}

.hero-content {
    max-width: 800px;
}

This creates a perfectly centered hero section with just a few lines of code—something that used to require complex positioning hacks.

align-content

When you have multiple lines of flex items (flex-wrap: wrap), align-content determines how the lines are spaced:

.container {
    align-content: stretch;      /* Lines stretch to fill container (default) */
    /* Or alternatively: */
    align-content: flex-start;   /* Lines packed to start of container */
    align-content: flex-end;     /* Lines packed to end of container */
    align-content: center;       /* Lines packed to center of container */
    align-content: space-between; /* Lines evenly distributed with first at start and last at end */
    align-content: space-around;  /* Lines evenly distributed with equal space around each */
    align-content: space-evenly;  /* Lines evenly distributed with equal space between them */
}

Note that align-content only has an effect when there are multiple lines of items and there's extra space in the cross-axis.

Flex Item Properties

While the container properties define the overall layout environment, individual flex items can also have properties that affect how they behave within that environment.

order

The order property controls the order in which items appear in the flex container:

.item {
    order: 0; /* Default is 0, items display in source order */
}

.first-item {
    order: -1; /* Negative values come before 0 */
}

.last-item {
    order: 1; /* Positive values come after 0 */
}

Example: Responsive Content Reordering

<div class="container">
    <header class="item">Header</header>
    <aside class="sidebar item">Sidebar</aside>
    <main class="content item">Main Content</main>
    <footer class="item">Footer</footer>
</div>
.container {
    display: flex;
    flex-wrap: wrap;
}

.item {
    flex: 1 1 100%; /* Full width by default */
}

/* Order for mobile - stacked layout */
.header { order: 1; }
.content { order: 2; }
.sidebar { order: 3; }
.footer { order: 4; }

/* Tablet and above - changed layout */
@media (min-width: 768px) {
    .header { order: 1; flex: 1 1 100%; }
    .sidebar { order: 2; flex: 1 1 30%; }
    .content { order: 3; flex: 1 1 70%; }
    .footer { order: 4; flex: 1 1 100%; }
}

This example shows how order can create completely different layouts across breakpoints without changing the HTML structure—a powerful technique for responsive design.

Accessibility Warning

While order changes the visual order, it doesn't change the source order in the HTML. Screen readers and keyboard navigation will still follow the source order. Be careful when using order to ensure the experience remains accessible.

flex-grow

The flex-grow property defines the ability for a flex item to grow if necessary:

.item {
    flex-grow: 0; /* Default is 0 (don't grow) */
}

.growing-item {
    flex-grow: 1; /* Grow to take up available space */
}

.grow-twice {
    flex-grow: 2; /* Grow twice as much as flex-grow: 1 */
}

The value acts as a proportion. If all items have flex-grow: 1, they will share the available space equally. If one has flex-grow: 2 and others have flex-grow: 1, the first one will take twice as much of the available space.

Example: Flexible Form Layout

<form class="flex-form">
    <input type="text" class="flex-grow" placeholder="Search...">
    <button type="submit">Search</button>
</form>
.flex-form {
    display: flex;
    margin-bottom: 20px;
}

.flex-grow {
    flex-grow: 1; /* Input takes all available space */
}

button {
    flex-grow: 0; /* Button doesn't grow */
}

This creates a search form where the input field automatically expands to fill available space, while the button remains the size it needs to be.

flex-shrink

The flex-shrink property defines the ability for a flex item to shrink if necessary:

.item {
    flex-shrink: 1; /* Default is 1 (do shrink) */
}

.non-shrinking-item {
    flex-shrink: 0; /* Don't shrink below base size */
}

.shrink-more {
    flex-shrink: 2; /* Shrink twice as much as others */
}

Like flex-grow, this is a proportion. Items with higher flex-shrink values will shrink more than others when space is limited.

flex-basis

The flex-basis property defines the default size of an element before the remaining space is distributed:

.item {
    flex-basis: auto; /* Default is auto (use item's content size) */
}

.item-with-basis {
    flex-basis: 200px; /* Start at 200px before growing/shrinking */
}

.percentage-basis {
    flex-basis: 50%; /* Start at 50% of the container */
}

Think of flex-basis as the "starting point" for the item's size before it grows or shrinks.

flex

The flex property is a shorthand for flex-grow, flex-shrink, and flex-basis:

.item {
    flex: 0 1 auto; /* Default (don't grow, do shrink, base size is auto) */
}

.flexible-item {
    flex: 1 1 auto; /* Grow and shrink as needed */
}

.fixed-width-item {
    flex: 0 0 200px; /* Don't grow or shrink, stay at 200px */
}

.fill-space {
    flex: 1 0 0%; /* Grow to fill space, don't shrink, start from 0% */
}

Example: Responsive Card Layout

.card-grid {
    display: flex;
    flex-wrap: wrap;
    margin: -10px; /* Offset the card margins */
}

.card {
    flex: 0 0 calc(100% - 20px); /* Full width on mobile, minus margins */
    margin: 10px;
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    padding: 20px;
}

@media (min-width: 600px) {
    .card {
        flex: 0 0 calc(50% - 20px); /* Two cards per row on tablets */
    }
}

@media (min-width: 1000px) {
    .card {
        flex: 0 0 calc(33.333% - 20px); /* Three cards per row on desktops */
    }
}

Common Flex Shorthand Values

  • flex: 1; - Same as flex: 1 1 0%; (grow, shrink, and start from 0 size)
  • flex: auto; - Same as flex: 1 1 auto; (grow, shrink, and use content size)
  • flex: none; - Same as flex: 0 0 auto; (don't grow or shrink, use content size)

align-self

The align-self property allows individual items to override the container's align-items property:

.container {
    align-items: center; /* All items centered */
}

.top-aligned {
    align-self: flex-start; /* This item aligns to the top */
}

.bottom-aligned {
    align-self: flex-end; /* This item aligns to the bottom */
}

Example: Feature Highlight

<div class="feature-list">
    <div class="feature">Feature 1</div>
    <div class="feature highlighted">Featured Item!</div>
    <div class="feature">Feature 3</div>
</div>
.feature-list {
    display: flex;
    align-items: center;
    min-height: 200px;
}

.feature {
    flex: 1;
    padding: 20px;
    border: 1px solid #ddd;
}

.highlighted {
    align-self: stretch; /* Make this item fill the height */
    background-color: #f8f9fa;
    box-shadow: 0 0 10px rgba(0,0,0,0.1);
    z-index: 1; /* Lift it above other items */
}

Common Flexbox Patterns for Responsive Design

Flexbox excels at creating layouts that adapt to different viewport sizes. Here are some of the most useful patterns:

Holy Grail Layout

The "Holy Grail" layout—header, footer, and three columns in between—has been a challenging layout to implement responsively. Flexbox makes it straightforward:

<div class="holy-grail">
    <header class="holy-grail-header">Header</header>
    <div class="holy-grail-body">
        <nav class="holy-grail-nav">Navigation</nav>
        <main class="holy-grail-content">Main Content</main>
        <aside class="holy-grail-sidebar">Sidebar</aside>
    </div>
    <footer class="holy-grail-footer">Footer</footer>
</div>
.holy-grail {
    display: flex;
    flex-direction: column;
    min-height: 100vh; /* Full viewport height */
}

.holy-grail-header,
.holy-grail-footer {
    flex: 0 0 auto; /* Don't grow or shrink */
    padding: 20px;
}

.holy-grail-body {
    display: flex;
    flex: 1 0 auto; /* Grow to fill space */
    flex-direction: column; /* Stack vertically on mobile */
}

.holy-grail-nav,
.holy-grail-sidebar,
.holy-grail-content {
    padding: 20px;
}

.holy-grail-content {
    flex: 1 0 auto; /* Fill available space */
}

/* Tablet and desktop styles */
@media (min-width: 768px) {
    .holy-grail-body {
        flex-direction: row; /* Horizontal layout on larger screens */
    }
    
    .holy-grail-nav,
    .holy-grail-sidebar {
        flex: 0 0 200px; /* Fixed width sidebars */
    }
}

Navigation Bar

Responsive navigation is one of the most common use cases for Flexbox:

<nav class="navbar">
    <div class="logo">Brand</div>
    <button class="hamburger">☰</button>
    <ul class="nav-menu">
        <li><a href="#">Home</a></li>
        <li><a href="#">About</a></li>
        <li><a href="#">Services</a></li>
        <li><a href="#">Contact</a></li>
    </ul>
</nav>
.navbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    padding: 15px;
}

.logo {
    font-size: 1.5rem;
    font-weight: bold;
}

.hamburger {
    display: block; /* Visible on mobile */
    background: none;
    border: none;
    font-size: 1.5rem;
    cursor: pointer;
}

.nav-menu {
    display: none; /* Hidden by default on mobile */
    width: 100%; /* Full width when visible */
    list-style: none;
    padding: 0;
    margin: 20px 0 0;
}

.nav-menu.active {
    display: flex;
    flex-direction: column;
}

.nav-menu li {
    margin: 10px 0;
}

/* Desktop styles */
@media (min-width: 768px) {
    .hamburger {
        display: none; /* Hide on desktop */
    }
    
    .nav-menu {
        display: flex;
        flex-direction: row;
        width: auto; /* Auto width */
        margin: 0;
    }
    
    .nav-menu li {
        margin: 0 0 0 20px;
    }
}

Card Grid

Card-based layouts are perfect for Flexbox:

<div class="card-grid">
    <div class="card">Card 1</div>
    <div class="card">Card 2</div>
    <div class="card">Card 3</div>
    <div class="card">Card 4</div>
    <div class="card">Card 5</div>
    <div class="card">Card 6</div>
</div>
.card-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 20px; /* Space between cards */
}

.card {
    flex: 1 1 300px; /* Grow, shrink, min 300px width */
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    padding: 20px;
}

This creates a responsive grid that automatically adjusts the number of cards per row based on available space, without any media queries.

Sticky Footer

Ensuring a footer stays at the bottom of the page even when content is short:

<body class="page-wrapper">
    <header>Header</header>
    <main class="content">Page content...</main>
    <footer>Footer</footer>
</body>
.page-wrapper {
    display: flex;
    flex-direction: column;
    min-height: 100vh; /* Full viewport height */
}

.content {
    flex: 1; /* Grow to fill available space */
}

Vertical Centering

One of the simplest but most useful Flexbox patterns:

<div class="centered-container">
    <div class="centered-content">
        <h2>Perfectly Centered</h2>
        <p>Both vertically and horizontally</p>
    </div>
</div>
.centered-container {
    display: flex;
    justify-content: center; /* Horizontal centering */
    align-items: center; /* Vertical centering */
    height: 100vh; /* Full height */
}

.centered-content {
    max-width: 600px;
    text-align: center;
}

This creates perfect vertical and horizontal centering, something that used to require complex absolute positioning or table-cell hacks.

Browser Support and Fallbacks

Flexbox is well-supported in modern browsers, but you might need fallbacks for older browsers in some projects.

Current Browser Support

Feature Detection

You can use feature detection with @supports to provide alternative layouts:

/* Default (non-flexbox) layout */
.container {
    overflow: hidden; /* Clear floats */
}

.item {
    float: left;
    width: 33.333%;
}

/* Flexbox layout for supporting browsers */
@supports (display: flex) {
    .container {
        display: flex;
        flex-wrap: wrap;
    }
    
    .item {
        float: none;
        flex: 1 1 300px;
    }
}

Progressive Enhancement

For projects requiring IE11 support, a common approach is to use a simple float-based or inline-block layout as the base, then enhance with Flexbox for modern browsers. This is preferable to using Flexbox with a polyfill, as polyfills add extra JavaScript and often have limitations.

Flexbox vs. CSS Grid: When to Use Each

Flexbox and CSS Grid are complementary layout systems, each with its own strengths:

Comparing Flexbox and Grid

Flexbox CSS Grid
One-dimensional (row OR column) Two-dimensional (rows AND columns)
Content-based layout ("intrinsic sizing") Layout-based positioning ("extrinsic sizing")
Better for components and small-scale layouts Better for overall page layout
Item size based on content by default Items placed in a defined grid
Space distribution among items Precise item placement

When to Use Flexbox

When to Use Grid

Analogy: Flexbox and Grid as Working Tools

Flexbox is like a toolbox organizer with adjustable dividers. You can make certain sections bigger or smaller, and items will naturally fill the space they need. It's excellent for organizing varying-sized tools along a single row or column.

CSS Grid is more like a pegboard with a precise grid of holes. You can place items exactly where you want them within the grid, spanning multiple rows or columns as needed. It's perfect for creating a structured, planned layout.

Using Them Together

For many projects, the best approach is to use both:

.page-layout {
    display: grid;
    grid-template-columns: 1fr 3fr 1fr;
    grid-template-areas: 
        "header header header"
        "nav main sidebar"
        "footer footer footer";
}

.nav-menu {
    display: flex; /* Flexbox for the navigation component */
    flex-direction: column;
}

.card-container {
    display: flex; /* Flexbox for card layout */
    flex-wrap: wrap;
}

Practical Workshop: Building a Responsive Dashboard

Let's apply what we've learned by building a responsive dashboard layout with Flexbox.

HTML Structure

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flexbox Dashboard</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="dashboard">
        <!-- Sidebar Navigation -->
        <aside class="sidebar">
            <div class="sidebar-header">
                <h2>Dashboard</h2>
            </div>
            <nav class="sidebar-nav">
                <ul>
                    <li class="active"><a href="#">Overview</a></li>
                    <li><a href="#">Analytics</a></li>
                    <li><a href="#">Reports</a></li>
                    <li><a href="#">Settings</a></li>
                </ul>
            </nav>
        </aside>
        
        <!-- Main Content Area -->
        <main class="content">
            <!-- Top Header Bar -->
            <header class="top-header">
                <button class="menu-toggle">☰</button>
                <div class="search-bar">
                    <input type="text" placeholder="Search...">
                    <button type="submit">🔍</button>
                </div>
                <div class="user-area">
                    <span class="user-name">John Doe</span>
                    <div class="user-avatar">JD</div>
                </div>
            </header>
            
            <!-- Dashboard Content -->
            <div class="dashboard-content">
                <h1>Overview</h1>
                
                <!-- Stats Row -->
                <div class="stats-container">
                    <div class="stat-card">
                        <h3>Total Users</h3>
                        <p class="stat-value">8,249</p>
                        <p class="stat-change positive">+5.25%</p>
                    </div>
                    <div class="stat-card">
                        <h3>Revenue</h3>
                        <p class="stat-value">$12,840</p>
                        <p class="stat-change positive">+10.8%</p>
                    </div>
                    <div class="stat-card">
                        <h3>Conversion Rate</h3>
                        <p class="stat-value">2.43%</p>
                        <p class="stat-change negative">-0.5%</p>
                    </div>
                    <div class="stat-card">
                        <h3>Avg. Session</h3>
                        <p class="stat-value">4:35</p>
                        <p class="stat-change positive">+1.2%</p>
                    </div>
                </div>
                
                <!-- Charts Section -->
                <div class="charts-container">
                    <!-- In a real app, these would contain charts -->
                    <div class="chart-card large">
                        <h3>User Growth</h3>
                        <div class="chart-placeholder">Line Chart</div>
                    </div>
                    <div class="chart-card">
                        <h3>Traffic Sources</h3>
                        <div class="chart-placeholder">Pie Chart</div>
                    </div>
                    <div class="chart-card">
                        <h3>Device Usage</h3>
                        <div class="chart-placeholder">Bar Chart</div>
                    </div>
                </div>
                
                <!-- Table Section -->
                <div class="table-container">
                    <h3>Recent Activity</h3>
                    <table class="data-table">
                        <thead>
                            <tr>
                                <th>User</th>
                                <th>Action</th>
                                <th>Date</th>
                                <th>Status</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>Alice Johnson</td>
                                <td>Purchase</td>
                                <td>Apr 18, 2025</td>
                                <td><span class="status complete">Complete</span></td>
                            </tr>
                            <tr>
                                <td>Bob Smith</td>
                                <td>Subscription</td>
                                <td>Apr 17, 2025</td>
                                <td><span class="status pending">Pending</span></td>
                            </tr>
                            <tr>
                                <td>Carol Davis</td>
                                <td>Account Update</td>
                                <td>Apr 16, 2025</td>
                                <td><span class="status complete">Complete</span></td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </main>
    </div>
    
    <script>
        // Simple toggle for mobile sidebar
        document.querySelector('.menu-toggle').addEventListener('click', function() {
            document.querySelector('.dashboard').classList.toggle('sidebar-open');
        });
    </script>
</body>
</html>

CSS Implementation

/* Base Styles */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    color: #333;
    background-color: #f5f7fa;
    line-height: 1.6;
}

ul {
    list-style: none;
}

a {
    text-decoration: none;
    color: inherit;
}

/* Dashboard Layout */
.dashboard {
    display: flex;
    min-height: 100vh;
}

/* Sidebar */
.sidebar {
    background-color: #2c3e50;
    color: white;
    width: 250px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 100;
    transform: translateX(-100%); /* Hidden by default on mobile */
    transition: transform 0.3s ease;
}

.sidebar-header {
    padding: 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.sidebar-nav {
    padding: 20px 0;
    flex-grow: 1;
}

.sidebar-nav ul {
    display: flex;
    flex-direction: column;
}

.sidebar-nav li {
    padding: 10px 20px;
    margin-bottom: 5px;
    border-left: 3px solid transparent;
    transition: background-color 0.2s;
}

.sidebar-nav li:hover {
    background-color: rgba(255, 255, 255, 0.1);
}

.sidebar-nav li.active {
    background-color: rgba(255, 255, 255, 0.1);
    border-left-color: #3498db;
}

/* Main Content Area */
.content {
    flex-grow: 1;
    margin-left: 0; /* No margin on mobile */
    transition: margin-left 0.3s ease;
}

/* Show sidebar when open */
.dashboard.sidebar-open .sidebar {
    transform: translateX(0);
}

/* Top Header */
.top-header {
    background-color: white;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    padding: 15px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: sticky;
    top: 0;
    z-index: 90;
}

.menu-toggle {
    background: none;
    border: none;
    font-size: 1.5rem;
    cursor: pointer;
    color: #333;
}

.search-bar {
    display: flex;
    flex-grow: 1;
    max-width: 400px;
    margin: 0 20px;
}

.search-bar input {
    flex-grow: 1;
    padding: 8px 12px;
    border: 1px solid #ddd;
    border-radius: 4px 0 0 4px;
    outline: none;
}

.search-bar button {
    padding: 8px 12px;
    background-color: #3498db;
    color: white;
    border: none;
    border-radius: 0 4px 4px 0;
    cursor: pointer;
}

.user-area {
    display: flex;
    align-items: center;
}

.user-name {
    margin-right: 10px;
    display: none; /* Hidden on mobile */
}

.user-avatar {
    width: 36px;
    height: 36px;
    background-color: #3498db;
    color: white;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
}

/* Dashboard Content Area */
.dashboard-content {
    padding: 20px;
}

.dashboard-content h1 {
    margin-bottom: 20px;
    color: #2c3e50;
}

/* Stats Container */
.stats-container {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 30px;
}

.stat-card {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    padding: 20px;
    flex: 1 1 calc(50% - 20px); /* Two per row on mobile */
}

.stat-value {
    font-size: 1.8rem;
    font-weight: 600;
    margin: 10px 0;
}

.stat-change {
    font-size: 0.9rem;
}

.stat-change.positive {
    color: #2ecc71;
}

.stat-change.negative {
    color: #e74c3c;
}

/* Charts Container */
.charts-container {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 30px;
}

.chart-card {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    padding: 20px;
    flex: 1 1 100%; /* Full width on mobile */
}

.chart-card h3 {
    margin-bottom: 15px;
}

.chart-placeholder {
    background-color: #f5f7fa;
    height: 200px;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #7f8c8d;
}

.chart-card.large .chart-placeholder {
    height: 300px;
}

/* Table Container */
.table-container {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    padding: 20px;
    margin-bottom: 30px;
    overflow-x: auto; /* Allow horizontal scroll for table */
}

.data-table {
    width: 100%;
    border-collapse: collapse;
}

.data-table th,
.data-table td {
    padding: 12px 15px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

.data-table th {
    background-color: #f5f7fa;
    font-weight: 600;
}

.status {
    padding: 5px 10px;
    border-radius: 20px;
    font-size: 0.85rem;
}

.status.complete {
    background-color: #e8f5e9;
    color: #2ecc71;
}

.status.pending {
    background-color: #fff8e1;
    color: #f39c12;
}

/* Tablet Styles */
@media (min-width: 768px) {
    .sidebar {
        transform: translateX(0); /* Sidebar always visible */
    }
    
    .content {
        margin-left: 250px; /* Adjust for sidebar width */
    }
    
    .menu-toggle {
        display: none; /* Hide menu toggle */
    }
    
    .user-name {
        display: block; /* Show username */
    }
    
    .stat-card {
        flex: 1 1 calc(25% - 20px); /* Four per row */
    }
    
    .chart-card {
        flex: 1 1 calc(50% - 20px); /* Two per row */
    }
    
    .chart-card.large {
        flex: 1 1 100%; /* Full width */
    }
}

/* Desktop Styles */
@media (min-width: 1200px) {
    .charts-container {
        display: grid;
        grid-template-columns: 2fr 1fr 1fr;
        gap: 20px;
    }
    
    .chart-card {
        flex: auto; /* Reset flex since we're using grid */
    }
    
    .chart-card.large {
        grid-column: 1;
        grid-row: 1 / 3;
    }
}

Key Flexbox features in this dashboard:

This example demonstrates how Flexbox can be used to create a complex, responsive application layout that works across different screen sizes.

Performance Considerations

Flexbox is generally well-optimized in modern browsers, but there are some considerations to keep in mind:

Layout Recalculation

Any change to a flex container or its items can trigger layout recalculations. For performant animations, consider these guidelines:

Large Numbers of Flex Items

For containers with many flex items (hundreds), you might notice performance issues. Consider:

Minimizing DOM Changes

When dynamically adding or removing flex items, batch DOM operations where possible:

// Less efficient (multiple reflows)
for (let i = 0; i < 100; i++) {
    const item = document.createElement('div');
    item.classList.add('flex-item');
    flexContainer.appendChild(item);
}

// More efficient (single reflow)
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
    const item = document.createElement('div');
    item.classList.add('flex-item');
    fragment.appendChild(item);
}
flexContainer.appendChild(fragment);

Flexbox Best Practices

General Best Practices

Common Mistakes to Avoid

Layout Debugging

When troubleshooting flexbox layouts, these strategies can help:

/* Temporary debugging styles */
.flex-container {
    outline: 2px solid red;
}

.flex-container > * {
    outline: 2px dashed blue;
    background-color: rgba(0, 0, 255, 0.1);
}

Conclusion: Flexbox in Your Workflow

CSS Flexbox has transformed how we approach web layouts, making many previously complex design challenges simple and straightforward. As you incorporate Flexbox into your workflow, remember:

The time you invest in mastering Flexbox will pay dividends in more efficient development, more robust layouts, and ultimately better user experiences across devices.

In our next session, we'll explore CSS Grid, Flexbox's companion in modern layout techniques, and see how these two technologies work together to create comprehensive layout systems.

Daily Assignment: Flexbox Landing Page

Apply what you've learned by creating a responsive landing page using Flexbox:

  1. Create a landing page with the following sections:
    • A responsive navigation bar with logo and menu (hamburger on mobile)
    • A hero section with centered content
    • A features section with 3-4 feature cards
    • A testimonials section with customer quotes
    • A pricing section with multiple pricing tiers
    • A contact form
    • A footer with multiple columns
  2. Implement a mobile-first approach with appropriate breakpoints
  3. Use Flexbox for all layouts (no floats or absolute positioning)
  4. Make sure all sections adapt appropriately to different screen sizes
  5. Include at least one instance of flexible ordering (different order on mobile vs. desktop)
  6. Ensure all interactive elements (buttons, form fields) are touch-friendly
  7. Test your implementation on at least two different device sizes

Requirements:

Create your project in a file called 04week/flexbox_assignment.html with accompanying CSS files, and submit it to the course repository by the end of day.

Additional Resources