Responsive Design dengan Claude Code: CSS Mobile-first dan Playwright
Bangun UI responsif dengan Claude Code: CSS mobile-first, clamp, grid, gambar, navigasi, tabel, dan check Playwright.
Tetapkan aturan sebelum Claude Code mengubah CSS
Responsive design bukan sekadar mengecilkan halaman desktop supaya muat di ponsel. Halaman yang siap dipublikasikan harus memperhatikan lebar layar, target sentuh, ukuran gambar, prioritas navigasi, keterbacaan tabel, iklan, dan CTA konversi. Jika Anda hanya meminta Claude Code “buat mobile friendly”, hasilnya bisa terlihat bagus di satu viewport tetapi masih menyisakan horizontal scroll, card dengan fixed width, hero image yang terlalu berat, menu yang sempit, atau tombol beli yang terkubur terlalu jauh.
Cara yang lebih aman adalah memberi kontrak kecil yang bisa diverifikasi. Mobile-first CSS berarti layout layar kecil menjadi dasar, lalu layar besar hanya menambah peningkatan. clamp() adalah fungsi CSS untuk menentukan nilai minimum, nilai ideal, dan nilai maksimum. Container query membuat komponen merespons lebar parent container, bukan hanya lebar viewport. Definisi singkat ini membantu Claude Code menghasilkan diff yang lebih mudah direview.
Untuk sumber resmi, gunakan MDN Responsive design, @container, clamp(), dan responsive images. Untuk verifikasi, gunakan Playwright Screenshots dan Visual comparisons. Dokumentasi Claude Code overview dan How Claude Code works menjelaskan Claude Code sebagai alat yang membaca codebase, mengedit file, menjalankan command, dan memverifikasi hasil. Gunakan pola itu untuk pekerjaan responsive.
Baca juga Claude Code design system, Claude Code accessibility, dan Claude Code Playwright testing agar konteks desain, aksesibilitas, dan browser check tersambung.
Peta implementasi
Pekerjaan responsive sering gagal ketika CSS ditambahkan sebagai tambalan terakhir. Minta Claude Code membaca halaman yang ada, mencari lebar tempat setiap komponen rusak, lalu memperbarui CSS, gambar, dan test secara bersamaan.
flowchart LR
A["Baca halaman yang ada"] --> B["CSS dasar mobile-first"]
B --> C["Fluid grid dan clamp()"]
C --> D["Komponen dengan container query"]
D --> E["Gambar dan tabel responsif"]
E --> F["Screenshot Playwright"]
Prompt berikut bisa dipakai langsung:
Buat halaman /responsive-demo yang ada menjadi responsif.
Requirement:
- Gunakan CSS mobile-first.
- Tidak boleh ada horizontal overflow di 320px, 390px, 768px, 1024px, dan 1440px.
- Navigasi, card, tabel harga, CTA artikel, dan footer tidak boleh saling menimpa.
- Gambar konten harus punya width/height, srcset, sizes, loading, dan alt yang berguna.
- Prioritaskan CSS Grid, clamp(), dan @container sebelum menambah JavaScript untuk membaca lebar.
- Setelah perubahan, jalankan screenshot Playwright dan assertion overflow.
Jangan:
- Jangan merusak URL, link CTA konversi, atau urutan heading yang aksesibel.
- Jangan menambah min-width fixed besar yang membuat seluruh halaman scroll horizontal.
Prompt ini memberi target, batasan, dan bukti. Review pun lebih mudah karena solusi yang hanya bagus di desktop langsung terlihat belum selesai.
HTML yang bisa disalin
Contoh ini berisi navigasi, hero, gambar, card, panel samping, dan tabel perbandingan. Ganti path gambar sesuai proyek, lalu pakai CSS di bagian berikutnya.
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Responsive Demo</title>
<link rel="stylesheet" href="./responsive-demo.css" />
</head>
<body>
<header class="site-nav">
<a class="brand" href="/">ClaudeCodeLab</a>
<nav class="nav-links" aria-label="Navigasi utama">
<a href="/id/blog/">Artikel</a>
<a href="/en/products/">Produk</a>
<a href="/en/training/">Konsultasi</a>
</nav>
</header>
<main class="page-shell">
<section class="hero">
<div>
<p class="eyebrow">Responsive Design</p>
<h1>Mulai dari layar kecil, lalu biarkan layout tumbuh</h1>
<p class="lead">
Implementasikan CSS mobile-first, fluid grid, responsive image, dan Playwright check dalam satu workflow.
</p>
<a class="primary-cta" href="/en/products/">Lihat template prompt</a>
</div>
<picture class="hero-media">
<source
type="image/avif"
srcset="/images/responsive-demo-640.avif 640w, /images/responsive-demo-1280.avif 1280w"
sizes="(width < 768px) 92vw, 40vw"
/>
<img
src="/images/responsive-demo-1280.jpg"
alt="Ponsel dan laptop menampilkan halaman responsif yang sama"
width="1280"
height="900"
loading="eager"
decoding="async"
/>
</picture>
</section>
<div class="layout-grid">
<aside class="side-panel" aria-label="Checklist viewport">
<h2>Lebar yang dicek</h2>
<ul>
<li>320px: ponsel kecil</li>
<li>390px: ponsel umum</li>
<li>768px: tablet</li>
<li>1024px ke atas: desktop</li>
</ul>
</aside>
<section class="cards" aria-label="Card perbaikan">
<article class="card featured">
<img src="/images/card-layout.jpg" alt="" width="720" height="480" loading="lazy" />
<div class="card-body">
<h2>Card merespons container</h2>
<p>Komponen reusable harus menyesuaikan kepadatan berdasarkan ruang tempat ia berada.</p>
</div>
</article>
<article class="card">
<div class="card-body">
<h2>Navigasi boleh wrap</h2>
<p>Jangan memaksa semua link desktop masuk ke satu baris mobile.</p>
</div>
</article>
<article class="card">
<div class="card-body">
<h2>Tabel tetap bermakna</h2>
<p>Di layar kecil, ubah row menjadi card dan tampilkan nama kolom dengan `data-label`.</p>
</div>
</article>
</section>
</div>
<section class="comparison">
<h2>Perbandingan paket</h2>
<table class="responsive-table">
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Solo</th>
<th scope="col">Tim</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Item">Tujuan</td>
<td data-label="Solo">Belajar dan perbaikan kecil</td>
<td data-label="Tim">Standar review konsisten</td>
</tr>
<tr>
<td data-label="Item">Bukti</td>
<td data-label="Solo">Playwright lokal</td>
<td data-label="Tim">Screenshot di CI</td>
</tr>
</tbody>
</table>
</section>
</main>
</body>
</html>
CSS mobile-first, fluid grid, dan clamp
CSS dimulai dari layar kecil. Kolom untuk layar besar ditambahkan kemudian. Card memakai repeat(auto-fit, minmax(...)), ukuran dan spacing memakai clamp(), dan card featured berubah hanya saat containernya cukup lebar.
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #172033;
background: #f7f8fb;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
img {
display: block;
max-width: 100%;
height: auto;
}
.site-nav,
.page-shell {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
.site-nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
padding-block: 1rem;
}
.brand,
.nav-links a,
.primary-cta {
min-height: 44px;
display: inline-flex;
align-items: center;
}
.nav-links {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.nav-links a {
padding-inline: 0.75rem;
color: inherit;
text-decoration: none;
}
.page-shell {
padding-block: clamp(1rem, 4vw, 3rem);
}
.hero {
display: grid;
gap: clamp(1rem, 4vw, 2.5rem);
align-items: center;
}
.hero h1 {
max-width: 11ch;
margin: 0;
font-size: clamp(2.25rem, 10vw, 5rem);
line-height: 1.02;
}
.lead {
max-width: 62ch;
font-size: clamp(1rem, 2vw, 1.25rem);
line-height: 1.8;
}
.primary-cta {
width: fit-content;
border-radius: 0.5rem;
padding: 0.75rem 1rem;
background: #172033;
color: white;
text-decoration: none;
font-weight: 700;
}
.layout-grid {
display: grid;
gap: clamp(1rem, 3vw, 2rem);
margin-block-start: 2rem;
}
.cards {
container: cards / inline-size;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: 1rem;
}
.side-panel,
.card,
.comparison {
background: white;
border: 1px solid #dbe3ef;
border-radius: 0.75rem;
}
.card {
overflow: hidden;
}
.card-body,
.side-panel,
.comparison {
padding: 1rem;
}
@container cards (width >= 42rem) {
.card.featured {
grid-column: span 2;
display: grid;
grid-template-columns: minmax(14rem, 0.8fr) 1fr;
}
.card.featured img {
height: 100%;
object-fit: cover;
}
}
.comparison {
margin-block-start: 2rem;
overflow-x: auto;
}
.responsive-table {
width: 100%;
border-collapse: collapse;
}
.responsive-table th,
.responsive-table td {
padding: 0.875rem;
border-block-end: 1px solid #dbe3ef;
text-align: left;
}
@media (width < 48rem) {
.responsive-table thead {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.responsive-table,
.responsive-table tbody,
.responsive-table tr,
.responsive-table td {
display: block;
width: 100%;
}
.responsive-table tr {
border: 1px solid #dbe3ef;
border-radius: 0.5rem;
margin-block: 0.75rem;
overflow: hidden;
}
.responsive-table td {
display: grid;
grid-template-columns: minmax(7rem, 40%) 1fr;
gap: 1rem;
}
.responsive-table td::before {
content: attr(data-label);
font-weight: 700;
color: #526071;
}
}
@media (width >= 64rem) {
.hero {
grid-template-columns: minmax(0, 1.1fr) minmax(18rem, 0.9fr);
}
.layout-grid {
grid-template-columns: 16rem minmax(0, 1fr);
}
}
Kuncinya bukan warna, tetapi menghindari fixed width besar. width: min(100% - 2rem, 72rem) memberi margin di layar kecil dan membatasi lebar baca di layar besar. minmax(min(100%, 18rem), 1fr) mencegah minimum card merusak container sempit.
Pola untuk gambar, navigasi, card, dan tabel
Berikan aturan per komponen kepada Claude Code.
| Komponen | Kegagalan umum | Perbaikan yang diminta |
|---|---|---|
| Navigasi | Link desktop dipaksa satu baris di mobile | Izinkan wrap, jaga area sentuh dan aria-label |
| Card | width: 320px atau min-width besar membuat overflow | Pakai auto-fit, minmax(), dan container query |
| Gambar | Ponsel mendapat JPEG besar yang sama | Tambahkan srcset, sizes, width, height, loading, dan alt |
| Tabel | Kolom menjadi sulit dibaca | Pilih wrapper scroll atau row card dengan data-label |
| CTA | Link revenue tenggelam | Cek scroll mobile pertama dan bagian akhir artikel |
Tidak semua navigasi kecil butuh hamburger menu. Jika hanya tiga link, wrap bisa lebih jelas. Tabel juga tidak selalu harus menjadi card: pricing matrix mungkin perlu perbandingan horizontal, sedangkan daftar tiket lebih mudah dibaca sebagai row card.
Use case yang perlu diuji
Use case pertama adalah SaaS dashboard. Sidebar, filter, KPI card, chart, dan data table muncul bersama. Di mobile, KPI utama sebaiknya muncul lebih dulu, filter sekunder dilipat, dan baris detail menjadi card.
Use case kedua adalah blog atau situs media. Lebar teks, daftar isi, iklan, related post, dan CTA PDF gratis saling berebut ruang. Periksa panjang baris, gambar yang ringan, code block tanpa overflow, dan CTA yang tetap terlihat.
Use case ketiga adalah landing page ecommerce atau kursus. Product card, perbandingan harga, tombol beli, dan FAQ berpengaruh langsung ke revenue. Jika harga atau tombol beli turun terlalu jauh di mobile, konversi bisa turun.
Use case keempat adalah admin internal. Pengguna harian lebih membutuhkan pencarian, filter, keyboard operation, dan tabel yang jelas daripada tampilan dekoratif. Perbaikan responsive harus menjaga urutan kerja mereka.
Verifikasi dengan Playwright
Jangan berhenti karena halaman terlihat bagus di satu browser. Playwright bisa mengecek beberapa lebar, memastikan elemen penting terlihat, mendeteksi horizontal overflow, dan menyimpan screenshot.
import { expect, test } from "@playwright/test";
const baseUrl = process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000";
const viewports = [
{ name: "mobile-320", width: 320, height: 740 },
{ name: "mobile-390", width: 390, height: 844 },
{ name: "tablet-768", width: 768, height: 1024 },
{ name: "desktop-1440", width: 1440, height: 1000 },
];
for (const viewport of viewports) {
test(`responsive demo has no horizontal overflow at ${viewport.name}`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await page.goto(`${baseUrl}/responsive-demo`);
await expect(page.getByRole("navigation", { name: "Navigasi utama" })).toBeVisible();
await expect(page.getByRole("link", { name: "Lihat template prompt" })).toBeVisible();
const hasHorizontalOverflow = await page.evaluate(() => {
return document.documentElement.scrollWidth > document.documentElement.clientWidth;
});
expect(hasHorizontalOverflow).toBe(false);
await expect(page).toHaveScreenshot(`responsive-${viewport.name}.png`, {
fullPage: true,
maxDiffPixels: 300,
});
});
}
Run pertama membuat baseline screenshot. Update dengan npx playwright test --update-snapshots hanya jika perubahan visual memang disengaja. Screenshot bisa berbeda karena OS, font rendering, GPU, dan mode headless, jadi bandingkan di environment CI yang sama.
Pitfall umum
Pitfall terbesar adalah CSS desktop-first yang ditumpuk override mobile. Awalnya cepat, tetapi cascade menjadi sulit dibaca. Minta Claude Code memindahkan layout layar kecil ke base rules, lalu menambah layout lebar setelahnya.
Pitfall kedua adalah lupa <meta name="viewport">. Tanpa tag ini, browser mobile dapat merender dengan lebar virtual desktop.
Pitfall ketiga adalah fixed width tersembunyi di card, tabel, gambar, atau embed. min-width: 960px tidak terlihat bermasalah di desktop, tetapi merusak ponsel. Jika tabel perlu scroll, berikan overflow-x: auto pada wrapper tabel saja.
Pitfall keempat adalah menulis srcset tanpa sizes. Browser butuh kandidat gambar dan hint ukuran render. Review srcset, sizes, width, height, dan alt sebagai satu paket.
Pitfall kelima adalah percaya screenshot saja. Iklan, tanggal, animasi, dan widget eksternal membuat noise. Gabungkan screenshot dengan DOM assertion untuk overflow, CTA terlihat, dan navigation landmark.
Sertakan jalur revenue
Responsive design juga harus melindungi tujuan bisnis. Di ClaudeCodeLab, pembaca mobile perlu menemukan PDF gratis, produk Gumroad, dan konsultasi. Untuk workflow siap pakai, lihat Claude Code products. Untuk memperbaiki layar tim nyata dengan review rule dan bukti Playwright, lihat Claude Code training.
Masukkan conversion path ke brief Claude Code. Halaman yang terlihat bersih tetapi menyembunyikan tombol beli atau link konsultasi belum selesai.
Ringkasan
Workflow praktisnya adalah: tulis kontrak mobile-first, bangun fluid grid, pakai clamp() untuk tipografi dan spacing, gunakan container query untuk komponen reusable, tangani gambar dengan benar, sesuaikan navigasi dan tabel dengan tugas pembaca, lalu verifikasi dengan Playwright.
Saya menguji pola demo ini di 320px, 390px, 768px, dan 1440px. Navigasi wrap tanpa overflow, card menjadi satu kolom di ponsel, tabel terbaca sebagai row card, dan assertion overflow Playwright lulus. Pelajaran praktis Masa: Claude Code paling berguna saat diminta menjadi implementer sekaligus reviewer yang curiga pada fixed width, image hint, posisi CTA, perilaku tabel, dan perbedaan screenshot.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission safety ladder Claude Code: perluas akses tanpa kehilangan kontrol
Naik dari read-only ke edit terbatas, command bukti, dan cek deploy dengan kontrol yang jelas.
Claude Code Small PR Proof Pack: perubahan kecil yang mudah direview
Paket bukti untuk PR Claude Code: diff, check, URL publik, jalur CTA, dan rollback.
Review gate Claude Code sebelum commit: diff, test, URL publik, dan CTA
Cara memakai Claude Code sebelum commit: diff scope, build, URL publik, link Gumroad, CTA konsultasi, missing test, dan file tidak terkait.