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)
User agent !important declarations
User !important declarations
Author !important declarations
Author normal declarations
User normal declarations
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
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.
/* 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)
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 */}
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
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
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.
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 */}
/* 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.
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
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%) */}
/* 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.
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 */}
Note: An element is "positioned" if its position is anything
other than static. Absolute and fixed positioned elements are removed from normal document flow.
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
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) NEWEXPERIMENTAL
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;}
/* 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 */}
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;}
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"
/* 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+
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;}
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).
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)
Note: Transforms don't affect document flow. Order matters:
transforms apply right-to-left. Use translate() instead of positioning for better performance (GPU-accelerated).
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
Note: Only animatable properties can transition (transform,
opacity, colors, etc.). Avoid transition: all in production (performance). Use specific properties
instead.
Note: Use animation-fill-mode: forwards to keep final state. infinite
loops should be performance-optimized. Use @keyframes for complex, multi-step
animations.
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)
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 transitionfunction updateView() { document.startViewTransition(() => { // Update DOM here document.querySelector('.content').textContent = 'New content'; });}// With promise handlingasync function animatedUpdate() { const transition = document.startViewTransition(() => { // DOM updates updateDOMState(); }); await transition.finished; console.log('Transition complete');}// Conditional transitionsfunction 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) { }
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
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.
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
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.
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;}
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.
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).
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.
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.
/* 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);}// Initializeconst savedTheme = localStorage.getItem('theme') || 'auto';setTheme(savedTheme);// Three-way toggle: auto → light → dark → autofunction cycleTheme() { const current = localStorage.getItem('theme') || 'auto'; const next = current === 'auto' ? 'light' : current === 'light' ? 'dark' : 'auto'; setTheme(next);}// Detect system preference changeswindow.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; }}
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
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.
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.
/* 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 base2. Use Normalize.css when: - Need cross-browser consistency - Want to preserve useful defaults - Supporting older browsers - Standard approach3. 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 needs5. 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.
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 animationelement.style.willChange = 'transform';// Start animationelement.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); }}// Usageconst optimizer = new AnimationOptimizer(document.querySelector('.card'));// Before hover animationcard.addEventListener('mouseenter', () => { optimizer.optimize('transform');});card.addEventListener('mouseleave', () => { optimizer.deoptimize();});// For FLIP animationsfunction 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 impactconsole.memory.usedJSHeapSize // Beforeelement.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>
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 readsconst heights = Array.from(elements).map(el => el.offsetHeight);// Then: Batch all writeselements.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.offsetLeftel.clientHeight, el.clientWidth, el.clientTop, el.clientLeftel.scrollHeight, el.scrollWidth, el.scrollTop, el.scrollLeftel.getBoundingClientRect()window.getComputedStyle()// Setting these properties causes reflow:width, height, margin, padding, bordertop, right, bottom, leftfont-size, line-heightdisplay, position, float*//* Properties that only repaint (MODERATE): *//*color, background, background-colorborder-color, box-shadowvisibility, 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 100container.appendChild(fragment);*//* Use classList for multiple class changes *//* ❌ BAD: Multiple reflows *//*el.className = 'box'; // Reflowel.className = 'box active'; // Reflowel.className = 'box active visible'; // Reflow*//* ✅ GOOD: Single update *//*el.className = 'box active visible'; // One reflow// Or use classListel.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'; // Reflowel.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 = [];}// UsagescheduleUpdate(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.
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) */
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.
/* 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.
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.
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.
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).
/* 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 changesprefersReducedMotion.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.
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
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)
Confirm gap is working (gaps don't have background)
Example: Systematic Debugging Workflow
<!-- Step 1: Isolate the problem -->1. Identify affected element in DevTools2. Check Computed styles for unexpected values3. 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 inheritable12. 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 names15. 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 */}
<!-- 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 containers2. **Interactive States** - :hover, :focus, :active, :disabled states - Form validation states (error, success, warning) - Loading states, skeleton screens3. **Theme Variations** - Light mode vs dark mode - High contrast mode (forced-colors) - Custom theme configurations4. **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 detection6. **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