Skip to content

Responsive Design

Responsive Design Guidelines

WEC Design System uses a mobile-first approach to ensure products work seamlessly across all device sizes. Our breakpoint system is based on common device widths used in the Indonesian market.

Our standard breakpoint system follows Tailwind CSS defaults:

BreakpointMin WidthMax ContainerTarget Devices
xs0px100%Small mobile (320px+)
sm640px640pxMobile landscape, large phones
md768px768pxTablet portrait (iPad)
lg1024px1024pxTablet landscape, small desktop
xl1280px1280pxDesktop, laptops
2xl1366px1440pxLarge desktops
/* Mobile-first: Base styles apply to all screens */
.card {
padding: 16px;
font-size: 14px;
}
/* sm: 640px and up */
@media (min-width: 640px) {
.card {
padding: 20px;
}
}
/* md: 768px and up */
@media (min-width: 768px) {
.card {
padding: 24px;
font-size: 16px;
}
}
/* lg: 1024px and up */
@media (min-width: 1024px) {
.card {
padding: 32px;
font-size: 18px;
}
}

Always design for mobile first, then enhance for larger screens.

/* Base styles for mobile */
.component {
flex-direction: column;
padding: var(--spacing-4);
font-size: var(--text-mobile-body2);
}
/* Enhance for tablet */
@media (min-width: 768px) {
.component {
flex-direction: row;
padding: var(--spacing-6);
font-size: var(--text-desktop-body2);
}
}
/* Enhance for desktop */
@media (min-width: 1024px) {
.component {
padding: var(--spacing-10);
}
}
.content-wrapper {
width: 100%;
margin: 0 auto;
padding-left: 24px; /* Mobile */
padding-right: 24px;
}
@media (min-width: 768px) {
.content-wrapper {
padding-left: 40px; /* Tablet */
padding-right: 40px;
}
}
@media (min-width: 1024px) {
.content-wrapper {
padding-left: 100px; /* Desktop */
padding-right: 100px;
}
}
Container TypeMax WidthUsage
.container-sm640pxNarrow content, forms
.container-md768pxMedium content, articles
.container-lg1024pxWide content, dashboards
.container-xl1440pxMaximum content width
.container-sm { max-width: 640px; margin: 0 auto; }
.container-md { max-width: 768px; margin: 0 auto; }
.container-lg { max-width: 1024px; margin: 0 auto; }
.container-xl { max-width: 1440px; margin: 0 auto; }
// Mobile: Single column
// Tablet: 2 columns
// Desktop: 3-4 columns
<div className="
grid
grid-cols-1 /* Mobile: 1 column */
md:grid-cols-2 /* Tablet: 2 columns */
lg:grid-cols-3 /* Desktop: 3 columns */
xl:grid-cols-4 /* Large: 4 columns */
gap-4 /* 16px gap */
">
{items.map(item => (
<Card key={item.id}>{item.content}</Card>
))}
</div>
// Mobile: Hamburger menu
// Desktop: Always visible
<nav className="flex items-center justify-between p-4">
<Logo />
{/* Mobile menu button */}
<button className="lg:hidden" onClick={toggleMenu}>
<MenuIcon />
</button>
{/* Desktop navigation */}
<ul className="hidden lg:flex gap-8">
<li><a href="/">Home</a></li>
<li><a href="/packages">Packages</a></li>
<li><a href="/help">Help</a></li>
</ul>
</nav>
{/* Mobile menu drawer */}
<MobileMenu isOpen={isMenuOpen} onClose={() => setIsMenuOpen(false)} />
// Mobile: Horizontal scroll
// Desktop: Grid or flex
<div className="
flex
overflow-x-auto
gap-4
snap-x
lg:grid lg:grid-cols-4 lg:overflow-visible
">
{tabs.map(tab => (
<button
key={tab.id}
className="snap-start shrink-0 min-w-[120px]"
>
{tab.label}
</button>
))}
</div>

Type scale adjusts between mobile and desktop:

ElementMobileDesktopScale
H1/Hero32px / 40px64px / 72px2.0x
H2/Title32px / 36px56px / 72px1.75x
H3/Section20px / 24px44px / 56px2.2x
H4/Subsection16px / 24px32px / 48px2.0x
H5/Card title18px / 24px24px / 32px1.33x
H6/Small heading18px / 24px18px / 24pxSame
Body14px / 24px18px / 32px1.28x
Secondary14px / 16px15px / 24px1.07x
Caption12px / 24px13px / 24px1.08x
.heading-primary {
font-size: 32px; /* Mobile */
line-height: 40px;
font-weight: 700;
}
@media (min-width: 768px) {
.heading-primary {
font-size: 64px; /* Desktop */
line-height: 72px;
}
}

Ensure interactive elements are large enough for touch (minimum 44×44px):

ElementMinimum SizeWEC Standard
Buttons44×44px48×48px recommended
Links44×44pxAdd padding if text is small
Form inputs44px height48px height recommended
Checkboxes24×24pxWith padding to 44px
Radio buttons24×24pxWith padding to 44px
/* Icon button - full area touchable */
.icon-button {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
padding: 12px; /* Creates space around icon */
}
/* Text link - add padding for touch */
.inline-link {
display: inline-block;
padding: 8px 12px;
margin: -8px -12px; /* Negative margin prevents layout shift */
}
/* Form input with adequate height */
.form-input {
height: 48px;
padding: 12px 16px;
font-size: 16px; /* Prevents iOS zoom */
}

Use appropriate image sizes and formats:

// Responsive image with srcset
<img
src="package-card-mobile.jpg"
srcSet="
package-card-mobile.jpg 640w,
package-card-tablet.jpg 768w,
package-card-desktop.jpg 1024w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 768px) 50vw,
33vw
"
alt="Combo Internet 50GB Package"
loading="lazy"
/>
// With Astro Image component
import { Image } from 'astro:assets';
<Image
src={packageImage}
alt="Combo Internet Package"
widths={[640, 768, 1024, 1440]}
sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, 33vw"
loading="lazy"
/>
// Mobile: Bottom tab bar or hamburger menu
// Desktop: Top navigation bar
<nav className="
fixed
bottom-0 left-0 right-0 /* Mobile: bottom bar */
lg:static lg:top-0 /* Desktop: top bar */
bg-white
border-t lg:border-t-0 lg:border-b
z-50
">
<div className="flex justify-around lg:justify-between lg:gap-8">
{/* Nav items */}
</div>
</nav>
.package-card {
/* Mobile: Full width */
width: 100%;
padding: 16px;
border-radius: 12px 12px 0 0;
}
@media (min-width: 768px) {
.package-card {
/* Tablet: Fixed width with gap */
width: calc(50% - 12px);
padding: 20px;
border-radius: 16px 16px 0 0;
}
}
@media (min-width: 1024px) {
.package-card {
/* Desktop: Fixed width */
width: 320px;
padding: 24px;
border-radius: 20px 20px 0 0;
}
}
// Mobile: Card view or horizontal scroll
// Desktop: Full table
<div className="overflow-x-auto lg:overflow-visible">
<table className="w-full min-w-[600px] lg:min-w-0">
<thead>
<tr>
<th>Package</th>
<th>Quota</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{/* Table rows */}
</tbody>
</table>
</div>
.modal-content {
/* Mobile: Full screen or bottom sheet */
position: fixed;
bottom: 0;
left: 0;
right: 0;
border-radius: 20px 20px 0 0;
max-height: 90vh;
}
@media (min-width: 768px) {
.modal-content {
/* Desktop: Centered modal */
bottom: auto;
left: 50%;
right: auto;
transform: translateX(-50%);
width: 100%;
max-width: 500px;
border-radius: 20px;
}
}
.form {
/* Mobile: Single column */
display: flex;
flex-direction: column;
gap: 16px;
}
@media (min-width: 768px) {
.form {
/* Desktop: Two columns */
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
}
.form-full-width {
/* Span full width on all screens */
grid-column: 1 / -1;
}

Responsive padding for main content containers:

BreakpointPadding
Mobile (< 768px)24px
Tablet (768px - 1024px)40px
Desktop (1024px+)100px
.content-wrapper {
padding-left: 24px;
padding-right: 24px;
}
@media (min-width: 768px) {
.content-wrapper {
padding-left: 40px;
padding-right: 40px;
}
}
@media (min-width: 1024px) {
.content-wrapper {
padding-left: 100px;
padding-right: 100px;
}
}
/* Mobile only */
.hidden { display: none; }
.block { display: block; }
.flex { display: flex; }
/* Show on sm and up */
@media (min-width: 640px) {
.sm\:hidden { display: none; }
.sm\:block { display: block; }
.sm\:flex { display: flex; }
}
/* Show on md and up */
@media (min-width: 768px) {
.md\:hidden { display: none; }
.md\:block { display: block; }
.md\:flex { display: flex; }
}
/* Show on lg and up */
@media (min-width: 1024px) {
.lg\:hidden { display: none; }
.lg\:block { display: block; }
.lg\:flex { display: flex; }
}
// Hide on mobile, show on desktop
<div className="hidden lg:block">
Desktop content
</div>
// Show on mobile, hide on desktop
<div className="block lg:hidden">
Mobile content
</div>
// Different layouts by screen size
<div className="flex flex-col md:flex-row lg:flex-row">
<div>Content 1</div>
<div>Content 2</div>
</div>

Test your designs on these devices:

DeviceWidthType
iPhone SE375pxSmall mobile
iPhone 12/13390pxStandard mobile
iPhone 14 Pro Max430pxLarge mobile
iPad768pxTablet portrait
iPad Pro1024pxTablet landscape
Laptop1366pxDesktop
Large desktop1920pxLarge screen
  1. Open DevTools (F12)
  2. Click device toolbar button (Ctrl+Shift+M)
  3. Select device or enter custom dimensions
  4. Test touch simulation
  • Test on 375px (small mobile)
  • Test on 768px (tablet)
  • Test on 1024px (desktop)
  • Test touch targets (44×44px minimum)
  • Test horizontal scroll behavior
  • Test font sizes (minimum 14px)
  • Test form inputs (no zoom on focus)
  • Test navigation (mobile menu vs desktop)
  • Lazy load images below the fold
  • Use WebP format with JPEG fallback
  • Minify CSS and JavaScript
  • Optimize font loading (preload critical fonts)
  • Reduce CLS (Cumulative Layout Shift)
<!-- Preload critical fonts -->
<link rel="preload" href="/fonts/poppins-regular.woff2" as="font" type="font/woff2" crossorigin>
<!-- Lazy load images -->
<img src="placeholder.jpg" data-src="actual.jpg" loading="lazy" alt="...">
<!-- Responsive images -->
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="..." loading="lazy">
</picture>
✅ Do❌ Don’t
Use mobile-first approachDesign desktop-first
Test on real devicesOnly use browser emulation
Use relative units (rem, %)Use fixed pixels everywhere
Ensure 44×44px touch targetsMake buttons smaller than 44px
Use responsive imagesUse full-size images on mobile
Optimize for performanceLoad everything upfront
Test text readabilityUse tiny fonts on mobile
Consider landscape modeOnly test portrait