local-link/skills/css-dev-skills/skills/css-responsive/SKILL.md
Modern responsive design using container queries, fluid typography, responsive images, logical properties, viewport units, intrinsic sizing, and preference queries. Prioritizes container queries over media queries. Use when the user asks for responsive CSS, responsive design, mobile layout, fluid typography, responsive images, dark mode, prefers-reduced-motion, or adaptive design.
npx skillsauth add lionad-morotar/local-tools css-responsiveInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a senior CSS engineer specializing in responsive design. Build responsive layouts and components using modern techniques. Always generate code. Prefer intrinsic sizing and container queries over breakpoint-driven media queries. For reference, see modern-patterns.md and browser-compat.md.
clamp(), intrinsic sizing, and flexible grids so most things just work without breakpoints.srcset, sizes, and <picture> for responsive images.Apply in this order — each layer reduces the need for the next:
1. Intrinsic sizing → content determines size naturally
2. Fluid values → clamp() adapts without breakpoints
3. Flexible layouts → Grid/Flexbox auto-fill, wrap, grow
4. Container queries → component adapts to its container
5. Media queries → page-level layout shifts only
.component-wrapper {
container-type: inline-size;
container-name: card;
}
.card-wrapper {
container-type: inline-size;
}
.card {
display: flex;
flex-direction: column;
gap: var(--space-s);
@container (inline-size >= 450px) {
flex-direction: row;
align-items: center;
}
@container (inline-size >= 700px) {
display: grid;
grid-template-columns: 250px 1fr auto;
}
}
| Unit | Meaning |
|------|---------|
| cqi | 1% of container inline size |
| cqb | 1% of container block size |
| cqmin | smaller of cqi and cqb |
| cqmax | larger of cqi and cqb |
.card-title {
font-size: clamp(1rem, 3cqi, 1.5rem);
}
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (inline-size > 300px) {
.widget { display: grid; grid-template-columns: 1fr 1fr; }
}
Use media queries only for page-level layout shifts.
@media (max-width: 48em) {
.page {
grid-template-columns: 1fr;
}
.sidebar { display: none; }
}
em units, never px — respects user font size.min-width (mobile-first) unless the desktop layout is simpler./* Compact */ @media (max-width: 48em) { }
/* Medium */ @media (min-width: 48em) and (max-width: 64em) { }
/* Expanded */ @media (min-width: 64em) { }
Use clamp() for font sizes that scale smoothly between a minimum and maximum.
:root {
--text-sm: clamp(0.8rem, 0.75rem + 0.25vw, 0.875rem);
--text-base: clamp(1rem, 0.925rem + 0.375vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.375rem);
--text-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.625rem);
--text-2xl: clamp(1.5rem, 1.25rem + 1.25vw, 2.25rem);
--text-3xl: clamp(2rem, 1.5rem + 2.5vw, 3.5rem);
}
clamp(minimum, preferred, maximum)
rem)rem + vw)rem):root {
--space-xs: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
--space-s: clamp(0.75rem, 0.6rem + 0.75vw, 1rem);
--space-m: clamp(1rem, 0.8rem + 1vw, 1.5rem);
--space-l: clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
--space-xl: clamp(2rem, 1.6rem + 2vw, 3rem);
--space-2xl: clamp(3rem, 2.4rem + 3vw, 4.5rem);
}
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="
(max-width: 48em) 100vw,
(max-width: 64em) 50vw,
33vw
"
alt="Description"
loading="lazy"
decoding="async"
>
<picture><picture>
<source
media="(min-width: 64em)"
srcset="hero-wide.avif"
type="image/avif"
>
<source
media="(min-width: 48em)"
srcset="hero-medium.avif"
type="image/avif"
>
<img
src="hero-narrow.jpg"
alt="Description"
loading="lazy"
decoding="async"
>
</picture>
img {
max-inline-size: 100%;
block-size: auto;
display: block;
}
.hero-image {
inline-size: 100%;
block-size: auto;
aspect-ratio: 16 / 9;
object-fit: cover;
}
| Unit | Meaning |
|------|---------|
| dvh | Dynamic viewport height (accounts for mobile browser chrome) |
| svh | Small viewport height (smallest possible viewport) |
| lvh | Large viewport height (largest possible viewport) |
| dvw | Dynamic viewport width |
.full-screen {
min-block-size: 100dvh;
}
.hero {
block-size: 100svh;
}
dvh for full-height layouts on mobile (accounts for URL bar).svh when you need the element to never exceed the visible area.100vh — it doesn't account for mobile browser UI..container {
inline-size: min(90%, 75rem);
margin-inline: auto;
}
.card-grid {
grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
.sidebar {
inline-size: clamp(200px, 25%, 350px);
}
.tag {
inline-size: fit-content;
padding-inline: var(--space-s);
}
.nav { inline-size: max-content; }
.label { inline-size: min-content; }
Use logical properties for all directional values. They support RTL/LTR automatically.
.element {
margin-block: var(--space-m);
padding-inline: var(--space-l);
border-inline-start: 3px solid var(--color-primary);
max-inline-size: 65ch;
text-align: start;
}
Full mapping reference in the css-layout skill.
:root {
color-scheme: light dark;
--color-surface: light-dark(white, oklch(15% 0 0));
--color-text: light-dark(oklch(20% 0 0), oklch(90% 0 0));
--color-border: light-dark(oklch(80% 0 0), oklch(30% 0 0));
}
light-dark() is preferred over @media (prefers-color-scheme) for individual values. Use the media query only for structural changes:
@media (prefers-color-scheme: dark) {
.logo { filter: brightness(1.2); }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
@media (prefers-contrast: more) {
:root {
--color-border: oklch(0% 0 0);
--color-text: oklch(0% 0 0);
}
.card {
border: 2px solid var(--color-border);
}
}
@media (forced-colors: active) {
.button {
border: 2px solid ButtonText;
}
.icon {
forced-color-adjust: none;
}
}
@media (prefers-reduced-transparency: reduce) {
.overlay {
background: var(--color-surface);
backdrop-filter: none;
}
}
Use clamp() for spacing that scales between viewports without breakpoints:
:root {
--space-xs: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
--space-s: clamp(0.75rem, 0.6rem + 0.75vw, 1rem);
--space-m: clamp(1rem, 0.8rem + 1vw, 1.5rem);
--space-l: clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
--space-xl: clamp(2rem, 1.6rem + 2vw, 3rem);
}
section { padding-block: var(--space-xl); }
.stack > * + * { margin-block-start: var(--space-m); }
em units (not px)clamp() with rem + vwleft/right/top/bottom)srcset/sizes or <picture> where appropriate100dvh used instead of 100vhprefers-reduced-motion handledprefers-color-scheme handled (or light-dark())prefers-contrast handled for key elementsmin()/max()/clamp()tools
理解用户意图;listen 模式通过 grill-me 深挖任务并归档经验
development
给 VSCode(Insiders/Stable) 内置 ripgrep 加 5s 超时包装,防止搜索卡死吃满 CPU。--on 包装(幂等) / --off 还原 / 不带参数查状态。Use when VSCode 搜索卡死、rg 进程占满 CPU,或 VSCode 更新后超时保护失效需要重包。
development
为指定项目创建完全隔离的 hapi(Claude Code On the Go)实例,包括独立数据目录、LaunchAgent 持久化、zsh wrapper 和 app.hapi.run 直连 URL。与全局 ~/.hapi 互不干扰。
development
关闭承载当前 Claude Code 会话的宿主(VSCode 窗口或其终端面板),连同 claude 一起退出。通过 close-host-window 扩展触发:先 SIGTERM claude(走完 SessionEnd hooks)再关 host,不抢焦点、不丢 hooks。--host vscode 关窗口、--host terminal(默认)只关终端面板。Use when 任务结束要随宿主退出,或需精确关闭某 claude 终端所在窗口/面板。