---
area: "Learn"
parent: "Learn"
url: "https://unpoly.com/learn/scrolling-and-focus"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [Learn](https://unpoly.com/learn.md)</nav>

# Scrolling & focus

After a fragment update, Unpoly decides what scrolls and what gets focus. Following a link scrolls to the top and focuses the new content, the way a full page load would. A minor update leaves scroll positions and focus where they are. Every default can be changed per link, per form or globally.

<nav aria-label="Contents">

#### Contents

- [What happens after following a link](#navigation-defaults)
- [Scrolling to new content](#scrolling-to-new-content)
- [Moving focus](#moving-focus)
- [Showing or hiding focus rings](#showing-or-hiding-focus-rings)
- [See also](#see-also)

</nav>

## What happens after following a link {#navigation-defaults}

Take a link that updates the page's [main fragment](https://unpoly.com/api/main.md):

```html
<a href="/posts/5" up-follow>Read post</a>
```

When the new `<main>` is rendered, the page scrolls to the top and the main fragment receives focus. A screen reader starts reading the new post, and a keyboard user continues tabbing from there.

Now take a link that updates a minor fragment:

```html
<a href="/posts/5/comments" up-target="#comments">Show comments</a>
```

Nothing scrolls. Focus stays where it was, unless the user's focus was inside the old `#comments` element. In that case focus moves to the counterpart of the element the user was in, or to the new fragment if there is none, so the user is not dropped at the `<body>`.

Both behaviors are controlled by the `[up-scroll]` and `[up-focus]` attributes (or the `{ scroll }` and `{ focus }` options in JavaScript). Their default value `auto` is the sequence above. It applies whenever a link or form [navigates](https://unpoly.com/learn/navigation-defaults.md). Low-level calls like [`up.render()`](https://unpoly.com/api/up.render.md) do not scroll or focus unless told to.

Scrolling an element into view is called _revealing_ it. The element that scrolls is a _viewport_. By default that is the document itself, but an app with its own scrolling panels can mark each panel with an [`[up-viewport]`](https://unpoly.com/api/up-viewport.md) attribute.

## Scrolling to new content {#scrolling-to-new-content}

To scroll the updated fragment into view, set an `[up-scroll]` attribute:

```html
<a href="/posts/5/comments" up-target="#comments" up-scroll="target">Show comments</a>
```

The viewport scrolls just far enough to make the new `#comments` element visible. If it is already on screen, nothing moves.

Other values scroll to the `top` or `bottom`, reveal an element matching a CSS selector, or `keep` the current positions through an update that would otherwise reset them. Strategies can be combined, and `[up-scroll="false"]` turns scrolling off for a navigating link.

<nav aria-label="Read more">

- [More: Scrolling](https://unpoly.com/learn/scrolling.md)

</nav>

## Moving focus {#moving-focus}

To place focus on a specific element after the update, set an `[up-focus]` attribute with a CSS selector:

```html
<a href="/search" up-target=".content" up-focus="input[type=search]">Search</a>
```

When the `.content` fragment is rendered, the search field is focused and the user can start typing. Other values focus the new `target`, `keep` the focus in a re-rendered form field, or `restore` the focus from an earlier visit of the same URL.

In an overlay, focus is trapped: tabbing past the last element wraps around to the start of the overlay. Only [popups](https://unpoly.com/learn/overlays.md#layer-modes) let focus leave. When the overlay closes, focus returns to the link that opened it.

<nav aria-label="Read more">

- [More: Controlling focus](https://unpoly.com/learn/focus.md)

</nav>

## Showing or hiding focus rings {#showing-or-hiding-focus-rings}

Because Unpoly focuses new content, focus rings can appear where mouse users don't expect them. Unpoly marks every element it focuses with an [`.up-focus-visible`](https://unpoly.com/api/up-focus-visible.md) class when the user interacted with the keyboard, and with [`.up-focus-hidden`](https://unpoly.com/api/up-focus-hidden.md) after a mouse or touch interaction:

```html
<main class="up-focus-hidden" tabindex="-1">
  ...
</main>
```

Style these classes in your CSS to show rings for keyboard users only. Unpoly's default stylesheet already removes the outline from [`.up-focus-hidden`](https://unpoly.com/api/up-focus-hidden.md) elements.

You can change which elements get which class in [`up.viewport.config.autoFocusVisible`](https://unpoly.com/api/up.viewport.config.md#config.autoFocusVisible), or override it for a single link with [`[up-focus-visible]`](https://unpoly.com/api/up-follow.md#up-focus-visible).

<nav aria-label="Read more">

- [More: Focus ring visibility](https://unpoly.com/learn/focus-visibility.md)

</nav>

## See also {#see-also}

To declare navigation bars that obstruct the viewport, animate the scroll motion, or add padding when revealing an element, see [Tuning the scroll behavior](https://unpoly.com/learn/scroll-tuning.md).

When the user presses the back button, scroll positions and focus are restored to where they were. See [Restoring history](https://unpoly.com/learn/restoring-history.md).

Loading more items as the user scrolls down is a [live fragment](https://unpoly.com/learn/live-fragments.md) rather than a scroll option. See [Infinite scrolling](https://unpoly.com/learn/infinite-scrolling.md).

<nav aria-label="In this chapter">

## In this chapter {#children-index-title}

- [Scrolling](https://unpoly.com/learn/scrolling.md)
- [Tuning the scroll behavior](https://unpoly.com/learn/scroll-tuning.md)
- [Controlling focus](https://unpoly.com/learn/focus.md)
- [Focus ring visibility](https://unpoly.com/learn/focus-visibility.md)

</nav>

<nav aria-label="Reading order">

- [Previous: Tracking page views](https://unpoly.com/learn/tracking-page-views.md)
- [Next: Scrolling](https://unpoly.com/learn/scrolling.md)

</nav>

