CSS Positioning and Layering

1. Position Values (static, relative, absolute, fixed, sticky)

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

Example: Position values comparison

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

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

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

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

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

Example: Practical positioning patterns

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

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

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

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

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

2. Z-index and Stacking Context

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

Example: Stacking context basics

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

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

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

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

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

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

Example: Debugging stacking issues

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

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

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

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

3. Sticky Positioning Patterns

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

Example: Sticky header with shadow on scroll

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

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

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

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

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

Example: Sticky table headers and columns

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

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

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

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

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

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

6.4 CSS Anchor Positioning (CSS4) NEW EXPERIMENTAL

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

Example: Basic anchor positioning

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

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

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

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

Example: Position fallback for viewport boundaries

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

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

5. Scroll-driven Positioning Techniques

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

Example: Scroll-driven animations

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

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

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

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

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

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

Example: Scroll snap positioning

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

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

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

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

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

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

Example: Position fixed with scroll context

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

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

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

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

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

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

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

Positioning Best Practices

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