Object Position

Object position starts with one question: which point on the object are you placing?

If you place a star with top and left, CSS places the star’s top-left corner. If you want the whole star to stay inside a frame, place its center inside a smaller inner frame.

This post is about positioning objects in a layout. CSS also has an object-position property for images and videos, but the idea here is lower level: choose an anchor point, then choose the area where that anchor point is allowed to move.

The Result

Click the button a few times. Each star gets a random position, but it never spills outside the outer frame.

Object Position

The Problem

top and left do not place the whole object. They place one point on the object.

By default, that point is the object’s top-left corner:

.star {
  position: absolute;
  top: 100%;
  left: 100%;
}

At left: 100%, the top-left corner reaches the right edge. The rest of the star keeps going, so it spills outside the frame.

Move The Anchor

The first fix is to move the object’s anchor from its top-left corner to its center:

.star {
  transform: translate(-50%, -50%);
}

The useful detail is that percentages in transform are based on the object itself. translate(-50%, -50%) moves the star left by half of its own width and up by half of its own height.

Now top and left describe the star’s center point.

Add The Inner Frame

Center positioning is balanced, but it still allows half the star to overflow when the center touches an edge.

That is why the demo has two frames:

<div class="starFrame">
  <div class="innerFrame"></div>
</div>

The inner frame is inset by half the star size:

.starFrame {
  --star-size: 2rem;
}

.innerFrame {
  position: absolute;
  inset: calc(var(--star-size) / 2);
}

The star is still allowed to sit on the inner frame’s edges. Since the inner frame is pulled away from the outer frame by half a star, the full star remains visible.

The Click Handler

The JavaScript only creates a star and gives it two random percentages:

function addStar() {
  const star = document.createElement("span");

  star.className = "star";
  star.style.setProperty("--top", randomPercent() + "%");
  star.style.setProperty("--left", randomPercent() + "%");
  star.innerHTML = "&#9733;";

  stage.appendChild(star);
}

The containment does not live in JavaScript. JavaScript picks a position. CSS decides what that position means.