# Modern CSS HTML

> Modern CSS and HTML standards reference. Use this skill whenever writing CSS or HTML in any context — components, pages, layouts, animations, forms, dialogs, colours, typography, selectors, or responsive design. Trigger on any frontend code generation, code review, or refactoring that involves CSS or HTML. Also trigger when the user asks about browser support, modern alternatives to old CSS hacks, or how to replace JavaScript with native CSS/HTML. This skill ensures output uses the latest standards supported by WebKit (Safari) and Chrome/Edge (Blink), avoiding outdated patterns.

- Skill: `kemiljk/modern-css-html-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add kemiljk/modern-css-html-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kemiljk/modern-css-html-2/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: kemiljk (https://skillmd.com/u/kemiljk)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/kemiljk/modern-css-html-2

---


# Modern CSS & HTML Standards

Write CSS and HTML using current standards. Every pattern here has shipped in WebKit and Chrome
unless explicitly noted otherwise. Avoid legacy hacks when a native solution exists.

## How to use this skill

1. Read this file first for the quick-reference rules and snippets.
2. For deeper detail on a specific area, read the relevant file in `references/`:
   - `references/snippets.md` — 82 old-vs-modern comparisons with browser support percentages
   - `references/properties.md` — modern CSS properties reference
   - `references/selectors.md` — modern selectors and pseudo-classes/elements
   - `references/at-rules.md` — modern at-rules (@layer, @scope, @property, @container, etc.)
   - `references/functions.md` — modern CSS functions (color-mix, clamp, anchor, etc.)
   - `references/css-2026.md` — features shipping in 2026
   - `references/css-2025.md` — features that shipped in 2025

## Browser support policy

Only use features supported in **both WebKit (Safari) and Chrome/Edge (Blink)**. This means:

- **Safe to use** (Widely Available, 90%+): ship without fallback
- **Use with progressive enhancement** (Newly Available, 80–90%): ship with a sensible fallback via `@supports`
- **Do not use by default** (Limited, <80% or single-engine only): mention as an option but provide a fallback

When a feature is Chrome-only (e.g. some Interop 2026 items), note it explicitly and provide a cross-browser alternative.

## Core rules

### Layout

- Centre elements with `display: grid; place-items: center` — not `position: absolute` + `transform: translate(-50%, -50%)`
- Use `gap` for spacing in flex and grid — not margin hacks on children
- Use `aspect-ratio: 16 / 9` — not the padding-top percentage hack
- Use `inset: 0` — not `top: 0; right: 0; bottom: 0; left: 0`
- Use `100dvh` for full-viewport height on mobile — not `100vh`
- Use `object-fit: cover` for responsive images — not `background-image` + `background-size: cover`
- Use `position: sticky` — not JavaScript scroll listeners for sticky headers
- Use `scroll-snap-type` and `scroll-snap-align` — not carousel JS libraries
- Use `overscroll-behavior: contain` — not JS wheel event prevention
- Use `scrollbar-gutter: stable` — not `overflow-y: scroll` or hardcoded padding
- Use `width: stretch` — not `calc(100% - margins)` or `-webkit-fill-available`
- Use logical properties (`margin-inline-start`, `padding-block-end`, `border-inline`) — not physical `left`/`right` for direction-aware layouts
- Use `grid-template-areas` for named layout regions — not line number hacks
- Use `subgrid` to align nested grid children to parent tracks — not duplicated column definitions
- Use `field-sizing: content` for auto-growing textareas — not JS resize listeners
- Use anchor positioning (`anchor-name`, `position-anchor`, `position-area`) for tooltips — not Floating UI/Popper.js (progressive enhancement, check support)

### Container queries

- Use `@container (width < 400px)` for component-level responsiveness — not viewport media queries for component layout
- Use `container-type: inline-size` on the parent

### Media queries

- Use range syntax: `@media (600px <= width <= 1200px)` — not `(min-width: 600px) and (max-width: 1200px)`

### Typography

- Use `text-wrap: balance` on headings — not manual `<br>` or JS
- Use `clamp()` for fluid type: `font-size: clamp(1rem, 2.5vw, 2rem)` — not multiple media query breakpoints
- Use `font-display: swap` in `@font-face` — not invisible text during load
- Use variable fonts with `font-weight: 100 900` — not multiple `@font-face` declarations per weight
- Use `initial-letter: 3` for drop caps — not `float: left` hacks
- Use `-webkit-line-clamp` / `line-clamp` for multiline truncation — not JS text slicing
- Use `text-box: trim-both cap alphabetic` for optical vertical centering — not padding tweaks (newly available)

### Colour

- Use `oklch()` for perceptually uniform colour palettes — not hand-picked hex values
- Use `color-mix(in oklch, ...)` — not Sass `mix()` or `lighten()`/`darken()`
- Use relative colour syntax `oklch(from var(--base) calc(l + 0.2) c h)` for tints/shades — not hardcoded variants
- Use `light-dark()` with `color-scheme: light dark` — not duplicated `@media (prefers-color-scheme)` blocks
- Use `accent-color` for form control theming — not `appearance: none` + full rebuild
- Use `backdrop-filter: blur()` for frosted glass — not pseudo-element image blur hacks

### Animation & transitions

- Use individual transform properties (`translate`, `rotate`, `scale`) — not the combined `transform` shorthand when animating independently
- Use `@starting-style` for entry animations — not `requestAnimationFrame` timing hacks
- Use `transition-behavior: allow-discrete` to animate `display: none` — not `visibility` + `opacity` + `pointer-events` workarounds
- Use `animation-timeline: view()` for scroll-driven animations — not IntersectionObserver JS (progressive enhancement)
- Use `linear()` easing for custom bounce/spring curves — not JS animation libraries for simple easing
- Use View Transitions API (`document.startViewTransition()`) for page transitions — not Barba.js
- Use `interpolate-size: allow-keywords` + `height: auto` transitions — not JS `scrollHeight` measurement
- Use `offset-path` for motion path animation — not GSAP motionPath plugin

### Selectors

- Use `:has()` for parent selection — not JavaScript class toggling
- Use `:is()` and `:where()` for grouping — not long repetitive selector lists
- Use `:focus-visible` — not `:focus` (which triggers on mouse click too)
- Use `:user-invalid` / `:user-valid` for form validation — not JS blur + class toggling
- Use `@layer` for specificity control — not `!important` chains
- Use `@scope` for component-scoped styles — not BEM naming or CSS Modules (progressive enhancement)

### CSS architecture

- Use native CSS nesting (`& a { }`) — not Sass/Less nesting
- Use CSS custom properties (`var(--primary)`) — not Sass variables
- Use `@property` for typed custom properties with animation — not untyped `--var` strings
- Use `@supports` for feature detection — not Modernizr or JS checks
- Use `color-scheme: light dark` for automatic dark mode defaults — not manual input/select restyling
- Use `content-visibility: auto` for lazy rendering of off-screen content — not IntersectionObserver

### HTML

- Use `<dialog>` for modals — not `div.modal` + JS focus trap
- Use `closedby="any"` on `<dialog>` for light dismiss — not click-outside JS listeners
- Use `<details>` / `<summary>` for accordions — not JS toggle handlers
- Use `<details name="group">` for exclusive accordions — not JS close-all-others logic
- Use `popover` attribute for dropdowns/tooltips — not JS show/hide + z-index management
- Use `commandfor` / `command` attributes for declarative button actions — not `onclick` handlers
- Use `<input list="..." />` + `<datalist>` for autocomplete — not JS typeahead libraries
- Use `<output for="...">` for live form output — not JS DOM writes
- Use `loading="lazy"` on images — not IntersectionObserver lazy loading
- Use `inputmode="numeric"` and `enterkeyhint` — not JS mobile keyboard detection
- Use `appearance: base-select` for customisable `<select>` — not Select2/Choices.js (newly available)
- Use `interestfor` + `popover=hint` for hover tooltips — not JS mouseenter/mouseleave

### CSS 2026 features (use with caution — check cross-browser support)

- `@mixin` / `@apply` — native CSS mixins (Chrome 146 expected, not yet in Safari)
- Cross-document view transitions — MPA page transitions via `@view-transition { navigation: auto }`
- Gap decorations — `column-rule` / `row-rule` on grid/flex (Chrome 147 expected)
- `reading-flow` — keyboard/screen reader order matching visual order
- Masonry layout — `grid-template-rows: masonry` (Firefox behind flag, Chrome in development)
- `contrast-color()` — automatic WCAG-compliant text colour (Firefox 146, Safari 26)

## What NOT to write

These patterns are outdated. If you catch yourself writing them, stop and use the modern alternative above:

- `position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)` for centering
- `padding-top: 56.25%` for aspect ratios
- `height: 100vh` on mobile (use `100dvh`)
- `overflow-y: scroll` to prevent layout shift (use `scrollbar-gutter: stable`)
- Sass variables, nesting, `mix()`, `lighten()`, `darken()` when native CSS alternatives exist
- `@media (min-width: ...) and (max-width: ...)` verbose syntax
- Float-based layouts
- `-webkit-` prefixed properties where the unprefixed version is widely available
- jQuery or JS for: accordions, modals, tooltips, dropdowns, lazy loading, scroll snapping, form validation styling, dark mode detection, feature detection

