Responsive Widget Design: Mobile-First, Breakpoints & Container Queries
In the ChatGPT ecosystem, 70% of users access conversations on mobile devices. Yet many developers build widgets with desktop-first assumptions, creating frustrating experiences on smaller screens. A widget that looks perfect on a 27" monitor becomes unusable when crammed into a 375px iPhone viewport.
Responsive widget design isn't optional—it's the difference between users completing tasks in-chat versus abandoning them for desktop. Mobile-first development, container queries, and touch optimization transform widgets from desktop afterthoughts into mobile-native experiences that feel natural on any device.
This guide reveals production-ready patterns for building responsive ChatGPT widgets: mobile-first CSS strategies, container query architectures, fluid typography systems, touch-optimized components, and comprehensive testing approaches. By the end, you'll have a complete responsive design toolkit that ensures your widgets deliver exceptional experiences across all screen sizes.
Let's explore how to build widgets that adapt seamlessly from 320px smartphones to 4K displays—without sacrificing usability or performance.
Mobile-First Strategy: Progressive Enhancement from Small Screens Up
Mobile-first design inverts traditional responsive development. Instead of starting with desktop layouts and "squashing down" for mobile, you begin with the smallest viewport and progressively enhance for larger screens. This approach forces ruthless prioritization—if a feature doesn't fit on mobile, it probably isn't essential.
Why Mobile-First Wins for ChatGPT Widgets
ChatGPT's mobile-first user base makes this approach non-negotiable. The conversation interface itself is vertical, touch-driven, and scroll-based. Widgets that embrace these constraints from the start integrate seamlessly; those that fight them feel like foreign objects.
Mobile-first development also improves performance. Smaller screens load baseline styles first (fast), then progressively enhance with desktop features (deferred). This creates perceived performance gains on mobile networks, where every kilobyte matters.
Breakpoint Strategy for Widget Development
Effective responsive widgets use semantic breakpoints aligned with content needs, not device pixels. While CSS frameworks offer generic breakpoints (768px, 1024px), ChatGPT widgets benefit from custom breakpoints tuned to their specific content density.
Mobile-First Breakpoint Hierarchy:
/* ==============================================
MOBILE-FIRST BREAKPOINT SYSTEM
Base: 320px - 639px (mobile-first baseline)
Small: 640px+ (large phones, small tablets)
Medium: 768px+ (tablets, narrow desktops)
Large: 1024px+ (desktops, wide tablets)
XLarge: 1280px+ (large desktops, fullscreen mode)
============================================== */
/* Base mobile styles (no media query needed) */
.widget-container {
/* Mobile-first defaults */
padding: 12px;
font-size: 14px;
line-height: 1.5;
/* Stack vertically by default */
display: flex;
flex-direction: column;
gap: 12px;
/* Touch-friendly spacing */
min-height: 44px;
}
.widget-card {
/* Full-width cards on mobile */
width: 100%;
padding: 16px;
border-radius: 8px;
background: var(--bg-secondary);
/* Single-column layout */
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
/* ==============================================
SMALL BREAKPOINT: 640px+ (Large Phones)
============================================== */
@media (min-width: 640px) {
.widget-container {
padding: 16px;
font-size: 15px;
gap: 16px;
}
.widget-card {
padding: 20px;
/* Two-column grid for cards */
grid-template-columns: auto 1fr;
gap: 16px;
}
/* Horizontal button groups */
.button-group {
flex-direction: row;
justify-content: flex-start;
}
}
/* ==============================================
MEDIUM BREAKPOINT: 768px+ (Tablets)
============================================== */
@media (min-width: 768px) {
.widget-container {
padding: 20px;
font-size: 16px;
gap: 20px;
/* Enable two-column layouts */
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.widget-header {
/* Sticky headers on larger screens */
position: sticky;
top: 0;
background: var(--bg-primary);
z-index: 10;
padding: 16px 20px;
border-bottom: 1px solid var(--border-color);
}
/* Show desktop-only features */
.desktop-nav {
display: flex;
}
}
/* ==============================================
LARGE BREAKPOINT: 1024px+ (Desktop)
============================================== */
@media (min-width: 1024px) {
.widget-container {
padding: 24px;
gap: 24px;
/* Three-column layouts */
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.widget-sidebar {
/* Reveal sidebars */
display: block;
width: 240px;
border-right: 1px solid var(--border-color);
}
/* Hover states (not available on mobile) */
.widget-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: all 0.2s ease;
}
}
/* ==============================================
XLARGE BREAKPOINT: 1280px+ (Wide Desktops)
============================================== */
@media (min-width: 1280px) {
.widget-container {
max-width: 1200px;
margin: 0 auto;
padding: 32px;
}
/* Four-column grid for wide displays */
.widget-grid {
grid-template-columns: repeat(4, 1fr);
}
/* Enhanced spacing for large screens */
.widget-section {
padding: 40px;
gap: 32px;
}
}
/* ==============================================
UTILITY CLASSES: Responsive Visibility
============================================== */
.mobile-only {
display: block;
}
.desktop-only {
display: none;
}
@media (min-width: 768px) {
.mobile-only {
display: none;
}
.desktop-only {
display: block;
}
}
This breakpoint system starts with minimal mobile styles and progressively layers complexity. Notice how desktop features like hover states only appear in larger breakpoints—mobile browsers don't support hover, so including these styles wastes bandwidth.
Container Queries: Component-Level Responsive Design
Container queries revolutionize responsive design by making components responsive to their container's size, not the viewport. This is critical for ChatGPT widgets, which appear in variable-width contexts: inline cards (300-400px), fullscreen modals (full viewport), and picture-in-picture windows (400-600px).
Why Viewport Media Queries Fail for Widgets
Traditional @media queries respond to viewport width. But a widget embedded in a 400px ChatGPT card doesn't know it's in a narrow container—viewport width might be 1920px on desktop. Container queries solve this by responding to the immediate parent's dimensions.
Container Query Architecture
/* ==============================================
CONTAINER QUERY SETUP
Modern responsive widgets with @container
============================================== */
/* Define container contexts */
.widget-root {
/* Establish container query context */
container-type: inline-size;
container-name: widget;
/* Base mobile styles */
padding: 12px;
width: 100%;
}
.card-container {
container-type: inline-size;
container-name: card;
}
/* ==============================================
COMPONENT RESPONDS TO ITS CONTAINER
Not to viewport width
============================================== */
/* Small container (< 400px): Stacked layout */
@container widget (max-width: 399px) {
.product-card {
display: flex;
flex-direction: column;
gap: 12px;
}
.product-image {
width: 100%;
aspect-ratio: 16 / 9;
}
.product-meta {
/* Stack all metadata vertically */
display: flex;
flex-direction: column;
gap: 8px;
}
.price-compare {
/* Hide comparison UI in narrow containers */
display: none;
}
}
/* Medium container (400-600px): Hybrid layout */
@container widget (min-width: 400px) and (max-width: 599px) {
.product-card {
display: grid;
grid-template-columns: 140px 1fr;
gap: 16px;
}
.product-image {
width: 140px;
aspect-ratio: 1;
}
.product-meta {
display: flex;
flex-direction: column;
gap: 10px;
}
.price-compare {
display: flex;
gap: 8px;
}
}
/* Large container (600px+): Full desktop layout */
@container widget (min-width: 600px) {
.product-card {
display: grid;
grid-template-columns: 200px 1fr auto;
gap: 20px;
align-items: center;
}
.product-image {
width: 200px;
aspect-ratio: 1;
}
.product-meta {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.price-compare {
flex-direction: column;
width: 180px;
}
}
/* ==============================================
NESTED CONTAINERS: Cards within widgets
============================================== */
@container card (max-width: 299px) {
.card-content {
padding: 12px;
font-size: 13px;
}
.card-actions {
/* Single-button layout */
display: flex;
flex-direction: column;
gap: 8px;
}
}
@container card (min-width: 300px) {
.card-content {
padding: 16px;
font-size: 14px;
}
.card-actions {
/* Horizontal button row */
display: flex;
flex-direction: row;
gap: 12px;
}
}
/* ==============================================
CONTAINER QUERY UNITS
Use cqi (container inline size) for fluid sizing
============================================== */
.responsive-heading {
/* Font size scales with container width */
font-size: clamp(18px, 5cqi, 28px);
margin-bottom: 2cqi;
}
.adaptive-button {
/* Button size adapts to container */
padding: 1.5cqi 3cqi;
font-size: clamp(13px, 2.5cqi, 16px);
}
Container queries enable truly modular components. A product card component doesn't need to "know" whether it's in a fullscreen widget, inline card, or PiP window—it adapts to whatever space it's given.
Fallback Strategy for Older Browsers
Container queries are modern (2023+), but ChatGPT's diverse user base includes older browsers. Implement graceful fallbacks:
/* Fallback: Use viewport media queries for older browsers */
@supports not (container-type: inline-size) {
.product-card {
/* Mobile-first fallback */
display: flex;
flex-direction: column;
}
@media (min-width: 640px) {
.product-card {
display: grid;
grid-template-columns: 140px 1fr;
}
}
}
Modern browsers use container queries; legacy browsers gracefully degrade to viewport-based responsive design.
Fluid Typography: Scalable Text Across All Viewports
Responsive widgets need typography that scales seamlessly between 320px smartphones and 4K displays. Fixed font sizes create readability issues: 16px text feels cramped on small screens but tiny on large displays.
The clamp() Function: Fluid Type Without Media Queries
CSS clamp() creates fluid typography that scales linearly with viewport width, clamped between minimum and maximum bounds:
/* ==============================================
FLUID TYPOGRAPHY SYSTEM
Scales smoothly from 320px to 1920px viewports
============================================== */
:root {
/* Base fluid scale (mobile to desktop) */
--text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem); /* 12px - 14px */
--text-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem); /* 14px - 16px */
--text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); /* 16px - 18px */
--text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.375rem); /* 18px - 22px */
--text-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.625rem); /* 20px - 26px */
--text-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem); /* 24px - 32px */
--text-3xl: clamp(1.875rem, 1.6rem + 1.375vw, 2.625rem); /* 30px - 42px */
/* Fluid line heights */
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.75;
/* Fluid spacing scale */
--space-xs: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem); /* 8px - 12px */
--space-sm: clamp(0.75rem, 0.6rem + 0.75vw, 1rem); /* 12px - 16px */
--space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem); /* 16px - 24px */
--space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem); /* 24px - 40px */
--space-xl: clamp(2rem, 1.5rem + 2.5vw, 4rem); /* 32px - 64px */
}
/* ==============================================
TYPOGRAPHY COMPONENTS
============================================== */
.widget-heading {
font-size: var(--text-2xl);
line-height: var(--leading-tight);
margin-bottom: var(--space-md);
font-weight: 700;
}
.widget-subheading {
font-size: var(--text-lg);
line-height: var(--leading-normal);
margin-bottom: var(--space-sm);
font-weight: 600;
}
.widget-body {
font-size: var(--text-base);
line-height: var(--leading-relaxed);
margin-bottom: var(--space-md);
}
.widget-caption {
font-size: var(--text-sm);
line-height: var(--leading-normal);
color: var(--text-secondary);
}
/* ==============================================
CONTAINER-RELATIVE FLUID TYPE
Font size scales with container, not viewport
============================================== */
.card-title {
/* Scales with container width using cqi units */
font-size: clamp(1rem, 4cqi, 1.5rem);
line-height: 1.3;
}
.card-description {
font-size: clamp(0.875rem, 3cqi, 1rem);
line-height: 1.6;
}
Viewport Units for True Fluidity
Combine viewport units (vw, vh) with clamp() for typography that responds to screen dimensions:
/* Hero text scales with viewport width */
.hero-title {
font-size: clamp(2rem, 5vw + 1rem, 4rem); /* 32px - 64px */
line-height: 1.1;
letter-spacing: -0.02em;
}
/* Sidebar text uses viewport height */
.sidebar-text {
font-size: clamp(0.875rem, 1.5vh, 1.125rem); /* 14px - 18px */
}
This approach creates truly fluid interfaces where text grows smoothly as viewports expand, without abrupt jumps at breakpoints.
Touch Optimization: Designing for Fingers, Not Cursors
Mobile users interact with widgets using thumbs and fingers, not precision mouse cursors. Touch targets need larger hit areas, clearer affordances, and gesture-friendly interactions.
Touch Target Sizing: The 44px Rule
Apple's Human Interface Guidelines recommend minimum 44×44px touch targets for reliable tap recognition. Smaller targets cause mis-taps and frustration, especially for users with accessibility needs.
/* ==============================================
TOUCH-OPTIMIZED COMPONENTS
44px minimum touch targets for accessibility
============================================== */
.touch-button {
/* Ensure 44px minimum height/width */
min-height: 44px;
min-width: 44px;
/* Visible padding smaller, but hit area larger */
padding: 12px 20px;
/* Visual feedback for touch */
transition: background-color 0.15s ease;
/* Prevent text selection on rapid taps */
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.touch-button:active {
/* Instant visual feedback on tap */
background-color: var(--button-active);
transform: scale(0.98);
}
/* Increase touch target with pseudo-element */
.icon-button {
position: relative;
width: 32px;
height: 32px;
}
.icon-button::before {
/* Expand hit area to 44×44px */
content: '';
position: absolute;
top: -6px;
left: -6px;
right: -6px;
bottom: -6px;
/* Invisible, but tappable */
}
/* ==============================================
SWIPE GESTURES: Horizontal scrolling
============================================== */
.carousel-container {
/* Enable smooth scrolling */
overflow-x: auto;
overflow-y: hidden;
/* Snap to items */
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
/* Hide scrollbar on mobile */
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.carousel-container::-webkit-scrollbar {
display: none;
}
.carousel-item {
scroll-snap-align: start;
flex-shrink: 0;
width: 85%; /* Show peek of next item */
margin-right: 16px;
}
/* ==============================================
FORM INPUTS: Mobile-friendly sizing
============================================== */
.form-input {
/* Larger input fields for touch */
min-height: 48px;
padding: 12px 16px;
font-size: 16px; /* Prevents iOS zoom on focus */
border-radius: 8px;
/* Clear touch affordances */
border: 2px solid var(--input-border);
transition: border-color 0.2s ease;
}
.form-input:focus {
border-color: var(--input-focus);
outline: none;
}
/* Checkbox/radio with larger hit areas */
.form-checkbox {
/* Increase visual size */
width: 24px;
height: 24px;
/* Larger tap target */
padding: 10px;
margin: -10px;
}
React Hook for Touch Detection
Detect touch capabilities and adapt interactions dynamically:
/**
* Touch Detection Hook
* Detects touch support and provides touch-optimized utilities
*/
import { useState, useEffect } from 'react';
interface TouchCapabilities {
isTouchDevice: boolean;
hasHover: boolean;
maxTouchPoints: number;
}
export function useTouchDetection(): TouchCapabilities {
const [capabilities, setCapabilities] = useState<TouchCapabilities>({
isTouchDevice: false,
hasHover: true,
maxTouchPoints: 0,
});
useEffect(() => {
const checkTouchCapabilities = () => {
const isTouchDevice =
'ontouchstart' in window ||
navigator.maxTouchPoints > 0 ||
// @ts-ignore - legacy IE property
navigator.msMaxTouchPoints > 0;
const hasHover = window.matchMedia('(hover: hover)').matches;
const maxTouchPoints = navigator.maxTouchPoints || 0;
setCapabilities({
isTouchDevice,
hasHover,
maxTouchPoints,
});
};
checkTouchCapabilities();
// Re-check on resize (handles device orientation changes)
window.addEventListener('resize', checkTouchCapabilities);
return () => window.removeEventListener('resize', checkTouchCapabilities);
}, []);
return capabilities;
}
// Usage in components:
function AdaptiveButton() {
const { isTouchDevice, hasHover } = useTouchDetection();
return (
<button
className={`
adaptive-button
${isTouchDevice ? 'touch-optimized' : 'mouse-optimized'}
`}
style={{
minHeight: isTouchDevice ? '44px' : '36px',
padding: isTouchDevice ? '12px 20px' : '8px 16px',
}}
>
{isTouchDevice ? 'Tap to Continue' : 'Click to Continue'}
</button>
);
}
Responsive Breakpoint Manager: Centralized Logic
Manage breakpoints consistently across your widget with a TypeScript breakpoint utility:
/**
* Breakpoint Manager
* Centralized responsive breakpoint logic
*/
export const breakpoints = {
mobile: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
xxl: 1536,
} as const;
export type Breakpoint = keyof typeof breakpoints;
/**
* Generate media query strings
*/
export const mediaQueries = {
mobile: `(min-width: ${breakpoints.mobile}px)`,
sm: `(min-width: ${breakpoints.sm}px)`,
md: `(min-width: ${breakpoints.md}px)`,
lg: `(min-width: ${breakpoints.lg}px)`,
xl: `(min-width: ${breakpoints.xl}px)`,
xxl: `(min-width: ${breakpoints.xxl}px)`,
} as const;
/**
* React hook for responsive breakpoints
*/
export function useBreakpoint() {
const [breakpoint, setBreakpoint] = useState<Breakpoint>('mobile');
useEffect(() => {
const updateBreakpoint = () => {
const width = window.innerWidth;
if (width >= breakpoints.xxl) setBreakpoint('xxl');
else if (width >= breakpoints.xl) setBreakpoint('xl');
else if (width >= breakpoints.lg) setBreakpoint('lg');
else if (width >= breakpoints.md) setBreakpoint('md');
else if (width >= breakpoints.sm) setBreakpoint('sm');
else setBreakpoint('mobile');
};
updateBreakpoint();
const debouncedUpdate = debounce(updateBreakpoint, 150);
window.addEventListener('resize', debouncedUpdate);
return () => window.removeEventListener('resize', debouncedUpdate);
}, []);
return breakpoint;
}
/**
* Debounce utility (prevents excessive resize events)
*/
function debounce<T extends (...args: any[]) => void>(
func: T,
wait: number
): T {
let timeout: NodeJS.Timeout | null = null;
return ((...args: Parameters<T>) => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
}) as T;
}
// Usage:
function ResponsiveWidget() {
const breakpoint = useBreakpoint();
return (
<div className={`widget widget-${breakpoint}`}>
{breakpoint === 'mobile' && <MobileNav />}
{breakpoint === 'lg' && <DesktopNav />}
</div>
);
}
Testing Responsive Layouts: Automated & Manual
Responsive design requires comprehensive testing across real devices and automated tools. Here's a Playwright-based testing suite:
/**
* Responsive Widget Testing Suite
* Playwright tests for breakpoint behavior
*/
import { test, expect } from '@playwright/test';
const viewports = {
mobile: { width: 375, height: 667 },
tablet: { width: 768, height: 1024 },
desktop: { width: 1440, height: 900 },
wide: { width: 1920, height: 1080 },
};
test.describe('Responsive Widget Layouts', () => {
test('mobile viewport: stacked layout', async ({ page }) => {
await page.setViewportSize(viewports.mobile);
await page.goto('/widget-demo');
// Verify vertical stacking
const card = page.locator('.product-card');
await expect(card).toHaveCSS('flex-direction', 'column');
// Check touch target sizes
const button = page.locator('.primary-button');
const box = await button.boundingBox();
expect(box?.height).toBeGreaterThanOrEqual(44);
});
test('tablet viewport: hybrid layout', async ({ page }) => {
await page.setViewportSize(viewports.tablet);
await page.goto('/widget-demo');
// Verify grid layout
const card = page.locator('.product-card');
await expect(card).toHaveCSS('display', 'grid');
// Check two-column grid
const gridColumns = await card.evaluate(
(el) => getComputedStyle(el).gridTemplateColumns
);
expect(gridColumns).toContain('140px');
});
test('desktop viewport: full layout', async ({ page }) => {
await page.setViewportSize(viewports.desktop);
await page.goto('/widget-demo');
// Verify three-column grid
const container = page.locator('.widget-container');
const gridColumns = await container.evaluate(
(el) => getComputedStyle(el).gridTemplateColumns
);
expect(gridColumns.split(' ').length).toBeGreaterThanOrEqual(3);
// Check desktop-only elements visible
await expect(page.locator('.desktop-nav')).toBeVisible();
await expect(page.locator('.mobile-only')).not.toBeVisible();
});
test('fluid typography scales correctly', async ({ page }) => {
// Test at mobile width
await page.setViewportSize(viewports.mobile);
await page.goto('/widget-demo');
const heading = page.locator('.widget-heading');
const mobileFontSize = await heading.evaluate(
(el) => getComputedStyle(el).fontSize
);
// Test at desktop width
await page.setViewportSize(viewports.desktop);
const desktopFontSize = await heading.evaluate(
(el) => getComputedStyle(el).fontSize
);
// Font size should increase with viewport
expect(parseFloat(desktopFontSize)).toBeGreaterThan(
parseFloat(mobileFontSize)
);
});
test('container queries adapt to parent width', async ({ page }) => {
await page.goto('/widget-demo');
// Narrow container
await page.evaluate(() => {
const container = document.querySelector('.widget-root');
(container as HTMLElement).style.width = '350px';
});
const card = page.locator('.product-card');
await expect(card).toHaveCSS('flex-direction', 'column');
// Wide container
await page.evaluate(() => {
const container = document.querySelector('.widget-root');
(container as HTMLElement).style.width = '650px';
});
await expect(card).toHaveCSS('display', 'grid');
});
test('touch targets meet 44px minimum', async ({ page }) => {
await page.setViewportSize(viewports.mobile);
await page.goto('/widget-demo');
const buttons = await page.locator('button, a, .interactive').all();
for (const button of buttons) {
const box = await button.boundingBox();
if (box) {
expect(box.height).toBeGreaterThanOrEqual(44);
expect(box.width).toBeGreaterThanOrEqual(44);
}
}
});
});
This test suite validates responsive behavior across viewports, ensuring layouts adapt correctly and touch targets meet accessibility standards.
Conclusion: Build Once, Work Everywhere
Responsive widget design isn't about supporting every screen size—it's about building interfaces that feel native on whatever device users choose. Mobile-first development forces you to prioritize essential features. Container queries make components truly modular. Fluid typography scales gracefully. Touch optimization ensures mobile usability matches desktop precision.
The ChatGPT ecosystem spans smartphones, tablets, desktops, and everything in between. By embracing mobile-first principles, leveraging modern CSS features like container queries and clamp(), and optimizing for touch interactions, you create widgets that deliver exceptional experiences regardless of screen size.
Ready to build responsive ChatGPT widgets that adapt seamlessly to any device? Start building with MakeAIHQ's no-code platform and deploy mobile-first widgets in minutes—no responsive CSS expertise required. Our visual editor handles breakpoints, container queries, and touch optimization automatically, so you can focus on creating great user experiences.
Internal Links
- ChatGPT Widget Development Guide
- Mobile-First Design Principles
- Container Query Patterns
- Fluid Typography Systems
- Touch-Optimized UX Design
- Accessibility in ChatGPT Widgets
- Performance Optimization Guide
- CSS Grid Layouts for Widgets
- Viewport Units Deep Dive
- Testing Responsive Components
External Links
- CSS Container Queries - MDN Web Docs
- Responsive Web Design Patterns - Google
- Touch Target Guidelines - WCAG 2.1
Schema Markup (HowTo):
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "How to Build Responsive ChatGPT Widgets with Mobile-First Design",
"description": "Complete guide to building responsive ChatGPT widgets using mobile-first strategies, container queries, fluid typography, and touch optimization.",
"step": [
{
"@type": "HowToStep",
"name": "Implement Mobile-First Breakpoints",
"text": "Start with base mobile styles (320px) and progressively enhance for larger screens using semantic breakpoints at 640px, 768px, 1024px, and 1280px.",
"url": "https://makeaihq.com/guides/cluster/widget-responsive-design#mobile-first-strategy"
},
{
"@type": "HowToStep",
"name": "Add Container Queries",
"text": "Use CSS container queries to make components responsive to their parent container's size, not viewport width. Define container contexts with container-type: inline-size.",
"url": "https://makeaihq.com/guides/cluster/widget-responsive-design#container-queries"
},
{
"@type": "HowToStep",
"name": "Implement Fluid Typography",
"text": "Use CSS clamp() function to create typography that scales smoothly between minimum and maximum sizes: clamp(1rem, 0.9rem + 0.5vw, 1.125rem).",
"url": "https://makeaihq.com/guides/cluster/widget-responsive-design#fluid-typography"
},
{
"@type": "HowToStep",
"name": "Optimize for Touch Interactions",
"text": "Ensure all interactive elements meet 44×44px minimum touch target size. Add touch-specific styles like :active states and disable hover effects on mobile.",
"url": "https://makeaihq.com/guides/cluster/widget-responsive-design#touch-optimization"
},
{
"@type": "HowToStep",
"name": "Test Across Devices",
"text": "Use Playwright or similar testing tools to validate responsive behavior across mobile (375px), tablet (768px), and desktop (1440px) viewports.",
"url": "https://makeaihq.com/guides/cluster/widget-responsive-design#testing-responsive-layouts"
}
]
}