追従状態検知
stimeo--sticky-observer
position: sticky 要素が貼り付いたかを検知し、data-stuck で公開する。
stimeo--sticky-observer コントローラは APG ウィジェットを持たない純粋な状態検知ユーティリティ。sticky 要素の直前に置いた番兵(sentinel)を IntersectionObserver で監視し、番兵がビューポート(または rootSelector のコンテナ)上端から外れたら sticky 要素に data-stuck="true" を、そうでなければ false を同期し、変化のたびに stimeo--sticky-observer:change を発火する。data-stuck は見た目専用のフックで、ARIA の役割・状態は持たせない。offset は上端の負の rootMargin に使い、sticky 要素の CSS top と一致させること。ライブラリは振る舞いのみを提供し、position: sticky・影・縮小の見た目は Playground が所有する。Observer は disconnect(Turbo 遷移含む)で切断する。
スクロール対象の段落 1。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 2。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 3。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 4。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 5。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 6。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 7。枠内をスクロールすると見出しが貼り付く。
スクロール対象の段落 8。枠内をスクロールすると見出しが貼り付く。
<%# Markup for the sticky-observer (stuck-state detection) demo.
The library watches a sentinel's intersection with IntersectionObserver and syncs
data-stuck="true" once the sticky element actually sticks. position: sticky and the
shadow rendering are owned by demo.css. rootSelector specifies the scroll parent
(this demo frame). %>
<div class="sticky-demo" data-controller="stimeo--sticky-observer"
data-stimeo--sticky-observer-root-selector-value=".sticky-demo__scroll">
<div class="sticky-demo__scroll">
<%# Place the sentinel right before the sticky element. A 1px non-zero height keeps
intersection detection stable. %>
<div class="sticky-demo__sentinel" aria-hidden="true"
data-stimeo--sticky-observer-target="sentinel"></div>
<header class="sticky-demo__header" data-stimeo--sticky-observer-target="element">
<%= t("components.sticky_observer.demo.header") %>
</header>
<div class="sticky-demo__body">
<% 8.times do |i| %>
<p><%= t("components.sticky_observer.demo.paragraph", number: i + 1) %></p>
<% end %>
</div>
</div>
</div>
/* Presentation CSS for sticky-observer. The library only syncs data-stuck, so
position: sticky and the "show a shadow once stuck" look are drawn here by reading data-stuck. */
.sticky-demo {
max-width: 460px;
}
/* The scroll parent (the element rootSelector points to). Stickiness is judged relative to it. */
.sticky-demo__scroll {
max-height: 220px;
overflow-y: auto;
border: 1px solid var(--border-strong);
border-radius: 8px;
position: relative;
}
.sticky-demo__sentinel {
height: 1px;
}
/* position: sticky is owned by the consumer's CSS; data-stuck is set by the library. */
.sticky-demo__header {
position: sticky;
top: 0;
padding: 0.75rem 1rem;
background: var(--surface-card);
font-weight: 600;
color: var(--color-text);
border-bottom: 1px solid var(--border-default);
transition: box-shadow 0.2s ease, background 0.2s ease;
}
/* Once stuck, show a shadow to convey that it's "floating". */
.sticky-demo__header[data-stuck="true"] {
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.18);
background: var(--color-primary-soft);
}
.sticky-demo__body {
padding: 0 1rem 1rem;
}
.sticky-demo__body p {
color: var(--color-text-muted);
}
@media (prefers-reduced-motion: reduce) {
.sticky-demo__header {
transition: none;
}
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--sticky-observer"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
sentinel
必須
|
stickyの直前に置く高さ0の番兵。交差状態でstuckを判定する。 | data-stimeo--sticky-observer-target="sentinel" |
element
必須
|
data-stuckが反映されるposition: sticky要素。 |
data-stimeo--sticky-observer-target="element" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
rootSelector
|
スクロールルートコンテナの任意セレクター。既定はビューポート。 | data-stimeo--sticky-observer-root-selector-value |
offset
|
observerのrootMarginに渡す上部オフセット(px)。stickyのCSS topと一致させる(既定0)。 |
data-stimeo--sticky-observer-offset-value |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
change
|
stuck状態が変わるたびに発火する。detailにstuckの真偽値を含む。 | stimeo--sticky-observer:change |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-stuck |
sticky 要素 | 貼り付き中は "true" / 通常は "false"。影や縮小の起点。 |