クリップボード
stimeo--clipboard
テキストをコピーし、成否を目と耳の両方へ伝えます。
指定した場所のテキストをコピーします。ブラウザ標準の機能を使うので、依存は増えません。うまくいったか失敗したかは、見える場所に出したうえで、共通の窓口から読み上げられます。アイコンの変化だけで済ませることはありません。結果の表示はしばらくすると自動で消え、ページを離れて戻ったときにも残りません。
実行中
結果をトーストへ渡す
キーボード操作
| キー | 動作 |
|---|---|
| Enter / Space | コピーボタンを実行(ネイティブボタン)。 |
<%# Markup for the clipboard (copy & completion feedback) demo.
Copies via navigator.clipboard, shows the result in the visible feedback slot,
and hands the announcement to the shared stimeo--announcer your app seats once,
in its layout. Keep the feedback slot free of live-region semantics — the
announcer already reads the result, so a role="status" here would say it twice.
For localization the visible labels come from the copied-label / error-label
Values and the spoken ones from the announce-copied-text / announce-error-text
Values. The look per data-state (idle / copied / error) lives in demo.css. %>
<div
class="clipboard"
data-controller="stimeo--clipboard"
data-stimeo--clipboard-feedback-duration-value="2000"
data-stimeo--clipboard-copied-label-value="<%= t('components.clipboard.demo.copied') %>"
data-stimeo--clipboard-error-label-value="<%= t('components.clipboard.demo.error') %>"
data-stimeo--clipboard-announce-copied-text-value="<%= t(
'components.clipboard.demo.announce_copied'
) %>"
data-stimeo--clipboard-announce-error-text-value="<%= t(
'components.clipboard.demo.announce_error'
) %>">
<input
class="clipboard__source"
type="text"
readonly
aria-label="<%= t('components.clipboard.demo.source_label') %>"
value="https://stimeo.example/share/abc123"
data-stimeo--clipboard-target="source">
<button
class="clipboard__button"
type="button"
data-stimeo--clipboard-target="button"
data-action="click->stimeo--clipboard#copy">
<%= t("components.clipboard.demo.button") %>
</button>
<span class="clipboard__feedback" data-stimeo--clipboard-target="feedback"></span>
</div>
<%# Second form: hand the result straight to a toast. The copy event carries the
wording, so `stimeo--clipboard:copy->stimeo--toast#show` is the whole wire — no
feedback slot and no consumer JS. The announce-* Values are left empty on
purpose: the toast's message slot is a role="status" live region, so wiring the
shared announcer as well would read the same result twice. %>
<h3 class="clipboard-toast__heading"><%= t("components.clipboard.demo.toast_heading") %></h3>
<div
class="clipboard-toast"
data-controller="stimeo--toast"
data-stimeo--toast-duration-value="4000"
data-action="stimeo--clipboard:copy->stimeo--toast#show">
<div
class="clipboard"
data-controller="stimeo--clipboard"
data-stimeo--clipboard-copied-label-value="<%= t('components.clipboard.demo.copied') %>"
data-stimeo--clipboard-error-label-value="<%= t('components.clipboard.demo.error') %>">
<input
class="clipboard__source"
type="text"
readonly
aria-label="<%= t('components.clipboard.demo.source_label') %>"
value="https://stimeo.example/share/xyz789"
data-stimeo--clipboard-target="source">
<button
class="clipboard__button"
type="button"
data-stimeo--clipboard-target="button"
data-action="click->stimeo--clipboard#copy">
<%= t("components.clipboard.demo.button") %>
</button>
</div>
<div
role="region"
aria-label="<%= t("components.clipboard.demo.toast_region_label") %>"
class="clipboard-toast__region">
<ol class="clipboard-toast__list" data-stimeo--toast-target="list"></ol>
<template data-stimeo--toast-target="template">
<li class="clipboard-toast__item" data-stimeo--toast-target="item" tabindex="0">
<span role="status" data-toast-slot="message"></span>
</li>
</template>
</div>
</div>
/*
* Presentation-only styles for the clipboard demo.
* Success / failure styling switches on the data-state the library sets on the
* controller element. The feedback slot is a plain visible label — the shared
* announcer does the reading, so it needs no live-region styling of its own.
*/
.clipboard {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
max-width: 32rem;
}
.clipboard__source {
flex: 1 1 16rem;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border-strong);
border-radius: 0.375rem;
font: inherit;
color: var(--fg);
background: var(--surface-subtle);
}
.clipboard__button {
padding: 0.5rem 0.9rem;
border: 1px solid var(--accent-fill);
border-radius: 0.375rem;
/* White needs the 700 step behind it: the brand accent (vital-500) measures
2.99:1 under white, a body-size AA failure. The fill token clears it in both. */
background: var(--accent-fill);
color: var(--white);
font: inherit;
font-weight: 600;
cursor: pointer;
}
.clipboard[data-state="copied"] .clipboard__button {
background: var(--leaf-500);
border-color: var(--leaf-500);
}
.clipboard[data-state="error"] .clipboard__button {
background: var(--danger-500);
border-color: var(--danger-500);
}
.clipboard__feedback {
flex-basis: 100%;
min-height: 1.25rem;
font-size: 0.875rem;
color: var(--leaf-500);
}
.clipboard[data-state="error"] .clipboard__feedback {
color: var(--danger-500);
}
/* The second form hands the copy result to a toast instead of the feedback slot. */
.clipboard-toast__heading {
margin: 1.5rem 0 0.5rem;
font-size: 0.9375rem;
font-weight: 600;
}
.clipboard-toast {
display: grid;
gap: 0.75rem;
}
.clipboard-toast__region {
min-height: 2.5rem;
}
.clipboard-toast__list {
display: grid;
gap: 0.5rem;
margin: 0;
padding: 0;
list-style: none;
}
.clipboard-toast__item {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-border);
border-radius: 0.375rem;
background: var(--color-surface);
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--clipboard"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
source
|
明示的な text 値がないときにコピー元となる、値またはテキストを保持する要素。 | data-stimeo--clipboard-target="source" |
button
必須
|
コピー操作を起動するボタン。 | data-stimeo--clipboard-target="button" |
feedback
|
コピー成功・失敗のラベルを表示する可視スロット。読み上げは共有アナウンサが行うので、ライブリージョン意味論は付けないでください。 | data-stimeo--clipboard-target="feedback" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
text
|
コピーするテキストを明示指定する。空(既定)の場合は source ターゲットの値またはテキストを使う。 | data-stimeo--clipboard-text-value |
feedbackDuration
|
完了表示が自動解除され idle に戻るまでのミリ秒(既定 2000)。0 以下ならタイマーを積まないので次のコピーまで残る(再接続では idle に戻る。キャッシュから戻したページも同じ)。 | data-stimeo--clipboard-feedback-duration-value |
copiedLabel
|
コピー成功時にフィードバックスロットへ表示するラベル(既定 "Copied")。 | data-stimeo--clipboard-copied-label-value |
errorLabel
|
コピー失敗時にフィードバックスロットへ表示するラベル(既定 "Copy failed")。 | data-stimeo--clipboard-error-label-value |
announceCopiedText
|
コピー成功時に共有アナウンサへ送る文言。空(既定)なら読み上げない。 | data-stimeo--clipboard-announce-copied-text-value |
announceErrorText
|
コピー失敗時に共有アナウンサへ送る文言。空(既定)なら読み上げない。 | data-stimeo--clipboard-announce-error-text-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
copy
|
解決したテキストをクリップボードへコピーし、結果を data-state に反映してフィードバックを表示し、読み上げる。 |
stimeo--clipboard#copy |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
copy
|
コピー試行ごと(失敗時も含む)に発火。detail に { success, text, message } を載せる。message は成否に応じて利用側が書いたラベルなので、stimeo--toast#show へそのまま繋がる。 |
stimeo--clipboard:copy |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-state |
ルート要素 | "idle" / "copied" / "error"。後の 2 つは一過性で、再接続時に idle へ戻ります。 |
テキスト |
feedback | 完了 / 失敗の可視メッセージ(ライブリージョンではありません)。 |