Lesson 6 of 12

CSS Display and Position

Before Flexbox and Grid, it helps to know how the browser places boxes by default and how you can change that. The CSS display property decides what kind of box an element creates, and the position property lets you move an element away from its normal place, pin it to the screen or stack it on top of other content.

Normal flow

Without any layout CSS, elements follow normal flow. Block elements stack from top to bottom, each taking the full width. Inline elements sit side by side inside lines of text and wrap when the line is full. Normal flow is responsive by default, so always start with it and only change what you need.

The CSS display property

ValueBehaviourTypical use
blockNew line, full width, accepts width, height and all margins<div>, <p>, sections
inlineFlows with text; ignores width and height<span>, <a>, <em>
inline-blockFlows with text but accepts width, height and paddingBadges, small buttons
noneRemoves the element from the page and from screen readersHidden menus, tabs
flex / gridTurns the element into a layout container for its childrenRows, columns, page layouts

CSS display changes how an element behaves, not what it means. A link styled as display: block is still a link to assistive technology. That is why you should choose HTML elements for meaning (see HTML Semantic Elements) and use CSS for looks.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>block, inline and inline-block</title>
<style>
body { font-family: system-ui, sans-serif; padding: 20px; }

.tag {
  background: #e7f5ff;
  border: 1px solid #74c0fc;
  padding: 6px 10px;
  margin: 4px;
  width: 120px;          /* ignored for inline */
}

.inline       { display: inline; }
.inline-block { display: inline-block; }
.block        { display: block; }

.hidden { display: none; }
</style>
</head>
<body>
<p>Inline: <span class="tag inline">Chennai</span><span class="tag inline">Mysuru</span></p>
<p>Inline-block: <span class="tag inline-block">Chennai</span><span class="tag inline-block">Mysuru</span></p>
<p>Block: <span class="tag block">Chennai</span><span class="tag block">Mysuru</span></p>
<p>There is a hidden span here: <span class="hidden">you cannot see me</span></p>
</body>
</html>

Tip: To hide something visually but keep it available to screen readers, such as a label for an icon button, use a “visually hidden” utility class instead of CSS display: none. visibility: hidden hides an element but keeps its space.

The position property

Every element starts with position: static, meaning it sits in normal flow. The other values switch on the offset properties top, right, bottom and left, or the shorthand inset.

  • relative: the element stays in the flow, and its original space is kept, but you can nudge it with offsets. Its main job today is to become the reference point for absolutely positioned children.
  • absolute: the element is removed from the flow and placed relative to its nearest positioned ancestor (any ancestor with a position other than static). Use it for badges, close buttons and overlays.
  • fixed: the element is removed from the flow and pinned to the viewport, so it stays put when the page scrolls. Use it for a floating chat or “back to top” button.
  • sticky: the element scrolls normally until it reaches an offset such as top: 0, then sticks while its parent is still on screen. Ideal for table headers and section headings.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>relative and absolute: a sale badge</title>
<style>
body { font-family: system-ui, sans-serif; padding: 30px; }

.product {
  position: relative;      /* reference point for the badge */
  width: 220px;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 10px;
}

.badge {
  position: absolute;
  top: -10px;
  right: -10px;
  background: #d6336c;
  color: white;
  font-size: 0.8rem;
  padding: 4px 10px;
  border-radius: 999px;
}

.overlay {
  position: absolute;
  inset: 0;               /* same as top/right/bottom/left: 0 */
  background: rgb(255 255 255 / 70%);
  display: none;
}
.product:hover .overlay { display: block; }
</style>
</head>
<body>
<div class="product">
  <span class="badge">20% off</span>
  <h3>Brass diya set</h3>
  <p>Handmade in Moradabad.</p>
  <div class="overlay"></div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>sticky header and fixed button</title>
<style>
body { font-family: system-ui, sans-serif; margin: 0; line-height: 1.6; }

header {
  position: sticky;
  top: 0;
  background: #1b4332;
  color: white;
  padding: 12px 20px;
  z-index: 10;
}

main { padding: 20px; }
main p { margin-block: 0 16px; }

.help {
  position: fixed;
  right: 16px;
  bottom: 16px;
  background: #f77f00;
  color: white;
  border: 0;
  padding: 12px 16px;
  border-radius: 999px;
  font: inherit;
}
</style>
</head>
<body>
<header>Kochi Spice Market</header>
<main>
  <p>Scroll down inside this preview. The header sticks to the top and the Help button stays fixed.</p>
  <p>Cardamom, pepper, cloves and cinnamon are sold by weight.</p>
  <p>Ask for fresh stock in the morning.</p>
  <p>Most shops accept UPI payments.</p>
  <p>Tea estates in Munnar are a short drive away.</p>
  <p>Carry a cloth bag for your purchases.</p>
  <p>Prices are usually fixed in larger stores.</p>
  <p>Smaller stalls are happy to explain each spice.</p>
  <p>End of the list.</p>
</main>
<button class="help">Help</button>
</body>
</html>

Stacking with z-index

When positioned elements overlap, z-index decides which one is on top: a higher number appears in front. It only works on positioned elements and on flex or grid items. Values are compared within a stacking context, so a child with z-index: 9999 cannot escape a parent that sits below another element. Keep a small, documented set of values, such as 10 for headers and 100 for modals.

Watch out: Do not build whole page layouts with position: absolute. Absolutely positioned elements are taken out of the flow, so they overlap when text grows or the screen gets narrower. Use Flexbox and Grid for layout, and positioning only for small overlays.

A sticky element that will not stick usually has one of two problems: you forgot an offset such as top: 0, or an ancestor has overflow: hidden, which changes the scrolling box it sticks to.

Fixed and sticky elements on small screens

Pinned elements are useful, but they take permanent space on the screen. On a phone in landscape mode, a tall sticky header plus a fixed cookie bar can leave only a thin strip for the actual content. Keep sticky headers short, avoid stacking several fixed bars, and make sure a fixed button never covers important text or form fields.

Remember to add space for fixed elements, too. Because a fixed footer is outside the normal flow, the last lines of the page can end up hidden behind it. Adding padding-block-end to the body equal to the footer height solves this. With these tools in place, you are ready for real layout systems, starting with CSS Flexbox.

Further reading

Key takeaways

  • Normal flow stacks block elements vertically and lines up inline elements in text.
  • display sets the box type: block, inline, inline-block, none, flex or grid.
  • relative keeps an element in flow and creates a reference point for absolute children.
  • fixed pins to the viewport; sticky sticks once it reaches its offset.
  • Use z-index on positioned elements to control overlap; use Flexbox and Grid, not absolute positioning, for layout.

Quick quiz

Check what you learned – pick an answer.

1. An element has position: absolute; top: 0; right: 0;. What is it positioned against?

2. Which value makes a header scroll with the page and then stay at the top?

3. What is the difference between display: none and visibility: hidden?

Practice exercise

Create a card with a photo placeholder. Place a "New" badge in the top-left corner of the card using absolute positioning, and add a footer bar that sticks to the bottom of the viewport with position: fixed.

Show a hint

Give the card position: relative first.

Show the solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Badge and footer</title>
<style>
body { font-family: system-ui, sans-serif; padding: 20px; padding-bottom: 80px; }

.card { position: relative; width: 240px; border: 1px solid #ccc; border-radius: 8px; overflow: hidden; }
.photo { height: 140px; background: #a5d8ff; }
.card h3 { margin: 12px; }

.new {
  position: absolute;
  top: 8px;
  left: 8px;
  background: #2b8a3e;
  color: white;
  padding: 2px 8px;
  border-radius: 4px;
}

.bar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  background: #212529;
  color: white;
  padding: 12px 20px;
}
</style>
</head>
<body>
<div class="card">
  <span class="new">New</span>
  <div class="photo"></div>
  <h3>Kanchipuram silk</h3>
</div>
<div class="bar">Free delivery in Tamil Nadu</div>
</body>
</html>

Progress is saved in this browser only.