Responsive Design Patterns with Bootstrap
A comprehensive guide to responsive design patterns using Bootstrap covering mobile-first approach, grid layouts, responsive navigation, images, and adaptive content.
Responsive Design Patterns with Bootstrap
Responsive design is not optional. Over 60% of web traffic comes from mobile devices, and that number keeps climbing. If your site does not adapt gracefully to different screen sizes, you are losing users. Bootstrap gives you a battle-tested system for building responsive layouts without reinventing the wheel every time. But using Bootstrap effectively means understanding the patterns underneath it, not just dropping grid classes into your markup and hoping for the best.
I have built dozens of production sites with Bootstrap over the past decade, from marketing pages to complex dashboards. This guide covers the responsive patterns I reach for most often, the ones that actually hold up when clients start resizing their browser windows.
Prerequisites
- Working knowledge of HTML and CSS
- Basic familiarity with Bootstrap (version 4 or 5)
- A code editor and a browser with developer tools
- Understanding of CSS box model and flexbox basics
All code examples in this article use Bootstrap 5. The concepts apply to Bootstrap 4 as well, with minor class name differences noted where relevant.
Mobile-First Design Philosophy
Bootstrap is built mobile-first. That means styles target the smallest screen by default, and you layer on complexity for larger screens using breakpoint modifiers. This is not just a technical detail. It changes how you think about layout.
Start with a single-column layout. Every piece of content stacked vertically. Then ask: what changes at a wider viewport? Maybe a sidebar appears. Maybe cards arrange into a grid. You are adding features as the screen grows, not removing them as it shrinks.
This matters because mobile-first CSS is smaller and faster. Phones download fewer styles, and the progressive enhancement approach means your base layout always works.
<!-- Mobile-first: single column by default, two columns at md breakpoint -->
<div class="container">
<div class="row">
<div class="col-12 col-md-8">
<h2>Main Content</h2>
<p>This takes full width on mobile, 8 columns on medium screens and up.</p>
</div>
<div class="col-12 col-md-4">
<h3>Sidebar</h3>
<p>This stacks below on mobile, sits alongside on medium screens.</p>
</div>
</div>
</div>
Bootstrap Breakpoint System
Bootstrap 5 defines six breakpoints. Understanding where each one kicks in is essential for making layout decisions:
| Breakpoint | Class Infix | Min Width | Typical Devices |
|---|---|---|---|
| Extra small | (none) | 0px | Phones portrait |
| Small | sm |
576px | Phones landscape |
| Medium | md |
768px | Tablets |
| Large | lg |
992px | Laptops |
| Extra large | xl |
1200px | Desktops |
| XXL | xxl |
1400px | Large desktops |
The key insight: breakpoints apply upward. col-md-6 means "6 columns at medium and everything above it." You do not need to repeat yourself for every breakpoint. Set the value where the change happens and let it cascade.
<!-- Responsive column layout using multiple breakpoints -->
<div class="container">
<div class="row">
<!-- Full width on phones, half on tablets, third on desktops -->
<div class="col-12 col-sm-6 col-lg-4">
<div class="p-3 border">Column 1</div>
</div>
<div class="col-12 col-sm-6 col-lg-4">
<div class="p-3 border">Column 2</div>
</div>
<div class="col-12 col-sm-12 col-lg-4">
<div class="p-3 border">Column 3</div>
</div>
</div>
</div>
Responsive Grid Patterns
Equal Columns
The simplest pattern. Columns that divide space evenly at every breakpoint:
<!-- Equal-width columns that stack on mobile -->
<div class="container">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4">
<div class="col mb-4">
<div class="card h-100">
<div class="card-body">Item 1</div>
</div>
</div>
<div class="col mb-4">
<div class="card h-100">
<div class="card-body">Item 2</div>
</div>
</div>
<div class="col mb-4">
<div class="card h-100">
<div class="card-body">Item 3</div>
</div>
</div>
<div class="col mb-4">
<div class="card h-100">
<div class="card-body">Item 4</div>
</div>
</div>
</div>
</div>
The row-cols-* classes are cleaner than applying column widths to every child element. Use them when every column should be the same width.
Sidebar Layouts
Sidebars should collapse below the main content on mobile. Order matters. Put the main content first in the HTML so it appears first on small screens, then use order- classes if you need the sidebar on the left at wider viewports:
<div class="container">
<div class="row">
<!-- Main content first in source order for mobile -->
<main class="col-12 col-lg-8 order-lg-2">
<h1>Article Title</h1>
<p>Main content here...</p>
</main>
<!-- Sidebar appears first visually on large screens -->
<aside class="col-12 col-lg-4 order-lg-1">
<nav class="sticky-top pt-3">
<h5>Navigation</h5>
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="#section1">Section 1</a></li>
<li class="nav-item"><a class="nav-link" href="#section2">Section 2</a></li>
</ul>
</nav>
</aside>
</div>
</div>
Masonry-Style Layouts
Bootstrap 5 supports CSS columns for masonry effects without JavaScript:
<div class="container">
<div class="row" data-masonry='{"percentPosition": true}'>
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
<div class="card-body">Short card</div>
</div>
</div>
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
<div class="card-body">
<p>Taller card with more content that creates visual variety.</p>
<p>Additional paragraph to increase height.</p>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
<div class="card-body">Medium card</div>
</div>
</div>
</div>
</div>
<!-- Requires Masonry.js: -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/masonry.pkgd.min.js"
integrity="sha384-GNFwBvfVxBkLMJpYMOABq3c+d3KnQxudP/mGPkzpZSTYykLBNsZEnG2D9G/X/+7D"
crossorigin="anonymous" async></script>
Responsive Navigation
Navbar Collapse
The collapsible navbar is the most common responsive navigation pattern. It shows a hamburger menu on mobile and a full horizontal nav on larger screens:
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">MySite</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#mainNav"
aria-controls="mainNav" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNav">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/projects">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/contact">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
Offcanvas Navigation
For content-heavy sites, offcanvas navigation gives mobile users a full sidebar that slides in. Bootstrap 5 supports this natively:
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="/">MySite</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="offcanvas" data-bs-target="#offcanvasNav"
aria-controls="offcanvasNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasNav"
aria-labelledby="offcanvasNavLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasNavLabel">Menu</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"
aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link" href="/projects">Projects</a></li>
<li class="nav-item"><a class="nav-link" href="/about">About</a></li>
<li class="nav-item"><a class="nav-link" href="/contact">Contact</a></li>
</ul>
</div>
</div>
</div>
</nav>
Responsive Images
img-fluid
The img-fluid class sets max-width: 100% and height: auto, preventing images from overflowing their containers:
<div class="container">
<div class="row">
<div class="col-md-6">
<img src="/images/project-screenshot.jpg"
class="img-fluid rounded"
alt="Project screenshot showing the dashboard interface">
</div>
<div class="col-md-6">
<h3>About This Project</h3>
<p>Description text alongside the responsive image.</p>
</div>
</div>
</div>
Picture Element for Art Direction
When you need different image crops at different breakpoints, use the HTML <picture> element alongside Bootstrap:
<picture>
<source media="(min-width: 992px)"
srcset="/images/hero-wide.jpg">
<source media="(min-width: 576px)"
srcset="/images/hero-medium.jpg">
<img src="/images/hero-mobile.jpg"
class="img-fluid w-100"
alt="Hero banner showcasing our platform">
</picture>
Responsive Typography
Bootstrap 5 enables responsive font sizes by default through the $enable-rfs variable. Display headings and lead text scale smoothly across viewports:
<div class="container py-5">
<!-- Display headings scale with viewport width -->
<h1 class="display-1">Large Display Heading</h1>
<h2 class="display-4">Smaller Display Heading</h2>
<!-- Lead text for introductions -->
<p class="lead">This introductory paragraph uses larger, lighter text
that adapts to the viewport size automatically.</p>
<!-- Responsive text alignment -->
<p class="text-center text-md-start">
Centered on mobile, left-aligned on medium screens and above.
</p>
<!-- Truncation for long text on small screens -->
<p class="text-truncate" style="max-width: 300px;">
This long text will be truncated with an ellipsis when it overflows.
</p>
</div>
Responsive Tables
Tables are notoriously difficult on mobile. Bootstrap provides two approaches:
<!-- Horizontal scrolling table wrapper -->
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th>Last Login</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Alice Johnson</td>
<td>[email protected]</td>
<td>Admin</td>
<td><span class="badge bg-success">Active</span></td>
<td>2026-02-12</td>
</tr>
</tbody>
</table>
</div>
<!-- Breakpoint-specific responsive table: only scrolls below md -->
<div class="table-responsive-md">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Widget Pro</td>
<td>$49.99</td>
<td>3</td>
<td>$149.97</td>
</tr>
</tbody>
</table>
</div>
For a stacked table on mobile, you can use custom CSS that turns each row into a card-like block. This is not built into Bootstrap but works well with it:
<style>
@media (max-width: 767.98px) {
.table-stacked thead { display: none; }
.table-stacked tbody tr {
display: block;
margin-bottom: 1rem;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 0.5rem;
}
.table-stacked tbody td {
display: flex;
justify-content: space-between;
border: none;
padding: 0.25rem 0.5rem;
}
.table-stacked tbody td::before {
content: attr(data-label);
font-weight: 600;
}
}
</style>
<table class="table table-stacked">
<thead>
<tr><th>Name</th><th>Role</th><th>Status</th></tr>
</thead>
<tbody>
<tr>
<td data-label="Name">Alice Johnson</td>
<td data-label="Role">Admin</td>
<td data-label="Status">Active</td>
</tr>
</tbody>
</table>
Responsive Forms
Forms need to adapt from full-width stacked inputs on mobile to inline or multi-column layouts on larger screens:
<div class="container">
<form>
<div class="row g-3">
<div class="col-md-6">
<label for="firstName" class="form-label">First Name</label>
<input type="text" class="form-control" id="firstName" required>
</div>
<div class="col-md-6">
<label for="lastName" class="form-label">Last Name</label>
<input type="text" class="form-control" id="lastName" required>
</div>
<div class="col-12">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" required>
</div>
<div class="col-md-6">
<label for="city" class="form-label">City</label>
<input type="text" class="form-control" id="city">
</div>
<div class="col-md-4">
<label for="state" class="form-label">State</label>
<select class="form-select" id="state">
<option selected>Choose...</option>
<option>California</option>
<option>New York</option>
<option>Texas</option>
</select>
</div>
<div class="col-md-2">
<label for="zip" class="form-label">Zip</label>
<input type="text" class="form-control" id="zip">
</div>
<!-- Responsive input group -->
<div class="col-12">
<label for="website" class="form-label">Website</label>
<div class="input-group">
<span class="input-group-text">https://</span>
<input type="text" class="form-control" id="website"
placeholder="www.example.com">
</div>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary w-100 w-md-auto">Submit</button>
</div>
</div>
</form>
</div>
Responsive Cards
Cards are the workhorse of modern web layouts. Use grid classes to control how they arrange at each breakpoint:
<div class="container">
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4">
<div class="col">
<div class="card h-100">
<img src="/images/project1.jpg" class="card-img-top" alt="Project 1">
<div class="card-body d-flex flex-column">
<h5 class="card-title">API Gateway</h5>
<p class="card-text">A high-performance API gateway built with Node.js
handling 10k requests per second.</p>
<a href="#" class="btn btn-primary mt-auto">View Project</a>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="/images/project2.jpg" class="card-img-top" alt="Project 2">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Data Pipeline</h5>
<p class="card-text">Real-time data processing pipeline using
event-driven architecture.</p>
<a href="#" class="btn btn-primary mt-auto">View Project</a>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="/images/project3.jpg" class="card-img-top" alt="Project 3">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Dashboard</h5>
<p class="card-text">Analytics dashboard with responsive charts and
real-time data visualization.</p>
<a href="#" class="btn btn-primary mt-auto">View Project</a>
</div>
</div>
</div>
</div>
</div>
Responsive Spacing Utilities
Bootstrap's spacing utilities accept breakpoint modifiers. Use them to adjust padding and margins at different viewport sizes:
<!-- Compact on mobile, spacious on desktop -->
<section class="py-3 py-md-5 py-lg-7">
<div class="container">
<h2 class="mb-2 mb-md-4">Section Title</h2>
<p class="mb-3 mb-lg-5">Content with responsive bottom margin.</p>
</div>
</section>
<!-- Responsive gap in flex/grid layouts -->
<div class="d-flex flex-wrap gap-2 gap-md-3 gap-lg-4">
<div class="p-3 border">Item 1</div>
<div class="p-3 border">Item 2</div>
<div class="p-3 border">Item 3</div>
</div>
Responsive Visibility
Show and hide elements at specific breakpoints. This is powerful for serving different content to different devices:
<!-- Show detailed table on desktop, summary cards on mobile -->
<div class="d-none d-lg-block">
<table class="table">
<thead><tr><th>Name</th><th>Email</th><th>Role</th><th>Actions</th></tr></thead>
<tbody>
<tr>
<td>Alice</td><td>[email protected]</td><td>Admin</td>
<td><button class="btn btn-sm btn-outline-primary">Edit</button></td>
</tr>
</tbody>
</table>
</div>
<div class="d-block d-lg-none">
<div class="card mb-2">
<div class="card-body">
<h6 class="card-title">Alice</h6>
<p class="card-text small text-muted">Admin</p>
<a href="#" class="btn btn-sm btn-primary">View Details</a>
</div>
</div>
</div>
<!-- Hide non-essential elements on mobile -->
<p>Main description text.
<span class="d-none d-md-inline">
This additional context only appears on tablets and larger screens
where there is room for it.
</span>
</p>
Responsive Embeds
Videos and maps need to maintain their aspect ratio while scaling. Bootstrap provides ratio utility classes:
<!-- 16:9 responsive video embed -->
<div class="ratio ratio-16x9">
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ"
title="Project Demo Video"
allowfullscreen></iframe>
</div>
<!-- 4:3 ratio for older content -->
<div class="ratio ratio-4x3">
<iframe src="https://maps.google.com/maps?q=san+francisco&output=embed"
title="Office Location Map"></iframe>
</div>
<!-- 1:1 square ratio -->
<div class="ratio ratio-1x1" style="max-width: 400px;">
<img src="/images/profile.jpg" class="object-fit-cover" alt="Profile photo">
</div>
Media Queries Beyond Bootstrap
Sometimes Bootstrap's breakpoints are not enough. You can write custom media queries that complement Bootstrap's system:
<style>
/* Match Bootstrap's breakpoint variables for consistency */
.custom-hero {
min-height: 60vh;
background-size: cover;
background-position: center;
}
/* Orientation-based adjustments */
@media (orientation: landscape) and (max-height: 500px) {
.custom-hero {
min-height: 100vh;
}
.navbar { padding-top: 0.25rem; padding-bottom: 0.25rem; }
}
/* High-DPI / Retina displays */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.logo-img {
background-image: url('/images/[email protected]');
background-size: 200px 50px;
}
}
/* Hover capability detection */
@media (hover: hover) {
.card:hover { transform: translateY(-4px); box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
.card { transition: none; }
.carousel { animation: none; }
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root { --bs-body-bg: #1a1a2e; --bs-body-color: #e0e0e0; }
}
/* Container query (modern browsers) */
@container (min-width: 400px) {
.adaptive-card { flex-direction: row; }
}
</style>
Testing Responsive Designs
Testing in a real browser is non-negotiable. Chrome DevTools Device Mode is the fastest way to iterate:
- Open DevTools (F12 or Ctrl+Shift+I)
- Click the device toggle icon (Ctrl+Shift+M)
- Select preset devices or enter custom dimensions
- Test at every Bootstrap breakpoint boundary: 575px, 576px, 767px, 768px, 991px, 992px, 1199px, 1200px, 1399px, 1400px
- Test both portrait and landscape orientations
- Throttle the network to simulate mobile connections
Do not rely solely on resizing your browser window. Real device testing catches touch interaction issues, font rendering differences, and viewport quirks that DevTools cannot replicate. Services like BrowserStack or physical devices are worth the investment for production sites.
Common Responsive Layout Patterns
Mostly Fluid
The most common pattern. A multi-column layout that stacks on small screens and uses fluid widths on larger screens with a max-width container:
<div class="container">
<div class="row">
<div class="col-12 col-sm-6 col-md-4">Column 1</div>
<div class="col-12 col-sm-6 col-md-4">Column 2</div>
<div class="col-12 col-sm-12 col-md-4">Column 3</div>
</div>
</div>
Column Drop
Columns drop below each other as the viewport narrows. This is Bootstrap's default grid behavior.
Layout Shifter
Content rearranges position, not just width. Use order- classes to shift layout between breakpoints:
<div class="container">
<div class="row">
<div class="col-12 col-md-8 order-2 order-md-1">Main Content</div>
<div class="col-12 col-md-4 order-1 order-md-2">Featured Banner</div>
<div class="col-12 order-3">Additional Info</div>
</div>
</div>
Off-Canvas
Content that lives off-screen on mobile and slides in on interaction. Used for navigation, filters, or secondary content. Bootstrap 5's offcanvas component handles this natively.
Responsive Print Styles
Do not forget print. Users print invoices, articles, and reports. Bootstrap includes basic print styles, but you should add your own:
<style>
@media print {
/* Hide navigation and interactive elements */
.navbar, .footer, .btn, .sidebar, .no-print { display: none !important; }
/* Remove background colors and shadows for ink savings */
body { background: white !important; color: black !important; }
.card { border: 1px solid #000 !important; box-shadow: none !important; }
/* Prevent page breaks inside content blocks */
.card, article, .table { break-inside: avoid; }
/* Show link URLs */
a[href]::after { content: " (" attr(href) ")"; font-size: 0.8em; }
a[href^="#"]::after, a[href^="javascript"]::after { content: ""; }
/* Full-width layout */
.container { max-width: 100% !important; }
.col-md-8 { width: 100% !important; flex: 0 0 100% !important; }
}
</style>
Complete Working Example: Responsive Portfolio Website
Here is a complete responsive portfolio page that demonstrates column drop, responsive navigation, responsive images, and adaptive content across all Bootstrap breakpoints:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shane Larson - Portfolio</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">
<style>
:root {
--accent: #2563eb;
--accent-dark: #1d4ed8;
}
/* Hero section */
.hero {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
color: white;
min-height: 70vh;
display: flex;
align-items: center;
}
.hero h1 { font-size: 2rem; }
@media (min-width: 768px) {
.hero h1 { font-size: 3rem; }
}
@media (min-width: 1200px) {
.hero h1 { font-size: 4rem; }
}
/* Project cards */
.project-card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
overflow: hidden;
}
@media (hover: hover) {
.project-card:hover {
transform: translateY(-6px);
box-shadow: 0 12px 24px rgba(0,0,0,0.15);
}
}
.project-card img {
height: 200px;
object-fit: cover;
width: 100%;
}
@media (min-width: 992px) {
.project-card img { height: 240px; }
}
/* Skills badges */
.skill-badge {
display: inline-block;
padding: 0.4rem 1rem;
border-radius: 2rem;
background: #e8f0fe;
color: var(--accent);
font-weight: 500;
font-size: 0.875rem;
margin: 0.25rem;
}
/* Footer */
.site-footer {
background: #1a1a2e;
color: #ccc;
}
.site-footer a { color: #8ab4f8; text-decoration: none; }
.site-footer a:hover { color: white; }
/* Print styles */
@media print {
.navbar, .site-footer, .btn, .hero { display: none !important; }
.container { max-width: 100% !important; }
}
</style>
</head>
<body>
<!-- Responsive Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<div class="container">
<a class="navbar-brand fw-bold" href="#">SL</a>
<button class="navbar-toggler border-0" type="button"
data-bs-toggle="collapse" data-bs-target="#portfolioNav"
aria-controls="portfolioNav" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="portfolioNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#projects">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contact">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Hero Section: column drop pattern -->
<header class="hero" id="home">
<div class="container">
<div class="row align-items-center">
<div class="col-12 col-lg-7 mb-4 mb-lg-0">
<p class="text-uppercase small tracking-wide mb-2 opacity-75">
Software Engineer
</p>
<h1 class="fw-bold mb-3">Building Reliable<br>Software Systems</h1>
<p class="lead mb-4 opacity-75">
I design and build scalable backend services, APIs, and
cloud-native applications.
</p>
<div class="d-flex flex-column flex-sm-row gap-2">
<a href="#projects" class="btn btn-primary btn-lg px-4">
View Projects
</a>
<a href="#contact" class="btn btn-outline-light btn-lg px-4">
Get in Touch
</a>
</div>
</div>
<!-- Profile image: hidden on mobile, shown on large screens -->
<div class="col-lg-5 d-none d-lg-block text-center">
<img src="/images/developer-illustration.svg"
class="img-fluid" alt="Developer illustration"
style="max-height: 400px;">
</div>
</div>
</div>
</header>
<!-- Projects Section: responsive card grid -->
<section class="py-4 py-md-5" id="projects">
<div class="container">
<h2 class="text-center mb-2 mb-md-4 display-6 fw-bold">Projects</h2>
<p class="text-center text-muted mb-4 mb-md-5 mx-auto" style="max-width: 600px;">
A selection of recent work spanning API development,
cloud infrastructure, and data engineering.
</p>
<!-- 1 col mobile, 2 cols tablet, 3 cols desktop -->
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4">
<div class="col">
<div class="card project-card h-100 border-0 shadow-sm">
<img src="/images/project-api.jpg" class="card-img-top"
alt="API Gateway architecture diagram">
<div class="card-body d-flex flex-column">
<h5 class="card-title">API Gateway Platform</h5>
<p class="card-text text-muted">High-throughput API gateway
handling authentication, rate limiting, and request routing
for microservices.</p>
<div class="mt-auto pt-3">
<span class="skill-badge">Node.js</span>
<span class="skill-badge">Redis</span>
<span class="skill-badge">Docker</span>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card project-card h-100 border-0 shadow-sm">
<img src="/images/project-pipeline.jpg" class="card-img-top"
alt="Data pipeline visualization">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Real-Time Data Pipeline</h5>
<p class="card-text text-muted">Event-driven data processing
system ingesting 500k events per minute with exactly-once
delivery guarantees.</p>
<div class="mt-auto pt-3">
<span class="skill-badge">Kafka</span>
<span class="skill-badge">Python</span>
<span class="skill-badge">PostgreSQL</span>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card project-card h-100 border-0 shadow-sm">
<img src="/images/project-dashboard.jpg" class="card-img-top"
alt="Analytics dashboard screenshot">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Analytics Dashboard</h5>
<p class="card-text text-muted">Real-time analytics platform
with responsive charts, custom report builder, and automated
alerting.</p>
<div class="mt-auto pt-3">
<span class="skill-badge">React</span>
<span class="skill-badge">D3.js</span>
<span class="skill-badge">AWS</span>
</div>
</div>
</div>
</div>
<!-- Additional projects hidden on mobile for speed -->
<div class="col d-none d-md-block">
<div class="card project-card h-100 border-0 shadow-sm">
<img src="/images/project-auth.jpg" class="card-img-top"
alt="Authentication service diagram">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Auth Service</h5>
<p class="card-text text-muted">OAuth 2.0 and OIDC compliant
authentication service supporting SSO across 12 internal
applications.</p>
<div class="mt-auto pt-3">
<span class="skill-badge">Go</span>
<span class="skill-badge">JWT</span>
<span class="skill-badge">Vault</span>
</div>
</div>
</div>
</div>
<div class="col d-none d-md-block">
<div class="card project-card h-100 border-0 shadow-sm">
<img src="/images/project-ci.jpg" class="card-img-top"
alt="CI/CD pipeline diagram">
<div class="card-body d-flex flex-column">
<h5 class="card-title">CI/CD Platform</h5>
<p class="card-text text-muted">Internal deployment platform
reducing release cycles from weekly to on-demand with
automated rollback.</p>
<div class="mt-auto pt-3">
<span class="skill-badge">GitHub Actions</span>
<span class="skill-badge">Terraform</span>
</div>
</div>
</div>
</div>
<div class="col d-none d-xl-block">
<div class="card project-card h-100 border-0 shadow-sm">
<img src="/images/project-monitor.jpg" class="card-img-top"
alt="Monitoring system interface">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Observability Stack</h5>
<p class="card-text text-muted">Unified monitoring, logging,
and tracing platform providing full-stack visibility across
200+ services.</p>
<div class="mt-auto pt-3">
<span class="skill-badge">Prometheus</span>
<span class="skill-badge">Grafana</span>
<span class="skill-badge">OTel</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- About Section: responsive two-column layout -->
<section class="bg-light py-4 py-md-5" id="about">
<div class="container">
<div class="row align-items-center g-4 g-lg-5">
<div class="col-12 col-md-5 text-center">
<picture>
<source media="(min-width: 768px)"
srcset="/images/headshot-large.jpg">
<img src="/images/headshot-small.jpg"
class="img-fluid rounded-circle shadow"
alt="Shane Larson headshot"
style="max-width: 280px;">
</picture>
</div>
<div class="col-12 col-md-7">
<h2 class="display-6 fw-bold mb-3">About Me</h2>
<p class="lead">Software engineer with 10+ years building
production systems that scale.</p>
<p class="text-muted">I specialize in backend development,
API design, and cloud architecture. I have worked with
startups and enterprises across fintech, healthcare, and
e-commerce. I write about the patterns and tools that
make engineering teams more effective.</p>
<!-- Detailed info hidden on small screens -->
<div class="d-none d-md-block">
<h5 class="mt-4 mb-3">Core Technologies</h5>
<div class="d-flex flex-wrap gap-2">
<span class="skill-badge">Node.js</span>
<span class="skill-badge">Python</span>
<span class="skill-badge">Go</span>
<span class="skill-badge">PostgreSQL</span>
<span class="skill-badge">AWS</span>
<span class="skill-badge">Docker</span>
<span class="skill-badge">Kubernetes</span>
<span class="skill-badge">Terraform</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Section: responsive form -->
<section class="py-4 py-md-5" id="contact">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<h2 class="text-center display-6 fw-bold mb-2 mb-md-4">
Get in Touch
</h2>
<p class="text-center text-muted mb-4 mb-md-5">
Have a project in mind? I am always open to discussing new
opportunities and interesting problems.
</p>
<form>
<div class="row g-3">
<div class="col-12 col-sm-6">
<label for="contactName" class="form-label">Name</label>
<input type="text" class="form-control form-control-lg"
id="contactName" placeholder="Your name" required>
</div>
<div class="col-12 col-sm-6">
<label for="contactEmail" class="form-label">Email</label>
<input type="email" class="form-control form-control-lg"
id="contactEmail" placeholder="[email protected]" required>
</div>
<div class="col-12">
<label for="contactSubject" class="form-label">Subject</label>
<select class="form-select form-select-lg" id="contactSubject">
<option selected>Project Inquiry</option>
<option>Consulting</option>
<option>Speaking</option>
<option>Other</option>
</select>
</div>
<div class="col-12">
<label for="contactMessage" class="form-label">Message</label>
<textarea class="form-control form-control-lg" id="contactMessage"
rows="5" placeholder="Tell me about your project..."
required></textarea>
</div>
<div class="col-12 text-center text-sm-end">
<button type="submit"
class="btn btn-primary btn-lg px-5 w-100 w-sm-auto">
Send Message
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
<!-- Responsive Footer -->
<footer class="site-footer py-4 py-md-5">
<div class="container">
<div class="row g-4">
<div class="col-12 col-md-4 text-center text-md-start">
<h5 class="fw-bold text-white mb-3">Shane Larson</h5>
<p class="small">Software engineer building reliable systems
and sharing what I learn along the way.</p>
</div>
<div class="col-6 col-md-4 text-center">
<h6 class="text-white mb-3">Links</h6>
<ul class="list-unstyled">
<li><a href="#projects">Projects</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div>
<div class="col-6 col-md-4 text-center text-md-end">
<h6 class="text-white mb-3">Connect</h6>
<ul class="list-unstyled">
<li><a href="https://github.com">GitHub</a></li>
<li><a href="https://linkedin.com">LinkedIn</a></li>
<li><a href="https://twitter.com">Twitter</a></li>
</ul>
</div>
</div>
<hr class="my-4 opacity-25">
<p class="text-center small mb-0">
© 2026 Shane Larson. All rights reserved.
</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js">
</script>
</body>
</html>
This portfolio demonstrates several responsive patterns working together: the navbar collapses on mobile, the hero section drops its illustration column on small screens, the project grid shifts from 1 to 2 to 3 columns, the about section stacks vertically on mobile, and the footer reorganizes from three columns to a stacked layout. Non-essential project cards are hidden on smaller screens using visibility utilities.
Common Issues and Troubleshooting
1. Content overflows the viewport horizontally on mobile.
This is almost always caused by an element with a fixed width wider than the viewport, or a row used without a parent container. Every .row must live inside a .container or .container-fluid. Check for images missing img-fluid, tables missing table-responsive, or hardcoded widths in inline styles.
2. Columns are not stacking on mobile.
If you used col-6 instead of col-12 col-md-6, the columns will be side by side at every breakpoint including mobile. Remember: no breakpoint suffix means the rule applies from 0px up. Always start with col-12 for mobile stacking, then add your wider breakpoint classes.
3. Fixed-position elements cover content on mobile. A fixed navbar steals vertical space. Add padding-top to your body or first content section equal to the navbar height. For fixed-bottom elements, add equivalent padding-bottom. Test on actual phones where the browser chrome further reduces available space.
4. Touch targets are too small. Buttons and links that work fine with a mouse cursor are frustrating to tap on a phone. Bootstrap's default button padding helps, but custom UI elements often fall short. The minimum recommended touch target is 44x44 CSS pixels. Use padding, not just font size, to increase tap areas.
5. Viewport meta tag is missing.
Without <meta name="viewport" content="width=device-width, initial-scale=1.0"> in your HTML head, mobile browsers render the page at a desktop width and scale it down. Every responsive page needs this tag. Bootstrap does not add it for you.
Best Practices
Design mobile-first, always. Start with the smallest layout and add complexity for larger screens. It is easier to add features than to remove them, and your base CSS will be leaner.
Use container classes consistently. Choose between
container(max-width at each breakpoint) andcontainer-fluid(full width always). Mixing them without intention creates inconsistent layouts. Usecontainer-lgorcontainer-xlfor hybrid approaches.Avoid hiding too much content on mobile. Using
d-none d-md-blockto hide entire sections means mobile users get a lesser experience. Instead, rethink the presentation: condense content, use accordions, or simplify the layout. Only hide genuinely supplementary content.Test at breakpoint boundaries, not just named devices. A layout might look perfect at 375px (iPhone) and 768px (iPad), but break at 680px. Test at every width between breakpoints, not just the standard device presets.
Set explicit aspect ratios on images and embeds. Use the
ratioutility or CSSaspect-ratioproperty to reserve space before images load. This prevents layout shift (CLS), which hurts both user experience and Core Web Vitals scores.Use relative units for custom breakpoints. If you need custom media queries, prefer
emoverpxfor breakpoint values. This respects the user's font size preferences. Bootstrap usespxinternally, but your custom queries can be more accessible.Minimize responsive visibility toggling. Every element hidden with
d-nonestill exists in the DOM and may still be downloaded (images, iframes). Useloading="lazy"on images and consider server-side rendering of device-appropriate content for performance-critical pages.Leverage Bootstrap's responsive utility API. Instead of writing custom CSS for responsive padding, margins, text alignment, and flex direction, use Bootstrap's utility classes.
flex-column flex-md-row,text-center text-lg-start, andp-2 p-md-4eliminate dozens of custom media queries.