Creating adaptable interfaces with responsive web design
In today's multi-device world, users expect seamless experiences regardless of whether they're on a desktop computer, tablet, smartphone, or even a smart watch. Responsive web design is no longer optional—it's a fundamental requirement for delivering effective digital experiences.
Understanding responsive web design
Responsive web design (RWD) is an approach that makes web pages render well on all devices and screen sizes by automatically adapting to the screen, whether the content is being viewed on a phone, tablet, laptop or desktop.
📝 Note: Responsive design is different from adaptive design, which creates multiple layouts for different screen sizes. Responsive design uses a single, fluid layout that changes based on the available screen space.
The core principles of responsive design include:
- Fluid layouts that use relative units rather than fixed units
- Flexible images and media that scale within their containing elements
- Media queries that apply different styles based on device characteristics
- Mobile-first approach that prioritizes designing for smaller screens first
The evolution of responsive design
Responsive design has evolved significantly over the years:
Era | Approach | Techniques |
---|---|---|
Pre-2010 | Fixed layouts | Fixed-width designs for specific screen sizes |
2010-2015 | Early responsive | Media queries, percentage-based layouts |
2015-2020 | Modern responsive | Flexbox, CSS Grid, viewport units |
2020+ | Intrinsic design | Container queries, min/max/clamp, logical properties |
Today's responsive design approaches leverage the most powerful features of modern CSS to create layouts that are truly flexible across all contexts.
Essential responsive design techniques
Fluid layouts with relative units
The foundation of responsive design is using relative units instead of fixed pixel values:
<Container> <Container p={"medium"} bw={1} br={"square"} mb={"medium"} maxW={"100%"} style={{ width: "500px" }} > <Text weight={"bold"}>Fixed width (500px)</Text> <Text>This container has a fixed width and will overflow on small screens.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} style={{ width: "80%" }} > <Text weight={"bold"}>Fluid width (80%)</Text> <Text>This container uses a percentage width and will adapt to its parent container.</Text> </Container> </Container>
↓Code Editor
<Container> <Container p={"medium"} bw={1} br={"square"} mb={"medium"} maxW={"100%"} style={{ width: "500px" }} > <Text weight={"bold"}>Fixed width (500px)</Text> <Text>This container has a fixed width and will overflow on small screens.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} style={{ width: "80%" }} > <Text weight={"bold"}>Fluid width (80%)</Text> <Text>This container uses a percentage width and will adapt to its parent container.</Text> </Container> </Container>
Key relative units to use in your CSS:
- Percentages (%): Sizing relative to the parent element
- em: Relative to the font-size of the element
- rem: Relative to the root font-size
- vw/vh: Percentage of viewport width/height
- vmin/vmax: Percentage of the smaller/larger viewport dimension
.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
padding: 1rem; /* Based on root font size */
}
.hero {
height: 50vh; /* 50% of viewport height */
font-size: calc(1rem + 1vw); /* Responsive font size */
}
Flexible images
Images often cause responsive design issues if not handled properly:
img {
max-width: 100%;
height: auto;
}
<Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} mb={"small"}>Responsive Image</Text> <Image src="https://kitchn.tonightpass.com/favicon.svg" alt="Kitchn logo" width="100%" height="auto" style={{ maxWidth: "500px" }} /> <Text mt={"small"} size={"small"} color={"lighter"}> This image will scale down on smaller screens but never exceed its natural size. </Text> </Container>
↓Code Editor
<Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} mb={"small"}>Responsive Image</Text> <Image src="https://kitchn.tonightpass.com/favicon.svg" alt="Kitchn logo" width="100%" height="auto" style={{ maxWidth: "500px" }} /> <Text mt={"small"} size={"small"} color={"lighter"}> This image will scale down on smaller screens but never exceed its natural size. </Text> </Container>
For modern image handling, also consider:
- The
srcset
attribute for providing different image resolutions - The
sizes
attribute to help browsers choose the right image - The
picture
element for art direction across breakpoints
<picture>
<source media="(max-width: 600px)" srcset="small-image.jpg">
<source media="(max-width: 1200px)" srcset="medium-image.jpg">
<img src="large-image.jpg" alt="Responsive image">
</picture>
Media queries
Media queries allow you to apply different styles based on the device characteristics, primarily the viewport width:
/* Base styles for all devices */
.card {
width: 100%;
}
/* Tablet and above */
@media (min-width: 768px) {
.card {
width: 50%;
}
}
/* Desktop */
@media (min-width: 1024px) {
.card {
width: 33.333%;
}
}
() => { const getResponsiveCardStyle = () => { const [windowWidth, setWindowWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWindowWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // Simulate media query behavior let width = '100%'; if (windowWidth >= 768) width = '48%'; if (windowWidth >= 1024) width = '31%'; return { width }; }; const cardStyle = getResponsiveCardStyle(); return ( <Container> <Text mb={"medium"}> Resize your window to see the cards respond. Current layout: {window.innerWidth < 768 ? ' Mobile (100% width)' : window.innerWidth < 1024 ? ' Tablet (48% width)' : ' Desktop (31% width)' } </Text> <Container row gap={"medium"} wrap={"wrap"}> <Container p={"medium"} bw={1} br={"square"} style={cardStyle} > <Text weight={"bold"}>Card 1</Text> <Text>This card will change width based on screen size.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} style={cardStyle} > <Text weight={"bold"}>Card 2</Text> <Text>This card will change width based on screen size.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} style={cardStyle} > <Text weight={"bold"}>Card 3</Text> <Text>This card will change width based on screen size.</Text> </Container> </Container> </Container> ); }
↓Code Editor
() => { const getResponsiveCardStyle = () => { const [windowWidth, setWindowWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWindowWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // Simulate media query behavior let width = '100%'; if (windowWidth >= 768) width = '48%'; if (windowWidth >= 1024) width = '31%'; return { width }; }; const cardStyle = getResponsiveCardStyle(); return ( <Container> <Text mb={"medium"}> Resize your window to see the cards respond. Current layout: {window.innerWidth < 768 ? ' Mobile (100% width)' : window.innerWidth < 1024 ? ' Tablet (48% width)' : ' Desktop (31% width)' } </Text> <Container row gap={"medium"} wrap={"wrap"}> <Container p={"medium"} bw={1} br={"square"} style={cardStyle} > <Text weight={"bold"}>Card 1</Text> <Text>This card will change width based on screen size.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} style={cardStyle} > <Text weight={"bold"}>Card 2</Text> <Text>This card will change width based on screen size.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} style={cardStyle} > <Text weight={"bold"}>Card 3</Text> <Text>This card will change width based on screen size.</Text> </Container> </Container> </Container> ); }
Mobile-first approach
The mobile-first approach starts with designing for the smallest screens and then progressively enhancing the layout for larger screens:
/* Mobile styles (default) */
.nav-menu {
display: flex;
flex-direction: column;
}
/* Tablet and above */
@media (min-width: 768px) {
.nav-menu {
flex-direction: row;
}
}
Benefits of the mobile-first approach include:
- Prioritizing content and features for the most constrained environment
- Faster load times on mobile devices (by loading only what's needed)
- Progressive enhancement for a better experience on all devices
- Focus on the growing mobile user base
💡 Tip: When using media queries with a mobile-first approach, use
min-width
queries to add complexity as the screen size increases, rather thanmax-width
queries to remove complexity as the screen size decreases.
Implementing responsive design with Kitchn
Kitchn provides built-in responsive capabilities that make it easier to create adaptive layouts:
Responsive Container component
The Container component in Kitchn supports dynamic direction based on screen size:
<Container mb={"medium"}> <Text weight={"bold"}>Responsive Container Layout</Text> <Text mb={"medium"}>Resize the window to see the layout change from column to row.</Text> <Container direction={["column", "row", "row"]} gap={"medium"} > <Container p={"medium"} bw={1} br={"square"} flex={1}> <Text weight={"bold"}>Section 1</Text> <Text>This section stacks vertically on mobile and aligns horizontally on larger screens.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} flex={1}> <Text weight={"bold"}>Section 2</Text> <Text>This section stacks vertically on mobile and aligns horizontally on larger screens.</Text> </Container> </Container> </Container>
↓Code Editor
<Container mb={"medium"}> <Text weight={"bold"}>Responsive Container Layout</Text> <Text mb={"medium"}>Resize the window to see the layout change from column to row.</Text> <Container direction={["column", "row", "row"]} gap={"medium"} > <Container p={"medium"} bw={1} br={"square"} flex={1}> <Text weight={"bold"}>Section 1</Text> <Text>This section stacks vertically on mobile and aligns horizontally on larger screens.</Text> </Container> <Container p={"medium"} bw={1} br={"square"} flex={1}> <Text weight={"bold"}>Section 2</Text> <Text>This section stacks vertically on mobile and aligns horizontally on larger screens.</Text> </Container> </Container> </Container>
Using useBreakpoint hook
Kitchn's useBreakpoint
hook provides an easy way to respond to viewport changes in your React components:
() => { const { isMobile, isTablet, isLaptop, isDesktop } = useBreakpoint(); return ( <Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} mb={"medium"}>Current Breakpoint</Text> <Container gap={"small"}> <Text> <Text weight={"bold"} span>Mobile:</Text> {isMobile ? "Yes" : "No"} </Text> <Text> <Text weight={"bold"} span>Tablet:</Text> {isTablet ? "Yes" : "No"} </Text> <Text> <Text weight={"bold"} span>Laptop:</Text> {isLaptop ? "Yes" : "No"} </Text> <Text> <Text weight={"bold"} span>Desktop:</Text> {isDesktop ? "Yes" : "No"} </Text> </Container> <Container mt={"medium"} p={"small"} bw={1} bg={"darker"} br={"square"}> <Text> Resize your browser window to see the detected breakpoint change. </Text> </Container> </Container> ); }
↓Code Editor
() => { const { isMobile, isTablet, isLaptop, isDesktop } = useBreakpoint(); return ( <Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} mb={"medium"}>Current Breakpoint</Text> <Container gap={"small"}> <Text> <Text weight={"bold"} span>Mobile:</Text> {isMobile ? "Yes" : "No"} </Text> <Text> <Text weight={"bold"} span>Tablet:</Text> {isTablet ? "Yes" : "No"} </Text> <Text> <Text weight={"bold"} span>Laptop:</Text> {isLaptop ? "Yes" : "No"} </Text> <Text> <Text weight={"bold"} span>Desktop:</Text> {isDesktop ? "Yes" : "No"} </Text> </Container> <Container mt={"medium"} p={"small"} bw={1} bg={"darker"} br={"square"}> <Text> Resize your browser window to see the detected breakpoint change. </Text> </Container> </Container> ); }
You can use this hook to conditionally render components or change behaviors based on screen size:
function ResponsiveNavigation() {
const { isMobile } = useBreakpoint();
return (
<Container>
{isMobile ? (
<MobileMenu />
) : (
<DesktopMenu />
)}
</Container>
);
}
Responsive props
Many Kitchn components accept responsive prop values that change based on breakpoints:
<Container
p={["small", "medium", "large"]} // Padding increases with screen size
direction={["column", "row"]} // Column on mobile, row on larger screens
align={["center", "flex-start"]} // Different alignment per breakpoint
>
{/* Content */}
</Container>
This approach allows you to express responsive behavior directly in your component props without writing media queries.
Advanced responsive design techniques
CSS Grid for complex layouts
CSS Grid provides powerful layout capabilities that adapt naturally to available space:
<Container> <Text weight={"bold"} mb={"medium"}>Responsive Grid Layout</Text> <Container style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem' }} > {[1, 2, 3, 4, 5].map(i => ( <Container key={i} p={"medium"} bw={1} br={"square"}> <Text weight={"bold"}>Card {i}</Text> <Text>This grid automatically adjusts the number of columns based on available space.</Text> </Container> ))} </Container> </Container>
↓Code Editor
<Container> <Text weight={"bold"} mb={"medium"}>Responsive Grid Layout</Text> <Container style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem' }} > {[1, 2, 3, 4, 5].map(i => ( <Container key={i} p={"medium"} bw={1} br={"square"}> <Text weight={"bold"}>Card {i}</Text> <Text>This grid automatically adjusts the number of columns based on available space.</Text> </Container> ))} </Container> </Container>
The repeat(auto-fit, minmax(200px, 1fr))
pattern is particularly useful, as it:
- Creates as many columns as can fit, with a minimum width of 200px
- Expands columns equally to fill the available space
- Automatically reflows when the container width changes
Container queries
While media queries look at the viewport size, container queries respond to the size of a specific container element:
.card {
padding: 1rem;
}
@container (min-width: 400px) {
.card {
padding: 2rem;
}
}
To use container queries, you need to establish a containment context:
.container {
container-type: inline-size;
}
This allows for components that respond to their available space rather than the entire viewport—a more modular approach to responsive design.
Fluid typography with CSS clamp
The clamp()
function creates fluid typography that scales smoothly between screen sizes:
h1 {
font-size: clamp(1.5rem, 5vw, 3rem);
/* Minimum: 1.5rem, Preferred: 5vw, Maximum: 3rem */
}
<Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} style={{ fontSize: 'clamp(1.2rem, 4vw, 2.5rem)' }} > This heading uses fluid typography </Text> <Text mt={"medium"}> Resize the window to see the heading text size adjust smoothly between minimum and maximum values. </Text> </Container>
↓Code Editor
<Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} style={{ fontSize: 'clamp(1.2rem, 4vw, 2.5rem)' }} > This heading uses fluid typography </Text> <Text mt={"medium"}> Resize the window to see the heading text size adjust smoothly between minimum and maximum values. </Text> </Container>
Responsive images with art direction
For images that need to change their crop or aspect ratio across different screen sizes, use the picture
element:
<picture>
<!-- Square crop for mobile -->
<source
media="(max-width: 600px)"
srcset="product-square.jpg"
>
<!-- 16:9 crop for desktop -->
<source
media="(min-width: 601px)"
srcset="product-widescreen.jpg"
>
<!-- Fallback -->
<img src="product-default.jpg" alt="Product image">
</picture>
Feature detection with @supports
Use feature detection to provide enhanced layouts for browsers that support newer CSS features:
/* Base layout for all browsers */
.gallery {
display: flex;
flex-wrap: wrap;
}
/* Enhanced layout for browsers that support grid */
@supports (display: grid) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-gap: 1rem;
}
}
Common responsive design patterns
Responsive navigation
Navigation menus often need different presentations across devices:
() => { const [menuOpen, setMenuOpen] = useState(false); const { isMobile } = useBreakpoint(); return ( <Container> <Container p={"medium"} bw={1} br={"square"} mb={"medium"} row justify={"space-between"} align={"center"} > <Text weight={"bold"} size={"large"}>Brand Logo</Text> {isMobile ? ( <Button onClick={() => setMenuOpen(!menuOpen)} size={"small"} > {menuOpen ? 'Close' : 'Menu'} </Button> ) : ( <Container row gap={"medium"}> {['Home', 'Products', 'About', 'Contact'].map(item => ( <Link key={item} href="#" variant={"highlight"}> {item} </Link> ))} </Container> )} </Container> {isMobile && menuOpen && ( <Container p={"medium"} bw={1} br={"square"} mb={"medium"} gap={"small"} > {['Home', 'Products', 'About', 'Contact'].map(item => ( <Link key={item} href="#"> {item} </Link> ))} </Container> )} <Text> {isMobile ? "Mobile navigation with hamburger menu" : "Desktop navigation with horizontal links" } </Text> </Container> ); }
↓Code Editor
() => { const [menuOpen, setMenuOpen] = useState(false); const { isMobile } = useBreakpoint(); return ( <Container> <Container p={"medium"} bw={1} br={"square"} mb={"medium"} row justify={"space-between"} align={"center"} > <Text weight={"bold"} size={"large"}>Brand Logo</Text> {isMobile ? ( <Button onClick={() => setMenuOpen(!menuOpen)} size={"small"} > {menuOpen ? 'Close' : 'Menu'} </Button> ) : ( <Container row gap={"medium"}> {['Home', 'Products', 'About', 'Contact'].map(item => ( <Link key={item} href="#" variant={"highlight"}> {item} </Link> ))} </Container> )} </Container> {isMobile && menuOpen && ( <Container p={"medium"} bw={1} br={"square"} mb={"medium"} gap={"small"} > {['Home', 'Products', 'About', 'Contact'].map(item => ( <Link key={item} href="#"> {item} </Link> ))} </Container> )} <Text> {isMobile ? "Mobile navigation with hamburger menu" : "Desktop navigation with horizontal links" } </Text> </Container> ); }
Card layouts
Card-based interfaces adapt well to different screen sizes:
<Container> <Container row wrap={"wrap"} gap={"medium"} > {[1, 2, 3, 4, 5, 6].map(i => ( <Container key={i} p={"medium"} bw={1} br={"square"} style={{ flex: '1 1 300px', // Grow, shrink, basis minWidth: 0, // Allow cards to shrink below basis maxWidth: '100%' // Prevent overflow on mobile }} > <Text weight={"bold"}>Card {i}</Text> <Text mt={"small"}>These cards will arrange themselves to fill the available space, with a minimum width of 300px.</Text> </Container> ))} </Container> </Container>
↓Code Editor
<Container> <Container row wrap={"wrap"} gap={"medium"} > {[1, 2, 3, 4, 5, 6].map(i => ( <Container key={i} p={"medium"} bw={1} br={"square"} style={{ flex: '1 1 300px', // Grow, shrink, basis minWidth: 0, // Allow cards to shrink below basis maxWidth: '100%' // Prevent overflow on mobile }} > <Text weight={"bold"}>Card {i}</Text> <Text mt={"small"}>These cards will arrange themselves to fill the available space, with a minimum width of 300px.</Text> </Container> ))} </Container> </Container>
Form adaptations
Forms often need significant adjustments across screen sizes:
() => { const { isMobile } = useBreakpoint(); return ( <Container p={"medium"} bw={1} br={"square"} maxW={600} > <Text weight={"bold"} size={"large"} mb={"medium"}> Contact Form </Text> <Container gap={"medium"}> <Container direction={isMobile ? "column" : "row"} gap={"medium"} > <Input label="First Name" placeholder="John" /> <Input label="Last Name" placeholder="Doe" /> </Container> <Input label="Email" placeholder="john.doe@example.com" /> <Input label="Subject" placeholder="Inquiry about your services" /> <Textarea label="Message" placeholder="Your message here..." height={100} /> <Container direction={isMobile ? "column" : "row"} gap={"small"} justify={isMobile ? "stretch" : "flex-end"} > <Button type={"dark"}>Cancel</Button> <Button>Submit</Button> </Container> </Container> </Container> ); }
↓Code Editor
() => { const { isMobile } = useBreakpoint(); return ( <Container p={"medium"} bw={1} br={"square"} maxW={600} > <Text weight={"bold"} size={"large"} mb={"medium"}> Contact Form </Text> <Container gap={"medium"}> <Container direction={isMobile ? "column" : "row"} gap={"medium"} > <Input label="First Name" placeholder="John" /> <Input label="Last Name" placeholder="Doe" /> </Container> <Input label="Email" placeholder="john.doe@example.com" /> <Input label="Subject" placeholder="Inquiry about your services" /> <Textarea label="Message" placeholder="Your message here..." height={100} /> <Container direction={isMobile ? "column" : "row"} gap={"small"} justify={isMobile ? "stretch" : "flex-end"} > <Button type={"dark"}>Cancel</Button> <Button>Submit</Button> </Container> </Container> </Container> ); }
Testing responsive designs
Thorough testing is essential for responsive designs:
1. Testing across devices
Test your design on actual devices whenever possible. At minimum, test on:
- A smartphone (both small and large)
- A tablet (both portrait and landscape)
- A laptop or desktop with various window sizes
2. Browser developer tools
Most modern browsers include responsive design tools:
- Chrome DevTools Device Mode
- Firefox Responsive Design Mode
- Safari Responsive Design Mode
<Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} mb={"small"}>Testing Checklist</Text> <Container gap={"tiny"}> <Text>✅ Test on multiple physical devices</Text> <Text>✅ Use browser developer tools</Text> <Text>✅ Check all breakpoints</Text> <Text>✅ Test in both orientations (portrait/landscape)</Text> <Text>✅ Ensure touch targets are appropriately sized</Text> <Text>✅ Verify all functionality works across devices</Text> <Text>✅ Check loading performance on mobile networks</Text> <Text>✅ Test with keyboard navigation</Text> <Text>✅ Verify screen reader compatibility</Text> </Container> </Container>
↓Code Editor
<Container p={"medium"} bw={1} br={"square"}> <Text weight={"bold"} mb={"small"}>Testing Checklist</Text> <Container gap={"tiny"}> <Text>✅ Test on multiple physical devices</Text> <Text>✅ Use browser developer tools</Text> <Text>✅ Check all breakpoints</Text> <Text>✅ Test in both orientations (portrait/landscape)</Text> <Text>✅ Ensure touch targets are appropriately sized</Text> <Text>✅ Verify all functionality works across devices</Text> <Text>✅ Check loading performance on mobile networks</Text> <Text>✅ Test with keyboard navigation</Text> <Text>✅ Verify screen reader compatibility</Text> </Container> </Container>
3. Automated testing
Use automated testing tools to catch responsive design issues:
- Cypress for visual regression testing
- Puppeteer for automated browser testing at different viewport sizes
- Lighthouse for performance and accessibility testing
Common responsive design challenges
1. Tables on small screens
Tables often overflow on mobile devices. Solutions include:
- Horizontal scrolling for complex tables
- Responsive tables that stack columns vertically on small screens
- Collapsible rows that expand to show additional information
function ResponsiveTable() {
const { isMobile } = useBreakpoint();
if (isMobile) {
return (
<Container>
{data.map(item => (
<Container key={item.id} p="small" bw={1} mb="small">
<Text weight="bold">{item.name}</Text>
<Text>Price: ${item.price}</Text>
<Text>Category: {item.category}</Text>
</Container>
))}
</Container>
);
}
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{data.map(item => (
<tr key={item.id}>
<td>{item.name}</td>
<td>${item.price}</td>
<td>{item.category}</td>
</tr>
))}
</tbody>
</table>
);
}
2. High-resolution images
For high-resolution displays (e.g., Retina), provide higher resolution images without slowing down page loads:
function ResponsiveImage() {
return (
<img
src="image-1x.jpg"
srcSet="image-1x.jpg 1x, image-2x.jpg 2x, image-3x.jpg 3x"
alt="Responsive image"
/>
);
}
3. Touch targets
Ensure touch targets are large enough for comfortable interaction on touch devices (minimum 44×44 pixels):
.button {
padding: 0.5rem 1rem;
}
/* Larger touch target on touch devices */
@media (pointer: coarse) {
.button {
padding: 0.75rem 1.5rem;
}
}
4. Font sizing
Maintain readable font sizes across all devices:
body {
font-size: 16px; /* Base font size */
}
h1 {
font-size: clamp(1.5rem, 5vw, 3rem);
}
h2 {
font-size: clamp(1.25rem, 4vw, 2.5rem);
}
p {
font-size: clamp(1rem, 2vw, 1.2rem);
}
Responsive design best practices
1. Design for flexibility
Create layouts that can accommodate varying content amounts:
- Avoid fixed heights whenever possible
- Use min-height instead of height
- Design components that gracefully expand with content
2. Prioritize content
Identify what's most important and ensure it's accessible on all devices:
- Focus on core content and functionality for mobile
- Consider hiding or deprioritizing secondary content on smaller screens
- Maintain a clear visual hierarchy across all breakpoints
3. Performance matters
Responsive sites should be fast on all devices:
- Optimize images using responsive techniques
- Minimize or defer non-critical resources
- Test performance on low-end devices and slow connections
⚠️ Warning: Mobile users often have slower connections and less powerful devices. Every kilobyte matters for performance, especially on mobile.
4. Progressive enhancement
Start with a solid baseline experience and enhance it for more capable browsers:
- Ensure core functionality works without JavaScript
- Use feature detection to provide enhanced experiences
- Test what happens when features aren't available
function ProgressiveImage() {
return (
<>
{/* Baseline experience */}
<img src="standard-image.jpg" alt="Product" />
{/* Enhanced experience for browsers that support it */}
<picture>
<source
type="image/webp"
srcSet="image.webp"
/>
<source
type="image/jpeg"
srcSet="image.jpg"
/>
{/* No img needed here, it's in the baseline experience */}
</picture>
</>
);
}
Frequently asked questions
What is the difference between responsive and adaptive design?
Responsive design uses fluid layouts that continuously adapt to all screen sizes using percentage-based widths and media queries. Adaptive design, on the other hand, employs distinct layouts for specific screen sizes, with little to no fluidity between them. Most modern websites use responsive design, sometimes with adaptive elements for highly optimized experiences.
How many breakpoints should I use?
While there's no one-size-fits-all answer, most designs work well with 3-4 primary breakpoints:
- Mobile (up to 767px)
- Tablet (768px to 1023px)
- Laptop (1024px to 1439px)
- Desktop (1440px and above)
However, let your content guide your breakpoints—add breakpoints where your layout starts to break, rather than targeting specific devices.
Is mobile-first always the best approach?
Mobile-first is generally recommended because it forces you to prioritize content and ensure core functionality works in constrained environments. However, if your analytics show that the vast majority of your users access your site on desktop (e.g., for certain B2B applications), you might consider a desktop-first approach. The key is to understand your users and optimize for their primary contexts.
How do I handle images responsively?
For responsive images, use a combination of:
max-width: 100%
andheight: auto
for basic responsivenesssrcset
andsizes
attributes for resolution switching- The
picture
element for art direction (different crops) - Lazy loading for better performance
How can I test my responsive design effectively?
Test your responsive designs by:
- Using real devices (not just browser emulators)
- Testing across multiple browsers
- Checking both portrait and landscape orientations
- Verifying touch interactions on mobile devices
- Testing with screen readers and keyboard navigation
- Using tools like Lighthouse to test performance
Getting started with responsive design in Kitchn
Kitchn provides excellent built-in support for responsive design through:
- A flexible Container component that adapts to different screen sizes
- The useBreakpoint hook for conditional rendering
- Responsive props that change based on screen size
- CSS-in-JS styling that supports media queries
By embracing responsive design principles and leveraging Kitchn's responsive capabilities, you can create interfaces that provide excellent experiences across all devices—from smartphones to large desktop monitors. Remember that responsive design is not just about making things fit on different screens; it's about delivering the right experience for each context.