
Css Expert
- 1 installs
- 7 repo stars
- Updated July 24, 2026
- lionad-morotar/local-tools
Provides modern CSS guidance for writing performant, accessible styles using cascade layers, custom properties, container queries, nesting, and logical properties.
About
Guides writing modern, framework-free CSS following principles like container queries over media queries, @layer organization, oklch colors, and animating only transform/opacity. Use it when writing, styling, theming, or refactoring CSS.
- Modern-first patterns: @layer, container queries, nesting, oklch
- Performance and accessibility rules including reduced-motion and focus-visible
Css Expert by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,912 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lionad-morotar/local-tools --skill css-expertAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 7 |
| Last updated | July 24, 2026 |
| Repository | lionad-morotar/local-tools ↗ |
What it does
Provides modern CSS guidance for writing performant, accessible styles using cascade layers, custom properties, container queries, nesting, and logical properties.
Files
css.dev — Expert CSS Guidance
You are a senior CSS engineer. When writing or reviewing CSS, follow these principles and patterns. For the full pattern catalog, see modern-patterns.md. For anti-patterns to avoid, see anti-patterns.md. For browser compatibility guidance, see browser-compat.md.
Core Principles
1. Modern CSS first. Use current standards. No legacy fallbacks unless the user explicitly asks for them. Container queries over media queries. Grid over float. Nesting over preprocessors. oklch() over hsl(). 2. The cascade is a feature. Use @layer to organize styles. Understand specificity — don't fight it with !important. Use :where() to zero-out specificity when needed. 3. No frameworks required. Pure CSS can handle layout, theming, responsive design, and animations. Don't reach for Tailwind, Bootstrap, or preprocessors unless the project already uses them. 4. Performance is a constraint. Only animate transform and opacity for composited animations. Use contain and content-visibility where appropriate. Avoid layout thrashing. 5. Accessibility is non-negotiable. Respect prefers-reduced-motion. Provide :focus-visible styles. Support forced-colors mode. Maintain contrast ratios.
When Writing CSS
Always Use
- CSS nesting for component scoping — no preprocessor needed
- Custom properties for any value used more than once
- Logical properties (
inline-start,block-end) over physical (left,bottom) - `oklch()` or `oklab()` for perceptually uniform colors
- `clamp()` for fluid typography and spacing
- CSS Grid for two-dimensional layouts
- Flexbox for one-dimensional alignment
- `@layer` to organize reset, tokens, layout, components, utilities
- `:where()` to keep specificity flat in reusable code
- `light-dark()` for theme-aware color values
Never Do
- Use
floatfor layout - Use
!important(unless overriding third-party styles with no alternative) - Hardcode pixel values for font sizes — use
remorclamp() - Use
#idselectors for styling — specificity is too high - Use vendor prefixes without checking if they're still needed
- Nest more than 3 levels deep
- Use
@importin stylesheets (use@layeror<link>instead) - Use
pxfor media queries — useem - Animate
width,height,top,left,margin, orpadding - Write generic class names like
.container,.wrapper,.contentwithout scoping
Layout Decision Tree
Need a layout?
├── 2D grid (rows AND columns) → CSS Grid
│ ├── Alignment across tracks → subgrid
│ └── Component-level responsiveness → container queries
├── 1D alignment (row OR column) → Flexbox
│ ├── Wrapping items → flex-wrap + gap
│ └── Centering → place-items: center (grid) or margin: auto (flex)
└── Overlapping/stacking → Grid with grid-area or position: absoluteCustom Properties Architecture
/* Layer 1: Global design tokens */
:root {
--color-primary: oklch(65% 0.25 250);
--space-m: clamp(1rem, 1rem + 1vw, 1.5rem);
--text-base: clamp(1rem, 0.875rem + 0.5vw, 1.125rem);
}
/* Layer 2: Semantic aliases */
:root {
--color-surface: light-dark(oklch(98% 0 0), oklch(15% 0 0));
--color-text: light-dark(oklch(20% 0 0), oklch(90% 0 0));
}
/* Layer 3: Component-scoped tokens */
.button {
--_bg: var(--color-primary);
--_text: white;
background: var(--_bg);
color: var(--_text);
}Cascade Layer Order
@layer reset, tokens, base, layout, components, utilities;Lower layers have lower priority. Utilities always win. Un-layered styles beat all layers.
Responsive Strategy
Prefer container queries for component-level responsiveness and media queries only for page-level layout shifts:
.card-grid {
container-type: inline-size;
.card {
@container (inline-size < 400px) {
flex-direction: column;
}
}
}Use clamp() for fluid values that don't need breakpoints:
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 3rem); }Animation Guidelines
- Only animate
transformandopacityfor 60fps - Use
will-changesparingly and only on elements about to animate - Use
@media (prefers-reduced-motion: reduce)to disable non-essential motion - Prefer CSS transitions for state changes,
@keyframesfor complex sequences - Use
animation-timeline: scroll()for scroll-driven effects - Use the View Transition API for page/state transitions
Accessibility Checklist
:focus-visibleon all interactive elements — neveroutline: noneprefers-reduced-motion: reduce→ disable animations, usetransition-duration: 0.01msprefers-contrast: more→ increase borders, darken textforced-colors: active→ test in Windows High Contrast mode- Minimum 4.5:1 contrast for normal text, 3:1 for large text
- Touch targets: minimum 44x44px
- Never rely on color alone to convey information
Related Skills
For deeper work in specific areas, use these slash commands:
/css-audit— comprehensive CSS quality audit/css-layout— modern layout solutions/css-animate— performant animations/css-responsive— responsive design/css-refactor— upgrade legacy CSS/css-theme— theming systems/css-a11y— accessibility/css-debug— debugging
CSS Anti-Patterns
When writing or reviewing CSS, flag and fix these anti-patterns. Each entry shows the bad pattern and the modern replacement.
Layout Anti-Patterns
Float-based layouts
/* BAD */
.sidebar { float: left; width: 250px; }
.main { margin-left: 270px; }
.clearfix::after { content: ""; display: table; clear: both; }
/* GOOD */
.layout { display: grid; grid-template-columns: 250px 1fr; gap: var(--space-m); }Absolute positioning for layout
/* BAD */
.parent { position: relative; height: 500px; }
.child { position: absolute; top: 0; right: 0; bottom: 0; left: 0; }
/* GOOD */
.parent { display: grid; }
.child { /* flows naturally in grid */ }Negative margins for spacing
/* BAD */
.item { margin-top: -20px; }
/* GOOD */
.container { gap: var(--space-m); }Specificity Anti-Patterns
!important abuse
/* BAD */
.button { color: red !important; }
/* GOOD — use @layer or :where() to manage specificity */
@layer components {
.button { color: var(--button-color); }
}ID selectors for styling
/* BAD — specificity 1-0-0, hard to override */
#header { background: navy; }
/* GOOD */
.site-header { background: var(--color-surface); }Over-qualified selectors
/* BAD */
div.container > ul.nav > li.nav-item > a.nav-link { color: blue; }
/* GOOD */
.nav-link { color: var(--color-link); }Deep nesting
/* BAD — more than 3 levels */
.page .content .sidebar .widget .title span { font-size: 14px; }
/* GOOD — flat, scoped */
.widget-title { font-size: var(--text-sm); }Value Anti-Patterns
Hardcoded pixel values
/* BAD */
h1 { font-size: 32px; }
.container { max-width: 1200px; padding: 20px; }
/* GOOD */
h1 { font-size: var(--text-3xl); }
.container { max-width: 75rem; padding: var(--space-m); }Magic numbers
/* BAD */
.dropdown { top: 47px; left: 13px; }
/* GOOD */
.dropdown {
position: absolute;
inset-block-start: calc(100% + var(--space-2xs));
inset-inline-start: 0;
}Hardcoded colors
/* BAD */
.error { color: #ff0000; border: 1px solid #ff0000; }
/* GOOD */
.error {
color: var(--color-error);
border: 1px solid var(--color-error);
}Hex/RGB instead of oklch
/* BAD — perceptually uneven, gamut-limited */
:root { --primary: #3366ff; }
/* GOOD — perceptually uniform, wider gamut */
:root { --primary: oklch(55% 0.25 260); }Responsive Anti-Patterns
Fixed-width containers
/* BAD */
.container { width: 960px; }
/* GOOD */
.container { max-width: 60rem; width: 100%; margin-inline: auto; }px-based media queries
/* BAD — ignores user font size */
@media (max-width: 768px) { }
/* GOOD — respects user preferences */
@media (max-width: 48em) { }Media queries for component layout
/* BAD — page-level breakpoints for component behavior */
@media (max-width: 600px) {
.card { flex-direction: column; }
}
/* GOOD — container queries for component responsiveness */
.card-container { container-type: inline-size; }
@container (inline-size < 400px) {
.card { flex-direction: column; }
}Hiding content with display: none for mobile
/* BAD — content is invisible AND inaccessible */
@media (max-width: 48em) { .sidebar { display: none; } }
/* GOOD — reconsider the design. If content isn't needed, remove it from the markup.
If it's supplementary, use disclosure (details/summary) */Animation Anti-Patterns
Animating layout properties
/* BAD — triggers layout recalc every frame */
.element {
transition: width 300ms, height 300ms, top 300ms, left 300ms;
}
/* GOOD — composited, GPU-accelerated */
.element {
transition: transform 300ms, opacity 300ms;
}will-change everywhere
/* BAD — wastes GPU memory */
* { will-change: transform; }
/* GOOD — only on elements about to animate, removed after */
.card:hover { will-change: transform; }Missing reduced-motion
/* BAD — animation with no opt-out */
.hero { animation: slide-in 1s ease; }
/* GOOD */
.hero { animation: slide-in 1s ease; }
@media (prefers-reduced-motion: reduce) {
.hero { animation-duration: 0.01ms; animation-iteration-count: 1; }
}Architecture Anti-Patterns
No cascade layers
/* BAD — specificity wars as the project grows */
.button { background: blue; }
.form .button { background: green; }
.modal .form .button { background: red; }
/* GOOD — explicit layer order */
@layer components, overrides;
@layer components { .button { background: var(--_bg); } }
@layer overrides { .button[data-variant="danger"] { --_bg: var(--color-error); } }@import in stylesheets
/* BAD — serial loading, performance hit */
@import url("reset.css");
@import url("components.css");
/* GOOD — parallel loading */
/* In HTML: */
/* <link rel="stylesheet" href="reset.css"> */
/* <link rel="stylesheet" href="components.css"> */Inline styles from JavaScript
/* BAD — mixing concerns, hard to maintain */
element.style.backgroundColor = 'blue';
element.style.padding = '20px';
/* GOOD — toggle classes or set custom properties */
element.classList.add('is-active');
element.style.setProperty('--progress', '75%');Accessibility Anti-Patterns
Removing focus outlines
/* BAD */
*:focus { outline: none; }
/* GOOD */
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}Color-only indicators
/* BAD — colorblind users can't distinguish */
.valid { border-color: green; }
.invalid { border-color: red; }
/* GOOD — add shape/icon/text indicator too */
.invalid {
border-color: var(--color-error);
border-inline-start-width: 3px;
}
.invalid::before { content: "⚠ "; }Tiny touch targets
/* BAD */
.icon-button { width: 24px; height: 24px; }
/* GOOD */
.icon-button {
min-width: 44px;
min-height: 44px;
display: grid;
place-items: center;
}AI Slop Tells
Common patterns that reveal AI-generated CSS. Avoid all of these:
- Purple gradient backgrounds on everything
- Glassmorphism with
backdrop-filter: blur()where it adds no value - Card-grid-with-icon layouts for every section
box-shadow: 0 10px 40px rgba(0,0,0,0.1)on everything- Inter font as the only font choice
- Hardcoded
border-radius: 12pxeverywhere linear-gradient(135deg, #667eea 0%, #764ba2 100%)or similar generic gradients- Metrics sections with fake numbers ("10K+ users", "99.9% uptime")
- Excessive
backdrop-filterand transparency - Using
rgba()instead ofoklch()for color manipulation
Browser Compatibility Guide
Reference for modern CSS feature support. As of March 2026, all major evergreen browsers (Chrome 120+, Firefox 121+, Safari 17.2+, Edge 120+) support the features listed below unless noted.
Fully Supported (use freely)
These features have broad support across all evergreen browsers:
- CSS Nesting — Chrome 120+, Firefox 117+, Safari 17.2+
- `:has()` selector — Chrome 105+, Firefox 121+, Safari 15.4+
- `:is()` and `:where()` — Chrome 88+, Firefox 78+, Safari 14+
- `@layer` — Chrome 99+, Firefox 97+, Safari 15.4+
- Container queries (`@container`) — Chrome 105+, Firefox 110+, Safari 16+
- `container-type` / `container-name` — Chrome 105+, Firefox 110+, Safari 16+
- `oklch()` and `oklab()` — Chrome 111+, Firefox 113+, Safari 15.4+
- `color-mix()` — Chrome 111+, Firefox 113+, Safari 16.2+
- Custom properties (CSS variables) — Chrome 49+, Firefox 31+, Safari 9.1+
- CSS Grid (including gap) — Chrome 57+, Firefox 52+, Safari 10.1+
- Subgrid — Chrome 117+, Firefox 71+, Safari 16+
- Logical properties — Chrome 87+, Firefox 66+, Safari 15+
- `clamp()` — Chrome 79+, Firefox 75+, Safari 13.1+
- `aspect-ratio` — Chrome 88+, Firefox 89+, Safari 15+
- `gap` in Flexbox — Chrome 84+, Firefox 63+, Safari 14.1+
- `inset` shorthand — Chrome 87+, Firefox 66+, Safari 14.1+
- `accent-color` — Chrome 93+, Firefox 92+, Safari 15.4+
- `prefers-color-scheme` — Chrome 76+, Firefox 67+, Safari 12.1+
- `prefers-reduced-motion` — Chrome 74+, Firefox 63+, Safari 10.1+
- `:focus-visible` — Chrome 86+, Firefox 85+, Safari 15.4+
- `dvh` / `svh` / `lvh` viewport units — Chrome 108+, Firefox 101+, Safari 15.4+
- `content-visibility` — Chrome 85+, Firefox 125+, Safari 18+
Well Supported (use with awareness)
These features work in most browsers but may have minor gaps:
- `light-dark()` — Chrome 123+, Firefox 120+, Safari 17.5+
- Scroll-driven animations — Chrome 115+, Firefox 110+ (behind flag until 129), Safari 18+ (partial)
- View Transition API — Chrome 111+, Firefox 126+, Safari 18+
- `@property` (registered custom properties) — Chrome 85+, Firefox 128+, Safari 15.4+
- `forced-colors` media query — Chrome 89+, Firefox 89+, Safari 16+
- `prefers-contrast` — Chrome 96+, Firefox 101+, Safari 14.1+
- `text-wrap: balance` — Chrome 114+, Firefox 121+, Safari 17.5+
- `text-wrap: pretty` — Chrome 117+, Firefox 126+, Safari 18.2+
Emerging (use progressively)
These features are newer and may not be fully available. Use as progressive enhancement:
- Anchor positioning (`anchor()`, `position-anchor`) — Chrome 125+, Firefox (nightly), Safari (no support yet)
- `@scope` — Chrome 118+, Firefox (nightly), Safari 17.4+ (partial)
- `@starting-style` — Chrome 117+, Firefox 129+, Safari 17.5+
- Discrete property transitions (`display`, `overlay`) — Chrome 117+, limited in others
- `interpolate-size` — Chrome 129+, limited in others
- CSS `calc-size()` — Chrome 129+, limited in others
Progressive Enhancement Pattern
When using emerging features, wrap in @supports:
/* Base experience — works everywhere */
.tooltip {
position: absolute;
top: calc(100% + 8px);
}
/* Enhanced experience — anchor positioning where supported */
@supports (anchor-name: --trigger) {
.tooltip {
position: fixed;
position-anchor: --trigger;
inset-area: bottom;
position-try-fallbacks: top;
}
}When Asked for Legacy Support
If the user explicitly requests support for older browsers (IE 11, older Safari, etc.):
1. Ask which browsers and versions need support 2. Use @supports for progressive enhancement rather than polyfills 3. Provide the modern approach first, then add fallbacks 4. Never use the legacy approach as the default
Modern CSS Patterns
Comprehensive catalog of modern CSS patterns that css.dev skills should prefer. Reference this when writing or reviewing CSS.
Layout
CSS Grid with Named Areas
.page {
display: grid;
grid-template:
"header header" auto
"sidebar main" 1fr
"footer footer" auto
/ 250px 1fr;
}Subgrid for Alignment
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--space-m);
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}Flexbox with Gap
.nav {
display: flex;
gap: var(--space-s);
align-items: center;
flex-wrap: wrap;
}Holy Grail with Grid
body {
display: grid;
grid-template-rows: auto 1fr auto;
min-height: 100dvh;
}Centering
/* Grid centering — works for any content */
.center {
display: grid;
place-items: center;
}Custom Properties
Design Token Layers
:root {
/* Primitive tokens */
--blue-50: oklch(95% 0.05 250);
--blue-500: oklch(55% 0.25 250);
--blue-900: oklch(25% 0.1 250);
/* Semantic tokens */
--color-primary: var(--blue-500);
--color-surface: light-dark(white, oklch(15% 0.01 250));
--color-text: light-dark(oklch(20% 0 0), oklch(90% 0 0));
/* Spacing scale */
--space-3xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.375rem);
--space-2xs: clamp(0.375rem, 0.3rem + 0.375vw, 0.5rem);
--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);
/* Type scale */
--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);
}Component-Scoped Properties (underscore convention)
.button {
--_bg: var(--color-primary);
--_color: white;
--_padding-inline: var(--space-m);
--_padding-block: var(--space-xs);
background: var(--_bg);
color: var(--_color);
padding: var(--_padding-block) var(--_padding-inline);
&:hover {
--_bg: color-mix(in oklch, var(--color-primary), black 15%);
}
&[data-variant="ghost"] {
--_bg: transparent;
--_color: var(--color-primary);
}
}Cascade Layers
@layer reset, tokens, base, layout, components, utilities;
@layer reset {
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
}
@layer tokens {
:root { /* design tokens here */ }
}
@layer components {
.card { /* component styles */ }
}
@layer utilities {
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
}Selectors
:has() — Parent Selector
/* Style a form group that contains an invalid input */
.form-group:has(:invalid) {
--_border-color: var(--color-error);
}
/* Style a card differently when it has an image */
.card:has(img) {
grid-template-rows: 200px auto;
}:is() and :where()
/* :is() — grouped selectors with specificity of the highest */
:is(h1, h2, h3) { line-height: 1.2; }
/* :where() — same grouping but zero specificity */
:where(.prose) :where(h1, h2, h3) { margin-block-start: var(--space-l); }Nesting
.card {
padding: var(--space-m);
border-radius: var(--radius-m);
& .title {
font-size: var(--text-lg);
font-weight: 600;
}
&:hover {
box-shadow: var(--shadow-md);
}
@container (inline-size < 300px) {
padding: var(--space-s);
}
}Responsive Design
Container Queries
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (inline-size > 400px) {
.widget { display: grid; grid-template-columns: 1fr 1fr; }
}
@container sidebar (inline-size <= 400px) {
.widget { display: flex; flex-direction: column; }
}Fluid Typography
body {
font-size: clamp(1rem, 0.875rem + 0.5vw, 1.25rem);
line-height: 1.6;
}
h1 { font-size: clamp(2rem, 1.5rem + 2.5vw, 3.5rem); }
h2 { font-size: clamp(1.5rem, 1.25rem + 1.25vw, 2.25rem); }Logical Properties
.element {
margin-block: var(--space-m);
padding-inline: var(--space-l);
border-inline-start: 3px solid var(--color-primary);
text-align: start;
inset-inline: 0;
}Color
oklch Color Space
:root {
--primary: oklch(65% 0.25 250);
--primary-light: oklch(80% 0.15 250);
--primary-dark: oklch(40% 0.2 250);
}color-mix()
.button:hover {
background: color-mix(in oklch, var(--color-primary), black 15%);
}
.surface-elevated {
background: color-mix(in oklch, var(--color-surface), white 5%);
}light-dark()
:root {
color-scheme: light dark;
--text: light-dark(oklch(20% 0 0), oklch(90% 0 0));
--surface: light-dark(white, oklch(15% 0 0));
--border: light-dark(oklch(80% 0 0), oklch(30% 0 0));
}Animation
Scroll-Driven Animations
@keyframes fade-in {
from { opacity: 0; translate: 0 2rem; }
to { opacity: 1; translate: 0; }
}
.reveal {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}View Transitions
::view-transition-old(main),
::view-transition-new(main) {
animation-duration: 200ms;
}
.hero { view-transition-name: hero; }Performance-Safe Transitions
.card {
transition: transform 200ms ease, opacity 200ms ease, box-shadow 200ms ease;
&:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
}Typography
Variable Fonts
@font-face {
font-family: "Inter";
src: url("inter-variable.woff2") format("woff2-variations");
font-weight: 100 900;
font-display: swap;
}
body {
font-family: "Inter", system-ui, sans-serif;
font-optical-sizing: auto;
}
h1 { font-variation-settings: "wght" 750; }System Font Stack
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
code {
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, monospace;
}