# Anchored Headings

**MUST** · **ID:** `content-anchored-headings` · **Category:** content
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/content-anchored-headings

> MUST: `scroll-margin-top` on headings for anchored links; include a "Skip to content" link; hierarchical `<h1–h6>`

Set scroll-margin-top for headers when linking to sections

When users click anchor links to jump to sections, fixed headers can cover the target heading. Use scroll-margin-top on headings to add offset so they appear below fixed headers. Also provide hierarchical h1-h6 structure and a "Skip to content" link.

## Bad — do not do this

`content-anchored-headings-bad`

```tsx
export function AnchoredHeadingsBad() {
  return (
    <div className="w-full max-w-sm">
      <div className="bg-card border border-border rounded-lg overflow-hidden">
        <div className="bg-card dark:bg-muted px-4 py-3 text-sm font-medium sticky top-0">
          Fixed Header
        </div>
        <div className="p-4">
          <div className="bg-error/10 border border-error/20 rounded-lg p-3 mb-4">
            <code className="text-xs text-error font-mono block">
              {'/* No scroll-margin-top */'}
              <br />
              {'h2 { }'}
            </code>
          </div>
          <p className="text-sm text-muted-foreground">
            When clicking anchor links (e.g., #section-2), the heading scrolls under the fixed header and becomes hidden.
          </p>
          <div className="mt-4 border-t pt-4">
            <div className="h-4 bg-muted rounded w-full" />
            <p className="text-xs text-muted-foreground mt-1">
              ↑ This heading is hidden behind the fixed header
            </p>
          </div>
        </div>
      </div>
      <p className="text-xs text-error mt-4">
        Heading hidden behind fixed header on anchor jump
      </p>
    </div>
  );
}
```

## Good — do this

`content-anchored-headings-good`

```tsx
export function AnchoredHeadingsGood() {
  return (
    <div className="w-full max-w-sm">
      <div className="bg-card border border-border rounded-lg overflow-hidden">
        <div className="bg-card dark:bg-muted px-4 py-3 text-sm font-medium sticky top-0">
          Fixed Header (48px)
        </div>
        <div className="p-4">
          <div className="bg-success/10 border border-success/20 rounded-lg p-3 mb-4">
            <code className="text-xs text-success font-mono block whitespace-pre">
{`h2 {
  scroll-margin-top: 60px;
  /* 48px header + 12px padding */
}`}
            </code>
          </div>
          <p className="text-sm text-muted-foreground">
            With scroll-margin-top, anchor links position the heading below the fixed header, keeping it fully visible.
          </p>
          <div className="mt-4 border-t pt-4">
            <div className="h-4 bg-primary/20 rounded w-full" />
            <p className="text-xs text-muted-foreground mt-1">
              ↑ Heading visible below the fixed header
            </p>
          </div>
        </div>
      </div>
      <p className="text-xs text-success mt-4">
        scroll-margin-top offsets anchor position below fixed header
      </p>
    </div>
  );
}
```

## References

- [scroll-margin-top](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scroll-margin-top)
