スクロール表示切替
stimeo--scroll-visibility
スクロール量や方向に応じて要素を出し入れする(トップへ戻る・隠れるヘッダ)。
stimeo--scroll-visibility コントローラに専用の APG パターンはなく、対象がボタンのときは Button の指針に従う。offset モードでは監視対象が offset px を超えてスクロールされると対象を表示し、direction モードでは下スクロールで隠し上スクロールで表示する。既定ではウィンドウのスクロールを監視するが、ページ自体がスクロールしない固定シェル型レイアウトでは root にコンテナのセレクタを与えると、そのコンテナのスクロールを監視し toTop でもそのコンテナを先頭へ戻せる。表示状態は hidden 属性(非表示時はフォーカス順からも外れる)と data-state に反映する。 scroll リスナは passive で requestAnimationFrame によりまとめられ、disconnect(Turbo 遷移含む)で解除する。toTop は prefers-reduced-motion を尊重して瞬時ジャンプに切り替え、 focusSelector の対象へフォーカスを移せる。ライブラリは振る舞いのみを提供し、見た目と遷移は Playground が所有する。
この枠内を下にスクロールすると、右下に「トップへ戻る」ボタンが現れる。
コンテンツ行 1
コンテンツ行 2
コンテンツ行 3
コンテンツ行 4
コンテンツ行 5
コンテンツ行 6
コンテンツ行 7
コンテンツ行 8
コンテンツ行 9
コンテンツ行 10
コンテンツ行 11
コンテンツ行 12
キーボード操作
| キー | 動作 |
|---|---|
| Enter / Space | 「トップへ戻る」ボタンを実行(標準ボタン挙動)。 |
<%# Markup for the scroll-visibility (scroll-driven visibility toggle / GoToTop) demo.
The library toggles the target via hidden / data-state once the scroll source passes a
threshold, and toTop scrolls that source back to the top. Here the scroll source is the
demo's own scroll box (root points at it), so the demo is self-contained and works even
when the page itself does not scroll on the window. The look and transition are demo.css's.
Scroll inside the box and a "Back to top" button appears at the bottom-right. %>
<div
class="scroll-visibility-demo"
data-controller="stimeo--scroll-visibility"
data-stimeo--scroll-visibility-root-value="#scroll-visibility-demo-scroller"
data-stimeo--scroll-visibility-offset-value="120"
data-stimeo--scroll-visibility-mode-value="offset">
<div id="scroll-visibility-demo-scroller" class="scroll-visibility-demo__scroller"
tabindex="0" role="region" aria-labelledby="scroll-visibility-demo-hint">
<p id="scroll-visibility-demo-hint" class="scroll-visibility-demo__hint"><%= t(
"components.scroll_visibility.demo.hint"
) %></p>
<% 12.times do |i| %>
<p class="scroll-visibility-demo__line"><%= t(
"components.scroll_visibility.demo.line", number: i + 1
) %></p>
<% end %>
</div>
<button
type="button"
class="go-to-top"
hidden
data-stimeo--scroll-visibility-target="element"
data-action="click->stimeo--scroll-visibility#toTop">
<%= t("components.scroll_visibility.demo.to_top") %>
</button>
</div>
/* Presentation CSS for scroll-visibility. The library only toggles hidden / data-state
and does the toTop scroll, so visuals like the scroll box, positioning, and fade are
drawn here. The demo scrolls inside its own box (the controller's root) rather than the
window, so the "Back to top" button is pinned to the box, not the viewport. */
.scroll-visibility-demo {
position: relative; /* positioning context for the absolutely-pinned button */
color: var(--color-text-muted);
}
/* The scroll source the controller observes (data-...-root-value points here). */
.scroll-visibility-demo__scroller {
max-height: 16rem;
overflow-y: auto;
padding: 1rem;
border: 1px solid var(--border-default);
border-radius: 0.5rem;
background: var(--surface-subtle);
}
.scroll-visibility-demo__scroller:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.scroll-visibility-demo__hint {
margin: 0 0 0.5rem;
font-weight: 600;
}
.scroll-visibility-demo__line {
margin: 0.5rem 0;
}
/* "Back to top" button, pinned to the demo box. Visible only once the library removes
hidden (and the control is back in the focus order). */
.go-to-top {
position: absolute;
right: 1.25rem;
bottom: 1.25rem;
z-index: 1;
padding: 0.6rem 1rem;
border: none;
border-radius: 999px;
background: var(--color-primary-hover);
color: var(--white);
font-weight: 600;
cursor: pointer;
box-shadow: 0 6px 16px rgba(var(--vital-rgb), 0.4);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.go-to-top:hover {
transform: translateY(-2px);
}
.go-to-top:focus-visible {
outline: 2px solid var(--vital-900);
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.go-to-top {
transition: none;
}
.go-to-top:hover {
transform: none;
}
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--scroll-visibility"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
element
必須
|
スクロールに応じてhidden/data-stateで表示・非表示される要素。 |
data-stimeo--scroll-visibility-target="element" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
offset
|
表示判定のスクロール量(offsetモード)または上部の常時表示域(directionモード)。既定400。 | data-stimeo--scroll-visibility-offset-value |
mode
|
表示ロジック。offset(offset超で表示)かdirection(上スクロールで表示)。既定offset。 |
data-stimeo--scroll-visibility-mode-value |
focusSelector
|
toTop後にフォーカスを移す任意のセレクター(既定は空)。 |
data-stimeo--scroll-visibility-focus-selector-value |
root
|
スクロール元コンテナの任意セレクター。既定はwindow。 | data-stimeo--scroll-visibility-root-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
toTop
|
スクロール元を最上部へ戻し、任意でfocusSelectorへフォーカスを移す(reduced motion尊重)。 |
stimeo--scroll-visibility#toTop |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
change
|
表示状態が変わると発火する。detailにvisibleの真偽値を含む。 | stimeo--scroll-visibility:change |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
hidden |
対象要素 | しきい値未満で非表示(フォーカス順からも除外)。 |
data-state |
ルート要素 | "hidden" / "visible"。 |