|$ curl https://forge-ai.dev/api/markdown?path=docs/css/positioning
$cat docs/css-positioning.md
updated Yesterday·11 min read·published

CSS Positioning

CSSPositioningLayoutIntermediate
Introduction

CSS positioning controls how elements are placed in the document flow. The position property accepts five values — static, relative, absolute, fixed, and sticky — each removing the element from normal flow to varying degrees and enabling offset properties (top, right, bottom, left).

Understanding positioning is essential for creating overlays, tooltips, modals, dropdown menus, and any UI that needs to break out of the normal document flow.

position-intro.css
CSS
1.static { position: static; } /* default — normal flow */
2.relative { position: relative; } /* offset from normal position */
3.absolute { position: absolute; } /* relative to nearest positioned ancestor */
4.fixed { position: fixed; } /* relative to viewport */
5.sticky { position: sticky; } /* hybrid relative/fixed */
Static Positioning

position: static is the default value for every element. Elements are positioned according to normal document flow — block elements stack vertically, inline elements flow horizontally. Offset properties have no effect on statically positioned elements.

static.css
CSS
1.box {
2 position: static; /* default — no special positioning */
3 top: 20px; /* ignored — has no effect */
4 left: 20px; /* ignored — has no effect */
5}

info

Static positioning is the default for a reason — it gives you predictable, accessible document flow. Only change it when you specifically need to break out of the flow.
Relative Positioning

position: relative keeps the element in normal flow but offsets it from its original position using top, right, bottom, or left. The element's original space is preserved — other elements do not reflow to fill the gap.

relative.css
CSS
1.box {
2 position: relative;
3 top: 20px; /* pushed down 20px from original spot */
4 left: 10px; /* pushed right 10px from original spot */
5}
6
7/* The original space is still occupied — other elements
8 cannot use the vacated space */
preview
Absolute Positioning

position: absolute removes the element entirely from normal flow — other elements behave as if it does not exist. It is positioned relative to its nearest positioned ancestor (any ancestor with position: relative, absolute, fixed, or sticky). If no positioned ancestor exists, it positions relative to the initial containing block (the viewport).

absolute.css
CSS
1.parent {
2 position: relative; /* establishes positioning context */
3}
4
5.child {
6 position: absolute;
7 top: 0;
8 right: 0;
9 /* pinned to top-right corner of .parent */
10}
11
12.overlay {
13 position: absolute;
14 inset: 0; /* shorthand for top:0; right:0; bottom:0; left:0 */
15 /* covers entire positioned ancestor */
16}
preview
absolute-modal.css
CSS
1.modal-backdrop {
2 position: fixed;
3 inset: 0;
4 background: rgba(0, 0, 0, 0.6);
5 z-index: 100;
6}
7
8.modal-content {
9 position: absolute;
10 top: 50%;
11 left: 50%;
12 transform: translate(-50%, -50%);
13 /* perfectly centered within the backdrop */
14 z-index: 101;
15}

warning

Absolutely positioned elements collapse to fit their content — they have no intrinsic width or height unless you define them explicitly. Always set dimensions or use inset: 0 if you need the element to fill its container.
Fixed Positioning

position: fixed removes an element from normal flow and positions it relative to the viewport (or the nearest ancestor with a transform, perspective, or filter property). Fixed elements stay in place when the page scrolls, making them ideal for sticky headers, floating action buttons, and persistent navigation.

fixed.css
CSS
1.navbar {
2 position: fixed;
3 top: 0;
4 left: 0;
5 right: 0;
6 height: 64px;
7 background: #0D0D0D;
8 border-bottom: 1px solid #222222;
9 z-index: 1000;
10}
11
12/* Add padding to body so content doesn't hide behind navbar */
13body {
14 padding-top: 64px;
15}
16
17.fab {
18 position: fixed;
19 bottom: 24px;
20 right: 24px;
21 width: 56px;
22 height: 56px;
23 border-radius: 50%;
24 background: #00FF41;
25 color: #0D0D0D;
26 box-shadow: 0 4px 12px rgba(0, 255, 65, 0.3);
27 z-index: 999;
28}
📝

note

A fixed element's containing block changes if any ancestor has transform, perspective, or filter set to anything other than none. This is a common cause of unexpected fixed positioning behavior.
Sticky Positioning

position: sticky is a hybrid of relative and fixed positioning. The element behaves as relatively positioned until it crosses a specified threshold, at which point it becomes fixed within its scrolling container. It is commonly used for sticky headers, table headers, and section labels.

sticky.css
CSS
1.section-header {
2 position: sticky;
3 top: 0;
4 /* becomes fixed at top: 0 when scrolled to that position */
5 background: #0D0D0D;
6 z-index: 10;
7}
8
9/* Sticky sidebar */
10.sidebar {
11 position: sticky;
12 top: 80px; /* accounts for fixed navbar height */
13 height: calc(100vh - 80px);
14 overflow-y: auto;
15}
16
17/* Sticky table headers */
18thead th {
19 position: sticky;
20 top: 0;
21 background: #0D0D0D;
22 z-index: 5;
23}
preview

best practice

For sticky to work, the element must have a defined top, right, bottom, or left threshold, and its parent must have a defined height that is larger than the sticky element. The parent cannot have overflow: hidden (it clips the sticky behavior).
Stacking Context

Every positioned element participates in a stacking context. The z-indexproperty controls the order within the same stacking context, but elements in different stacking contexts are compared by their parent context's z-index, not their own.

stacking-context.css
CSS
1.layer-1 {
2 position: relative;
3 z-index: 1;
4}
5
6.layer-2 {
7 position: relative;
8 z-index: 2; /* appears above layer-1 */
9}
10
11/* Different stacking contexts */
12.parent-a {
13 position: relative;
14 z-index: 1;
15}
16
17.parent-b {
18 position: relative;
19 z-index: 2; /* entire parent-b stack is above parent-a */
20}
21
22.child-of-a {
23 position: absolute;
24 z-index: 9999; /* still behind parent-b's children! */
25}
26
27.child-of-b {
28 position: absolute;
29 z-index: 1; /* appears above child-of-a */

info

New stacking contexts are created by positioned elements with z-index other than auto, elements with opacity less than 1, transform other than none, filter other than none, isolation: isolate, and mix-blend-mode other than normal.
Positioning with Transforms

Using transform for positioning is often better than top/left because it only triggers compositing, not layout. This is especially important for animation performance.

positioning-transforms.css
CSS
1.bad {
2 position: relative;
3 top: 100px; /* triggers layout recalculation */
4 left: 100px;
5}
6
7.good {
8 position: relative;
9 transform: translate(100px, 100px); /* compositor-only, 60fps */
10}
11
12/* Centering with transform */
13.center {
14 position: absolute;
15 top: 50%;
16 left: 50%;
17 transform: translate(-50%, -50%);
18 /* perfect centering regardless of element size */
19}

warning

When you use transform on an element with position: fixed, it changes the element's containing block from the viewport to the transformed ancestor. Always test fixed positioning when transforms are involved.
Practical Examples

Tooltip

tooltip.css
CSS
1.tooltip-container {
2 position: relative;
3 display: inline-block;
4}
5
6.tooltip {
7 position: absolute;
8 bottom: calc(100% + 8px);
9 left: 50%;
10 transform: translateX(-50%);
11 padding: 6px 12px;
12 background: #1A1A2E;
13 color: #E0E0E0;
14 border: 1px solid #222222;
15 border-radius: 6px;
16 font-size: 12px;
17 white-space: nowrap;
18 opacity: 0;
19 pointer-events: none;
20 transition: opacity 0.2s ease;
21}
22
23.tooltip-container:hover .tooltip {
24 opacity: 1;
25}

Modal

modal.css
CSS
1.modal-overlay {
2 position: fixed;
3 inset: 0;
4 background: rgba(0, 0, 0, 0.7);
5 display: flex;
6 align-items: center;
7 justify-content: center;
8 z-index: 1000;
9 opacity: 0;
10 pointer-events: none;
11 transition: opacity 0.3s ease;
12}
13
14.modal-overlay.active {
15 opacity: 1;
16 pointer-events: all;
17}
18
19.modal {
20 background: #0D0D0D;
21 border: 1px solid #222222;
22 border-radius: 12px;
23 padding: 24px;
24 max-width: 480px;
25 width: 90%;
26 max-height: 80vh;
27 overflow-y: auto;
28}

Dropdown Menu

dropdown.css
CSS
1.dropdown {
2 position: relative;
3 display: inline-block;
4}
5
6.dropdown-menu {
7 position: absolute;
8 top: 100%;
9 left: 0;
10 min-width: 200px;
11 background: #0D0D0D;
12 border: 1px solid #222222;
13 border-radius: 8px;
14 padding: 4px;
15 opacity: 0;
16 transform: translateY(-4px);
17 pointer-events: none;
18 transition: opacity 0.2s ease, transform 0.2s ease;
19 z-index: 50;
20}
21
22.dropdown:hover .dropdown-menu {
23 opacity: 1;
24 transform: translateY(0);
25 pointer-events: all;
26}
27
28.dropdown-item {
29 padding: 8px 12px;
30 border-radius: 4px;
31 color: #A0A0A0;
32 font-size: 13px;
33 cursor: pointer;
34 transition: background 0.15s ease;
35}
36
37.dropdown-item:hover {
38 background: #1A1A2E;
39 color: #E0E0E0;
40}

Terminal Overlay

terminal-overlay.css
CSS
1.terminal-overlay {
2 position: fixed;
3 inset: 0;
4 background: rgba(13, 13, 13, 0.95);
5 display: flex;
6 align-items: center;
7 justify-content: center;
8 z-index: 9999;
9}
10
11.terminal-window {
12 width: 80%;
13 max-width: 800px;
14 background: #0D0D0D;
15 border: 1px solid #222222;
16 border-radius: 8px;
17 overflow: hidden;
18}
19
20.terminal-header {
21 display: flex;
22 align-items: center;
23 gap: 8px;
24 padding: 12px 16px;
25 background: #1A1A2E;
26 border-bottom: 1px solid #222222;
27}
28
29.terminal-dot {
30 width: 10px;
31 height: 10px;
32 border-radius: 50%;
33}
34
35.terminal-dot.red { background: #FF5F56; }
36.terminal-dot.yellow { background: #FFBD2E; }
37.terminal-dot.green { background: #27C93F; }
38
39.terminal-body {
40 padding: 24px;
41 color: #00FF41;
42 font-family: monospace;
43 font-size: 14px;
44 line-height: 1.8;
45}
Browser Support
Browserstatic/relative/absolute/fixedsticky
Chrome 56+
Firefox 59+
Safari 13+
Edge 16+
Best Practices
Use static positioning by default — only change it when you need to break out of document flow
Always set position: relative on a parent container before using absolute children inside it
Use transform: translate() instead of top/left for animations — better performance
Remember that fixed elements are affected by ancestors with transform or filter
Use inset: 0 shorthand instead of separately setting top/right/bottom/left
Stacking contexts: a child's z-index is relative to its parent's stacking context
For sticky positioning, ensure the container has a defined height and no overflow: hidden
Positioned elements with z-index: auto do not create a new stacking context
$Blueprint — Engineering Documentation·Section ID: CSS-20·Revision: 1.0