Claude Code로 CSS 미디어 쿼리 구현하기
Claude Code로 모바일 우선 CSS, 컨테이너 쿼리, prefers 설정, Playwright 검증까지 구현합니다.
노트북에서 멀쩡한 페이지도 실제 수익 지점에서는 쉽게 깨집니다. 모바일 글 화면에 가로 스크롤이 생기고, 사이드바의 상품 카드가 찌그러지며, 다크 모드에서 글자가 흐려지고, 관리자 화면의 애니메이션이 장시간 사용자에게 부담이 될 수 있습니다. Claude Code에 “반응형으로 만들어줘”라고만 말하면 이런 문제를 충분히 잡기 어렵습니다.
이 글은 미디어 쿼리를 실무 구현 절차로 정리합니다. 미디어 쿼리는 브라우저나 기기 조건에 따라 CSS를 적용하는 방식입니다. 컨테이너 쿼리는 화면 전체가 아니라 컴포넌트가 놓인 부모 컨테이너의 크기에 따라 CSS를 바꾸는 방식입니다. prefers-reduced-motion, prefers-color-scheme은 사용자의 OS 설정을 존중하기 위한 미디어 특성입니다.
공식 문서는 MDN의 CSS media queries, Using media queries, CSS container queries, CSSWG Media Queries Level 5, W3C CSS Containment Module Level 3, Playwright emulation을 기준으로 삼으세요.
함께 보면 좋은 글은 CSS Grid, Flexbox 패턴, 접근성 구현, 성능 최적화입니다.
기본 원칙
모바일 우선으로 시작하세요. 작은 화면을 기본 CSS로 만들고, @media (width >= 48rem)처럼 넓은 화면에서만 레이아웃을 확장합니다. iPhone용, iPad용처럼 기기 이름으로 분기하지 말고 콘텐츠가 답답해지는 지점에서 브레이크포인트를 잡습니다.
| 기준 | 미디어 쿼리 | 컨테이너 쿼리 |
|---|---|---|
| 보는 대상 | viewport, 인쇄, 사용자 설정 | 부모 컨테이너의 크기나 상태 |
| 적합한 곳 | 페이지 레이아웃, 내비게이션, 전체 여백 | 카드, CTA, 가격 박스, 재사용 UI |
| 흔한 실수 | 기기별 브레이크포인트 남발 | 부모에 container-type 누락 |
| Claude Code 지시 | “화면 폭에 따라 전체 레이아웃 변경” | “카드의 사용 가능 폭에 따라 내부 변경” |
실제 사용 사례
블로그 글에서는 모바일에서 본문, CTA, 관련 글을 세로로 쌓고, 충분히 넓을 때만 사이드바를 추가합니다. 광고나 제휴 카드 내부는 컨테이너 쿼리로 처리하면 본문, 사이드바, 관련 글 영역에 같은 컴포넌트를 넣어도 안정적입니다.
SaaS 가격 페이지에서는 가격 카드가 홈페이지, 캠페인 페이지, 결제 사이드바에 반복해서 등장합니다. viewport만 기준으로 삼으면 좁은 사이드바 안에서도 넓은 카드 레이아웃이 적용될 수 있습니다.
관리자 화면에서는 필터, 표, 내보내기 버튼, 검색창이 함께 놓입니다. 좁은 화면에서는 표를 카드형으로 바꾸고, 넓은 화면에서는 열을 유지합니다. prefers-reduced-motion도 장시간 작업자에게 중요합니다.
수익화 콘텐츠에서는 CTA가 보여야 하지만 본문을 압박하면 이탈이 늘어납니다. 페이지 전체 리듬은 미디어 쿼리로, CTA 내부는 컨테이너 쿼리로 다루는 편이 실무적으로 안전합니다.
바로 실행할 수 있는 HTML/CSS
아래 코드를 responsive-demo.html로 저장해 브라우저에서 열 수 있습니다. 모바일 우선, 컨테이너 쿼리, 다크 모드, 움직임 줄이기, clamp() 타이포그래피를 포함합니다. font-size: 4vw처럼 viewport 단독 값은 극단적인 화면에서 글자 크기를 망가뜨릴 수 있습니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Responsive media query demo</title>
<style>
:root { color-scheme: light dark; --bg: #f7f8fb; --surface: #ffffff; --text: #1f2937; --line: #d8dee8; --accent: #0f766e; --accent-strong: #115e59; --shadow: 0 12px 30px rgb(15 23 42 / 0.12); }
* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: clamp(1rem, 0.94rem + 0.25vw, 1.125rem); line-height: 1.7; color: var(--text); background: var(--bg); }
a { color: var(--accent-strong); }
.site-header { padding: 1rem; border-bottom: 1px solid var(--line); background: var(--surface); position: sticky; top: 0; z-index: 10; }
.nav { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: center; justify-content: space-between; max-width: 72rem; margin: 0 auto; }
.brand { font-weight: 800; }
.nav-links { display: flex; gap: 0.75rem; padding: 0; margin: 0; list-style: none; }
.page { width: min(100% - 2rem, 72rem); margin: 2rem auto; display: grid; gap: 1.5rem; }
.hero, .article, .sidebar-card, .offer { background: var(--surface); border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow); }
.hero { padding: clamp(1.25rem, 1rem + 1.5vw, 2.5rem); }
h1 { margin: 0 0 0.75rem; font-size: clamp(2rem, 1.65rem + 1.6vw, 3.2rem); line-height: 1.15; }
.layout { display: grid; gap: 1.5rem; }
.article, .sidebar-card { padding: 1rem; }
.sidebar { display: grid; gap: 1rem; align-content: start; }
.offer-wrap { container-type: inline-size; container-name: offer; }
.offer { display: grid; gap: 1rem; padding: 1rem; overflow: hidden; }
.offer-media { min-height: 10rem; border-radius: 6px; background: linear-gradient(135deg, rgb(15 118 110 / 0.85), rgb(37 99 235 / 0.75)), repeating-linear-gradient(45deg, rgb(255 255 255 / 0.18) 0 10px, transparent 10px 20px); }
.button { display: inline-flex; width: fit-content; min-height: 2.75rem; align-items: center; justify-content: center; padding: 0.7rem 1rem; border-radius: 6px; background: var(--accent); color: white; font-weight: 700; text-decoration: none; transition: transform 180ms ease, background-color 180ms ease; }
.button:hover { transform: translateY(-2px); background: var(--accent-strong); }
@media (width >= 48rem) { .article { padding: 1.5rem; } .layout { grid-template-columns: minmax(0, 1fr) 18rem; align-items: start; } }
@media (width >= 72rem) { .layout { grid-template-columns: minmax(0, 2fr) minmax(18rem, 0.8fr); } }
@container offer (width >= 34rem) { .offer { grid-template-columns: 14rem minmax(0, 1fr); align-items: center; padding: 1.25rem; } }
@media (prefers-color-scheme: dark) { :root { --bg: #10151f; --surface: #18202d; --text: #eef2f7; --line: #334155; --accent: #2dd4bf; --accent-strong: #5eead4; --shadow: none; } }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: 0.01ms !important; } }
</style>
</head>
<body>
<header class="site-header"><nav class="nav" aria-label="Main navigation"><div class="brand">ClaudeCodeLab</div><ul class="nav-links"><li><a href="#guide">Guide</a></li><li><a href="#offer">Template</a></li></ul></nav></header>
<main class="page">
<section class="hero"><h1>Media queries that survive real layouts</h1><p>Mobile-first CSS, container-aware cards, dark mode, and reduced motion.</p></section>
<div class="layout" id="guide">
<article class="article"><h2>Readable article body</h2><p>The article column stays readable on phones and gains a sidebar only when there is enough room.</p><div class="offer-wrap" id="offer"><section class="offer"><div class="offer-media" aria-hidden="true"></div><div><h2>Responsive review checklist</h2><p>Use this area for a download, newsletter, or product CTA.</p><a class="button" href="#">Get the checklist</a></div></section></div></article>
<aside class="sidebar" aria-label="Related content"><section class="sidebar-card"><h2>Related</h2><p>Grid, Flexbox, accessibility, and performance belong in the same review.</p></section></aside>
</div>
</main>
</body>
</html>
Claude Code 프롬프트
대상: 글 상세 페이지의 반응형 CSS
규칙:
- 기본 CSS는 모바일 우선
- 페이지 전체 레이아웃은 viewport media queries 사용
- 재사용 카드와 CTA는 container queries 사용
- font-size는 vw 단독 지정 금지, clamp() 사용
- prefers-color-scheme과 prefers-reduced-motion 존중
검증:
- 375, 768, 1024, 1440px 확인
- Playwright에서 dark mode와 reduced motion 확인
- 중복되거나 불필요한 브레이크포인트 보고
이 diff를 반응형 CSS 관점에서 리뷰해 주세요.
가로 스크롤, 읽기 어려운 글자 크기, 찌그러진 CTA/광고, container-type 누락,
reduced-motion 위반, 과도한 브레이크포인트를 우선해서 구체적 줄과 최소 수정안만 주세요.
Playwright 검증
import { test, expect } from "@playwright/test";
const fileUrl = "file:///absolute/path/to/responsive-demo.html";
for (const width of [375, 768, 1024, 1440]) {
test(`no horizontal overflow at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 900 });
await page.goto(fileUrl);
const hasOverflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth);
await expect(hasOverflow).toBe(false);
await expect(page.locator(".offer")).toBeVisible();
});
}
test("dark mode keeps text readable", async ({ page }) => {
await page.emulateMedia({ colorScheme: "dark" });
await page.goto(fileUrl);
await expect(page.locator("body")).toHaveCSS("color", "rgb(238, 242, 247)");
});
test("reduced motion disables hover timing", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await page.goto(fileUrl);
const duration = await page.locator(".button").evaluate((el) => getComputedStyle(el).transitionDuration);
expect(duration).toBe("0.01ms");
});
flowchart TD
A["모바일 우선 기본 CSS"] --> B["페이지 레이아웃용 미디어 쿼리"]
B --> C["재사용 카드용 컨테이너 쿼리"]
C --> D["사용자 선호 설정"]
D --> E["Playwright 스크린샷과 오버플로 검사"]
E --> F["Claude Code 리뷰 프롬프트"]
실수와 수익화 CTA
max-width 덮어쓰기를 계속 추가하거나, 여러 위치에서 쓰는 카드를 viewport 기준으로만 만들거나, vw만으로 글자 크기를 정하면 유지보수가 어려워집니다. reduced motion을 장식으로 보지 말고 실제 사용자 설정으로 다루세요. Claude Code가 CSS를 만든 뒤에는 실제 번역 문장, 광고 영역, 다크 모드, 좁은 화면까지 확인해야 합니다.
반응형 CSS는 수익에도 영향을 줍니다. 무료 체크리스트 CTA는 글 중간에 자연스럽게, 템플릿이나 상담 CTA는 글 끝에서 더 명확하게 배치할 수 있습니다. 무료 자료, 제품, Claude Code 트레이닝으로 이어지는 흐름도 레이아웃이 안정적이어야 측정할 수 있습니다.
Masa가 샘플 레이아웃에서 확인했을 때 가장 큰 개선은 CTA 카드를 컨테이너 쿼리로 분리한 점이었습니다. 같은 카드를 본문, 사이드바, 관련 글 영역에 놓아도 추가 viewport 브레이크포인트 없이 맞았고, Playwright에서 375px 가로 스크롤 없음, 다크 모드 글자색, reduced motion 적용을 확인했습니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Claude Code 권한 세이프티 래더: 통제력을 잃지 않고 allow 넓히기
read-only에서 제한 편집, 검증 명령, deploy 확인까지 권한을 단계적으로 넓히는 방법.
Claude Code Small PR Proof Pack: 작은 PR을 리뷰 가능한 상태로 만드는 증거 세트
Claude Code의 작은 PR에 diff, 검증, 공개 URL, CTA 경로, rollback을 붙이는 실무 체크리스트.
Claude Code 커밋 전 리뷰 게이트: diff, 테스트, 공개 URL, CTA 확인
Claude Code 작업을 커밋하기 전에 diff 범위, build, 공개 URL, Gumroad 링크, 상담 CTA, 테스트 누락과 무관한 파일을 확인하는 방법입니다.