---
title: "Keeping Anchors Perfectly on :target"
description: "Learn how to style and smooth-scroll to in-page anchors with CSS :target, fix sticky header overlap, and keep jumps perfectly on target."
---

# Keeping Anchors Perfectly on :target

![an olympic runner doing a long jump attempt over a "<a>" 3d text obstacle object at an olympic stadium, sports photography](https://cdn.sanity.io/images/cyu7k2r0/production/e1232f724aa4d39eacb90562c24f8cd6474089bf-1536x1024.png)

Hyperlinks are a key feature of webpages. You can either jump to another webpage, or you can jump to a specific location on the same page.

## The basics

1. Give the element you want to jump to an `id` attribute.
2. Add an `<a>` tag with an `href` that matches that `id`.
3. Use the CSS `:target` selector to style the element once it’s in view.

With this simple setup, clicking the “Skip to…” link scrolls visitors right to your chosen section.

**Pro tip:** Add `scroll-behavior: smooth;` for a silky-smooth glide instead of a jarring jump.

```html
<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="Basic :target practice"
  src="https://codepen.io/nuotsu/embed/RNrRbQa?default-tab=html%2Cresult"
  frameborder="no"
  loading="lazy"
  allowtransparency="true"
  allowfullscreen="true"
></iframe>
<p>
  See the Pen
  <a href="https://codepen.io/nuotsu/pen/RNrRbQa">Basic :target practice</a>
  by Mitchell Christ (<a href="https://codepen.io/nuotsu">@nuotsu</a>)
  on <a href="https://codepen.io">CodePen</a>.
</p>
```

## When a sticky header gets in the way

Let’s say you add a sticky header (and maybe a bit of Tailwind polish). Now when you click the jump link, the target element can hide **under** the header—oops.

That’s because the browser aligns the target to the very top of the viewport by default.

**Pro tip:** Animate your `:target` (for example, fade out after a second or two) to give visitors a visual cue.

```html
<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title=":target with sticky header"
  src="https://codepen.io/nuotsu/embed/raxLBqP?default-tab=html%2Cresult"
  frameborder="no"
  loading="lazy"
  allowtransparency="true"
  allowfullscreen="true"
></iframe>
<p>
  See the Pen
  <a href="https://codepen.io/nuotsu/pen/raxLBqP">Basic :target practice</a>
  by Mitchell Christ (<a href="https://codepen.io/nuotsu">@nuotsu</a>)
  on <a href="https://codepen.io">CodePen</a>.
</p>
```

## The fix

Use the `scroll-margin-*` property to give the target element breathing room.

For example:

```css
:target {
  scroll-margin-top: 56px; /* or calc(1lh + 1rem) */
}
```

A fixed value works, but what if your header height changes with viewport size? Set a dynamic CSS custom property instead.

```css
:root {
  --header-height: 0px;
}

:target {
  scroll-margin-top: calc(var(--header-height) + 1rem);
}
```

Then update `--header-height` with a couple lines of JavaScript whenever the viewport resizes.

```html
<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="Typical :target styling"
  src="https://codepen.io/nuotsu/embed/OPMXLex?default-tab=html%2Cresult"
  frameborder="no"
  loading="lazy"
  allowtransparency="true"
  allowfullscreen="true"
></iframe>
<p>
  See the Pen
  <a href="https://codepen.io/nuotsu/pen/OPMXLex">Typical :target styling</a>
  by Mitchell Christ (<a href="https://codepen.io/nuotsu">@nuotsu</a>)
  on <a href="https://codepen.io">CodePen</a>.
</p>
```

## 🎯 Closing thought

Whether you’re styling a `:target` in CSS or aiming for the perfect bullseye in life, a little precision goes a long way. Stay on target, keep experimenting, and have fun breaking the (style) rules when it makes the hit even sweeter.

![a ship anchor falling onto the Target store](https://cdn.sanity.io/images/cyu7k2r0/production/07432614db38742da311b4855bf84d06391a5da4-1536x1024.png "Not sponsored. All trademarks belong to their owners—this bullseye is purely for laughs.")