DEMO
A good interface feels less like a tool and more like a conversation with someone who already knows what you need.
CODE
<div class="demo-stage tge-stage"> <p class="tge-text"><span class="tge-sr" id="tge-src">A good interface feels less like a tool and more like a conversation with someone who already knows what you need.</span><span id="tge-text" aria-hidden="true"></span></p> <button type="button" class="tge-replay" id="tge-replay">REPLAY</button> </div>
.tge-stage { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1.4rem; padding:2rem; background:#0B0B10; min-height:280px; }
.tge-text { max-width:440px; margin:0; font-family:'Inter',sans-serif; font-size:1.25rem; line-height:1.6; font-weight:600; color:#EDEDED; text-align:center; }
.tge-sr { position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
.tge-word { display:inline-block; opacity:0; filter:blur(10px); animation: tge-in .6s ease forwards; animation-delay: calc(var(--i) * 70ms); }
@keyframes tge-in { to { opacity:1; filter:blur(0); } }
.tge-replay { padding:.45rem 1rem; border:1px solid rgba(255,255,255,.2); border-radius:999px; background:transparent; color:#C9C9D1; font-family:'Oswald',sans-serif; letter-spacing:.2em; font-size:.72rem; cursor:pointer; }
.tge-replay:hover { border-color:#E91B89; color:#fff; }
.tge-replay:focus-visible { outline:2px solid #E91B89; outline-offset:2px; }
@media (prefers-reduced-motion: reduce) {
.tge-word { animation:none; opacity:1; filter:none; }
}
(function(){
var el = document.getElementById('tge-text');
var src = document.getElementById('tge-src');
var btn = document.getElementById('tge-replay');
if (!el || !src) return;
var source = src.textContent.trim();
function build(){
el.innerHTML = '';
source.split(/\s+/).forEach(function(w, i){
var s = document.createElement('span');
s.className = 'tge-word'; s.style.setProperty('--i', i); s.textContent = w;
el.appendChild(s);
el.appendChild(document.createTextNode(' '));
});
}
build();
if (btn) btn.addEventListener('click', build);
})();
AI PROMPT
文章を単語ごとに span に分け、1語ずつぼかしから浮かび上がらせる Text Generate Effect を作って。各 span は display:inline-block; opacity:0; filter: blur(10px) から始め、@keyframes で opacity:1; filter: blur(0) へ .6秒で変化させる。遅延はカスタムプロパティ --i(単語の番号)を使い animation-delay: calc(var(--i) * 70ms) にすると、JSは番号を振るだけで済む。単語の間には span の外に半角スペースのテキストノードを入れ、改行位置が崩れないようにする。分割すると読み上げが単語ごとに途切れるので、元の文章は視覚的に隠した(クリップした)span にそのまま残し、単語に分けた側の入れ物は aria-hidden="true" にすること(p や div への aria-label は読まれないことがある)。REPLAY ボタンで要素を作り直して再生する。日本語の文章に使う場合は空白で分けられないので、Intl.Segmenter の granularity: 'word' で分割する。@media (prefers-reduced-motion: reduce) では animation を無効にして最初から表示する。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
文章を単語ごとに、ぼかしと透明の状態から順番にくっきり表示させる演出です。Aceternity UI の同名コンポーネントで知られ、AIが文章を生成しているような印象を与えるため、AI関連サービスのヒーローや紹介文によく使われます。
当サイトの Blur Fade In は要素のまとまりを1回でふわっと出す演出、Word Pull Up は単語を下から持ち上げる演出、Typewriter は1文字ずつ打ち込む演出です。本ページは単語単位でぼかしを解いていく別テーマで、動きが小さいぶん長めの文章にも使えます。
遅延の管理をカスタムプロパティに任せると、JavaScriptは単語を分けて番号を振るだけになります。読み上げへの配慮と、動きを減らす設定の人には最初から表示すること、の2点を忘れずに入れてください。
日本語の文章にも使えますか?
使えますが、日本語は単語の間に空白がないため split(' ') では分割できません。Intl.Segmenter(granularity: 'word')を使うと、ブラウザの辞書で「単語」単位に切り分けられます。1文字ずつ出す方法もありますが、長い文章だと表示が終わるまでの時間が長くなりすぎる点に注意してください。
単語ごとに分けるとスクリーンリーダーではどう読まれますか?
span ごとに区切られると、読み上げが単語ごとに途切れたり、演出の途中で読まれたりすることがあります。元の文章を画面外にクリップした span に残し、分割した単語の入れ物を aria-hidden="true" にしておくと、文章は1つのまとまりとして読まれます。p や div に付けた aria-label は読み上げで無視されることがあるため、この形が確実です。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。