タイプライター
複数のフレーズを1文字ずつ打ち込み、カーソルが点滅。打つ→消す→次のフレーズをループする。
プレビュー
操作自動で再生されます。
仕様書
- 向く場面
- ヒーローで複数の言い方を順に見せる
- 検索欄・チャットの入力例の提示
- 3〜5 フレーズのループ
- 避ける場面
- 本文・説明文(出揃うまで待たせる)
- 同じ画面に複数のタイプライター
- 構成要素
- 打ち込まれる文字
- 点滅カーソル
- ループするフレーズ列
- 持っている状態
- 通常
- キーボード
操作なし(自動でループ再生)。停止・再生の操作要素は無い
- 支援技術
動く文字は aria-hidden、全フレーズを sr-only の静的テキストで読ませる。タブが隠れている間はタイマーを止める。reduced-motion では点滅を止め、最初のフレーズを全文表示
- 動き
打つ 80ms/字・消す 40ms/字・打ち終わり 1,200ms 停止・次まで 300ms。カーソルは 0.7s step-end 点滅。人が打つ感触
デザインの要点
- 打つ 80ms/消す 40ms と、消す方を倍速にする。同じ速さだと消えるのを打つのと同じだけ待たされる
- 打ち終わりに 1,200ms 止める。ここを削ると読み切る前に消え始め、何を言っていたか残らない
- setTimeout の再帰で 1 文字ずつ進め、visibilitychange で止める。タブを離れている間に進むと戻ったとき文字が飛ぶ
- カーソルの点滅は step-end(0.7s)。ease でふわっと消えると入力欄でなく装飾に見える
使いどころ
ヒーロー領域で「このサービスは何をするものか」を複数の言い方で見せたいときに向く。1つの見出しを置くだけでは伝わらない多面的な価値を、順番に見せられるのが利点。検索やチャットの入力欄のプレースホルダで「こんな聞き方ができます」と例示する使い方も相性がよい。一方で、本文や説明文をタイプライターで出すのは避けたい。読み手は自分のペースで読みたいのに、文字が出揃うまで待たされることになる。同じ理由で、ページ内に複数のタイプライターを同時に動かすのも視線が散って読みづらい。ループさせる場合は、フレーズを3〜5個に抑えるとよい。それ以上あると、ユーザーが一周を見届けられず「まだ何か言おうとしている」印象だけが残る。
仕組み
実装は「対象の文字列を1文字ずつ増やして textContent に代入する」だけで、setInterval か、setTimeout の再帰で1文字あたり50〜100msの間隔を作る。打ち終わったら少し止め、今度は1文字ずつ減らして消し、次のフレーズへ進むという状態遷移になる。カーソルは疑似要素で縦棒を置き、CSSアニメーションで opacity を0と1に交互させる。打鍵の速度を完全に均一にすると機械的に見えるので、間隔に少し乱数を混ぜると人が打っている感じに近づく。文字を足す方向と消す方向で速度を変え、消すときを速くするのも定番の調整。innerHTML ではなく textContent を使うのが重要で、外部から来た文字列をタイプさせる場合に HTML として解釈される事故を防げる。
実装の注意
最大の問題はレイアウトシフトで、文字数が増減するたびに要素の幅が変わり、周囲のボタンや文章が左右に揺れる。対策は、最も長いフレーズの幅を min-width か、見えない状態で最長文字列を置いた要素で確保しておくこと。折り返しが起きる場合は高さも変わるので、行数分の min-height も併せて固定する。次に多いのが、タブを離れている間もタイマーが走り続けて戻ってきたときに文字が飛ぶ現象。document.visibilitychange で一時停止するか、requestAnimationFrame ベースにすると自然に止まる。SEOの観点では、JavaScriptで後から流し込むテキストは見出しとして評価されにくい。h1 には固定の文言を置き、タイプライターは補助的な副見出しに使うのが安全。絵文字や結合文字を含むフレーズでは、文字列を単純に1文字ずつ切ると壊れるため、Intl.Segmenter か Array.from で書記素単位に分ける。
アクセシビリティ
1文字ずつ変化するテキストは、live region になっているとスクリーンリーダーが1文字ごとに読み上げてしまい実用にならない。タイプライターの要素には aria-hidden="true" を付け、同じ内容を静的なテキストとして視覚的に隠した要素(sr-only)に置いて、そちらを読ませる。こうすれば見える人には演出、読み上げる人には完全な文章が届く。prefers-reduced-motion では点滅とタイプを止め、最初のフレーズを最初から全部表示する。とくにカーソルの点滅は、光の点滅に敏感なユーザーには負担になりうるため、reduce の指定があるときは確実に止めたい。認知面の配慮としては、文字が出揃うまで読めない状態を長引かせないことが大切で、1フレーズが2〜3秒で読み終わる長さに収めるとよい。
調整して使う
調整するのは打鍵速度・停止時間・カーソルの形。打つ速度は1文字50〜80ms、消す速度はその半分程度が心地よい。打ち終わってから消し始めるまでの停止は1.2〜2秒取り、読み切れる余裕を必ず作る(ここを短くすると一気に読みづらくなる)。カーソルは2〜3px幅の縦棒で0.7〜1秒周期の点滅が標準的だが、打っている最中は点滅を止めて常時表示にすると本物のエディタらしくなる。フレーズを強調したいときは、可変部分だけをブランドカラーにし、前後の固定文(「〜のための」「ツール」など)は通常色のまま置くと読みやすさを保てる。速度に ±20% の乱数を混ぜる指定を入れると機械的な均一さが消える。
コード(コピーして使えます)
<div class="tw-wrap" aria-hidden="true">
<span class="tw-text" id="twText"></span><span class="tw-cursor" id="twCursor">|</span>
</div>
<!-- 1 文字ずつ変わる要素を読ませず、静的な全文をこちらで読ませる -->
<p class="sr-only">こんにちは、世界。/Hello, World!/良いデザインは/見えない。/文字が踊り出す。</p>