Lesson 14 / Layout
CSS Positioning
Use CSS positioning to precisely control element placement, stacking, sticky headers, and layered components.
Course Role
Core
Part of the main course path. Prioritize this before moving to optional polish or deeper references.
Teacher Notes / In-Class Use
Demo Live
- Walk through the interactive demo before students start changing their own project files.
- Connect the demo back to the first goal: Understand `static`, `relative`, `absolute`, `fixed`, and `sticky` positioning
Try In Class
- Create one `relative` element and move it with `top` and `left`.
- Have students make one visible change, save, refresh, and explain what changed.
Submit Or Check
- Ask students to show the work in the browser, not only in the editor.
- Have students commit their progress with a clear message when the checkpoint is stable.
Watch For
- Students copying code without checking file paths, spelling, or capitalization.
- Visual changes that work locally but break when the project is published.
Learning Goals
- Understand `static`, `relative`, `absolute`, `fixed`, and `sticky` positioning
- Use `top`, `right`, `bottom`, `left`, `inset`, and positioned ancestors correctly
- Use `z-index` and positioning to control layer order without breaking layout or accessibility
Interactive Demo
How to use this demo.
Use the demo as a small lab. Change one thing, observe the result, then connect it back to your own project.
What To Try
- Switch between static, relative, absolute, and sticky positioning.
- Adjust top, left, and z-index to see which properties affect the target.
What Changes
- The target element moves differently depending on the positioning mode.
- The code sample updates with the active position and offset values.
What To Notice
- Static elements ignore top and left.
- Absolute positioning is measured from the nearest positioned ancestor.
Apply It
- Use positioning for one layered or anchored UI element, then verify nearby content still behaves.
Interactive Demo
CSS Positioning Playground
Change the positioning mode and offsets to see how an element leaves, keeps, or responds to normal flow.
This demo uses extra JavaScript for teaching. The code sample shows the pattern to practice. View full demo source.
Introduction
CSS positioning gives you precise control over where an element appears on the page.
Use positioning for layers, badges, overlays, sticky headers, tooltips, and small visual adjustments. For the main structure of a page, use normal flow, Flexbox, or Grid first.
Positioning is powerful, but it is usually not the first tool for full page layout.
How to Choose a Positioning Method
Start by asking what the element needs to do. Most elements should stay in normal document flow. Reach for position when something needs to be nudged, layered, attached to a parent, or attached to the viewport.
| Need | Use | Common Example |
|---|---|---|
| Normal page layout | No special positioning | Text, sections, cards, and page structure |
| Nudge an element visually | position: relative | Move an icon or label slightly |
| Place something inside a parent | Parent relative, child absolute | Badge in the corner of a card |
| Keep something in the viewport | position: fixed | Cookie banner or floating button |
| Stick while scrolling | position: sticky | Header or sidebar that sticks at the top |
Normal Flow and Static Positioning
Normal flow is the browser's default layout behavior. Block elements stack vertically, inline content flows with text, and each element takes up space in the document.
position: static is the default value. Static elements ignore top, right, bottom, left, and z-index.
.element {
position: static;
} Relative Positioning
position: relative keeps an element in normal flow, then lets you visually move it from its original spot.
The original space is still reserved, so nearby elements behave as if the element never moved.
.element {
position: relative;
top: 10px;
left: 20px;
} Absolute Positioning and Containing Blocks
position: absolute removes an element from normal flow and places it relative to its nearest positioned ancestor.
A positioned ancestor is a parent or ancestor with position set to anything other than static. If no positioned ancestor exists, the element can position itself relative to the page instead, which is a common source of bugs.
.card {
position: relative;
}
.badge {
position: absolute;
top: 1rem;
right: 1rem;
} Fixed Positioning
position: fixed locks an element to the browser viewport. It stays in the same place even when the page scrolls.
Use fixed positioning carefully. Fixed elements can cover content, especially on small screens.
.site-alert {
position: fixed;
top: 0;
left: 0;
right: 0;
} Sticky Positioning
position: sticky starts in normal flow, then sticks when it reaches a scroll offset such as top: 0.
Sticky elements need room to scroll. They can also stop working when a parent has certain overflow values, such as overflow: hidden.
.site-header {
position: sticky;
top: 0;
z-index: 10;
} Offset Properties
top, right, bottom, and left control where positioned elements move or attach.
inset is shorthand for setting all four sides at once. It is especially useful for overlays that need to fill a parent.
.image-overlay {
position: absolute;
inset: 0;
} Position Types Compared
| Value | In Normal Flow? | Positioned Relative To | Use For |
|---|---|---|---|
static | Yes | Normal document flow | Default layout |
relative | Yes | Its original position | Small visual adjustments or creating an anchor for children |
absolute | No | Nearest positioned ancestor | Badges, close buttons, overlays, and layered card pieces |
fixed | No | Viewport | Floating buttons, alerts, and persistent UI |
sticky | Yes until it sticks | Nearest scrolling area | Sticky headers, labels, and sidebars |
Z-index and Layer Order
z-index controls which positioned elements appear in front of other elements.
Use small, intentional values instead of huge numbers. A simple scale is easier to maintain than random values like 999999.
.card {
position: relative;
z-index: 0;
}
.dropdown {
position: absolute;
z-index: 10;
}
.modal {
position: fixed;
z-index: 100;
} Stacking Contexts
Sometimes z-index appears not to work because the element is inside a stacking context. A stacking context is a layer group. Children cannot always escape the layer order of their parent.
Positioned elements with z-index, transformed elements, elements with certain opacity values, and several other CSS properties can create stacking contexts.
When z-index is confusing, inspect the parent elements too. The problem is often higher in the HTML tree.
Example: Card Badge
This pattern places a badge in the corner of a card. The card becomes the positioning anchor, and the badge is placed inside that anchor.
<article class="card">
<span class="badge">New</span>
<h2>CSS Positioning</h2>
<p>Build layered interface pieces.</p>
</article>
.card {
position: relative;
}
.badge {
position: absolute;
top: 0.75rem;
right: 0.75rem;
} Example: Notification Dot
A notification dot uses the same parent-child pattern. The button is the anchor, and the dot is positioned inside it.
.icon-button {
position: relative;
}
.notification-dot {
position: absolute;
top: 0;
right: 0;
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
} Example: Image Overlay
For overlay text on an image, the image can fill the card with position: absolute and inset: 0. The text stays above it with position: relative and z-index.
.hero-card {
position: relative;
overflow: hidden;
}
.hero-card img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.hero-card-content {
position: relative;
z-index: 1;
} Accessibility and Usability Notes
- Do not use positioning to visually reorder meaningful content. Keep the HTML reading order logical.
- Make sure fixed and sticky elements do not cover headings, links, form fields, or important content.
- Use
scroll-padding-topwhen a sticky header covers anchor links. - Modal overlays need keyboard focus management and a clear way to close.
- Check positioned UI on small screens. A layout that works on desktop can cover content on mobile.
html {
scroll-padding-top: 5rem;
} Common Pitfalls and Troubleshooting
| Problem | Why It Happens | Fix |
|---|---|---|
| Absolute child appears in the wrong place | No positioned parent exists | Add position: relative to the intended parent |
z-index does nothing | The element is not participating in a stack as expected | Add positioning when appropriate and inspect parent stacking contexts |
| Sticky element does not stick | There is not enough scroll room or a parent has restrictive overflow | Check parent containers and remove unnecessary overflow: hidden |
| Fixed header covers content | The header is removed from normal flow | Add spacing, use scroll-padding-top, and test anchor links |
| Whole page is built with positioning | Positioning is being used instead of layout tools | Use normal flow, Flexbox, or Grid for structure |
Debugging Checklist
- Inspect the element and identify its nearest positioned ancestor.
- Temporarily add
outlineor background colors to parent and child elements. - Toggle
positionvalues in DevTools to see which rule changes the behavior. - Check whether a parent has
overflow: hidden,transform,opacity, or az-indexthat creates a stacking context. - Confirm whether the element should stay in normal flow or be removed from it.
Checkpoint
Before moving on, make sure these feel true.
- I can explain the main concept in my own words.
- I can apply this lesson to my current project.
- I can verify the result in the browser.
- I can commit the change with a clear message.
Practice
- Create one
relativeelement and move it withtopandleft. - Add an
absolutebadge inside a card by making the cardposition: relative. - Create a notification dot inside an icon button.
- Build a sticky header and add
scroll-padding-topso anchor links are not covered. - Layer text over an image with
position: absolute,inset: 0, andz-index. - Debug a broken badge that is positioning itself relative to the wrong parent.