# scroll-into-view

Scrolls an element into view — on connect by default, or on click / any event via the inherited `listen-for`.


```html
<div>
  <div class="scroll-area">
    <p>Keep scrolling…</p>
    <p>Keep scrolling…</p>
    <p>Keep scrolling…</p>
    <scroll-into-view>
      <p role="note">👋 Scrolled into view on connect — no attributes needed.</p>
    </scroll-into-view>
    <p>More content below.</p>
    <p>More content below.</p>
  </div>
</div>
```


## Features

- **Scroll on connect** Works with no attributes needed
- **Any trigger** Combine with `listen-for` to scroll on click, custom events, or lifecycles
- **Alignment control** `scroll-align` picks start / center / end / nearest per axis
- **Offset for fixed headers** `scroll-offset` nudges the final position after alignment
- **Skip redundant scrolls** `if-needed` scrolls only when the target isn't already visible

## Installation


`@excom/scroll-into-view` v0.1.4

```bash
pnpm add @excom/scroll-into-view
```

```bash
npm install @excom/scroll-into-view
```

```bash
yarn add @excom/scroll-into-view
```

### Import

```ts
import "@excom/scroll-into-view";
```



## Usage

By default, `<scroll-into-view>` scrolls *itself* into view as soon as it connects to the DOM. Set `target-ref` to scroll a different element instead, and pair with the inherited `listen-for` to trigger on click or any event rather than on connect.

```html
<!-- Jump to a section on click -->
<scroll-into-view target-ref="#pricing" listen-for="click">
  See pricing
</scroll-into-view>
```

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description | Inherited from |
| --- | --- | --- | --- | --- | --- | --- |
| `target-ref` | option | `string` |  | `<CSS Selector>` | CSS selector of the element to scroll to. Unset scrolls this element itself. |  |
| `scroll-align` | option | `tokenlist` | `"nearest start"` | `"start"` \| `"center"` \| `"end"` \| `"nearest"` \| `"none"` | Two tokens `<inline> <block>` (x and y, respectively) controlling how the target aligns inside the scrollport. Each token is `start`, `center`, `end`, `nearest`, or `none` (skip alignment on that axis). |  |
| `scroll-offset` | option | `tokenlist` | `"0 0"` | `<px> <px>` | Two pixel offsets `<x> <y>` applied after alignment — e.g. leave room for a sticky header by using a negative `<y>`. |  |
| `scroll-behavior` | option | `string` | `"auto"` | `"auto"` \| `"smooth"` \| `"instant"` | Scroll animation. |  |
| `if-needed` | option | `boolean` |  |  | Only scroll if the target isn't already fully visible. |  |
| `host-ref` | option | `string` |  | `<CSS Selector>` \| `"window"` \| `"document"` \| `"html"` \| `"body"` \| `"head"` | Listen on another element / `window` / `document` — e.g. Escape to dismiss a dialog from a global `keydown`. Defaults to `:scope`. Used with `listen-for`. Not compatible with `listen-for-lifecycle`. The selector MUST resolve when `host-ref` is set — it will not wait for a match to appear. | `@excom/listenable-element` |
| `listen-for` | option | `tokenlist` |  | `<EventName>…` | Space-separated event names to listen for. Defaults to `click` when unset (and no lifecycle list is set). | `@excom/listenable-element` |
| `listen-for-lifecycle` | option | `tokenlist` |  | `"connected"` \| `"disconnected"` \| `"adopted"` | Space-separated element lifecycles to handle. | `@excom/listenable-element` |
| `listen-once` | option | `boolean` |  |  | Handle each distinct event name / lifecycle at most once. | `@excom/listenable-element` |
| `selector-filter` | option | `string` |  | `<CSS Selector>` | Only handle events whose `event.target` matches this CSS selector. Does not support `:scope` in the selector. | `@excom/listenable-element` |
| `keycode-filter` | option | `tokenlist` |  | `<key` \| `mod+key>…` | Space-separated key filters (OR). Join modifiers with `+` (AND, any order): `shift+k tab` → Shift+K or Tab. Modifiers: `shift`, `alt`, `ctrl`/`control`, `meta`/`cmd`. Name the space bar `space` / `spacebar` and the plus key `plus` (`shift+space`). Case-insensitive. | `@excom/listenable-element` |
| `pathname-filter` | option | `tokenlist` |  | `<pathname>…` | Only handle when `location.pathname` is one of these values — route-aware behaviors without a separate router element. | `@excom/listenable-element` |
| `prevent-default` | option | `boolean` |  |  | Call `preventDefault()` on matched events (ignored for lifecycles). | `@excom/listenable-element` |
| `stop-propagation` | option | `boolean` |  |  | Call `stopPropagation()` on matched events (ignored for lifecycles). | `@excom/listenable-element` |
| `stop-immediate-propagation` | option | `boolean` |  |  | Call `stopImmediatePropagation()` on matched events (ignored for lifecycles). | `@excom/listenable-element` |
| `vibrate-ms` | option | `number` | `"20 (when attribute is present with no value)"` |  | Vibrate on handle (`navigator.vibrate`). Empty / `0` uses a 20ms pulse. | `@excom/listenable-element` |
| `delay-ms` | option | `number` |  |  | Delay handling by this many milliseconds. | `@excom/listenable-element` |
| `is-debounced` | option | `boolean` |  |  | With `delay-ms`, coalesce bursts into one trailing call (debounce). | `@excom/listenable-element` |

#### CSS Aliases

| Alias | Kind | Matches | Description |
| --- | --- | --- | --- |
| `:--scroll-into-view` | element | `scroll-into-view`, `.tag-scroll-into-view` | Apply `<scroll-into-view>` host styles (positioning + the inherited pointer cursor when clickable) to any element, without registering the custom element — e.g. server-rendered markup. |



### Examples

#### Offset for a sticky header

`scroll-offset="0 -50"` (negative `<y>`) leaves room for a sticky header after alignment; `scroll-behavior="smooth"` animates the scroll. This is the pattern used to jump between sections without the header covering the target's heading.


```html
<div>
  <scroll-into-view role="button" target-ref="#section-3" listen-for="click"
    scroll-offset="0 -50" scroll-behavior="smooth">
    Jump to Section 3
  </scroll-into-view>
  <div class="scroll-area">
    <header>Sticky header</header>
    <section id="section-1">
      <h4>Section 1</h4>
      <p>The target section is further down.</p>
    </section>
    <section id="section-2">
      <h4>Section 2</h4>
      <p>Keep scrolling…</p>
    </section>
    <section id="section-3">
      <h4>Section 3</h4>
      <p>The sticky header would normally cover this —
        <code>scroll-offset="0 -50"</code> leaves room for it.</p>
    </section>
  </div>
</div>
```

## Release notes

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
