GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

css text-shadow 9パターン — 影の値で立つ文字

css text-shadowでは、影とは光が消えた場所のはずなのに、この九つの文字は影だけで輝く — 1番は重ねた層の値で立体を立て、5番はずれた色の値で揺らし、9番はカーソルが乗った瞬間その深さの値を押し上げる。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

値ひとつがcss text-shadowをどう作り変えるか — 9パターンの並び順

以下の九つはどれも、ひとつの数値をそのままtext-shadowの文字列に書き込んでいるだけです。offset-x offset-y blur-radius colorという4つの値の並びはMDNのtext-shadowリファレンスにまとまっています。並び順にも理由があり、1番から4番までは層を重ねた立体をほぼ静止した状態で作り、5番はその安定をわざと壊し、6番と7番は色や枚数そのものを動く値にし、8番と9番はその値を読者自身のスクロールやポインターに委ねます。

こういう影の文字が実際に置かれる画面

1番の押し出しと6番の光沢はどちらも余白のあるヒーロータイトル向きです — ランディングページやプレミアムなブランドロゴタイプなど、影が100〜140pxほど伸びても本文と競合しない場所です。2番の凹凸と3番の輪郭は逆の規模で効きます — カードのバッジラベルやポスターの数字のように、浮き出て見えるか凹んで見えるか、八方向のリングが一目で読み取れる小ささが要ります。

5番のグリッチと4番のレトロな尾は、大きさより先に雰囲気を選ぶ二つです — グリッチはエラー画面の見出し、長い斜めの尾はビンテージキャンペーンの見出しで、どちらも元から寄せていないニュートラルなUIに置いても効きません。8番と9番は読者の動作そのものに従う二つで、長い紹介ページのセクション見出しがスクロール量だけ影を伸ばしたり、ポートフォリオのカードタイトルがカーソルやキーボードのフォーカスが実際に届いた時だけ持ち上がったりします。

01層の値が積み重なり立体になる押し出し文字

六層の影がそれぞれ同じ1ピクセル刻みで同じ対角線上にずれ、extrudeColor()が層番号に応じて明度を落とすので、奥の層ほど暗く沈んで見えます — perspectiveを使わず積み重ねだけで作る立体表現です。buildExtrudeShadow()は1pxから15pxの間を1秒周期のサインカーブで往復するstep値をひとつだけ読み、ランディングページのヒーロータイトルが欲しがる「静止時は薄く、動くと厚く」という表情をそのまま作ります。

層の値が積み重なり立体になる押し出し文字
新しいタブで開く

01 · 層の値が積み重なり立体になる押し出し文字 — 計測 累積面積 10.384% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

SHADOW_LAYER_COUNTbuildExtrudeShadowrenderExtrude
  // step値ひとつからSHADOW_LAYER_COUNT層分の影を文字列として組み立てる
  function buildExtrudeShadow(step) {
    var parts = [];
    for (var i = 1; i <= SHADOW_LAYER_COUNT; i++) {
      var d = (i * step).toFixed(2);
      parts.push(d + 'px ' + d + 'px 0 ' + extrudeColor(i, SHADOW_LAYER_COUNT));
    }
    return parts.join(', ');
  }

測定値: 累積面積10.4%、平均強度26.0、動いたフレーム22/23。

02明るい値と暗い値が逆に重なり浮き出る文字

angleDegという値ひとつがbuildEmbossShadow()に渡り、明るい影を一方向へ、暗い影をちょうど逆方向へ押し出します — 同じ式、符号だけが逆なので、角度が回るたびカードバッジのラベルは浮き出て見えたり凹んで見えたりを行き来します。「押された状態」を別に切り替えるスイッチはなく、角度が今どこにあるかだけで見え方が決まります。

明るい値と暗い値が逆に重なり浮き出る文字
新しいタブで開く

02 · 明るい値と暗い値が逆に重なり浮き出る文字 — 計測 累積面積 10.903% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

EMBOSS_DEPTH_PXbuildEmbossShadowrenderEmboss
  // angleDeg値ひとつで明るい影と暗い影を互いに反対方向へ配置する
  function buildEmbossShadow(angleDeg) {
    var rad = (angleDeg * Math.PI) / 180;
    var lx = (Math.cos(rad) * EMBOSS_DEPTH_PX).toFixed(2);
    var ly = (Math.sin(rad) * EMBOSS_DEPTH_PX).toFixed(2);
    var dx = (-Math.cos(rad) * EMBOSS_DEPTH_PX).toFixed(2);
    var dy = (-Math.sin(rad) * EMBOSS_DEPTH_PX).toFixed(2);
    return [
      lx + 'px ' + ly + 'px 0 rgba(255,255,255,.85)',
      dx + 'px ' + dy + 'px 1px rgba(10,10,14,.75)'
    ].join(', ');
  }

測定値: 累積面積10.9%、平均強度129.2、動いたフレーム23/23。

03八方向の値が重なり輪郭線になる文字

DIRSは上下左右の軸四方向と対角線の四方向、合わせて八方向(軸4+斜め4)を保持し、buildOutlineShadow()はひとつの距離値をその八回すべてに使って、塗りを残さず輪郭線だけをグリフの周りに残します。css text-shadow 縁取りが必要なイベントポスターの数字のように、背景が何であっても読める必要がある文字に向いています。この値を0.4pxの下限より小さくしても、八つのコピーは常に全部描かれ続けます。

八方向の値が重なり輪郭線になる文字
新しいタブで開く

03 · 八方向の値が重なり輪郭線になる文字 — 計測 累積面積 10.6% · 動いたフレーム 21/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

OUTLINE_MAX_PXbuildOutlineShadowrenderOutline
  var DIRS = [
    [1, 0], [-1, 0], [0, 1], [0, -1],
    [0.7071, 0.7071], [-0.7071, 0.7071], [0.7071, -0.7071], [-0.7071, -0.7071]
  ];

  var word = document.getElementById('olWord');
  var value = document.getElementById('olValue');
  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  // 距離値dひとつを八方向に重ね、塗りがなくても輪郭線だけを残す
  function buildOutlineShadow(d) {
    var parts = [];
    for (var i = 0; i < DIRS.length; i++) {
      var x = (DIRS[i][0] * d).toFixed(2);
      var y = (DIRS[i][1] * d).toFixed(2);
      parts.push(x + 'px ' + y + 'px 0 rgba(23,20,26,.92)');
    }
    return parts.join(', ');
  }

測定値: 累積面積10.6%、平均強度140.2、動いたフレーム21/23。

04斜め方向の長さ値だけ長く伸びるレトロ文字

buildRetroShadow()は影をひとつずらすのではなく、lenの値だけ1px刻みの対角線の層を増やしていきます — ビンテージキャンペーンの見出しが必要とするだけの段数が積み上がり、後ろの層ほど少しずつ透明になります。ループの低い側では4段しかなく、高い側の34では全段が揃う、という差が短い一押しと長く引きずる尾の違いを生みます。

対角線の長さ値だけ伸びるレトロな文字
新しいタブで開く

04 · 対角線の長さ値だけ伸びるレトロな文字 — 計測 累積面積 10.081% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

RETRO_MAX_LENbuildRetroShadowrenderRetro
  // len値ひとつを対角線方向へ1pxずつ繰り返し、その長さ分だけ伸ばす
  function buildRetroShadow(len) {
    var parts = [];
    var n = Math.round(len);
    for (var i = 1; i <= n; i++) {
      parts.push(i + 'px ' + i + 'px 0 rgba(10,79,232,' + (0.9 - (i / n) * 0.35).toFixed(2) + ')');
    }
    return parts.join(', ');
  }

測定値: 累積面積10.1%、平均強度140.3、動いたフレーム22/23。

05色分離値の分だけずれて崩れるグリッチ文字

seedRand()はMath.random()ではなく固定シードの生成器で、同じ11というシードから同じ数列が毎回再生されます — 見た目はランダムでも、buildGlitchShadow()が作る揺れは実は決まった手順です。クリックすると新しいシードで作り直され、magが基準値の14倍まで220ms跳ね上がります — この9つの中で唯一、ループだけでなく実際のクリックリスナーを持つ項目です。

色分離の値だけずれて壊れるグリッチ文字
新しいタブで開く

05 · 色分離の値だけずれて壊れるグリッチ文字 — 計測 累積面積 10.158% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

GLITCH_SPLIT_PXseedRandbuildGlitchShadow
  // mag値ひとつをシード付きノイズで揺らし、赤い影と青い影を反対方向へずらす
  function buildGlitchShadow(mag) {
    var rx = (mag * (rnd() * 2 - 1)).toFixed(2);
    var ry = (mag * 0.28 * (rnd() * 2 - 1)).toFixed(2);
    var bx = (-mag * (rnd() * 2 - 1)).toFixed(2);
    var by = (-mag * 0.28 * (rnd() * 2 - 1)).toFixed(2);
    return [
      rx + 'px ' + ry + 'px 0 rgba(255,54,96,.88)',
      bx + 'px ' + by + 'px 0 rgba(58,164,255,.88)'
    ].join(', ');
  }

測定値: 累積面積10.2%、平均強度144.0、動いたフレーム23/23。

06明るさ値が層ごとに異なり光沢がまわる金属文字

buildSheenShadow()は七つの層それぞれを少しずつ位相のずれたサインカーブに通し、角度の値を動かすとハイライトが一点で動くのではなく、層を伝って上下に流れる帯として見えます。「金属」という言葉はプレミアムなブランドロゴタイプが求める見え方を指しているだけで、特定の素材名や商標は使っていません。

層ごとに明るさの値が変わり光沢が乗る金属文字
新しいタブで開く

06 · 層ごとに明るさの値が変わり光沢が乗る金属文字 — 計測 累積面積 10.386% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

SHEEN_LAYER_COUNTbuildSheenShadowrenderSheen
  // 角度値ひとつで層ごとの明るさをサイン曲線に沿って変え、上と下で光り方を変える
  // 金属光沢を作る。位相がずれるほど明るい帯が層を伝って流れる
  function buildSheenShadow(angleDeg) {
    var rad = (angleDeg * Math.PI) / 180;
    var parts = [];
    for (var i = 1; i <= SHEEN_LAYER_COUNT; i++) {
      var phase = rad + (i / SHEEN_LAYER_COUNT) * Math.PI * 1.4;
      var bright = (Math.sin(phase) + 1) / 2;
      var l = Math.round(22 + bright * 58);
      var y = (i * 6.8).toFixed(2);
      parts.push('0px ' + y + 'px 0 hsl(220 10% ' + l + '%)');
    }
    return parts.join(', ');
  }

測定値: 累積面積10.4%、平均強度43.9、動いたフレーム23/23。

07層の値ごとに紙が一枚ずつ重なったような文字

buildPaperShadow()のループは一段ごとに影を二枚ずつ積みます — フラットな紙の色と、その下のやわらかい影の縁 — 層の偶奇で暖色と寒色の紙色を交互に切り替えるので、カードニュースのタイトルは最大六枚のばらけた紙のように見えます。動いているのは層の数そのもので、1なら一枚、PAPER_LAYER_COUNT(6)なら全段です。

層の値ごとに紙を一枚ずつ重ねたような文字
新しいタブで開く

07 · 層の値ごとに紙を一枚ずつ重ねたような文字 — 計測 累積面積 11.642% · 動いたフレーム 18/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

PAPER_LAYER_COUNTbuildPaperShadowrenderPaper
  // 層値nひとつから色と距離が異なる紙の層をn枚重ねる —
  // 枚数が増えるほど紙を何枚も重ねたように見える
  function buildPaperShadow(n) {
    var parts = [];
    for (var i = 1; i <= n; i++) {
      var d = (i * OFFSET_STEP_PX).toFixed(2);
      var warm = i % 2 === 0;
      var sheet = warm ? 'rgba(255,247,230,.92)' : 'rgba(246,238,222,.92)';
      parts.push(d + 'px ' + d + 'px 0 ' + sheet);
      parts.push(d + 'px ' + (Number(d) + 1.4).toFixed(2) + 'px 2px rgba(23,20,26,' + (0.22 - i * 0.02).toFixed(2) + ')');
    }
    return parts.join(', ');
  }

測定値: 累積面積11.6%、平均強度145.0、動いたフレーム18/23。

08スクロール値の分だけ影が育つ文字

buildScrollShadow()はタイマーでは動きません — ratioFromScroll()がビューポート自身のscrollTopをscrollHeightと比べて実際のscrollイベントから読むので、長い紹介ページのセクション見出しは読者が実際にスクロールした分だけ影を伸ばします。本物のwheel・touchstart・keydownが起きるまでは、台本通りのループが同じ比率を演じてGIFが0%のまま止まって見えないようにし、実際のスクロールが一度でも起きればそちらに完全に置き換わります。

スクロール値の分だけ影が育つ文字
新しいタブで開く

08 · スクロール値の分だけ影が育つ文字 — 計測 累積面積 9.894% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

SCROLL_MAX_PXbuildScrollShadowrenderScroll
  // スクロール進行値(ratio 0〜1)ひとつをそのまま影の距離値に渡し、下るほど影が育つ
  function buildScrollShadow(ratio) {
    var d = (ratio * SCROLL_MAX_PX).toFixed(2);
    return [
      (d * 0.4).toFixed(2) + 'px ' + d + 'px 0 rgba(10,79,232,.5)',
      (d * 0.4).toFixed(2) + 'px ' + (d * 2).toFixed(2) + 'px ' + (4 + ratio * 10).toFixed(1) + 'px rgba(10,79,232,' + (0.28 + ratio * 0.3).toFixed(2) + ')'
    ].join(', ');
  }

  function renderScroll(ratio) {
    word.style.textShadow = buildScrollShadow(ratio);
    value.textContent = Math.round(ratio * 100) + '%';
  }

  function ratioFromScroll() {
    var max = viewport.scrollHeight - viewport.clientHeight;
    return max > 0 ? Math.max(0, Math.min(1, viewport.scrollTop / max)) : 0;
  }

  viewport.addEventListener('scroll', function () {
    renderScroll(ratioFromScroll());
  });
  ['wheel', 'touchstart', 'keydown'].forEach(function (ev) {
    viewport.addEventListener(ev, function () { hasUser = true; }, { passive: true });
  });

測定値: 累積面積9.9%、平均強度66.5、動いたフレーム23/23。

09カーソルが乗ると深さの値が押し上がる文字

buildLiftShadow()は0〜1の近接度値からオフセットとぼかしの両方を同時に押し上げ、curP += (targetP - curP) * LERPという別の行がその値自体をなめらかにならすので、ポートフォリオのプロジェクトカードタイトルはポインターが乗った瞬間に満杯の深さへ飛びつきません。フォーカスとホバーは同じtargetPを動かすので、キーボードでタブ移動してもマウスと同じだけ持ち上がります。

カーソルが乗ると深さの値が押し上がる文字
新しいタブで開く

09 · カーソルが乗ると深さの値が押し上がる文字 — 計測 累積面積 11.546% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

HOVER_LIFT_PXbuildLiftShadowrenderLift
  // 近接度値(p 0〜1)ひとつで影のオフセットとぼかしを一緒に押し上げ、文字が背景から
  // 浮き上がって見えるようにする。深さの基準単位はHOVER_LIFT_PX
  function buildLiftShadow(p) {
    var y = (p * HOVER_LIFT_PX * 1.6).toFixed(2);
    var y2 = (p * HOVER_LIFT_PX * LIFT_MULTIPLIER).toFixed(2);
    var blur = (4 + p * HOVER_LIFT_PX * 6.5).toFixed(1);
    return [
      '0px ' + y + 'px 0 rgba(10,79,232,' + (0.55 + p * 0.3).toFixed(2) + ')',
      '0px ' + y2 + 'px ' + blur + 'px rgba(10,79,232,' + (0.22 + p * 0.36).toFixed(2) + ')'
    ].join(', ');
  }

測定値: 累積面積11.5%、平均強度48.3、動いたフレーム23/23。

値が影の形を変える場所 — 層の数・オフセット距離・色の分離

項目 累積面積 平均強度 動いたフレーム
01 押し出し層 10.4% 26.0 22/23
02 凹凸(明暗) 10.9% 129.2 23/23
03 八方向の輪郭 10.6% 140.2 21/23
04 対角線のレトロな尾 10.1% 140.3 22/23
05 RGB分離グリッチ 10.2% 144.0 23/23
06 層状の金属光沢 10.4% 43.9 23/23
07 紙の重なり 11.6% 145.0 18/23
08 スクロール連動の深さ 9.9% 66.5 23/23
09 ホバーで持ち上がる深さ 11.5% 48.3 23/23

9つの中で平均強度が最も高いのは07(145.0)で、動いたフレーム数は最も少ない18/23です — PAPER_LAYER_COUNTはループの中で6段の整数状態しか取らないので、09のcurPのようになめらかに補間される値より一段あたりのピクセルの飛び幅が大きくなります。この差の大半は三つの定数が担っています — SHADOW_LAYER_COUNT(1番)はそもそも何枚のオフセットコピーが存在するかを、GLITCH_SPLIT_PX(5番)は二色がどれだけ離れるかを、HOVER_LIFT_PX(9番)はひとつのインタラクションがオフセットとぼかしをどれだけ押し上げるかを決めます — css text shadow multipleと呼べるほど何層も重ねるこれらの関数は、結局この三種類の数値のどれかを別の速さで読んでいるだけです。

どこで壊れるか — 動作を減らす設定・低スペック端末・長いテキストの折り返し

9つの.ex__word系ルールはすべてwhite-space: nowrapを指定しているので、コンテナより長い見出しは折り返さず、はみ出すか周囲に切り取られます。9つのどの関数もオフセットは行の高さや列幅ではなく一枚のピクセル数として書かれているので、二行目のためにずれを計算し直すことはありません。デモの一文字か二文字の単語では問題になりませんが、狭いスマホ画面で実際の見出しが三語・四語と長くなった瞬間、clamp()がフォントサイズを縮めてもレイアウトの規則自体は変わらないので効かなくなります。

層の数はただの見た目ではなく、フレームごとのコストでもあります — 1番のバリエーションはSHADOW_LAYER_COUNTを14まで押し上げられ、3番は常に八層、7番は紙の層ひとつにつき影を二枚描きます。clamp()が現在描いている大きさのグリフ輪郭を、ループが動いている間ずっと毎フレーム塗り直すということです。低スペックの端末では、8番の二層構成(実際のscrollイベントでしか計算し直さない)より、連続したrequestAnimationFrameループで十数層以上を140pxのtext-shadowに描くほうが、フレームあたりの塗り作業ははっきり重くなります。

prefers-reduced-motion: reduceはすべてのrequestAnimationFrameループを止め、各パターンをひとつの固定状態に留めます — 多くは最大値(1・3・4・6・7番は最大値へ直行)ですが、8番はもともとタイマーで動いていなかったので例外、9番は実際のホバーやフォーカスには応答し続けるもののLERPの補間なしで満杯まで一気に跳びます。正直に言っておくべき例外が5番です — モーション低減設定の下でもクリックすれば基準値の14倍まで220msの単発バーストが起きます。これは連続したアニメーションではなく、クリックという操作に対する一回きりの状態変化なので、無効化せずそのまま残しています。9つすべてのindex.html・style.scssを開けるkdbb8nchはこの段落にわざと置いています — ここまで読んだ人は、九つの定数のうちどれを最初に変えたいかもう分かっているはずです。

prefers-reduced-motionのアクセシビリティが3つのバリエーションでも変わらない理由

バリエーション 値の変化 見え方の変化
立体の層数値を調整する SHADOW_LAYER_COUNT 6 → 14 値を減らすと文字は薄く平たい立体に見え、増やすと層が幾重にも重なりずっと厚い立体に見える
グリッチの色分離距離値を調整する GLITCH_SPLIT_PX 2 → 6 値を減らすと色はわずかにずれてほのかに揺らぎ、増やすと色が大きく離れてより荒く壊れたように見える
ホバー時の深さ上昇値を調整する HOVER_LIFT_PX 4 → 12 値を減らすとホバーしても影はわずかしか伸びず変化が分かりにくく、増やすと影が大きく伸び文字が画面から浮いたように見える

3つのバリエーションのどれも、上で触れたモーション低減設定の挙動そのものは変えません — SHADOW_LAYER_COUNTが6でも14でもprefers-reduced-motion: reduceの下では最大値へ直行しますし、HOVER_LIFT_PXが4でも12でもpointerenterとfocusにはなめらかな補間なしで反応します。どの定数を動かしても9つのアクセシビリティ面で変わらないことがひとつあります — 9つの文字はどれもaria-hiddenを持たないので、スクリーンリーダーは文字そのものを読み上げ、背後に積まれた影の層には一切触れません。影は読み飛ばしてかまわない装飾のままです。

css text-shadow ジェネレーターや既定のtext-shadowと、この9パターンは何が違うか?

手で書くtext-shadowは数枚のコンマ区切りレイヤーを一度決めて終わりです — デザイナーがオフセットを一度選べば、CSSはそれ以降変わりません。上の九つはどれも同じプロパティを毎フレームか毎イベントごとにひとつの数値から書き直しているだけで、実際の違いはCSSプロパティ自体(9つとも結局.style.textShadowを書いています)ではなく、その数値を人が選ぶかループが選ぶかです。値を一度だけ決めて出力する生成ツールは前者の代わりにしかならず、ループが毎フレーム読み替える9つの関数そのものの代わりにはなりません。MDNのfilterリファレンスがdrop-shadow()という最も近いCSS単体の代替を扱っています — グリフの実際のアルファ形状に沿う点はtext-shadowと違いますが、重ねがけはできないので、上の9つのどれも使っていません。

css text-shadowを触る前によくある質問

css text-shadowを何層も重ねると文字の描画は重くなりますか?

層の数とグリフの大きさに比例して重くなります — 各層は同じ文字の背後にある別々の塗りパスなので、3番の固定八層は2番の二層より毎フレームのコストが高く、1番のバリエーションでSHADOW_LAYER_COUNTを14まで上げると既定の6のおよそ倍近いコストになります。9つのどれもレイアウトは変えません(text-shadowは要素の箱を変更しません)から、コストはリフローではなくペイントにとどまります — 遅い端末では確かに重くなりますが、上限のある重さです。

5番のグリッチはなぜクリックするたびに崩れ方が変わるのですか?

seedRand()が固定シードの生成器で、毎回同じ11というシードから始まる決定的な数列を返すからです。クリックするとseedの式自体が新しい数へ書き換わり、そこからseedRand()を作り直すので、次に返ってくる数列がそのつど変わって見えます — 本物の乱数ではなく、毎回違うスタート地点から同じ計算をやり直しているだけです。

css text shadow syntaxの基本だけ知りたい場合、この9パターンは複雑に見えませんか?

text-shadowの構文自体はoffset-x offset-y blur-radius colorという4つの値だけで、9つのどのパターンも結局この4つに数値を書き込んでいるにすぎません。複雑に見えるのはループやseedRand()のような周辺のJavaScriptのほうで、まず1番か2番のbuildExtrudeShadow()・buildEmbossShadow()だけを読めば、残り7つも同じ4値構文の上に別の計算を足しているだけだと分かります。

同じく「ひとつの値が場面全体を動かす」という考え方でまとめた編としては、【コピペOK】CSS ネオン・グロー効果9選が色とぼかしを深さの代わりに動かしていて近く、CSS 下線 アニメーション9選 — 一行でコピペOKは2番の明暗が逆になる凹凸と見比べる価値があります — どちらも文字そのものには触れずに、背景に対する読み方だけを変えるからです。そして【コピペOK】css テキスト アニメーション 9選はもっとも近い親戚です — あちらは文字そのものを動かして際立たせ、ここでは影だけを動かして際立たせています。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。