プレビュー中の揮発要素ガード
stimeo--preview-guard
Turbo が一瞬だけ見せる古い画面の間、変わりやすい値を隠します。
戻るボタンを押したときなど、Turbo は新しい内容が届くまでのあいだ、覚えておいた古い画面を一瞬だけ見せます。そのとき、残高や未読数や時刻のような変わりやすい値は、古いまま目に入ってしまいます。この部品はその瞬間だけ値を隠します。隠し方は 2 通りです。何も指定しなければ場所を保ったまま見えなくするので、レイアウトが動きません。文言を指定すると、その文言に差し替わり、元に戻るとマークアップもそのまま戻ります。隠している間であることは CSS からも分かります。最新の値を取ってくるのは、ふつうの描画の仕事です。
- 残高
- ¥123,456
- 更新
- 12:34:56
「戻る/進む」で Turbo がキャッシュを一瞬表示する間だけ、残高はプレースホルダに・時刻は非表示になり、古い値が一瞬見える事故を防ぎます(上のボタンでその約 1.5 秒を擬似発生)。
キーボード操作
このコンポーネント自体はキーボード操作を持ちません。
<%# Preview-guard demo: data-turbo-preview is a Turbo-internal attribute, so this catalog
has no real preview to show. demo.js toggles it on <html> for ~1.5s to reproduce a
preview window — exactly what Turbo does on a back/restore visit. The balance declares a
placeholder, so it swaps to —; the timestamp declares none, so it goes invisible with its
box kept. The library only watches the attribute and reflects data-preview-hidden. %>
<div class="preview-guard-demo">
<button type="button" class="demo-trigger" data-preview-guard-demo-toggle>
<%= t("components.preview_guard.demo.toggle") %>
</button>
<dl class="preview-guard-demo__list">
<div class="preview-guard-demo__row">
<dt><%= t("components.preview_guard.demo.balance_label") %></dt>
<dd>
<span
class="preview-guard-demo__value"
data-controller="stimeo--preview-guard"
data-stimeo--preview-guard-placeholder-value="—"
><%= t("components.preview_guard.demo.balance") %></span>
</dd>
</div>
<div class="preview-guard-demo__row">
<dt><%= t("components.preview_guard.demo.updated_label") %></dt>
<dd>
<span
class="preview-guard-demo__value"
data-controller="stimeo--preview-guard"
><%= t("components.preview_guard.demo.updated") %></span>
</dd>
</div>
</dl>
<p class="preview-guard-demo__note"><%= t("components.preview_guard.demo.note") %></p>
</div>
/*
* Presentation-only styles for the preview-guard demo. The library swaps the text
* (placeholder mode) or sets visibility:hidden (hide mode) and reflects
* data-preview-hidden; this CSS only lays out the rows and tints a guarded value so the
* placeholder swap is easy to spot.
*/
.preview-guard-demo {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 24rem;
}
.preview-guard-demo__list {
margin: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.preview-guard-demo__row {
display: flex;
justify-content: space-between;
gap: 1rem;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
}
.preview-guard-demo__row dt {
color: var(--color-text-muted);
}
.preview-guard-demo__row dd {
margin: 0;
font-variant-numeric: tabular-nums;
}
.preview-guard-demo__value[data-preview-hidden] {
color: var(--color-text-subtle);
}
.preview-guard-demo__note {
margin: 0;
color: var(--color-text-muted);
}
// Preview-guard demo (consumer-side JS).
//
// Turbo sets html[data-turbo-preview] itself while a cached preview is on screen. This
// catalog navigates with Turbo but opts its component pages out of previews, so the button
// toggles that attribute for ~1.5s to reproduce the preview window. Every preview-guard on
// the page reacts (just as they would during a real preview), guarding their volatile
// values until it clears.
document.querySelectorAll(".preview-guard-demo").forEach((root) => {
const button = root.querySelector("[data-preview-guard-demo-toggle]");
if (!button) return;
// Idempotent: Turbo can re-run this inline module on navigation; wire each root once. The
// marker is a property, not an attribute: Turbo copies attributes into its page snapshot,
// so an attribute one comes back set on a restored page whose elements carry no listeners.
if (root.demoWired) return;
root.demoWired = true;
button.addEventListener("click", () => {
document.documentElement.setAttribute("data-turbo-preview", "");
setTimeout(() => {
document.documentElement.removeAttribute("data-turbo-preview");
}, 1500);
});
});
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--preview-guard"
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
placeholder
|
ガード中に要素の中身を置き換える文言。空(既定)なら差し替えず visibility:hidden で視覚的に隠します(箱は残ります)。 |
data-stimeo--preview-guard-placeholder-value |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
hide
|
プレビュー突入で要素をガードしたとき発火。 | stimeo--preview-guard:hide |
show
|
プレビュー解除で要素を復帰したとき発火。 | stimeo--preview-guard:show |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-preview-hidden |
コントローラ要素 | プレビュー中にガードしている間付与(true)。 |