mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 17:47:16 +00:00
10 KiB
10 KiB
Breakpoint Strategies
Overview
Effective breakpoint strategies focus on content needs rather than device sizes. Modern responsive design uses fewer, content-driven breakpoints combined with fluid techniques.
Mobile-First Approach
Core Philosophy
Start with the smallest screen, then progressively enhance for larger screens.
/* Base styles (mobile first) */
.component {
display: flex;
flex-direction: column;
padding: 1rem;
}
/* Enhance for larger screens */
@media (min-width: 640px) {
.component {
flex-direction: row;
padding: 1.5rem;
}
}
@media (min-width: 1024px) {
.component {
padding: 2rem;
}
}
Benefits
- Performance: Mobile devices load only necessary CSS
- Progressive Enhancement: Features add rather than subtract
- Content Priority: Forces focus on essential content first
- Simplicity: Easier to reason about cascading styles
Common Breakpoint Scales
Tailwind CSS Default
/* Tailwind breakpoints */
/* sm: 640px - Landscape phones */
/* md: 768px - Tablets */
/* lg: 1024px - Laptops */
/* xl: 1280px - Desktops */
/* 2xl: 1536px - Large desktops */
@media (min-width: 640px) {
/* sm */
}
@media (min-width: 768px) {
/* md */
}
@media (min-width: 1024px) {
/* lg */
}
@media (min-width: 1280px) {
/* xl */
}
@media (min-width: 1536px) {
/* 2xl */
}
Bootstrap 5
/* Bootstrap breakpoints */
/* sm: 576px */
/* md: 768px */
/* lg: 992px */
/* xl: 1200px */
/* xxl: 1400px */
@media (min-width: 576px) {
/* sm */
}
@media (min-width: 768px) {
/* md */
}
@media (min-width: 992px) {
/* lg */
}
@media (min-width: 1200px) {
/* xl */
}
@media (min-width: 1400px) {
/* xxl */
}
Minimalist Scale
/* Simplified 3-breakpoint system */
/* Base: Mobile (< 600px) */
/* Medium: Tablets and small laptops (600px - 1024px) */
/* Large: Desktops (> 1024px) */
:root {
--bp-md: 600px;
--bp-lg: 1024px;
}
@media (min-width: 600px) {
/* Medium */
}
@media (min-width: 1024px) {
/* Large */
}
Content-Based Breakpoints
Finding Natural Breakpoints
Instead of using device-based breakpoints, identify where your content naturally needs to change.
/* Bad: Device-based thinking */
@media (min-width: 768px) {
/* iPad breakpoint */
}
/* Good: Content-based thinking */
/* Breakpoint where sidebar fits comfortably next to content */
@media (min-width: 50rem) {
.layout {
display: grid;
grid-template-columns: 1fr 300px;
}
}
/* Breakpoint where cards can show 3 across without crowding */
@media (min-width: 65rem) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}
Testing Content Breakpoints
// Find where content breaks
function findBreakpoints(selector) {
const element = document.querySelector(selector);
const breakpoints = [];
for (let width = 320; width <= 1920; width += 10) {
element.style.width = `${width}px`;
// Check for overflow, wrapping, or layout issues
if (element.scrollWidth > element.clientWidth) {
breakpoints.push({ width, issue: "overflow" });
}
}
return breakpoints;
}
Design Token Integration
Breakpoint Tokens
:root {
/* Breakpoint values */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
/* Container widths for each breakpoint */
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1280px;
--container-2xl: 1536px;
}
.container {
width: 100%;
max-width: var(--container-lg);
margin-inline: auto;
padding-inline: var(--space-4);
}
JavaScript Integration
// Breakpoint constants
export const breakpoints = {
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
"2xl": 1536,
} as const;
// Media query hook
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
setMatches(media.matches);
const listener = () => setMatches(media.matches);
media.addEventListener("change", listener);
return () => media.removeEventListener("change", listener);
}, [query]);
return matches;
}
// Breakpoint hook
function useBreakpoint() {
const isSmall = useMediaQuery(`(min-width: ${breakpoints.sm}px)`);
const isMedium = useMediaQuery(`(min-width: ${breakpoints.md}px)`);
const isLarge = useMediaQuery(`(min-width: ${breakpoints.lg}px)`);
const isXLarge = useMediaQuery(`(min-width: ${breakpoints.xl}px)`);
return {
isMobile: !isSmall,
isTablet: isSmall && !isLarge,
isDesktop: isLarge,
current: isXLarge
? "xl"
: isLarge
? "lg"
: isMedium
? "md"
: isSmall
? "sm"
: "base",
};
}
Feature Queries
@supports for Progressive Enhancement
/* Feature detection instead of browser detection */
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
}
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
@supports (aspect-ratio: 16/9) {
.video-container {
aspect-ratio: 16/9;
}
}
/* Fallback for older browsers */
@supports not (gap: 1rem) {
.flex-container > * + * {
margin-left: 1rem;
}
}
Combining Feature and Size Queries
/* Only apply grid layout if supported and screen is large enough */
@supports (display: grid) {
@media (min-width: 768px) {
.layout {
display: grid;
grid-template-columns: 250px 1fr;
}
}
}
Responsive Patterns by Component
Navigation
.nav {
/* Mobile: vertical stack */
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.nav {
/* Tablet+: horizontal */
flex-direction: row;
align-items: center;
}
}
/* Or with container queries */
.nav-container {
container-type: inline-size;
}
@container (min-width: 600px) {
.nav {
flex-direction: row;
}
}
Cards Grid
.cards {
display: grid;
gap: 1.5rem;
grid-template-columns: 1fr;
}
@media (min-width: 640px) {
.cards {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.cards {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 1280px) {
.cards {
grid-template-columns: repeat(4, 1fr);
}
}
/* Better: auto-fit with minimum size */
.cards-auto {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}
Hero Section
.hero {
min-height: 50vh;
padding: var(--space-lg) var(--space-md);
text-align: center;
}
.hero-title {
font-size: clamp(2rem, 5vw + 1rem, 4rem);
}
.hero-subtitle {
font-size: clamp(1rem, 2vw + 0.5rem, 1.5rem);
}
@media (min-width: 768px) {
.hero {
min-height: 70vh;
display: flex;
align-items: center;
text-align: left;
}
.hero-content {
max-width: 60%;
}
}
@media (min-width: 1024px) {
.hero {
min-height: 80vh;
}
.hero-content {
max-width: 50%;
}
}
Tables
/* Mobile: cards or horizontal scroll */
.table-container {
overflow-x: auto;
}
.responsive-table {
min-width: 600px;
}
/* Alternative: transform to cards on mobile */
@media (max-width: 639px) {
.responsive-table {
min-width: 0;
}
.responsive-table thead {
display: none;
}
.responsive-table tr {
display: block;
margin-bottom: 1rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1rem;
}
.responsive-table td {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border: none;
}
.responsive-table td::before {
content: attr(data-label);
font-weight: 600;
}
}
Print Styles
@media print {
/* Remove non-essential elements */
.nav,
.sidebar,
.footer,
.ads {
display: none;
}
/* Reset colors and backgrounds */
* {
background: white !important;
color: black !important;
box-shadow: none !important;
}
/* Ensure content fits on page */
.container {
max-width: 100%;
padding: 0;
}
/* Handle page breaks */
h1,
h2,
h3 {
page-break-after: avoid;
}
img,
table {
page-break-inside: avoid;
}
/* Show URLs for links */
a[href^="http"]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
}
}
Preference Queries
/* Dark mode preference */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #f0f0f0;
}
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* High contrast preference */
@media (prefers-contrast: high) {
:root {
--text: #000;
--bg: #fff;
--border: #000;
}
.button {
border: 2px solid currentColor;
}
}
/* Reduced data preference */
@media (prefers-reduced-data: reduce) {
.hero-video {
display: none;
}
.hero-image {
display: block;
}
}
Testing Breakpoints
// Automated breakpoint testing
async function testBreakpoints(page, breakpoints) {
const results = [];
for (const [name, width] of Object.entries(breakpoints)) {
await page.setViewportSize({ width, height: 800 });
// Check for horizontal overflow
const hasOverflow = await page.evaluate(() => {
return (
document.documentElement.scrollWidth >
document.documentElement.clientWidth
);
});
// Check for elements going off-screen
const offscreenElements = await page.evaluate(() => {
const elements = document.querySelectorAll("*");
return Array.from(elements).filter((el) => {
const rect = el.getBoundingClientRect();
return rect.right > window.innerWidth || rect.left < 0;
}).length;
});
results.push({
breakpoint: name,
width,
hasOverflow,
offscreenElements,
});
}
return results;
}