Cascading
Style Sheets
A deep-dive reference covering every major CSS3 feature — from the cascade algorithm and specificity, through flexbox and grid, to container queries, Houdini, blend modes, and beyond. 25 sections, dozens of live interactive demos.
§ 01
The Cascade
The cascade is an algorithm that picks one winning declaration when multiple rules target the same property. It weighs four factors in order: Origin, @layer position, Specificity, Order.
/* ── ORIGIN PRIORITY (high → low) ───────────── 1. !important user-agent 2. !important author styles ← our !important 3. Author styles ← our normal styles 4. User agent (browser defaults) ─────────────────────────────────────────── */ /* ── SPECIFICITY (0,0,0) tuple ─────────────── (IDs, Classes+Attrs+Pseudoclasses, Elements) */ h1 { /* (0,0,1) */ } .title { /* (0,1,0) */ } #hero { /* (1,0,0) */ } #hero .title { /* (1,1,0) beats both above */ } /* ── !important breaks the algorithm ───────── Always prefer higher specificity instead! */ .text { color: red !important; } /* ── :is() / :where() and specificity ──────── :is() takes specificity of its HIGHEST arg :where() ALWAYS has zero specificity */ :is(#header, .title) p { /* (1,0,1) from #header */ } :where(#header, .title) p { /* (0,0,1) zero! */ }
§ 02
CSS Selectors
CSS3 has an enormous selector vocabulary. Combine basic selectors with combinators, attribute matchers, structural pseudo-classes, and state pseudo-classes for surgical precision.
- First item
- Second item
- Bold — nth-child(3)
- Tinted (odd)
- Hover for :not()
- Last item
/* ── BASIC ───────────────────── */ * /* universal */ p /* element */ .class /* class */ #id /* ID */ /* ── COMBINATORS ─────────────── */ div p /* descendant */ div > p /* direct child */ h2 + p /* adjacent sibling */ h2 ~ p /* general sibling */ /* ── ATTRIBUTE ───────────────── */ [type] /* has attr */ [type="text"]/* exact value */ [href^="https"] /* starts with */ [src$=".png"] /* ends with */ [class*="btn"] /* contains */ /* ── STRUCTURAL ──────────────── */ :first-child :last-child :nth-child(3) :nth-child(2n+1) /* odd */ :only-child :empty :not(.active)
/* ── STATE PSEUDO-CLASSES ─────────────────── */ a:hover a:active a:visited input:focus input:checked button:disabled :focus-visible /* keyboard focus only */ :focus-within /* any descendant focused */ /* ── FORM PSEUDO-CLASSES ─────────────────── */ :valid :invalid :required :placeholder-shown :autofill :in-range :out-of-range /* ── MODERN ──────────────────────────────── */ :is(h1, h2, h3) a /* grouping, keeps specificity */ :where(section, article) p /* zero specificity */ :has(img) /* parent has child */ :has(+ p) /* followed by p */ /* ── PSEUDO-ELEMENTS ─────────────────────── */ ::before ::after ::first-line ::first-letter ::selection ::placeholder ::marker /* list bullet */ ::backdrop /* behind dialogs */ ::file-selector-button
§ 03
The Box Model
Every element is a rectangular box with four nested layers. box-sizing: border-box makes width and height include padding and border — the modern default in every CSS reset.
/* ── UNIVERSAL RESET ─────────── */ *, *::before, *::after { box-sizing: border-box; } .card { width: 300px; padding: 20px; border: 2px solid violet; margin: 16px auto; border-radius: 12px; /* Multiple box-shadows! */ box-shadow: 0 2px 4px rgba(0,0,0,.3), 0 8px 32px rgba(167,139,250,.15), inset 0 1px 0 rgba(255,255,255,.1); } /* Margin collapse — adjacent block margins merge; take MAX */ p { margin-bottom: 20px; } h2{ margin-top: 30px; } /* gap between = 30px not 50px! */ /* outline — does not affect layout */ :focus { outline: 2px solid violet; outline-offset: 4px; }
§ 04
Custom Properties
CSS variables live in the cascade — they inherit, can be overridden per-element, and are readable/writable by JavaScript. Combined with @property, they become type-safe and animatable.
/* ── DEFINE on :root (global) ───────────── */ :root { --color-primary: #7c3aed; --spacing-md: 16px; --radius: 10px; --shadow: 0 8px 32px rgba(0,0,0,.3); } /* ── USE with var() ─────────────────────── */ .card { background: var(--color-primary); padding: var(--spacing-md); border-radius: var(--radius); /* fallback value: */ color: var(--text-color, #fff); } /* ── LOCAL OVERRIDE (scoped) ────────────── */ .danger { --color-primary: #f43f8a; } /* ── JAVASCRIPT ACCESS ──────────────────── */ // Read getComputedStyle(document.documentElement) .getPropertyValue('--color-primary'); // Write document.documentElement.style .setProperty('--color-primary', '#22c55e'); /* ── @property (typed, animatable) ──────── */ @property --progress { syntax: '<percentage>'; initial-value: 0%; inherits: false; } /* Now --progress can be transitioned! */
§ 05
Flexbox
One-dimensional layout along a main axis. The flex container controls direction, wrapping, and alignment. Flex items control how they grow, shrink, and base their initial size.
/* ── CONTAINER ────────────────────────────── */ .flex-container { display: flex; flex-direction: row; /* row | column | row-reverse | column-reverse */ flex-wrap: wrap; /* nowrap | wrap | wrap-reverse */ justify-content: space-between; /* main axis */ align-items: center; /* cross axis (single line) */ align-content: flex-start; /* cross axis (multi-line) */ gap: 16px 12px; /* row-gap column-gap */ } /* ── ITEMS ────────────────────────────────── */ .flex-item { flex-grow: 1; /* how much extra space to absorb */ flex-shrink: 0; /* 0 = prevent shrinking */ flex-basis: 200px; /* initial size before growing */ flex: 1 0 200px; /* shorthand: grow shrink basis */ align-self: flex-end; /* override align-items for one */ order: 2; /* visual reordering */ min-width: 0; /* IMPORTANT: prevents overflow in flex */ } /* ── COMMON FLEX PATTERNS ─────────────────── */ /* Perfect centering */ .center { display: flex; align-items: center; justify-content: center; } /* Holy Grail layout */ .layout { display: flex; flex-direction: column; min-height: 100vh; } .layout main { flex: 1; } /* main grows to push footer down */ /* Responsive card row that wraps */ .cards { display: flex; flex-wrap: wrap; gap: 16px; } .cards .card { flex: 1 1 280px; } /* min 280px, grows equally */
§ 06
CSS Grid
Two-dimensional layout with explicit rows AND columns. Name areas with grid-template-areas for readable, maintainable layouts.
/* ── CONTAINER ────────────────────────────── */ .grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 80px auto 60px; gap: 16px; /* Named areas — dots = empty cell */ grid-template-areas: "head head head" "nav main side" "foot foot . "; /* Responsive — NO media queries! */ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); } /* ── ITEMS ────────────────────────────────── */ .header { grid-area: head; } .span-2 { grid-column: span 2; } .big { grid-column: 1 / 3; grid-row: 1 / 3; } /* ── SUBGRID ─────────────────────────────── */ .card-grid { display: grid; grid-template-rows: subgrid; } /* Children align to PARENT grid tracks! */ /* ── ALIGNMENT ────────────────────────────── */ .grid { justify-items: start; /* items within cells, inline */ align-items: center; /* items within cells, block */ place-items: center; /* shorthand for both */ justify-content: space-between; /* tracks in container */ }
§ 07
Positioning & Z-index
Five position modes: static (default), relative (offset without leaving flow), absolute (relative to positioned ancestor), fixed (viewport), and sticky (relative until threshold).
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
.relative { position: relative; top: 10px; left: 20px; /* offset from normal */ } .absolute { position: absolute; /* leaves flow */ top: 0; right: 0; /* positioned relative to nearest positioned ancestor */ } .fixed { position: fixed; /* to viewport */ top: 0; width: 100%; /* navbar */ z-index: 100; } .sticky { position: sticky; /* relative until threshold */ top: 0; /* then "sticks" */ z-index: 10; } /* Centering trick */ .centered { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } /* Stacking context formed by: - position + z-index - opacity < 1 - transform, filter, will-change - isolation: isolate */ .isolated { isolation: isolate; }
§ 08
Animations & Transitions
transition interpolates between two states. @keyframes creates multi-step sequences that run independently. Both are GPU-composited on transform and opacity.
rotate — linear
scale — ease-in-out
bounce — cubic-bezier
translate — alternate
morph — border-radius
typewriter — steps()
orbit — transform-origin
glow — box-shadow
@property — hue
/* ── TRANSITION ──────────────────────────── */ .btn { transition: all 0.3s ease; /* Multi-property (more precise): */ transition: background 0.3s ease, transform 0.2s cubic-bezier(.34,1.56,.64,1), box-shadow 0.3s ease 0.05s; /* delay */ } /* ── @KEYFRAMES ──────────────────────────── */ @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes orbit { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .element { animation: slideUp 0.6s ease-out 0.2s both; /* name dur easing delay fill-mode */ /* Full longhand: */ animation-name: slideUp; animation-duration: 0.6s; animation-timing-function: ease-out; animation-delay: 0.2s; animation-iteration-count: infinite; animation-direction: alternate; animation-fill-mode: both; /* forwards | backwards | both */ animation-play-state: running; /* Steps for typewriter / sprite sheets */ animation-timing-function: steps(12, end); } /* Stacked animations */ .multi { animation: spin 2s linear infinite, morph 4s ease infinite; } /* Respect user preference */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; } }
§ 09
CSS Transforms
Move, rotate, scale, and skew without affecting layout. Applied right-to-left — order matters! 3D transforms require a perspective context.
.scene { perspective: 400px; } .card { transform-style: preserve-3d; } .scene:hover .card { transform: rotateY(30deg) rotateX(15deg); }
/* ── 2D TRANSFORMS ─────────────────────────── */ transform: translateX(20px); transform: translateY(-50%); /* % of element's own height */ transform: translate(-50%, -50%); /* centering trick */ transform: rotate(45deg); transform: scale(1.2); transform: scaleX(-1); /* horizontal flip */ transform: skewX(15deg) skewY(5deg); /* Chaining — applied RIGHT to LEFT */ transform: rotate(30deg) scale(1.1) translateX(20px); transform-origin: top left; /* pivot point */ transform-origin: 50% 0; /* top center */ /* ── 3D TRANSFORMS ─────────────────────────── */ .parent { perspective: 800px; } /* 3D context */ transform: rotateY(45deg); transform: rotateX(20deg) translateZ(50px); transform-style: preserve-3d; /* children share 3D space */ backface-visibility: hidden; /* hide back face */ /* ── INDIVIDUAL TRANSFORM PROPS (2023) ────── */ translate: 10px 20px; /* can animate separately! */ rotate: 45deg; scale: 1.2;
§ 10
CSS Gradients
Gradients are images — use them anywhere an image is accepted. Three types: linear, radial, and conic. Layer multiple for mesh effects.
background, then -webkit-background-clip: text; -webkit-text-fill-color: transparent;
§ 11
Filters & Effects
CSS filter applies Photoshop-like effects to any element. backdrop-filter applies them to what's behind the element — enabling glassmorphism.
§ 12
Clip-path & Masking
Clip any element to a geometric shape with clip-path. Animate between shapes for smooth morphing effects. mask-image adds alpha transparency via gradients or SVG masks.
/* ── clip-path shapes ─────────────────────── */ clip-path: circle(50%); clip-path: ellipse(60% 40% at 50% 50%); clip-path: inset(10px 20px round 8px); clip-path: polygon(50% 0%, 100% 100%, 0 100%); clip-path: path("M0,0 L100,0 L100,80 ..."); /* SVG path */ /* Animated morphing */ .blob { clip-path: circle(0% at 50% 50%); transition: clip-path 0.5s ease; } .blob:hover { clip-path: circle(100% at 50% 50%); } /* Diagonal section divider */ .hero { clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%); } /* ── mask-image ───────────────────────────── */ mask-image: linear-gradient(to bottom, black, transparent); mask-image: radial-gradient(circle, black 40%, transparent 70%); mask-image: url('mask.svg'); /* SVG mask shape */ mask-mode: alpha; mask-size: cover;
§ 13
CSS Typography
From @font-face to variable fonts, text shadows to gradient text — CSS gives deep control over every typographic detail.
/* ── @font-face ─────────────────────────────── */ @font-face { font-family: 'MyFont'; src: url('font.woff2') format('woff2'), url('font.woff') format('woff'); font-weight: 100 900; /* variable font range */ font-display: swap; /* show fallback until loaded */ } /* ── GRADIENT TEXT ─────────────────────────── */ h1 { background: linear-gradient(90deg, violet, pink); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } /* ── FLUID SIZE with clamp ──────────────────── */ body { font-size: clamp(14px, 1.5vw, 18px); } h1 { font-size: clamp(1.8rem, 5vw, 5rem); } /* ── MULTI-COLUMN TEXT ─────────────────────── */ .article { column-count: 3; column-gap: 2rem; column-rule: 1px solid #ccc; } /* ── TEXT OVERFLOW ─────────────────────────── */ .truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Multi-line clamp (webkit) */ .clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
§ 14
Colors & Color Spaces
Modern CSS supports wide-gamut color spaces like oklch and display-p3, plus the powerful color-mix() function — interpolating between any two colors in any space.
/* ── ALL COLOR FORMATS ──────────────────────── */ color: #7c3aed; /* hex */ color: rgb(124 58 237); /* rgb — space-separated (modern) */ color: rgb(124 58 237 / 80%); /* with alpha */ color: hsl(263 76% 58%); /* hue saturation lightness */ color: hsl(263 76% 58% / .5); /* with alpha */ /* ── OKLCH — perceptual (wide gamut) ───────── Lightness (0-1), Chroma (0-0.4), Hue (0-360) */ color: oklch(0.6 0.2 293); /* vivid violet */ color: oklch(0.7 0.25 330 / .8); /* pink, 80% opacity */ /* ── DISPLAY-P3 (wider gamut display) ──────── Access colors outside sRGB on capable screens */ color: color(display-p3 0.5 0.2 0.9); /* ── color-mix() ───────────────────────────── Interpolate two colors in any color space */ color: color-mix(in oklch, violet 30%, pink); color: color-mix(in srgb, #7c3aed, transparent); /* 50% fade */ background: color-mix(in hsl, hsl(263,76%,58%) 60%, white); /* ── light-dark() — OS theme aware ──────────── */ :root { color-scheme: light dark; } color: light-dark(#111, #fff); /* auto-switches */
§ 15
CSS Math Functions
calc(), min(), max(), and clamp() bring real mathematics to CSS. Newer additions add round(), mod(), sin(), cos(), and more.
/* ── calc() — mixed-unit arithmetic ────────── */ width: calc(100% - 2rem); height: calc(100vh - var(--header-h)); font-size: calc(16px + 0.5vw); margin: calc(var(--gap) * 2); /* ── min() / max() ─────────────────────────── */ width: min(400px, 90%); /* use the smallest */ padding: 0 max(20px, 3vw); /* at least 20px */ /* ── clamp(min, preferred, max) ─────────────── */ font-size: clamp(1rem, 2.5vw, 3rem); padding: clamp(1rem, 5vw, 4rem); /* ── round() — CSS 4 ────────────────────────── */ width: round(10px, 45.6px); /* round to nearest 10px */ /* ── Trigonometry (CSS 4) ───────────────────── */ transform: rotate(atan2(-1, 0)); --x: calc(sin(45deg) * 100px); /* 70.7px */ --y: calc(cos(45deg) * 100px); /* ── env() — device safe areas ─────────────── */ padding-bottom: env(safe-area-inset-bottom, 0);
§ 16
Scroll Behaviors
CSS-only carousels with scroll-snap. Control momentum with overscroll-behavior. Add scroll-driven animations tied to the scrollbar position.
↔ Scroll or drag the slider above
/* ── SCROLL SNAP ────────────────────────────── */ .container { scroll-snap-type: x mandatory; /* axis type: x|y|both, proximity|mandatory */ overflow-x: scroll; display: flex; } .slide { flex: 0 0 100%; scroll-snap-align: start; /* start | center | end */ scroll-snap-stop: always; /* stop at EVERY slide */ } /* ── scroll-margin / padding ─────────────────── */ .section { scroll-margin-top: 80px; /* offset for sticky header */ } /* ── overscroll-behavior ─────────────────────── */ .modal-content { overscroll-behavior: contain; /* don't propagate to page */ } /* ── Scroll-driven animations (CSS 4) ────────── */ @keyframes reveal { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .card { animation: reveal linear both; animation-timeline: view(); /* tied to element entering viewport */ animation-range: entry 0% cover 30%; }
§ 17
Media Queries
Target any aspect of the user's environment — viewport size, color scheme, pointer type, reduced motion preference, print — and adapt the layout accordingly.
/* ── MOBILE FIRST (recommended) ─────────────── */ .layout { display: grid; grid-template-columns: 1fr; } @media (min-width: 640px) { .layout { grid-template-columns: 1fr 1fr; } } @media (min-width: 1024px) { .layout { grid-template-columns: 240px 1fr 1fr; } } /* ── DARK MODE ──────────────────────────────── */ @media (prefers-color-scheme: dark) { :root { --bg: #111; --text: #eee; } } /* ── REDUCED MOTION ─────────────────────────── */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; } } /* ── POINTER TYPE ───────────────────────────── */ @media (pointer: coarse) { .btn { min-height: 44px; } /* touch target */ } @media (hover: none) { .btn:hover { /* no hover effects on touch */ } } /* ── PRINT ──────────────────────────────────── */ @media print { .sidebar, .nav, .ads { display: none; } } /* ── RANGE SYNTAX (CSS 4) ───────────────────── */ @media (640px <= width < 1024px) { /* new range syntax */ }
§ 18
Modern Selectors
:has() is the "parent selector" CSS developers waited decades for. :is() and :where() reduce repetition. Native CSS nesting eliminates Sass.
Check the box — the card's border changes color using only CSS :has():
/* ── :has() — "parent selector" ─────────────── */ /* Card that contains an img gets wider */ .card:has(img) { grid-column: span 2; } .card:has(input:checked) { border-color: lime; } form:has(:invalid) { border: 2px solid red; } /* ── :is() — forgiving selector list ────────── */ /* Old: h1 a, h2 a, h3 a, h4 a { } */ :is(h1, h2, h3, h4) a { color: violet; } /* ── :where() — zero specificity ────────────── */ :where(article, section) p { margin-bottom: 1em; } /* Easy to override — specificity = (0,0,0)! */ /* ── :not() with complex args ────────────────── */ a:not([href], .active) { opacity: 0.5; } li:not(:first-child, :last-child) { } /* ── CSS NESTING (native, 2023) ──────────────── */ .card { padding: 1rem; /* Direct nesting */ h2 { color: violet; } p { line-height: 1.7; } /* Pseudo-classes */ &:hover { box-shadow: var(--glow); } &.featured { border-color: gold; } &:has(img) { padding: 0; } /* Nested media query */ @media (min-width: 768px) { display: flex; } }
§ 19
Logical Properties
Logical properties replace physical directions (top/right/bottom/left) with flow-relative ones (block/inline-start/end) — essential for multi-language, RTL, and vertical writing mode support.
margin-right
padding-left
border-bottom
width
height
margin-inline-end
padding-inline-start
border-block-end
inline-size
block-size
/* block = vertical axis (in horizontal writing) inline = horizontal axis */ /* ── MARGINS / PADDING ──────────────────────── */ margin-block: 1rem; /* top + bottom */ margin-inline: auto; /* left + right (centering!) */ padding-block-start: 2rem; /* padding-top */ padding-inline-end: 1rem; /* padding-right (LTR) */ /* ── SIZE ───────────────────────────────────── */ inline-size: 100%; /* width (horizontal mode) */ block-size: 100vh; /* height */ max-inline-size: 60ch; /* max-width */ min-block-size: 200px; /* min-height */ /* ── BORDERS ────────────────────────────────── */ border-block: 1px solid violet; /* top + bottom borders */ border-inline-start: 4px solid pink; /* left border (LTR) */ /* ── POSITION ───────────────────────────────── */ inset: 0; /* shorthand for top/right/bottom/left */ inset-block-start: 0; /* top */ inset-inline-end: 0; /* right (LTR) */ /* RTL support — automatic! */ [dir="rtl"] { direction: rtl; } /* padding-inline-start now refers to RIGHT side */
§ 20
Blend Modes
mix-blend-mode blends an element with what's below it. background-blend-mode blends multiple background layers within one element — Photoshop in pure CSS.
§ 21
Container Queries
Unlike media queries which respond to the viewport, container queries respond to a component's own parent size — enabling truly reusable components.
The card below automatically switches from vertical to horizontal layout when its container is wide enough — no media queries!
Container Queries
This card adapts to its container, not the viewport. Resize the dashed box.
/* ── DEFINE a container ───────────────────── */ .card-wrapper { container-type: inline-size; /* respond to width */ container-name: card; /* optional name */ } /* ── QUERY the container ──────────────────── */ @container (min-width: 360px) { .card { flex-direction: row; } } /* ── Named container query ────────────────── */ @container card (min-width: 500px) { .card-title { font-size: 1.5rem; } } /* ── Container query units ────────────────── */ .card { font-size: 2cqi; /* 2% of container inline-size */ padding: 5cqb; /* 5% of container block-size */ } /* ── Style queries (CSS 4) ─────────────────── */ @container style(--featured: true) { .card { border: 2px solid gold; } }
§ 22
@layer Cascade Layers
Cascade layers let you explicitly control the priority of CSS groups. Styles in later layers win over earlier layers, regardless of specificity — solving the specificity arms race.
/* ── DECLARE order up front ───────────────── */ @layer base, theme, components, utilities; /* ── ASSIGN styles to layers ─────────────── */ @layer base { * { box-sizing: border-box; } a { color: blue; } /* (1,0,1) in base layer */ } @layer utilities { .text-red { color: red; } /* (0,1,0) BUT in utilities layer! */ /* utilities wins over base even at lower specificity */ } /* ── IMPORT into a layer ─────────────────── */ @import url('reset.css') layer(base); @import url('components.css') layer(components); /* ── Anonymous layer ─────────────────────── */ @layer { .internal { color: grey; } } /* always loses — no name to reference */ /* ── UNLAYERED styles win over all layers ── */ .override { color: purple; } /* beats all @layer styles */
§ 23
Timing Functions
The timing function defines how a transition or animation progresses over time — the velocity curve. Use cubic-bezier() to craft custom easing.
§ 24
@property & Houdini
Register custom properties with a type, initial value, and inheritance flag. The browser can now interpolate and animate them between values — unlocking previously impossible animations.
/* ── REGISTER a typed property ───────────── */ @property --hue { syntax: '<number>'; initial-value: 0; inherits: false; } @property --progress { syntax: '<percentage>'; initial-value: 0%; inherits: false; } @property --angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; } /* ── ANIMATE the registered property ─────── */ .hue-ball { background: hsl(var(--hue), 90%, 60%); animation: hue-spin 4s linear infinite; } @keyframes hue-spin { to { --hue: 360; } } /* ── CONIC progress ring ─────────────────── */ @property --pct { syntax: '<number>'; initial-value: 0; inherits: false; } .ring { background: conic-gradient(violet calc(var(--pct) * 1%), #111 0%); transition: --pct 0.5s ease; }
§ 25
Pseudo-elements Advanced
::before and ::after inject virtual elements for decoration. Other pseudo-elements give fine-grained control over selection, placeholders, list markers, and dialogs.
- Each item has
::beforearrow - Generated with
content: '›' - No extra HTML required
- Pure CSS decoration only
/* ── ::before / ::after ─────────────────── */ p::before { content: '›'; color: violet; position: absolute; left: 0; } /* Decorative quote mark */ blockquote::before { content: '\201C'; /* " entity */ font-size: 5rem; opacity: 0.3; } /* Underline animation */ a::after { content: ''; display: block; height: 2px; background: violet; transform: scaleX(0); transition: transform .3s; } a:hover::after { transform: scaleX(1); } /* ── ::selection ────────────────────────── */ ::selection { background: rgba(167,139,250,.35); color: white; } /* ── ::placeholder ──────────────────────── */ input::placeholder { color: rgba(255,255,255,.3); font-style: italic; } /* ── ::marker ───────────────────────────── */ li::marker { content: '★ '; color: violet; font-size: 1.2em; } /* ── ::backdrop (dialog, fullscreen) ────── */ dialog::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(8px); }