<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Reinvent Notes Blog</title>
        <link>https://notes.rewheel.dev/blog</link>
        <description>個人的な技術発信ブログのフィード</description>
        <lastBuildDate>Sun, 14 Jun 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>ja</language>
        <copyright>© 2026 rasshii</copyright>
        <item>
            <title><![CDATA[日本語 Web フォントと Core Web Vitals — LCP と CLS を実測で直す]]></title>
            <link>https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts</link>
            <guid>https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts</guid>
            <pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[技術的 SEO は満点なのに Lighthouse の Performance だけ伸びない。原因を chrome-devtools の実測で追うと、LCP の主犯は日本語 Web フォントの render-blocking、CLS の主犯は寸法未指定の navbar ロゴだった。フォント swap 犯人説を実測で棄却し、size-adjust を入れずに CLS を 0 にするまでの記録。]]></description>
            <content:encoded><![CDATA[<p>このサイト (notes.rewheel.dev) の SEO を点検したとき、技術的な項目はほぼ満点でした。sitemap も robots.txt も canonical も OGP も JSON-LD も揃っていて、Lighthouse の SEO スコアは全ページ 100。それなのに Performance だけが 58〜63 で止まっていました。</p>
<p>調べていくと、足を引っ張っていたのは Core Web Vitals の 2 つ — LCP (読み込みの速さ) と CLS (レイアウトのずれ) でした。そして犯人は、最初に疑った場所とは違うところにいました。LCP は日本語 Web フォントの読み込み方、CLS は本文の画像ではなくヘッダーのロゴ。この記事は、その犯人を実測で特定し、効かない対策を入れずに直すまでの記録です。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>対象読者</div><div class="admonitionContent_BuS1"><p>Web フロントエンドの基本 (HTML/CSS、ブラウザの読み込み) が分かる方を想定しています。Docusaurus 固有の話も出ますが、render-blocking や CLS の考え方は静的サイト全般に通用します。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-core-web-vitals-を直すのか">なぜ Core Web Vitals を直すのか<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E3%81%AA%E3%81%9C-core-web-vitals-%E3%82%92%E7%9B%B4%E3%81%99%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ Core Web Vitals を直すのか への直接リンク" title="なぜ Core Web Vitals を直すのか への直接リンク" translate="no">​</a></h2>
<p>Core Web Vitals は Google が定義したユーザー体験の指標で、検索ランキングのシグナルにもなっています。3 つの指標があり、2026 年時点の「良好 (good)」のしきい値は次のとおりです (<a href="https://web.dev/articles/vitals" target="_blank" rel="noopener noreferrer" class="">web.dev</a> より)。</p>
<table><thead><tr><th>指標</th><th>測るもの</th><th>good</th><th>poor</th></tr></thead><tbody><tr><td>LCP (Largest Contentful Paint)</td><td>主要コンテンツが描画されるまでの時間</td><td>≤ 2.5s</td><td>&gt; 4.0s</td></tr><tr><td>CLS (Cumulative Layout Shift)</td><td>読み込み中の累積レイアウトずれ</td><td>&lt; 0.1</td><td>&gt; 0.25</td></tr><tr><td>INP (Interaction to Next Paint)</td><td>操作への応答の速さ</td><td>&lt; 200ms</td><td>&gt; 500ms</td></tr></tbody></table>
<p>「技術的 SEO が満点なら十分では」とも思えますが、SEO スコアはあくまで <em>機械可読性</em> の点検です。タイトルや構造化データが正しくても、ページが遅かったり読み込み中にガクッとずれたりすれば、ユーザーは離脱しランキングも伸びません。CWV は、その <em>体感</em> の側を数値化したものです。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>このサイトの前提</div><div class="admonitionContent_BuS1"><p><a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> v3.10 で構築した静的サイトで、本文フォントに日本語の Klee One を使っています。日本語 Web フォントはグリフ数が多くファイルが大きいため、CWV では英語サイトと違うクセが出ます。そこが今回の肝でした。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ベースライン--何が未達だったのか">ベースライン<span class="heading-subtitle">何が「未達」だったのか</span><a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E3%83%99%E3%83%BC%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%B3--%E4%BD%95%E3%81%8C%E6%9C%AA%E9%81%94%E3%81%A0%E3%81%A3%E3%81%9F%E3%81%AE%E3%81%8B" class="hash-link" aria-label="ベースライン--何が未達だったのか への直接リンク" title="ベースライン--何が未達だったのか への直接リンク" translate="no">​</a></h2>
<p>まず本番 (notes.rewheel.dev) を Lighthouse のモバイル設定 (CPU 4x スロットリング) で計測しました。</p>
<table><thead><tr><th>ページ</th><th>Performance</th><th>LCP</th><th>CLS</th><th>SEO</th></tr></thead><tbody><tr><td>トップ (/)</td><td>58</td><td><strong>9.3s</strong></td><td>0</td><td>100</td></tr><tr><td>ブログ記事</td><td>62</td><td><strong>4.4s</strong></td><td><strong>0.287</strong></td><td>100</td></tr><tr><td>ドキュメント</td><td>63</td><td><strong>4.1s</strong></td><td><strong>0.305</strong></td><td>100</td></tr></tbody></table>
<p>LCP は全ページが poor (4 秒超)、CLS はブログとドキュメントが poor (0.25 超)。SEO は満点。問題の所在がはっきりしました。<strong>Performance、それも LCP と CLS</strong> です。</p>
<p>ここから 2 つの犯人を別々に追いました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="犯人-1--lcp-を遅らせていた-render-blocking-なフォント">犯人 1<span class="heading-subtitle">LCP を遅らせていた render-blocking なフォント</span><a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E7%8A%AF%E4%BA%BA-1--lcp-%E3%82%92%E9%81%85%E3%82%89%E3%81%9B%E3%81%A6%E3%81%84%E3%81%9F-render-blocking-%E3%81%AA%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88" class="hash-link" aria-label="犯人-1--lcp-を遅らせていた-render-blocking-なフォント への直接リンク" title="犯人-1--lcp-を遅らせていた-render-blocking-なフォント への直接リンク" translate="no">​</a></h2>
<p>Lighthouse の <code>render-blocking-insight</code> が指していたのは、<code>&lt;head&gt;</code> で同期読み込みしている CSS でした。中でも重かったのが Google Fonts の CSS です。</p>
<p>当時の指定はこの 4 ファミリを 1 つの URL でまとめて読み込んでいました。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">https://fonts.googleapis.com/css2?family=Klee+One&amp;family=Noto+Sans+JP&amp;family=Inter&amp;family=JetBrains+Mono</span><br></div></code></pre></div></div>
<p>問題は日本語フォント (Klee One / Noto Sans JP) です。日本語は常用漢字だけで 2,000 字を超え、フォントは数百のサブセットファイルに分割して配信されます。CSS 本体だけで約 150KB あり、しかもこれが <strong>render-blocking</strong> — つまりブラウザはこの CSS を取得し終えるまで本文を描画できません。CPU とネットワークが絞られたモバイルでは、ここで数秒が溶けていました。</p>
<p>ブラウザの読み込みを図にするとこうなります。同期読み込みでは、フォント CSS が First Paint の手前に割り込んでいます。</p>
<!-- -->
<p>さらに 2 つの無駄がありました。</p>
<ul>
<li class=""><strong>Inter は読み込んでいるのに使っていなかった</strong>。<code>font-family</code> のどこにも Inter は登場せず、純粋に転送量を増やすだけのファミリでした。</li>
<li class=""><strong>KaTeX の CSS も全ページで同期読み込み</strong>していました。数式は一部の記事にしか出ないのに、トップページでも render-blocking になっていました。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="犯人-2--cls-を起こしていたのは画像ではなくロゴ">犯人 2<span class="heading-subtitle">CLS を起こしていたのは画像ではなくロゴ</span><a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E7%8A%AF%E4%BA%BA-2--cls-%E3%82%92%E8%B5%B7%E3%81%93%E3%81%97%E3%81%A6%E3%81%84%E3%81%9F%E3%81%AE%E3%81%AF%E7%94%BB%E5%83%8F%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%83%AD%E3%82%B4" class="hash-link" aria-label="犯人-2--cls-を起こしていたのは画像ではなくロゴ への直接リンク" title="犯人-2--cls-を起こしていたのは画像ではなくロゴ への直接リンク" translate="no">​</a></h2>
<p>CLS の方は、最初フォントを疑いました。Web フォントは読み込み完了時にフォールバックから切り替わる (swap) ので、字幅が変わって行が組み直され、レイアウトがずれる — というのは典型的な CLS 要因です。</p>
<p>ところが数字が合いません。CLS が高いのはブログ (0.287) とドキュメント (0.305) で、ほぼ同じ値。もしフォント swap が主犯なら本文量に応じて差が出るはずですが、そうなっていない。そして決定的だったのが、<strong>hero 画像を持たないドキュメントページでも CLS 0.305 だった</strong>ことです。本文中の画像が犯人なら、画像の無いページでは下がるはず。下がっていない。</p>
<p>つまり犯人は <strong>全ページに共通する要素</strong>。それは <code>&lt;head&gt;</code> の次に来る navbar、その中のロゴでした。</p>
<!-- -->
<p><code>navbar</code> のロゴ <code>&lt;img&gt;</code> に <code>width</code>/<code>height</code> が無いと、ブラウザは読み込み前にロゴの占める領域を確保できません。いったん高さ 0 で本文を上に詰めて描画し、ロゴ画像が届いた瞬間に領域を確保して本文を下へ押し下げる。これが累積されて CLS になっていました。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>教訓</div><div class="admonitionContent_BuS1"><p>CLS の調査は「本文の派手な要素」から疑いがちですが、まず <strong>全ページ共通のヘッダー/フッターに寸法未指定の画像が無いか</strong>を見るほうが早いことが多いです。共通要素は全ページの CLS を底上げします。</p></div></div>
<p>フォント swap については、この時点では「副次的に効いているかもしれない」と保留にしました。後で実測して決着をつけます (伏線)。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="対策の実装">対策の実装<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E5%AF%BE%E7%AD%96%E3%81%AE%E5%AE%9F%E8%A3%85" class="hash-link" aria-label="対策の実装 への直接リンク" title="対策の実装 への直接リンク" translate="no">​</a></h2>
<p>特定できた原因に対して、5 つ手を入れました。前半 3 つが CWV へ直接効く本丸です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-未使用の-inter-を削除">1. 未使用の Inter を削除<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#1-%E6%9C%AA%E4%BD%BF%E7%94%A8%E3%81%AE-inter-%E3%82%92%E5%89%8A%E9%99%A4" class="hash-link" aria-label="1. 未使用の Inter を削除 への直接リンク" title="1. 未使用の Inter を削除 への直接リンク" translate="no">​</a></h3>
<p><code>font-family</code> に存在しない Inter を Google Fonts の URL から外しました。見た目はまったく変わらず、転送量だけが減ります。リスクゼロの純利益です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-navbar-ロゴに-widthheight-を付与">2. navbar ロゴに width/height を付与<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#2-navbar-%E3%83%AD%E3%82%B4%E3%81%AB-widthheight-%E3%82%92%E4%BB%98%E4%B8%8E" class="hash-link" aria-label="2. navbar ロゴに width/height を付与 への直接リンク" title="2. navbar ロゴに width/height を付与 への直接リンク" translate="no">​</a></h3>
<p>CLS の主犯への直接の対処です。Docusaurus の <code>navbar.logo</code> は <code>width</code>/<code>height</code> を受け付けます。</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">docusaurus.config.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">navbar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  logo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    alt</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Reinvent Notes Logo'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    src</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'img/logo.svg'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// CLS 対策: 読み込み前に領域を確保する (実測した CLS の主因)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    width</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    height</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>これでブラウザは読み込み前にロゴ領域を予約でき、画像到着時の押し下げが起きなくなります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-フォントと-katex-の-css-を非同期化">3. フォントと KaTeX の CSS を非同期化<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#3-%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%81%A8-katex-%E3%81%AE-css-%E3%82%92%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%8C%96" class="hash-link" aria-label="3. フォントと KaTeX の CSS を非同期化 への直接リンク" title="3. フォントと KaTeX の CSS を非同期化 への直接リンク" translate="no">​</a></h3>
<p>LCP の主犯への対処です。render-blocking を解消するため、<code>preload</code> で取得しておき、読み込み完了時に <code>rel="stylesheet"</code> へ差し替える定番パターンに移しました。Docusaurus では <code>headTags</code> で <code>&lt;head&gt;</code> にタグを注入できます。</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">docusaurus.config.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">headTags</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tagName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'link'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    attributes</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      rel</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preload'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">as</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'style'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      href</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://fonts.googleapis.com/css2?family=Klee+One:wght@400;600&amp;family=Noto+Sans+JP:wght@400;500;700&amp;family=JetBrains+Mono:wght@400;500&amp;display=swap'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// 読み込み完了時に stylesheet へ昇格させ、render-blocking を回避する</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      onload</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"this.onload=null;this.rel='stylesheet'"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// KaTeX CSS も同じ要領で非同期化（数式の無いページの render-blocking を解消）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// JS 無効環境向けに &lt;noscript&gt; で同期版のフォールバックも併記する</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><br></div></code></pre></div></div>
<p>読み込みのタイムラインは、同期から非同期へこう変わります。</p>
<!-- -->
<p>修正後はフォールバックの <code>system-ui</code> でまず描画し、フォントが届いてから差し替えます。<code>display=swap</code> を付けているので、フォント取得中もテキストは見えたままです (FOIT を避けられます。詳しくは <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display" target="_blank" rel="noopener noreferrer" class="">MDN: font-display</a>)。</p>
<p>あわせて、これまで <code>custom.css</code> の <code>@import</code> で読んでいたフォントも <code>headTags</code> 側へ寄せ、CSS 内 <code>@import</code> による直列読み込みを解消しました。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-画像を-webp-化">4. 画像を WebP 化<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#4-%E7%94%BB%E5%83%8F%E3%82%92-webp-%E5%8C%96" class="hash-link" aria-label="4. 画像を WebP 化 への直接リンク" title="4. 画像を WebP 化 への直接リンク" translate="no">​</a></h3>
<p>トップの hero 画像と OGP の social-card を PNG から WebP へ差し替え、旧 PNG は削除しました。hero は約 90% の軽量化です。LCP 候補になりうる大きな画像の転送量を下げる効果があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-llmstxt-の生成おまけ">5. llms.txt の生成（おまけ）<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#5-llmstxt-%E3%81%AE%E7%94%9F%E6%88%90%E3%81%8A%E3%81%BE%E3%81%91" class="hash-link" aria-label="5. llms.txt の生成（おまけ） への直接リンク" title="5. llms.txt の生成（おまけ） への直接リンク" translate="no">​</a></h3>
<p>CWV とは別軸ですが、AI クローラ向けに <code>/llms.txt</code> をビルド時生成するプラグインも足しました。生成 AI 経由の流入 (GEO/AEO) を意識した対応です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="検証--スロットリングなしでは-cls-は測れない">検証<span class="heading-subtitle">スロットリングなしでは CLS は測れない</span><a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E6%A4%9C%E8%A8%BC--%E3%82%B9%E3%83%AD%E3%83%83%E3%83%88%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AA%E3%81%97%E3%81%A7%E3%81%AF-cls-%E3%81%AF%E6%B8%AC%E3%82%8C%E3%81%AA%E3%81%84" class="hash-link" aria-label="検証--スロットリングなしでは-cls-は測れない への直接リンク" title="検証--スロットリングなしでは-cls-は測れない への直接リンク" translate="no">​</a></h2>
<p>修正の効果は、測り方を間違えると見えません。最初、ローカルのビルドを制限なしで計測したら CLS は 0.01。これでは元の 0.3 がどこへ行ったのか分かりません。</p>
<p>種明かしは <strong>CLS はネットワーク/CPU の遅さに強く依存する</strong>点にあります。回線が速いとフォントもロゴも一瞬で届き、シフトが起きる前に描画が固まってしまう。本番のモバイル相当の遅さを再現しないと、そもそも問題が再現しないのです。</p>
<p>そこで <a href="https://developer.chrome.com/docs/devtools" target="_blank" rel="noopener noreferrer" class="">Chrome DevTools (MCP 経由)</a> の performance trace で、本番相当のスロットリングをかけて計測しました。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">CPU:      4x スロットリング</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Network:  Slow 4G</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Viewport: 360 x 640 @2x（モバイル・タッチ）</span><br></div></code></pre></div></div>
<p>この条件で、ロゴに寸法を付けたあとのドキュメントページを再計測した結果がこれです。</p>
<table><thead><tr><th>ページ</th><th>修正前 CLS</th><th>修正後 CLS</th></tr></thead><tbody><tr><td>ドキュメント (カテゴリ)</td><td>0.305</td><td><strong>0.00</strong></td></tr><tr><td>ドキュメント (本文 250KB)</td><td>—</td><td><strong>0.00</strong></td></tr></tbody></table>
<p>本文量の多いページでも 0.00。DevTools の CLSCulprits インサイトも「該当するレイアウトシフトなし」で、シフトそのものが消えました。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>LCP の本番値について</div><div class="admonitionContent_BuS1"><p>LCP もローカルでは大きく改善しましたが、localhost には CDN までの往復遅延が無いため、この数値を本番の LCP と直接比べることはできません。本番 after の LCP は別途あらためて計測する必要があります (この記事では断定しません)。確実に言えるのは「render-blocking だったフォント/KaTeX CSS を critical path から外した」という構造の変化までです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="size-adjust-を入れなかった理由">size-adjust を入れなかった理由<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#size-adjust-%E3%82%92%E5%85%A5%E3%82%8C%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E7%90%86%E7%94%B1" class="hash-link" aria-label="size-adjust を入れなかった理由 への直接リンク" title="size-adjust を入れなかった理由 への直接リンク" translate="no">​</a></h2>
<p>ここで、保留にしていたフォント swap の話に戻ります。</p>
<p>日本語フォントの CLS 対策としてよく挙がるのが、フォールバックフォントに <code>size-adjust</code> を効かせて Web フォントとメトリクスを揃える手法です。swap の瞬間に字幅・行高が変わらないようにして、組み直しによるシフトを防ぐものです。「日本語フォント + CLS」と来れば、これを入れるのが定石にも見えます。</p>
<p>しかし今回は <strong>入れませんでした</strong>。理由は、実測でフォント swap が CLS にほぼ寄与していないと分かったからです。ロゴに寸法を付けただけで、本文量の多いページでも CLS は 0.00。swap で組み直しが起きているなら、ここに値が残るはずですが、残っていません。</p>
<p>構造的にも説明がつきます。本文の行の高さは、<code>custom.css</code> でこう決めています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector pseudo-class" style="color:#00009f">:root</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token variable" style="color:#36acaa">--ifm-font-size-base</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token variable" style="color:#36acaa">--ifm-line-height-base</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.8</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">/* 単位なし */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>line-height</code> が <strong>単位なし (1.8)</strong> で、<code>font-size</code> が固定 (16px)。すると 1 行の高さは常に <code>16px × 1.8 = 28.8px</code> で、どのフォントに swap しても行ボックスの高さは変わりません。縦方向の組み直しが起きにくい構造になっていたわけです。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>計測してから決める</div><div class="admonitionContent_BuS1"><p><code>size-adjust</code> は正しい手法ですが、それが効くのは「フォント swap が実際に CLS を起こしている」ときだけです。今回はその前提が成立していませんでした。<strong>効くか分からない対策を「念のため」入れると、検証されないコードが残り、メンテナンスの負債になります。</strong> 目的の指標 (CLS) を計測し、達成済みなら追加の防御は入れない — そう判断しました。</p></div></div>
<p>ひとつ正直に補足すると、今回の after 計測はフォントがキャッシュ済みの状態 (warm cache) での swap を見ています。完全な初回訪問 (cold) では、字幅の差で行が再ワラップして微小な CLS が出る理論的な余地は残ります。ただし以前 cold に近い高速条件で測ったときも CLS は 0.01 で、good のしきい値 0.1 の 1/10。実害が出る水準ではないと判断しています。気になる場合は cold cache を分離した計測でさらに詰められます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://notes.rewheel.dev/blog/core-web-vitals-japanese-fonts#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>このサイトの Core Web Vitals を直して分かったことは、突き詰めると 3 点です。</p>
<ul>
<li class=""><strong>LCP の主犯は日本語 Web フォントの render-blocking</strong> だった。150KB の同期 CSS を <code>preload → swap</code> で非同期化し、未使用の Inter を外して critical path から退けた。</li>
<li class=""><strong>CLS の主犯はフォントではなく、寸法未指定の navbar ロゴ</strong> だった。画像の無いページでも CLS が高いことが決め手で、<code>width</code>/<code>height</code> を付けるだけでシフトが消えた。</li>
<li class=""><strong>size-adjust は計測の結果として入れなかった</strong>。単位なし <code>line-height</code> のおかげで swap による組み直しが小さく、CLS は既に 0。効かない対策を足さないことも最適化のうち。</li>
</ul>
<p>「日本語フォントが重い → とにかくフォント対策」と短絡せず、LCP と CLS をそれぞれ計測して別々の犯人を特定したのが効きました。次にやるなら、本番 after の LCP 再計測と、cold cache を分離した CLS の詰めです。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>関連リソース</div><div class="admonitionContent_BuS1"><ul>
<li class=""><a href="https://web.dev/articles/vitals" target="_blank" rel="noopener noreferrer" class="">Web Vitals (web.dev)</a>：各指標の定義としきい値</li>
<li class=""><a href="https://web.dev/articles/lcp" target="_blank" rel="noopener noreferrer" class="">Largest Contentful Paint (web.dev)</a>：LCP の内訳と改善</li>
<li class=""><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display" target="_blank" rel="noopener noreferrer" class="">font-display (MDN)</a>：swap と FOIT/FOUT</li>
<li class=""><a href="https://docusaurus.io/docs/seo" target="_blank" rel="noopener noreferrer" class="">Docusaurus: head metadata</a>：<code>headTags</code> での <code>&lt;head&gt;</code> 注入</li>
</ul></div></div>]]></content:encoded>
            <category>performance</category>
            <category>core-web-vitals</category>
            <category>docusaurus</category>
            <category>web-font</category>
            <category>seo</category>
        </item>
        <item>
            <title><![CDATA[hero に車が走るアニメーションを実装した]]></title>
            <link>https://notes.rewheel.dev/blog/hero-car-animation</link>
            <guid>https://notes.rewheel.dev/blog/hero-car-animation</guid>
            <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Docusaurus のトップ hero に走る車のアニメーションを自前実装した記録。SVG の線画を svgr でコンポーネント化し、タイヤ回転と背景パララックスを純 CSS で動かす。CSS mask での背景描画・継ぎ目のない無限ループ・テーマ色追従・prefers-reduced-motion 対応まで初学者向けに解説する。]]></description>
            <content:encoded><![CDATA[<p>このサイト「Reinvent Notes」のトップページを開くと、車が走り抜けていくアニメーションが目に入ります。Docusaurus の hero に自前で実装したメインビジュアルで、SVG の線画を React コンポーネント化し、タイヤの回転と背景のパララックスをすべて CSS で動かしています。</p>
<p>派手なアニメーションライブラリは使わず、svgr (Docusaurus 同梱) と純粋な CSS だけで作りました。テーマのライト / ダーク切り替えにも追従し、<code>prefers-reduced-motion</code> を有効にしている人には静止画として見せています。</p>
<p>この記事は、<strong>Web 開発を始めたばかりの人でも最後まで追えること</strong> を目標に書きました。そのため「SVG とは何か」「<code>viewBox</code> とは」「CSS の <code>mask</code> とは」といった<strong>基礎から順番に</strong>説明し、その土台の上で実装を読み解いていきます。長くなりますが、一つひとつの仕組みが分かるように噛み砕いたので、気になる章から拾い読みしても構いません。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>2026-06-13 改訂</div><div class="admonitionContent_BuS1"><p>公開後にデザインを調整したため、現行の実装に合わせて本文を更新しました。主な変更は 3 つです。ビルを 2 層にして窓を抜いたこと、街路の標識・街灯などを車と同じ線画にしたこと、ループの境目で背景がカクッとずれる問題を直したこと。とくに 3 つ目は「無限ループの章」をまるごと書き直しています。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ車なのか--reinvent-notes-とロゴの必然性">なぜ車なのか<span class="heading-subtitle">Reinvent Notes とロゴの必然性</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%81%AA%E3%81%9C%E8%BB%8A%E3%81%AA%E3%81%AE%E3%81%8B--reinvent-notes-%E3%81%A8%E3%83%AD%E3%82%B4%E3%81%AE%E5%BF%85%E7%84%B6%E6%80%A7" class="hash-link" aria-label="なぜ車なのか--reinvent-notes-とロゴの必然性 への直接リンク" title="なぜ車なのか--reinvent-notes-とロゴの必然性 への直接リンク" translate="no">​</a></h2>
<p>このブログのテーマは、サイト名そのままに <strong>「車輪の再発明 (reinventing the wheel)」</strong> です。タグラインも「車輪を再発明して学ぶ」。既存のツールや概念をあえて自分で作り直すことで仕組みを深く理解する——そういう学びを記録する場所として運営しています (この名前に至った経緯は <a class="" href="https://notes.rewheel.dev/blog/rewheel-naming">ブログのドメインを notes.rewheel.dev に変更しました</a> に書きました)。</p>
<p>このサイトのロゴは、文字通り <strong>車輪</strong> です。外周のリムと中心のハブ、そして 4 本のスポークだけのシンプルな図形で、favicon にも同じものを使っています。</p>
<p>ある日ふと、「ロゴの車輪が止まったままなのはもったいない」と思いました。トップページくらいは、この車輪を実際に回してみたい。車輪が回るなら、いっそ車体に乗せて走らせてしまおう——というわけで、hero に「車が走るアニメーション」を置くことにしました。サイトの世界観を一目で伝える装飾であり、同時に「車輪の再発明」というコンセプトを動きで表現する小さな試みです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="完成形--何が動いているのか">完成形<span class="heading-subtitle">何が動いているのか</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%8C%E6%88%90%E5%BD%A2--%E4%BD%95%E3%81%8C%E5%8B%95%E3%81%84%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="完成形--何が動いているのか への直接リンク" title="完成形--何が動いているのか への直接リンク" translate="no">​</a></h2>
<p>まず、画面で何が起きているのかを大づかみに把握しておきましょう。細かい数値はあとの章で扱うので、ここでは <strong>「どんな部品が、どう重なって、どう動くか」</strong> だけを押さえます。</p>
<p>中央に線画の車があり、前輪と後輪が回転しています。車体は左を向いていて、背景の雲・ビル・街路の標識や街灯・路面の破線が右へ流れていきます。背景が右に流れると、相対的に車が左へ進んでいるように見える——という仕掛けです。</p>
<p>色はテーマに追従します。ライトモードでは落ち着いた sage green、ダークモードでは明るい緑。背景のビルや街路、雲もすべて同じ色なので、テーマを切り替えると全体の色がまとめて変わります。</p>
<p><img decoding="async" loading="lazy" alt="ライトテーマのトップページ hero。sage green で車と背景が統一されている" src="https://notes.rewheel.dev/assets/images/hero-car-animation-light-0cdcff4075ffb50b801eba804987668a.webp" width="1280" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="同じ hero をダークテーマで開いたところ。currentColor が明るい緑に切り替わる" src="https://notes.rewheel.dev/assets/images/hero-car-animation-dark-9b44f7d76a24c7d3473b8c8f12e2650a.webp" width="1280" height="900" class="img_ev3q"></p>
<p>画面には、奥から手前へ次の部品が重なっています。いちばん奥に雲、その手前に遠景のビル、中景のビル (窓付き)、街路 (標識・街灯・街路樹・信号機)、路面、そしていちばん手前に車。これを図にすると、こうなります。</p>
<!-- -->
<p>ここで 1 つ大事なポイントがあります。この重なり順は、CSS で重なりを指定する <code>z-index</code> ではなく、<strong>HTML に書いた順番</strong> で決まっています。後に書いた要素ほど手前に重なる、というブラウザの基本ルールをそのまま使っているだけです。だから雲を最初に、車を最後に書いています。</p>
<p>「なぜ HTML の順番で重なるのか」「なぜ部品が縦に積まれず同じ場所に重なるのか」は、次の章の CSS の基礎で説明します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="css-レイアウトの基礎">CSS レイアウトの基礎<a href="https://notes.rewheel.dev/blog/hero-car-animation#css-%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%81%AE%E5%9F%BA%E7%A4%8E" class="hash-link" aria-label="CSS レイアウトの基礎 への直接リンク" title="CSS レイアウトの基礎 への直接リンク" translate="no">​</a></h2>
<p>この章では、背景や車を「同じ場所に重ねて配置する」ために使っている CSS の道具を 3 つ押さえます。実装で何度も出てくる前提知識なので、先に固めておきましょう。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="position-と要素の重なり">position と要素の重なり<a href="https://notes.rewheel.dev/blog/hero-car-animation#position-%E3%81%A8%E8%A6%81%E7%B4%A0%E3%81%AE%E9%87%8D%E3%81%AA%E3%82%8A" class="hash-link" aria-label="position と要素の重なり への直接リンク" title="position と要素の重なり への直接リンク" translate="no">​</a></h3>
<p>ふつう、HTML の要素は上から下へ順番に「積まれて」並びます。段落を 2 つ書けば、2 段に並びますよね。</p>
<p>ところが、この hero では雲もビルも車も <strong>同じ場所に重ねたい</strong>。それを実現するのが <code>position: absolute</code> です。</p>
<ul>
<li class=""><code>position: relative</code> を付けた要素は「基準点」になります。この hero では親要素 <code>.scene</code> がこれにあたります。</li>
<li class=""><code>position: absolute</code> を付けた子要素は、<strong>通常の「積み上げ」の流れから外れて</strong>、基準点 (<code>.scene</code>) を原点として自由な位置に浮かびます。</li>
</ul>
<p><code>.scene</code> の中の雲・ビル・街路・路面・車は、すべて <code>position: absolute</code> で浮いています。だから縦に積まれず、同じ <code>.scene</code> の上に重なって配置されるのです。そして重なったときの前後関係が、前章で触れた「HTML に書いた順番」で決まります。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.scene</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">position</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> relative</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">/* 子要素の配置の基準点になる */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">overflow</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hidden</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">/* はみ出した背景を枠の外に見せない */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.clouds</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">position</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> absolute</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">/* .scene を基準に浮く */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>overflow: hidden</code> は、枠 (<code>.scene</code>) からはみ出した部分を切り取って見せなくする指定です。背景は枠より広く作って流すので、これがないと横にはみ出してしまいます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-は何に対する割合か">% は何に対する割合か<a href="https://notes.rewheel.dev/blog/hero-car-animation#-%E3%81%AF%E4%BD%95%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E5%89%B2%E5%90%88%E3%81%8B" class="hash-link" aria-label="% は何に対する割合か への直接リンク" title="% は何に対する割合か への直接リンク" translate="no">​</a></h3>
<p>この実装では、位置やサイズの多くを <strong>% (パーセント)</strong> で指定しています。<code>left: 78%</code>、<code>top: 80%</code>、<code>height: 38%</code> のように。</p>
<p>CSS の % は、原則として <strong>親要素のサイズに対する割合</strong> です。雲レイヤの <code>height: 38%</code> は「親 <code>.scene</code> の高さの 38%」、タイヤの <code>left: 78%</code> は「親 <code>.car</code> の幅の 78% の位置」という意味になります。</p>
<p>% を使う利点は、<strong>画面幅が変わっても比率が保たれる</strong> ことです。スマホでも PC でも、タイヤは車体の同じ位置に付いてきます。ピクセル固定だと画面サイズごとに調整が必要ですが、% なら 1 つの値で追従してくれます。</p>
<p>ただし例外もあります。この実装では、ビルと街路のレイヤの高さだけは <strong>あえてピクセル固定</strong> にしています。なぜ % にしないのかは、無限ループの章で説明します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="after-疑似要素と-background">::after 疑似要素と background<a href="https://notes.rewheel.dev/blog/hero-car-animation#after-%E7%96%91%E4%BC%BC%E8%A6%81%E7%B4%A0%E3%81%A8-background" class="hash-link" aria-label="::after 疑似要素と background への直接リンク" title="::after 疑似要素と background への直接リンク" translate="no">​</a></h3>
<p>最後に 3 つ、背景描画でよく使う道具を紹介します。</p>
<p><strong><code>::after</code> 疑似要素</strong> は、HTML を増やさずに「その要素の後ろに、もう 1 つ仮想の子要素を生やす」CSS の機能です。この hero では、路面 <code>.road</code> に <code>.road::after</code> を生やして「流れる破線」を作っています。<code>content: ''</code> を書くと中身が空の要素が現れ、そこに自由な見た目を与えられます。</p>
<p><strong><code>background</code> 系のプロパティ</strong> は背景の塗り方を決めます。よく使うのは次の 4 つです。</p>
<ul>
<li class=""><code>background-color</code>: 背景を単色で塗る。</li>
<li class=""><code>background-image</code>: 背景に画像やグラデーションを敷く。</li>
<li class=""><code>background-size</code>: 敷いた背景 1 枚分の大きさ。</li>
<li class=""><code>background-repeat</code>: 背景を繰り返すかどうか (<code>repeat-x</code> なら横方向に繰り返す)。</li>
</ul>
<p>そして <strong><code>opacity</code></strong> は要素全体の <strong>透明度</strong> です。<code>1</code> で不透明、<code>0</code> で完全に透明。<code>0.2</code> のように小さくすると、その要素がうっすら透けます。この hero では、奥のレイヤほど薄くして奥行きを強めるのに使っています。</p>
<p>これらを組み合わせると、画像ファイルを用意しなくても CSS だけで雲や破線を描けます。詳しくは背景の章で見ていきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まずは基礎から--svg-とは何か">まずは基礎から<span class="heading-subtitle">SVG とは何か</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%81%BE%E3%81%9A%E3%81%AF%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89--svg-%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B" class="hash-link" aria-label="まずは基礎から--svg-とは何か への直接リンク" title="まずは基礎から--svg-とは何か への直接リンク" translate="no">​</a></h2>
<p>車体とタイヤの素材は SVG という形式の画像です。アニメーションの主役なので、まず SVG が何者なのかを理解しておきましょう。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ベクター画像とラスター画像">ベクター画像とラスター画像<a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%83%99%E3%82%AF%E3%82%BF%E3%83%BC%E7%94%BB%E5%83%8F%E3%81%A8%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E7%94%BB%E5%83%8F" class="hash-link" aria-label="ベクター画像とラスター画像 への直接リンク" title="ベクター画像とラスター画像 への直接リンク" translate="no">​</a></h3>
<p>画像には大きく 2 種類あります。</p>
<ul>
<li class=""><strong>ラスター画像</strong> (JPEG / PNG など): 写真のように、<strong>小さな点 (ピクセル) の集まり</strong> で絵を表します。拡大すると点が大きくなり、ぼやけたりギザギザになったりします。</li>
<li class=""><strong>ベクター画像</strong> (SVG): 「ここからここへ線を引く」「半径◯◯の円を描く」という <strong>図形の指示 (座標と数式)</strong> で絵を表します。拡大しても指示を計算し直して描くだけなので、<strong>どれだけ大きくしても劣化しません</strong>。</li>
</ul>
<p>SVG は Scalable Vector Graphics の略で、まさに「拡大縮小できるベクター画像」という意味です。車のロゴやアイコンのような線画は、SVG にぴったりの題材です。</p>
<p>SVG の中身はテキスト (XML) で、こんな図形要素を組み合わせて絵を作ります。</p>
<ul>
<li class=""><code>&lt;rect&gt;</code>: 長方形</li>
<li class=""><code>&lt;circle&gt;</code>: 円</li>
<li class=""><code>&lt;polygon&gt;</code>: 多角形</li>
<li class=""><code>&lt;path&gt;</code>: 自由な曲線・輪郭 (いちばん表現力が高い)</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="viewbox-は座標を切り取る窓">viewBox は「座標を切り取る窓」<a href="https://notes.rewheel.dev/blog/hero-car-animation#viewbox-%E3%81%AF%E5%BA%A7%E6%A8%99%E3%82%92%E5%88%87%E3%82%8A%E5%8F%96%E3%82%8B%E7%AA%93" class="hash-link" aria-label="viewBox は「座標を切り取る窓」 への直接リンク" title="viewBox は「座標を切り取る窓」 への直接リンク" translate="no">​</a></h3>
<p>SVG を理解するうえで最重要なのが <code>viewBox</code> です。SVG は座標で図形を描きますが、その**「どの座標範囲を表示枠に映すか」を決める窓** が <code>viewBox</code> です。</p>
<p><code>viewBox</code> は 4 つの数値で指定します。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">viewBox="min-x min-y width height"</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>min-x / min-y</strong>: 映し始める左上の座標。</li>
<li class=""><strong>width / height</strong>: そこから右・下に向かって、どれだけの範囲を映すか。</li>
</ul>
<p>つまり「座標 (min-x, min-y) を左上として、幅 width・高さ height の長方形を切り取り、それを画面の枠いっぱいに引き伸ばして表示する」という指示です。下の図で確認してください。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>説明用の図です（実装の car.svg そのものではありません）</div><div class="admonitionContent_BuS1"><svg viewBox="0 0 460 240" role="img" aria-label="viewBox の4つの数値が指す範囲" style="max-width:460px;width:100%;height:auto"><circle cx="70" cy="60" r="4" fill="currentColor"></circle><text x="82" y="56" font-size="13" fill="var(--ifm-font-color-base)">(min-x, min-y)</text><text x="82" y="72" font-size="11" fill="var(--ifm-color-emphasis-600)">映し始める左上の座標</text><rect x="70" y="60" width="300" height="130" fill="none" stroke="currentColor" stroke-width="2"></rect><line x1="70" y1="205" x2="370" y2="205" stroke="var(--ifm-color-emphasis-500)" stroke-width="1"></line><line x1="70" y1="200" x2="70" y2="210" stroke="var(--ifm-color-emphasis-500)" stroke-width="1"></line><line x1="370" y1="200" x2="370" y2="210" stroke="var(--ifm-color-emphasis-500)" stroke-width="1"></line><text x="200" y="223" font-size="13" text-anchor="middle" fill="var(--ifm-color-emphasis-700)">width（映す幅）</text><line x1="395" y1="60" x2="395" y2="190" stroke="var(--ifm-color-emphasis-500)" stroke-width="1"></line><line x1="390" y1="60" x2="400" y2="60" stroke="var(--ifm-color-emphasis-500)" stroke-width="1"></line><line x1="390" y1="190" x2="400" y2="190" stroke="var(--ifm-color-emphasis-500)" stroke-width="1"></line><text x="408" y="130" font-size="13" fill="var(--ifm-color-emphasis-700)">height</text></svg></div></div>
<p>実際の車体 <code>car.svg</code> は <code>viewBox="83 349 2650 747"</code> です。座標 (83, 349) を左上として、幅 2650・高さ 747 の横長の範囲を切り取っています。横長なのは、横向きの車のシルエットを過不足なく収めるためです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="g-グループと-pathfillstroke"><code>&lt;g&gt;</code> グループと path・fill・stroke<a href="https://notes.rewheel.dev/blog/hero-car-animation#g-%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%81%A8-pathfillstroke" class="hash-link" aria-label="g-グループと-pathfillstroke への直接リンク" title="g-グループと-pathfillstroke への直接リンク" translate="no">​</a></h3>
<p>もう少しだけ SVG の部品を知っておきましょう。あとの章で「色が変わらなかった事件」を読み解くのに必要になります。</p>
<ul>
<li class=""><strong><code>&lt;g&gt;</code></strong>: 複数の図形を 1 つに <strong>グループ化</strong> するタグです (group の g)。まとめて移動・拡大したり、まとめて色を指定したりできます。</li>
<li class=""><strong><code>&lt;path&gt;</code></strong>: 前述の「自由な輪郭」を描く要素。車の線画は、いくつもの <code>&lt;path&gt;</code> でできています。</li>
<li class=""><strong><code>fill</code></strong>: 図形の <strong>内側の塗り色</strong>。</li>
<li class=""><strong><code>stroke</code></strong>: 図形の <strong>輪郭線の色</strong>。</li>
</ul>
<p>ここで覚えておいてほしい性質が 1 つあります。<strong><code>fill</code> 属性が指定されていない図形は、デフォルトで黒で塗られます</strong>。逆に言えば、<code>fill</code> 属性が無いということは、あとから CSS で色を上書きできる余地がある、ということです。この伏線は最後の「ハマりどころ」の章で回収します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="currentcolor-と-css-の色の借り方">currentColor と CSS の色の借り方<a href="https://notes.rewheel.dev/blog/hero-car-animation#currentcolor-%E3%81%A8-css-%E3%81%AE%E8%89%B2%E3%81%AE%E5%80%9F%E3%82%8A%E6%96%B9" class="hash-link" aria-label="currentColor と CSS の色の借り方 への直接リンク" title="currentColor と CSS の色の借り方 への直接リンク" translate="no">​</a></h2>
<p>この実装の <strong>色まわりの心臓部</strong> が <code>currentColor</code> です。一度理解すれば、テーマ追従の仕組みが一気に腑に落ちます。</p>
<p><code>currentColor</code> は CSS のキーワードで、<strong>「いま効いている <code>color</code> プロパティの値を借りてくる」</strong> という意味です。<code>color</code> は本来「文字の色」を決めるプロパティですが、<code>currentColor</code> と書くと、文字以外のプロパティ (<code>fill</code> や <code>background-color</code> やグラデーション) でも同じ色を使い回せます。</p>
<p>たとえばこう書くと：</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.box</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token color">green</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic">/* この要素の「現在の色」は緑 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">border</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token unit">px</span><span class="token plain"> solid currentColor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* 枠線も緑（color を借りる） */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">fill</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> currentColor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* SVG の塗りも緑 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>color</code> を 1 か所変えるだけで、<code>currentColor</code> を使っているすべての場所の色がまとめて変わります。</p>
<p>この hero では、<code>.scene</code> (アニメーション全体の親) に <code>color</code> を 1 つ置き、車・タイヤ・雲・ビル・標識・路面の塗りをすべて <code>currentColor</code> で参照しています。だから <strong>テーマを切り替えて <code>.scene</code> の <code>color</code> が変わると、画面全体の色が一斉に追従する</strong> のです。「色の指示は 1 か所、あとはみんなそれを借りる」という設計になっています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="css-アニメーションの基礎">CSS アニメーションの基礎<a href="https://notes.rewheel.dev/blog/hero-car-animation#css-%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%9F%BA%E7%A4%8E" class="hash-link" aria-label="CSS アニメーションの基礎 への直接リンク" title="CSS アニメーションの基礎 への直接リンク" translate="no">​</a></h2>
<p>動きの部分に入る前に、CSS で物を動かす道具を 4 つ押さえます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="keyframes-と-animation">@keyframes と animation<a href="https://notes.rewheel.dev/blog/hero-car-animation#keyframes-%E3%81%A8-animation" class="hash-link" aria-label="@keyframes と animation への直接リンク" title="@keyframes と animation への直接リンク" translate="no">​</a></h3>
<p>CSS アニメーションは 2 つの部品でできています。</p>
<ul>
<li class=""><strong><code>@keyframes</code></strong>: アニメーションの <strong>振り付け</strong>。「開始 (<code>from</code>) はこの状態、終了 (<code>to</code>) はこの状態」と書きます。</li>
<li class=""><strong><code>animation</code></strong>: その振り付けを <strong>どう再生するか</strong>。かける時間・繰り返し回数・速度の変化のしかたを指定します。</li>
</ul>
<p>最小の例はこうです。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* 振り付け: 左から右へ動く */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token atrule rule" style="color:#00a4db">@keyframes</span><span class="token atrule" style="color:#00a4db"> slide</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector" style="color:#00009f">from</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translateX</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector" style="color:#00009f">to</span><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translateX</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* 再生: slide を 2 秒かけて・無限に・一定速度で */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.box</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">animation</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> slide </span><span class="token number" style="color:#36acaa">2</span><span class="token unit">s</span><span class="token plain"> linear infinite</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>2s</code> は 1 回の再生にかける時間。短いほど速く動きます。</li>
<li class=""><code>linear</code> は速度変化のしかた。<code>linear</code> は「最初から最後まで一定速度」という意味で、車や背景のように一定で流れ続けるものに向いています。</li>
<li class=""><code>infinite</code> は「ずっと繰り返す」。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="transform--移動と回転">transform<span class="heading-subtitle">移動と回転</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#transform--%E7%A7%BB%E5%8B%95%E3%81%A8%E5%9B%9E%E8%BB%A2" class="hash-link" aria-label="transform--移動と回転 への直接リンク" title="transform--移動と回転 への直接リンク" translate="no">​</a></h3>
<p><code>transform</code> は要素を <strong>変形</strong> させるプロパティです。この hero では主に 2 つを使います。</p>
<ul>
<li class=""><code>translate(x, y)</code>: 平行移動 (ずらす)。<code>translateX</code> は横だけ、<code>translate3d(x, y, z)</code> は 3 次元版 (後述)。</li>
<li class=""><code>rotate(角度)</code>: 回転。<code>rotate(360deg)</code> で 1 回転、マイナスを付けると逆回りになります。</li>
</ul>
<p>回転には <strong>どこを軸に回すか</strong> という基準点があり、それが <code>transform-origin</code> です。<code>transform-origin: center</code> なら要素の中心を軸に回ります。タイヤをきれいに回すには、この軸が重要になります (次章)。</p>
<p>そしてもう 1 つ、必ず覚えておきたい落とし穴があります。<strong><code>transform</code> は後勝ちで、まるごと上書きされる</strong> という性質です。<code>translate</code> と <code>rotate</code> の両方を効かせたいなら、<code>transform: translate(...) rotate(...)</code> のように <strong>1 つの <code>transform</code> にまとめて書く</strong> 必要があります。片方だけ書くと、もう片方は消えます。これがタイヤ実装でハマるポイントです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="translate3d-と-gpu-合成">translate3d と GPU 合成<a href="https://notes.rewheel.dev/blog/hero-car-animation#translate3d-%E3%81%A8-gpu-%E5%90%88%E6%88%90" class="hash-link" aria-label="translate3d と GPU 合成 への直接リンク" title="translate3d と GPU 合成 への直接リンク" translate="no">​</a></h3>
<p><code>translate3d(x, y, 0)</code> は <code>translate</code> の 3 次元版ですが、見た目の動きは <code>translate(x, y)</code> と同じです。ではなぜ 3d を使うのか。</p>
<p>ブラウザは、<code>translate3d</code> のような変形を <strong>GPU (画像処理専用の装置) に任せる</strong> ことができます。GPU に任せると、ページの再計算 (どこに何を並べ直すか、という重い処理) を起こさずに、要素だけをスッと動かせます。結果として <strong>動きが滑らかになり、カクつきにくくなる</strong> のです。</p>
<p>さらに <code>will-change: transform</code> を添えると、「この要素はこれから <code>transform</code> で動きますよ」とブラウザに事前予告でき、最適化を促せます。この hero では、流れる背景と回るタイヤすべてに <code>translate3d</code> + <code>will-change</code> を使っています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prefers-reduced-motion--動きを減らす配慮">prefers-reduced-motion<span class="heading-subtitle">動きを減らす配慮</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#prefers-reduced-motion--%E5%8B%95%E3%81%8D%E3%82%92%E6%B8%9B%E3%82%89%E3%81%99%E9%85%8D%E6%85%AE" class="hash-link" aria-label="prefers-reduced-motion--動きを減らす配慮 への直接リンク" title="prefers-reduced-motion--動きを減らす配慮 への直接リンク" translate="no">​</a></h3>
<p>最後に、見落としがちですが大切な配慮です。OS には <strong>「視差効果を減らす」「動きを減らす」</strong> という設定があります (めまいを起こしやすい人などのための機能)。</p>
<p>CSS はこの設定を <code>@media (prefers-reduced-motion: reduce)</code> というメディアクエリで読み取れます。この設定が ON の人には、アニメーションを止めて静止画として見せる——という出し分けができます。実装の最後でこれを使います。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="パララックスの仕組み">パララックスの仕組み<a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%83%91%E3%83%A9%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B9%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="パララックスの仕組み への直接リンク" title="パララックスの仕組み への直接リンク" translate="no">​</a></h2>
<p>背景に奥行きを出しているのが <strong>パララックス (parallax)</strong> です。電車の窓から外を見ると、近くの電柱はビュンと速く流れ、遠くの山はゆっくり動いて見えますよね。あの <strong>「近いものほど速く、遠いものほど遅く動く」</strong> 効果がパララックスです。</p>
<p>この hero でも、奥のビルはゆっくり、手前の路面の破線は速く流すことで、平面なのに奥行きを感じさせています。レイヤごとの速さは「<strong>1 周で動く距離 ÷ 1 周にかける時間</strong>」で決まります (距離と時間の具体値は実装の章で見ます)。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">                流れる速さ         イメージ</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 奥   遠景ビル   毎秒 約 24px      ──→            （いちばんゆっくり）</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      中景ビル   毎秒 約 40px      ────→</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      雲         毎秒 約 60px      ──────→</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      街路       毎秒 約 93px      ──────────→</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 手前 路面の破線  毎秒 約 233px     ──────────────→（いちばん速い）</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      タイヤ     1.1 秒で 1 回転    ◯ くるくる回転</span><br></div></code></pre></div></div>
<p>(これは静的な図です。実際の動きはトップページで確認してください。)</p>
<p>奥ほど遅く、手前ほど速い。これだけで奥行きが生まれます。なお、これらの速さに理論的な正解はなく、実際の画面を見ながら「それらしく」調整した値です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="継ぎ目なく無限ループさせる仕掛け">継ぎ目なく無限ループさせる仕掛け<a href="https://notes.rewheel.dev/blog/hero-car-animation#%E7%B6%99%E3%81%8E%E7%9B%AE%E3%81%AA%E3%81%8F%E7%84%A1%E9%99%90%E3%83%AB%E3%83%BC%E3%83%97%E3%81%95%E3%81%9B%E3%82%8B%E4%BB%95%E6%8E%9B%E3%81%91" class="hash-link" aria-label="継ぎ目なく無限ループさせる仕掛け への直接リンク" title="継ぎ目なく無限ループさせる仕掛け への直接リンク" translate="no">​</a></h3>
<p>もう 1 つの工夫が、<strong>途切れずに延々と流れ続ける</strong> ループです。</p>
<p>背景の各レイヤは、同じ絵 (タイル) を横に繰り返し敷き詰めたものです。これを一定距離だけ横に動かし、動き終わったら開始位置に瞬間的に戻す——を繰り返すと無限ループになります。問題は、<strong>開始位置に戻った瞬間、絵がズレて見えないか</strong> です。</p>
<p>ここには明確な条件があります。<strong>動かす距離が「タイル 1 枚の幅」のちょうど整数倍</strong> なら、戻った後の見た目は戻る前と完全に一致します。同じ絵がタイル幅ごとに繰り返されているので、タイル何枚ぶんズレても見分けがつかないからです。逆に、整数倍からずれた距離で戻すと、その瞬間に背景がカクッと飛びます。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">タイル幅 600px の絵を敷き詰め、ちょうど 2 枚ぶん (1200px) 動かして戻す。</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  開始        │[タイル][タイル][タイル][タイル]…</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              └── 表示窓 ──┘</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  1200px 移動 │        [タイル][タイル][タイル]…</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              └── 表示窓 ──┘</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 ↑ どのタイルも同じ絵なので、2 枚ぶんズレても見た目は同一。</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                   ここで開始位置に瞬間的に戻しても、見分けがつかない。</span><br></div></code></pre></div></div>
<p>実は、この hero の最初の実装は「画面幅の 100% ぶん動かして戻す」方式でした。一見問題なさそうですが、<strong>画面幅はタイル幅の整数倍とは限りません</strong>。たとえば画面幅 1440px・タイル幅 600px なら 1440 ÷ 600 = 2.4 枚ぶん。ループのたびに 0.4 枚ぶん絵が飛び、「背景が一定のタイミングでカクッと動く」バグになっていました。</p>
<p>そこで現在の実装では、動かす距離を <strong>タイル幅のちょうど 2 倍の px 値</strong> に固定しています。距離を px で確定させるには、タイル幅も px で確定している必要があります。ビルと街路のタイル (後述する <code>mask</code>) は「レイヤの高さに合わせて拡大縮小」されるため、<strong>レイヤの高さを px 固定</strong> にすることでタイル幅を確定させました。% の章で予告した例外がこれです。具体的なコードは実装の章で見ます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装--svg-をコンポーネント化する">実装<span class="heading-subtitle">SVG をコンポーネント化する</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%9F%E8%A3%85--svg-%E3%82%92%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E5%8C%96%E3%81%99%E3%82%8B" class="hash-link" aria-label="実装--svg-をコンポーネント化する への直接リンク" title="実装--svg-をコンポーネント化する への直接リンク" translate="no">​</a></h2>
<p>ここからは実際のコードです。まず素材の SVG を React で使えるようにします。</p>
<p>素材は車体の <code>car.svg</code> とタイヤの <code>tire.svg</code> の 2 枚です。Docusaurus の classic preset には <a href="https://react-svgr.com/" target="_blank" rel="noopener noreferrer" class="">svgr</a> という仕組みが同梱されていて、SVG を <strong><code>import</code> するだけ</strong> で React コンポーネントとして扱えます。</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">React</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">clsx</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'clsx'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Car</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@site/static/img/car.svg'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Tire</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@site/static/img/tire.svg'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">styles</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./HeroCarAnimation.module.css'</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>あとは普通のコンポーネントとして配置します。<code>car.svg</code> は車輪まで描かれた完成車なので、その前後輪の位置に <code>tire.svg</code> を 2 枚重ねて、タイヤだけを回転させる構成にしました。コンポーネント全体はこうです (<code>clsx</code> は複数のクラス名を 1 つにまとめるユーティリティで、ここでは共通の <code>tire</code> と個別の <code>tireRear</code> / <code>tireFront</code> を合成しています)。</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">HeroCarAnimation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">ReactElement</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">scene</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">aria-hidden</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">true</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">clouds</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">buildingsFar</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">buildingsMid</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">street</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">road</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">car</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Car</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">carBody</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Tire</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript function" style="color:#d73a49">clsx</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">tire</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">tireRear</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Tire</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript function" style="color:#d73a49">clsx</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">tire</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">tireFront</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>返り値の型 <code>React.ReactElement</code> を明示し、<code>&lt;div&gt;</code> 1 つを頂点に、背景レイヤ → 車の順で子要素を並べているだけのシンプルなコンポーネントです。</p>
<p>完成形の章で見た「奥から手前へ」の重なり順が、そのまま HTML の記述順 (clouds → buildingsFar → buildingsMid → street → road → car) になっているのが分かります。ビルが <code>buildingsFar</code> (遠景) と <code>buildingsMid</code> (中景) の 2 層に分かれているのは、流す速さと濃さを変えて奥行きを強めるためです。</p>
<p><code>*.svg</code> 用の型定義ファイル (<code>svg.d.ts</code>) を自分で書く必要はありませんでした。Docusaurus の型設定にコンポーネント宣言が含まれているため、<code>yarn typecheck</code> もそのまま通ります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装--テーマ色への追従">実装<span class="heading-subtitle">テーマ色への追従</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%9F%E8%A3%85--%E3%83%86%E3%83%BC%E3%83%9E%E8%89%B2%E3%81%B8%E3%81%AE%E8%BF%BD%E5%BE%93" class="hash-link" aria-label="実装--テーマ色への追従 への直接リンク" title="実装--テーマ色への追従 への直接リンク" translate="no">​</a></h2>
<p><code>currentColor</code> の章で学んだ仕組みを、ここで実際に使います。色の制御は CSS の <code>color</code> 1 か所に集約できるので、<code>.scene</code> (アニメーション全体の親) に <code>color</code> を与えるだけで、配下の車・タイヤ・背景すべてに伝わります。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.scene</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">position</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> relative</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic">/* 背景・車（子要素）の配置基準になる */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">width</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">margin</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2.5</span><span class="token unit">rem</span><span class="token plain"> auto </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">height</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">clamp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">220</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token unit">vw</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">300</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">/* 高さを先に予約してレイアウトのガタつきを防ぐ */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">--ifm-color-primary-darker</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* この色を配下の currentColor が借りる */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">overflow</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hidden</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic">/* 枠からはみ出した背景を隠す */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector attribute punctuation" style="color:#393A34">[</span><span class="token selector attribute attr-name" style="color:#00a4db">data-theme</span><span class="token selector attribute operator" style="color:#393A34">=</span><span class="token selector attribute attr-value" style="color:#e3116c">'dark'</span><span class="token selector attribute punctuation" style="color:#393A34">]</span><span class="token selector" style="color:#00009f"> </span><span class="token selector class" style="color:#00009f">.scene</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">--ifm-color-primary-light</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>.scene</code> は色だけでなく、子要素の配置基準 (<code>position: relative</code>)、高さの予約 (<code>height: clamp(...)</code>：読み込み時のガタつき防止)、はみ出した背景を隠す <code>overflow: hidden</code> もまとめて担う、このアニメーションの土台です。色について言うと、ライトでは少し濃い sage green (<code>--ifm-color-primary-darker</code>)、ダークでは明るい緑 (<code>--ifm-color-primary-light</code>) を使っています。<code>[data-theme='dark']</code> は「ダークモードのとき」を表す Docusaurus の指定で、テーマを切り替えると <code>color</code> が差し替わります。どちらも Docusaurus が用意しているテーマ変数をそのまま参照しているだけで、<strong>この機能のために新しい色を定義してはいません</strong>。</p>
<p>そして車体とタイヤの線画は、CSS でこう塗っています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.carBody</span><span class="token selector" style="color:#00009f"> path</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f"></span><span class="token selector class" style="color:#00009f">.tire</span><span class="token selector" style="color:#00009f"> path</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">fill</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> currentColor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>SVG の <code>path</code> の塗り (<code>fill</code>) を <code>currentColor</code> にしているので、<code>.scene</code> の <code>color</code> がそのまま線画の色になります。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>なぜ CSS で塗る必要があるのか</div><div class="admonitionContent_BuS1"><p>「SVG の色は SVG ファイル側で決まっているのでは？」と思った人もいるかもしれません。実はここに、この実装でいちばんハマったポイントが隠れています。<strong>なぜ <code>fill: currentColor</code> をわざわざ CSS で書くのか</strong> の種明かしは、最後の「ハマりどころ」の章で詳しく語ります。いまは「色は <code>.scene</code> の <code>color</code> 1 つで決まる」とだけ覚えておいてください。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装--タイヤを軸ブレなく回す">実装<span class="heading-subtitle">タイヤを軸ブレなく回す</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%9F%E8%A3%85--%E3%82%BF%E3%82%A4%E3%83%A4%E3%82%92%E8%BB%B8%E3%83%96%E3%83%AC%E3%81%AA%E3%81%8F%E5%9B%9E%E3%81%99" class="hash-link" aria-label="実装--タイヤを軸ブレなく回す への直接リンク" title="実装--タイヤを軸ブレなく回す への直接リンク" translate="no">​</a></h2>
<p>タイヤを回すときに気をつけたのは、<strong>回転軸がブレないこと</strong> です。回転の軸 (中心) がホイールの中心からずれていると、回したときにガタガタ揺れて見えてしまいます。</p>
<p>SVG 基礎の章で学んだ <code>viewBox</code> が、ここで効いてきます。<code>tire.svg</code> の <code>viewBox</code> は <strong>正方形</strong> にクロップしてあります。</p>
<div class="language-svg codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">static/img/tire.svg</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-svg codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">viewBox</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">682 41 1450 1450</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">fill-rule</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">evenodd</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>幅も高さも 1450 で正方形です。<strong><code>viewBox</code> が正方形なら、その枠の中心がそのままホイールの中心になります</strong>。あとは CSS で「要素の中心を軸に回す」だけで、軸ブレなく回転します。横長の枠だと中心がずれてしまうので、ここは正方形であることが重要です。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>説明用の図です</div><div class="admonitionContent_BuS1"><svg viewBox="0 0 300 300" role="img" aria-label="正方形の viewBox では枠の中心と回転中心が一致する" style="max-width:300px;width:100%;height:auto"><rect x="40" y="40" width="220" height="220" fill="none" stroke="var(--ifm-color-emphasis-500)" stroke-width="2" stroke-dasharray="6 4"></rect><text x="150" y="30" font-size="12" text-anchor="middle" fill="var(--ifm-color-emphasis-700)">正方形の viewBox（幅 = 高さ）</text><circle cx="150" cy="150" r="92" fill="none" stroke="currentColor" stroke-width="3"></circle><circle cx="150" cy="150" r="14" fill="none" stroke="currentColor" stroke-width="3"></circle><line x1="150" y1="58" x2="150" y2="242" stroke="currentColor" stroke-width="2"></line><line x1="58" y1="150" x2="242" y2="150" stroke="currentColor" stroke-width="2"></line><circle cx="150" cy="150" r="4" fill="currentColor"></circle><text x="150" y="285" font-size="12" text-anchor="middle" fill="var(--ifm-color-emphasis-700)">枠の中心 ＝ ホイールの中心 ＝ 回転軸</text></svg></div></div>
<p>CSS 側はこうです。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.tire</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">position</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> absolute</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">width</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">17</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">aspect-ratio</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">               </span><span class="token comment" style="color:#999988;font-style:italic">/* 縦横比を 1:1（正方形）に保つ */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">top</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* 自分の中心を配置基準に合わせる */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">transform-origin</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> center</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">/* 回転の軸 = 要素の中心 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">animation</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hero-tire-spin </span><span class="token number" style="color:#36acaa">1.1</span><span class="token unit">s</span><span class="token plain"> linear infinite</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token atrule rule" style="color:#00a4db">@keyframes</span><span class="token atrule" style="color:#00a4db"> hero-tire-spin</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector" style="color:#00009f">from</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rotate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token unit">deg</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector" style="color:#00009f">to</span><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rotate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">-360</span><span class="token unit">deg</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>aspect-ratio: 1 / 1</code> は「縦と横の比を 1:1 に保つ」指定で、要素自体も正方形に保ちます。<code>transform: translate(-50%, -50%)</code> は、要素の左上ではなく <strong>中心</strong> を配置の基準点に合わせるための定番テクニックです。</p>
<p>そしてここで、CSS アニメーション基礎の章で予告した <strong>「<code>transform</code> は後勝ち」</strong> の落とし穴に気をつけます。<code>@keyframes</code> の中で <code>rotate()</code> だけを書くと、中心合わせの <code>translate(-50%, -50%)</code> が上書きで消えてしまい、タイヤが画面外へ飛んでいきます。だから <strong>回転のキーフレームでも <code>translate</code> を併記</strong> しています。</p>
<p>回転量は <code>-360deg</code> (マイナスの 1 回転) で、車の進行方向に合う向きにしています (符号を逆にすると逆回りになります)。</p>
<p>前後輪の位置は <code>left</code> の % で指定します。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.tireFront</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">left</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">78</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.tireRear</span><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">left</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">17.5</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>車体側を <code>width: min(88%, 680px)</code> で可変にしているので (<code>min()</code> は「2 つのうち小さい方を採用」する関数で、画面が広いときは 680px で頭打ち、狭いときは 88% に縮みます)、タイヤも % で配置しておけば、画面幅が変わっても車輪の位置に追従します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装--背景のパララックス">実装<span class="heading-subtitle">背景のパララックス</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%9F%E8%A3%85--%E8%83%8C%E6%99%AF%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B9" class="hash-link" aria-label="実装--背景のパララックス への直接リンク" title="実装--背景のパララックス への直接リンク" translate="no">​</a></h2>
<p>パララックスの仕組みは基礎の章で見たとおりです。ここでは実際のコードに落とし込みます。</p>
<p>まず「タイル幅の整数倍だけ動かして戻す」ループの振り付けです。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token atrule rule" style="color:#00a4db">@keyframes</span><span class="token atrule" style="color:#00a4db"> hero-scroll</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector" style="color:#00009f">from</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translate3d</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">calc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">-1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">--loop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector" style="color:#00009f">to</span><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translate3d</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>ここで <code>var(--loop)</code> という書き方が出てきました。<code>--loop</code> は <strong>CSS カスタムプロパティ (CSS 変数)</strong> です。<code>--名前: 値;</code> の形で好きな値に名前を付けて宣言し、<code>var(--名前)</code> でその値を参照できます。ポイントは、<strong>同じ <code>@keyframes</code> を使い回しながら、<code>--loop</code> の値だけを各レイヤで変えられる</strong> ことです。振り付けは 1 つ、動く距離はレイヤごと、という構成になります。</p>
<p>各レイヤは「自分のタイル幅 × 2」を <code>--loop</code> に設定し、左にその距離だけずらした状態から元の位置へ動かします。ループの章で見たとおり、移動距離がタイル幅の整数倍なので、先頭に戻る瞬間も絵は飛びません。<code>translate3d</code> を使っているのは、基礎の章で説明したとおり GPU 合成に乗せて滑らかに動かすためです (各レイヤに <code>will-change: transform</code> も添えています)。</p>
<p>あとは、この <code>hero-scroll</code> を <strong>各レイヤで違う距離・違う秒数</strong> で再生するだけです。</p>
<table><thead><tr><th>レイヤ</th><th>1 周の距離 (<code>--loop</code>)</th><th>周期</th><th>速さ</th></tr></thead><tbody><tr><td>遠景ビル (<code>.buildingsFar</code>)</td><td>1200px (タイル 600px × 2)</td><td>50s</td><td>24px/s</td></tr><tr><td>中景ビル (<code>.buildingsMid</code>)</td><td>1320px (タイル 660px × 2)</td><td>33s</td><td>40px/s</td></tr><tr><td>雲 (<code>.clouds</code>)</td><td>1440px (タイル 720px × 2)</td><td>24s</td><td>60px/s</td></tr><tr><td>街路 (<code>.street</code>)</td><td>2688px (タイル 1344px × 2)</td><td>29s</td><td>約 93px/s</td></tr><tr><td>路面の破線 (<code>.road::after</code>)</td><td>1400px (破線 70px × 20)</td><td>6s</td><td>約 233px/s</td></tr><tr><td>タイヤ (<code>.tire</code>)</td><td>—</td><td>1.1s</td><td>最前面で回転</td></tr></tbody></table>
<p>ここで「周期が短い = 速い」ではないことに注意してください。速さは距離 ÷ 時間です。たとえば街路 (29s) は雲 (24s) より周期が長いのに、1 周で進む距離が約 2 倍あるので、流れは速くなります。</p>
<p>たとえば雲のレイヤは、こう書いています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.clouds</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token variable" style="color:#36acaa">--loop</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1440</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic">/* 1 周で動く距離 = タイル幅 720px × 2 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">position</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> absolute</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">left</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">top</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">width</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">calc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token unit">%</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">--loop</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* 動いている間も画面を覆い続ける幅 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">height</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">38</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">opacity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.22</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">background-size</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">720</span><span class="token unit">px</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">/* タイル 1 枚の幅を 720px に固定 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* 雲の形（radial-gradient）の描き方は次章 */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">animation</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hero-scroll </span><span class="token number" style="color:#36acaa">24</span><span class="token unit">s</span><span class="token plain"> linear infinite</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">will-change</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> transform</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>幅の <code>calc(100% + var(--loop))</code> は「画面幅 + 移動距離」です。最大で <code>--loop</code> ぶん左にずれても背景が途切れないように、あらかじめ移動距離のぶんだけレイヤの幅を広げています。</p>
<p>ビル・街路・路面も骨組みは同じで、違うのは <code>--loop</code> の距離、<code>animation</code> の秒数、背景の描き方だけです。なお、雲は <code>background-size</code> でタイル幅を直接 px 指定できるので高さは % のままですが、ビルと街路は次章の <code>mask</code> で描くため「タイルがレイヤの高さに合わせて拡縮」されます。この 2 種類のレイヤだけ高さを px 固定 (遠景ビル 120px、中景ビル 102px、街路 84px) にして、タイル幅を確定させています。</p>
<p>1 つ細かい注意点があります。<strong>流れているのは路面の「破線」だけで、地平線そのものは固定</strong> です。地平線は <code>.road</code> 本体 (<code>width: 100%</code> の固定要素) で描き、その上を流れる破線は <code>.road::after</code> (<code>hero-scroll</code> で流れる) で描いています。地面の線まで一緒に流れると不自然なので、線は止めて破線だけ流しているわけです。破線の <code>--loop</code> は 1400px で、これは破線 1 周期 (塗り + すき間 = 70px) のちょうど 20 個ぶん。ここでも「パターンの整数倍」のルールを守っています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装--背景を描き分けるmask--gradient">実装<span class="heading-subtitle">背景を描き分ける（mask / gradient）</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%9F%E8%A3%85--%E8%83%8C%E6%99%AF%E3%82%92%E6%8F%8F%E3%81%8D%E5%88%86%E3%81%91%E3%82%8Bmask--gradient" class="hash-link" aria-label="実装--背景を描き分けるmask--gradient への直接リンク" title="実装--背景を描き分けるmask--gradient への直接リンク" translate="no">​</a></h2>
<p>背景の雲・ビル・街路・破線は、<strong>画像ファイルを 1 枚も使わず</strong>、すべて CSS だけで描いています。形の性質に合わせて 3 通りの方法 (mask / radial-gradient / repeating-linear-gradient) を使い分けました。いずれも塗りは <code>currentColor</code> なので、まとめてテーマ色に追従します。</p>
<p>加えて、各レイヤには <code>opacity</code> (透明度) を与えています。いちばん遠いビルを最も薄く (<code>0.15</code>)、中景のビルはやや濃く (<code>0.25</code>)、上空の雲はうっすら (<code>0.22</code>)、線の細い街路はしっかりめ (<code>0.5</code>)、手前の路面はくっきり (<code>0.4</code>〜<code>0.7</code>)。奥ほど薄く・手前ほど濃くすることで、パララックスの速度差と合わせて奥行きを強めています。</p>
<p>下の図が 3 つの方法のイメージです。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>説明用の図です</div><div class="admonitionContent_BuS1"><svg viewBox="0 0 460 360" role="img" aria-label="mask・radial-gradient・repeating-linear-gradient の描画イメージ" style="max-width:460px;width:100%;height:auto"><text x="0" y="18" font-size="13" fill="var(--ifm-font-color-base)">① mask：黒いシルエットの形だけ下地を残す</text><rect x="10" y="30" width="90" height="60" fill="currentColor" opacity="0.5"></rect><text x="55" y="105" font-size="10" text-anchor="middle" fill="var(--ifm-color-emphasis-600)">下地（currentColor）</text><text x="120" y="65" font-size="20" fill="var(--ifm-color-emphasis-600)">+</text><rect x="150" y="60" width="14" height="30" fill="currentColor"></rect><rect x="170" y="45" width="12" height="45" fill="currentColor"></rect><rect x="188" y="68" width="16" height="22" fill="currentColor"></rect><text x="177" y="105" font-size="10" text-anchor="middle" fill="var(--ifm-color-emphasis-600)">シルエット（mask）</text><text x="220" y="65" font-size="20" fill="var(--ifm-color-emphasis-600)">=</text><rect x="250" y="60" width="14" height="30" fill="currentColor"></rect><rect x="270" y="45" width="12" height="45" fill="currentColor"></rect><rect x="288" y="68" width="16" height="22" fill="currentColor"></rect><text x="280" y="105" font-size="10" text-anchor="middle" fill="var(--ifm-color-emphasis-600)">ビルの形だけ残る</text><text x="0" y="160" font-size="13" fill="var(--ifm-font-color-base)">② radial-gradient：楕円を重ねて雲にする</text><ellipse cx="60" cy="210" rx="40" ry="22" fill="currentColor" opacity="0.4"></ellipse><ellipse cx="100" cy="218" rx="30" ry="18" fill="currentColor" opacity="0.4"></ellipse><ellipse cx="135" cy="212" rx="24" ry="15" fill="currentColor" opacity="0.4"></ellipse><text x="100" y="250" font-size="10" text-anchor="middle" fill="var(--ifm-color-emphasis-600)">楕円のグラデーションを 3 つ重ねる</text><text x="0" y="300" font-size="13" fill="var(--ifm-font-color-base)">③ repeating-linear-gradient：等間隔の破線</text><rect x="10" y="315" width="26" height="6" fill="currentColor"></rect><rect x="54" y="315" width="26" height="6" fill="currentColor"></rect><rect x="98" y="315" width="26" height="6" fill="currentColor"></rect><rect x="142" y="315" width="26" height="6" fill="currentColor"></rect><rect x="186" y="315" width="26" height="6" fill="currentColor"></rect><text x="120" y="340" font-size="10" text-anchor="middle" fill="var(--ifm-color-emphasis-600)">塗り 26px・透明 44px を繰り返す</text></svg></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ビル--css-mask">ビル<span class="heading-subtitle">CSS mask</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%83%93%E3%83%AB--css-mask" class="hash-link" aria-label="ビル--css-mask への直接リンク" title="ビル--css-mask への直接リンク" translate="no">​</a></h3>
<p><strong><code>mask</code> (マスク)</strong> は、<strong>「シルエット (型抜きの形) を使って、下地の見える部分を切り抜く」</strong> CSS の機能です。不透明に描かれた形の部分だけ下地が残り、それ以外は透明になります。クッキーの型でクッキー生地を抜くイメージです。</p>
<p>ビルは、シルエットを <strong>data URI の SVG</strong> にして <code>mask</code> に渡し、下地を <code>currentColor</code> で塗っています。</p>
<p>遠景ビルのシルエットは矩形の組み合わせです。ビル本体に加えて、アンテナや給水タンクといった屋上のディテールも細い矩形で添えて、単調な箱の並びにならないようにしています。</p>
<div class="language-svg codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">遠景ビルのシルエット（mask 用の SVG・抜粋）</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-svg codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">viewBox</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">0 0 1000 200</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">rect</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">x</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">40</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">y</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">96</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">80</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">104</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- ビル本体 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">rect</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">x</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">78</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">y</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">62</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">4</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">   </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">34</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- アンテナ --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">rect</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">x</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">64</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">y</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">74</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">32</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">4</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">   </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- アンテナの横棒 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">rect</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">x</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">310</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">y</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">116</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">110</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">84</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- ビル本体 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">rect</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">x</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">330</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">y</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">68</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">70</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">28</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">rx</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">4</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 給水タンク --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 以下、計 3 棟ぶん続く --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>この SVG を data URI の文字列にエンコードして、CSS の <code>mask</code> に渡します。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.buildingsFar</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">height</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">120</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic">/* px 固定 → タイル幅が 600px に確定する */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">background-color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> currentColor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* 下地をテーマ色で塗る */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* ↑ のビル SVG を data:image/svg+xml,... の形にエンコードしたもの */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token variable" style="color:#36acaa">--bg-mask</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token url function" style="color:#d73a49">url</span><span class="token url punctuation" style="color:#393A34">(</span><span class="token url string url" style="color:#36acaa">"data:image/svg+xml,%3Csvg ... %3C/svg%3E"</span><span class="token url punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">-webkit-mask</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">--bg-mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> repeat-x left bottom </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> auto </span><span class="token number" style="color:#36acaa">100</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">mask</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">--bg-mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> repeat-x left bottom </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> auto </span><span class="token number" style="color:#36acaa">100</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>ここで <strong>data URI</strong> という言葉が出てきました。これは「画像ファイルを外部に置く代わりに、画像の中身を直接 URL の文字列として埋め込む」方法だと思ってください (<code>data:image/svg+xml,...</code> の部分がそれです)。おかげで <code>.svg</code> ファイルを別途用意しなくても、CSS の中にシルエットを書けます。</p>
<p><code>repeat-x</code> はシルエットを横に繰り返し敷き詰める指定です。末尾の <code>auto 100%</code> はタイルのサイズ指定で、「高さはレイヤに合わせ、幅は元の縦横比なりに」という意味です。viewBox が 1000×200 のタイルを高さ 120px に合わせると、幅は 1000 × (120 ÷ 200) = 600px。レイヤの高さを px で固定したからこそ、ループの章で使った「タイル幅 600px」が確定します。<code>-webkit-mask</code> を併記しているのは Safari 対応のためです (Safari は <code>-webkit-</code> という接頭辞付きの書き方を必要とする場合があります)。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="中景ビルの窓--fill-rule-evenodd-で穴を抜く">中景ビルの窓<span class="heading-subtitle">fill-rule: evenodd で穴を抜く</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E4%B8%AD%E6%99%AF%E3%83%93%E3%83%AB%E3%81%AE%E7%AA%93--fill-rule-evenodd-%E3%81%A7%E7%A9%B4%E3%82%92%E6%8A%9C%E3%81%8F" class="hash-link" aria-label="中景ビルの窓--fill-rule-evenodd-で穴を抜く への直接リンク" title="中景ビルの窓--fill-rule-evenodd-で穴を抜く への直接リンク" translate="no">​</a></h3>
<p>中景のビルには窓があります。といっても窓を描き足したのではなく、<strong>シルエットに窓の形の穴を空けています</strong>。mask は「不透明な部分だけ下地が残る」仕組みなので、穴の部分は下地が消え、向こう側の背景が抜けて見えます。これが窓になります。</p>
<p>穴空けに使うのが SVG の <strong><code>fill-rule="evenodd"</code></strong> です。1 つの <code>&lt;path&gt;</code> の中に「外形」と「窓」を重ねて描くと、evenodd ルールでは <strong>図形に偶数回囲まれた部分が塗られなくなる</strong> ため、外形の内側に重ねた窓の長方形だけが穴になります。</p>
<div class="language-svg codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">中景ビルのシルエット（窓抜き・抜粋）</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-svg codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">path</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">fill-rule</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">evenodd</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">d</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token tag attr-value" style="color:#e3116c">  M60 60H220V170H60Z    ← ビルの外形（大きな長方形）</span><br></div><div class="token-line" style="color:#393A34"><span class="token tag attr-value" style="color:#e3116c">  M84 78h24v20H84Z      ← 窓（外形の内側の小さな長方形 → 穴になる）</span><br></div><div class="token-line" style="color:#393A34"><span class="token tag attr-value" style="color:#e3116c">  M134 78h24v20H134Z    ← 窓</span><br></div><div class="token-line" style="color:#393A34"><span class="token tag attr-value" style="color:#e3116c">  ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token tag attr-value" style="color:#e3116c"></span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="街路の線画--stroke-も-mask-になる">街路の線画<span class="heading-subtitle">stroke も mask になる</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E8%A1%97%E8%B7%AF%E3%81%AE%E7%B7%9A%E7%94%BB--stroke-%E3%82%82-mask-%E3%81%AB%E3%81%AA%E3%82%8B" class="hash-link" aria-label="街路の線画--stroke-も-mask-になる への直接リンク" title="街路の線画--stroke-も-mask-になる への直接リンク" translate="no">​</a></h3>
<p>街路の標識・街灯・街路樹・信号機は、ビルと違って <strong>線画</strong> で描いています。車体 (<code>car.svg</code>) が線画なので、車に近い位置に並ぶオブジェクトはテイストを揃えたかったからです。</p>
<p>mask は「不透明な部分を残す」仕組みなので、塗り (<code>fill</code>) だけでなく <strong>輪郭線 (<code>stroke</code>) でも機能します</strong>。線そのものが不透明な部分になり、線画がそのまま型になります。</p>
<div class="language-svg codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">街路の線画（mask 用の SVG・抜粋）</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-svg codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">viewBox</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">0 0 2400 150</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">g</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">none</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">stroke</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">stroke-width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">3.5</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">     </span><span class="token tag attr-name" style="color:#00a4db">stroke-linecap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">round</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">stroke-linejoin</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">round</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">path</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">d</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">M180 150V78</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 標識のポール --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">circle</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">cx</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">180</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">cy</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">56</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">r</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">22</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 丸標識 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">path</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">d</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">M760 150V44Q760 26 782 26H796</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 街灯のポールとアーム --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">circle</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">cx</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">803</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">cy</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">26</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">r</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">7</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain">           </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 街灯のランプ --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- 街路樹・信号機と続く --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">g</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p><code>fill="none"</code> で塗りを消し、<code>stroke</code> で線だけを描いています。<code>stroke-linecap="round"</code> と <code>stroke-linejoin="round"</code> は線の端や角を丸める指定で、車の線画の柔らかい質感に寄せるためのものです。オブジェクト同士の間隔は広めに取っています。最初はもっと密に並べていたのですが、主役の車が背景に埋もれてしまったため、数を減らして間隔を空けました。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="雲--radial-gradient">雲<span class="heading-subtitle">radial-gradient</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E9%9B%B2--radial-gradient" class="hash-link" aria-label="雲--radial-gradient への直接リンク" title="雲--radial-gradient への直接リンク" translate="no">​</a></h3>
<p><strong><code>radial-gradient</code> (放射状グラデーション)</strong> は、中心から外へ向かって色が変化する円・楕円のグラデーションです。雲は、この楕円のグラデーションを <code>currentColor</code> でいくつか重ねて表現しています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.clouds</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">background-image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">radial-gradient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ellipse </span><span class="token number" style="color:#36acaa">20</span><span class="token unit">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">26</span><span class="token unit">%</span><span class="token plain"> at </span><span class="token number" style="color:#36acaa">22</span><span class="token unit">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">40</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentColor </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token color">transparent</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">62</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">radial-gradient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ellipse </span><span class="token number" style="color:#36acaa">15</span><span class="token unit">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><span class="token unit">%</span><span class="token plain"> at </span><span class="token number" style="color:#36acaa">42</span><span class="token unit">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">52</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentColor </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token color">transparent</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">62</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">radial-gradient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ellipse </span><span class="token number" style="color:#36acaa">12</span><span class="token unit">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token unit">%</span><span class="token plain"> at </span><span class="token number" style="color:#36acaa">9</span><span class="token unit">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">55</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentColor </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token color">transparent</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">62</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>ellipse 20% 26% at 22% 40%</code> は「幅 20%・高さ 26% の楕円を、位置 (22%, 40%) に置く」という意味。<code>currentColor 0 60%, transparent 62%</code> は「中心から 60% までは色を塗り、62% で透明にする」指定で、この境目がふわっとした雲の輪郭になります。これらを <code>background-size: 720px 100%</code> で幅 720px のタイルにし、<code>background-repeat: repeat-x</code> で横に繰り返しています (タイル幅を px で固定する理由は、ループの章で説明したとおりです)。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="路面の破線--repeating-linear-gradient">路面の破線<span class="heading-subtitle">repeating-linear-gradient</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E8%B7%AF%E9%9D%A2%E3%81%AE%E7%A0%B4%E7%B7%9A--repeating-linear-gradient" class="hash-link" aria-label="路面の破線--repeating-linear-gradient への直接リンク" title="路面の破線--repeating-linear-gradient への直接リンク" translate="no">​</a></h3>
<p><strong><code>repeating-linear-gradient</code> (繰り返し直線グラデーション)</strong> は、一定のパターンを繰り返す直線グラデーションです。路面の破線はこれ 1 つで作っています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.road</span><span class="token selector pseudo-element" style="color:#00009f">::after</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">background</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">repeating-linear-gradient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">to right</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentColor </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">26</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token color">transparent</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">26</span><span class="token unit">px</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">70</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>to right</code> は左から右への向き。<code>currentColor 0 26px</code> で「0 から 26px までは色」、<code>transparent 26px 70px</code> で「26px から 70px まで (＝ 44px ぶん) は透明」。これが延々と繰り返され、塗り 26px・すき間 44px の破線になります。</p>
<p>シルエットや線画には <code>mask</code> (ベタ塗りは <code>fill</code>、窓抜きは <code>fill-rule: evenodd</code>、線画は <code>stroke</code>)、ふんわりした塊や規則的な線には <code>gradient</code>、と使い分けると、画像ファイルを 1 枚も足さずに背景が組めます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装--アクセシビリティとレスポンシブ">実装<span class="heading-subtitle">アクセシビリティとレスポンシブ</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E5%AE%9F%E8%A3%85--%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B7%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3%E3%81%A8%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96" class="hash-link" aria-label="実装--アクセシビリティとレスポンシブ への直接リンク" title="実装--アクセシビリティとレスポンシブ への直接リンク" translate="no">​</a></h2>
<p>最後に、いろいろな人・いろいろな画面で気持ちよく見えるための配慮です。</p>
<p>このアニメーションは純粋な装飾なので、スクリーンリーダー (画面の内容を読み上げる支援技術) からは隠しています。</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">scene</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">aria-hidden</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">true</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p><code>aria-hidden="true"</code> は「この要素は支援技術に伝えなくてよい」という印です。hero の見出し (サイト名の <code>&lt;h1&gt;</code>) が内容を担保しているので、装飾の車まで読み上げる必要はありません。</p>
<p>そして CSS アニメーション基礎の章で触れた <strong><code>prefers-reduced-motion</code></strong> を、ここで使います。動きを減らす設定をしている人には、すべてのアニメーションを止めます。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token atrule rule" style="color:#00a4db">@media</span><span class="token atrule" style="color:#00a4db"> </span><span class="token atrule punctuation" style="color:#393A34">(</span><span class="token atrule property" style="color:#36acaa">prefers-reduced-motion</span><span class="token atrule punctuation" style="color:#393A34">:</span><span class="token atrule" style="color:#00a4db"> reduce</span><span class="token atrule punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector class" style="color:#00009f">.clouds</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">  </span><span class="token selector class" style="color:#00009f">.buildingsFar</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">  </span><span class="token selector class" style="color:#00009f">.buildingsMid</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">  </span><span class="token selector class" style="color:#00009f">.street</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">  </span><span class="token selector class" style="color:#00009f">.road</span><span class="token selector pseudo-element" style="color:#00009f">::after</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">  </span><span class="token selector class" style="color:#00009f">.tire</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">animation</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> none</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>ここで効いてくるのが、<strong>「止まっていても 1 枚の絵として成立する」ようにレイアウトを先に組んでおく</strong> という設計です。このアニメーションは、まず静止状態 (車が道に佇んでいる絵) を作り、そこに動きを後付けしました。だから動きを止めても、ちゃんと成立した静止画になります。</p>
<p>レスポンシブ (画面幅への対応) も同じ考え方です。背景は画面幅いっぱいに広げ、車は中央でサイズの上限を持たせています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token selector class" style="color:#00009f">.car</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">position</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> absolute</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">left</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">bottom</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">18</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">width</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">88</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">680</span><span class="token unit">px</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">/* 画面が広いときは 680px で頭打ち、狭いときは 88% */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translateX</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">-50</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">/* 中央寄せ */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.carBody</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">display</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> block</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">width</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token unit">%</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                 </span><span class="token comment" style="color:#999988;font-style:italic">/* 車体 SVG を .car の幅いっぱいに広げる */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">height</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> auto</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>タイヤ位置は % 指定なので車のサイズ変化に追従します。モバイルでは、上の余白を少し詰めるだけでそのまま表示しています。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token atrule rule" style="color:#00a4db">@media</span><span class="token atrule" style="color:#00a4db"> </span><span class="token atrule punctuation" style="color:#393A34">(</span><span class="token atrule property" style="color:#36acaa">max-width</span><span class="token atrule punctuation" style="color:#393A34">:</span><span class="token atrule" style="color:#00a4db"> </span><span class="token atrule number" style="color:#36acaa">720</span><span class="token atrule unit" style="color:#00a4db">px</span><span class="token atrule punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token selector class" style="color:#00009f">.scene</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">margin-top</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.5</span><span class="token unit">rem</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころ--replaceattrvalues-はなぜ効かなかったか">ハマりどころ<span class="heading-subtitle">replaceAttrValues はなぜ効かなかったか</span><a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D--replaceattrvalues-%E3%81%AF%E3%81%AA%E3%81%9C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%8B" class="hash-link" aria-label="ハマりどころ--replaceattrvalues-はなぜ効かなかったか への直接リンク" title="ハマりどころ--replaceattrvalues-はなぜ効かなかったか への直接リンク" translate="no">​</a></h2>
<p>ここからは、この実装でいちばん時間を取られた <strong>色の問題</strong> の種明かしです。テーマ追従の章で「<code>fill: currentColor</code> をなぜ CSS で書くのか、あとで説明する」と予告した、その回収です。</p>
<p>素材の SVG は黒一色の線画 (<code>#000000</code>) でした。これをテーマ色に追従させたい。svgr には属性値を置換する機能があると知っていたので、まず <code>docusaurus.config.ts</code> でこう設定しました。</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">svgr</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  svgrConfig</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    replaceAttrValues</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string-property property" style="color:#36acaa">'#000000'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'currentColor'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-property property" style="color:#36acaa">'#000'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'currentColor'</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><br></div></code></pre></div></div>
<p><code>#000000</code> という色の指定を <code>currentColor</code> に置き換えれば、あとは CSS の <code>color</code> で色を制御できる——はずでした。<strong>ところが、これが効きませんでした。</strong> ビルドしても車は黒いまま。</p>
<p>原因を追うために、ソースの SVG と、ビルドされた後の HTML を見比べてみました。</p>
<p>まずソース (<code>static/img/car.svg</code>)。色は <code>&lt;g&gt;</code> (グループ) の <code>fill="#000000"</code> に <strong>1 か所だけ</strong> あり、その中の <code>&lt;path&gt;</code> には <code>fill</code> 属性がありません。SVG 基礎の章で「<code>fill</code> が無い図形はデフォルトで黒」と説明した、まさにその状態です。(<code>&lt;path&gt;</code> の <code>d</code> 属性は実際には数千文字におよぶ 1 本の長大な座標列なので、ここでは先頭だけ示して <code>...</code> で省略しています。完全な中身は <code>static/img/car.svg</code> で見られます。)</p>
<div class="language-svg codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">static/img/car.svg (ソース)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-svg codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">viewBox</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">83 349 2650 747</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">car-line-art</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">g</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">transform</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">translate(0,1536) scale(0.05,-0.05)</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">fill</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#000000</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">stroke</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">none</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">path</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">d</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">M31820 23734 c-1590 -37 -2289 -76 ...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- path が続くが、いずれも fill 属性を持たない --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">g</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>次にビルド後の HTML。<strong><code>&lt;g&gt;</code> が丸ごと消え、<code>&lt;path&gt;</code> が <code>&lt;svg&gt;</code> の直下に並んでいます</strong>。座標は <code>&lt;g&gt;</code> の <code>transform</code> が各 path に計算し直されて畳み込まれ、肝心の <code>fill</code> 属性は付いていません。</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">ビルド後の DOM (build/index.html)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">viewBox</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">83 349 2650 747</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">carBody_DE43</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">car-line-art</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">path</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">d</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">M1591 349.3c-79.5 1.85-114.45 3.8 ...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- g が消滅 / path に fill 属性なし --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">svg</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>何が起きたのか。svgr は内部で <strong>svgo</strong> という「SVG 最適化ツール」を通してから React コンポーネントに変換します。その最適化の過程で、<code>&lt;g&gt;</code> が畳まれて消え (<code>transform</code> は各 path の座標に適用され)、<strong><code>fill="#000000"</code> の情報は最終的な <code>&lt;path&gt;</code> に残りませんでした</strong>。</p>
<p><code>replaceAttrValues</code> は「<code>#000000</code> という文字列を探して置換する」機能です。ところが、置換すべき <code>#000000</code> が最適化後の HTML に存在しないので、置換のしようがなく、何も起きなかったのです。下の図が、その流れです。</p>
<!-- -->
<p>最終的に効いたのは、CSS のたった数行でした。</p>
<div class="language-css codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-css codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* path に fill 属性が無い（svgo が除去）ので、CSS の fill が SVG デフォルトの黒を上書きできる */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.carBody</span><span class="token selector" style="color:#00009f"> path</span><span class="token selector punctuation" style="color:#393A34">,</span><span class="token selector" style="color:#00009f"></span><br></div><div class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f"></span><span class="token selector class" style="color:#00009f">.tire</span><span class="token selector" style="color:#00009f"> path</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">fill</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> currentColor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>ポイントは、SVG 基礎の章で伏線を張った性質——<strong><code>&lt;path&gt;</code> に <code>fill</code> 属性が無いとき、CSS の <code>fill</code> が SVG デフォルトの黒を上書きできる</strong> ——です。属性が無いからこそ、CSS で素直に塗れる。皮肉にも、<code>replaceAttrValues</code> が効かなかった理由 (path に <code>fill</code> 属性が残らなかったこと) が、そのまま CSS で解決できる理由になっていました。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>config の設定はなぜ残してあるか</div><div class="admonitionContent_BuS1"><p>上記の経緯で <code>replaceAttrValues</code> は実質的に効いていませんが、<code>docusaurus.config.ts</code> には今も残してあります。理由は 2 つで、(1)「この SVG はテーマ色化したい」という意図を設定として残す価値があること、(2) svgo の最適化結果は素材次第なので、別の SVG を足したときには効く可能性があること。実際の色制御は CSS が担っている、という整理です。</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="その他のつまずき">その他のつまずき<a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%81%9D%E3%81%AE%E4%BB%96%E3%81%AE%E3%81%A4%E3%81%BE%E3%81%9A%E3%81%8D" class="hash-link" aria-label="その他のつまずき への直接リンク" title="その他のつまずき への直接リンク" translate="no">​</a></h3>
<p>色の問題ほどではありませんが、他にもいくつか引っかかった点を、本文で説明した内容のおさらいとして並べておきます。</p>
<ul>
<li class=""><strong>タイヤの軸ブレ</strong>: <code>viewBox</code> がいびつだと回転中心がずれてガタつく。<code>viewBox</code> を <strong>正方形</strong> にクロップして中心を揃えると解決 (「タイヤを軸ブレなく回す」の章)。</li>
<li class=""><strong><code>transform</code> の上書き</strong>: <code>@keyframes</code> で <code>rotate()</code> だけを書くと、中心合わせの <code>translate(-50%, -50%)</code> が消えてタイヤが飛ぶ。回転キーフレームでも <code>translate</code> を <strong>併記</strong> する (「CSS アニメーションの基礎」の章)。</li>
<li class=""><strong>ループの境目で背景が飛ぶ</strong>: 初版は移動距離を「画面幅の 100%」にしていたため、画面幅がタイル幅の整数倍にならず、ループのたびに背景がカクッとずれていた。移動距離をタイル幅の整数倍 (px) に固定して解決 (「継ぎ目なく無限ループさせる仕掛け」の章)。</li>
<li class=""><strong>背景の密度は控えめに</strong>: ビルや標識を増やすほど街は賑やかになるが、主役の車が埋もれる。実際に初版は密度が高すぎて、ビルの棟数と街路オブジェクトを減らし、間隔を広げる調整をした。</li>
<li class=""><strong>速度・位置・向きは実測で</strong>: パララックスの距離・秒数やタイヤの座標 (%) に理論値はなく、画面を見ながら調整した。回転や流れの向きも符号 1 つで逆転するので、最後は目で見て決めるのが早い。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ再利用のヒント">まとめ・再利用のヒント<a href="https://notes.rewheel.dev/blog/hero-car-animation#%E3%81%BE%E3%81%A8%E3%82%81%E5%86%8D%E5%88%A9%E7%94%A8%E3%81%AE%E3%83%92%E3%83%B3%E3%83%88" class="hash-link" aria-label="まとめ・再利用のヒント への直接リンク" title="まとめ・再利用のヒント への直接リンク" translate="no">​</a></h2>
<p>画像ファイルを 1 枚も足さず、svgr と純 CSS だけで「車が走る」hero を作りました。同じ構成は他の SVG メインビジュアルにも応用できます。要点を再利用しやすい形でまとめます。</p>
<ol>
<li class=""><strong>SVG はコンポーネント化</strong>: Docusaurus なら svgr が同梱なので <code>import X from './x.svg'</code> で済む。型定義も不要。</li>
<li class=""><strong>色は <code>currentColor</code> に寄せる</strong>: <code>path</code> に <code>fill</code> 属性が無ければ CSS の <code>fill</code> で塗れる。<code>.scene</code> に <code>color</code> を置き、テーマで切り替える。色の指示は 1 か所に集約する。</li>
<li class=""><strong>背景は mask / gradient で</strong>: シルエットは CSS <code>mask</code> (窓抜きは <code>fill-rule: evenodd</code>、線画は <code>stroke</code>)、ふんわりした塊は <code>radial-gradient</code>、規則的な線は <code>repeating-linear-gradient</code>。画像を増やさずに済む。</li>
<li class=""><strong>動きは <code>transform</code> だけで</strong>: <code>translate3d</code> と <code>rotate</code> を GPU 合成に乗せ、<code>will-change</code> を添える。レイアウトを揺らさない。<code>transform</code> は後勝ちなので、効かせたい変形は 1 つにまとめて書く。</li>
<li class=""><strong>ループの移動距離はタイル幅の整数倍に</strong>: 繰り返し背景は「パターンの整数倍だけ動かして戻す」と境目が見えない。タイル幅と移動距離を px で固定するのが確実。</li>
<li class=""><strong>止まった状態を先に作る</strong>: <code>prefers-reduced-motion</code> で止めても 1 枚の絵になるよう、静止レイアウトを出発点にする。</li>
</ol>
<p>ロゴの車輪を動かしたいという思いつきから始めましたが、SVG と CSS の素直な性質を組み合わせるだけで、ライブラリなしでここまで作れました。車輪の再発明、なかなか楽しいものです。</p>]]></content:encoded>
            <category>docusaurus</category>
            <category>react</category>
            <category>css</category>
            <category>svg</category>
            <category>animation</category>
        </item>
        <item>
            <title><![CDATA[Claude Code ベストプラクティス集 shanraisshan/claude-code-best-practice の歩き方]]></title>
            <link>https://notes.rewheel.dev/blog/claude-code-best-practice-guide</link>
            <guid>https://notes.rewheel.dev/blog/claude-code-best-practice-guide</guid>
            <pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[GitHub Trending #1 のキュレーション集 shanraisshan/claude-code-best-practice (445 ファイル) を、ディレクトリ構成・概念・ワークフロー・Tips・レポートの 5 軸で読み解く見取り図。自分のレベルに合った要素を選べるよう整理した。]]></description>
            <content:encoded><![CDATA[<p>Claude Code を業務に取り入れ始めると、公式ドキュメントを読んでも「次に何を導入すべきか」「自分のレベルに合うのはどれか」が見えにくくなる段階があります。Anthropic 自身が出している機能だけでも 30 種類を超え、コミュニティが公開しているワークフローも 12 件以上、Boris Cherny や Thariq といった Claude Code 開発陣の Tips も 80 件以上が散らばっている状態です。</p>
<p>その整理に役立つのが、2026 年 3 月の GitHub Trending #1 を獲得した <a href="https://github.com/shanraisshan/claude-code-best-practice" target="_blank" rel="noopener noreferrer" class="">shanraisshan/claude-code-best-practice</a> です。Shayan Raisshan 氏 (Claude Pakistan community 主宰、Claude Community Ambassador) が MIT ライセンスで公開・継続更新しているリポジトリです。執筆時点 (2026 年 5 月 25 日) で 445 ファイル、585 行の <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/README.md" target="_blank" rel="noopener noreferrer" class="">README</a> を持つキュレーション集です。</p>
<p>本記事はこのリポジトリを <strong>自分のレベルと用途に合った要素だけ拾い出せる</strong> ように整理した見取り図です。長めの 1 本ですが通読は前提にしておらず、目次から拾い読みできる構成にしてあります。気になった箇所はそのまま GitHub 上のファイルへ飛べるよう、本文中の重要リンクはリポジトリ内の具体ファイルへの直リンクで張っています。</p>
<p>加えて、各章末には <code>:::info 筆者の実装例</code> ブロックを設けて、筆者自身の <code>~/.claude/</code> (agents 7 件 / skills 38 件 / rules 31 件 / hooks 3 script / teams 1 件 / ADR 16 件) でどう実践しているかを併載しました。「他人の整理 (キュレーション)」と「自分の harness (実装)」の橋渡しとして使えるようにしてあります。実装例が不要な方は <code>:::info</code> ブロックを読み飛ばし、<code>:::note 採用判断</code> ブロックだけを拾って章をまたいでも、本筋は崩れない構成にしています。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>本記事の読み方</div><div class="admonitionContent_BuS1"><p>このリポジトリは作者自身が「コードリポではなくリファレンス・コース」と位置づけています (<a href="https://github.com/shanraisshan/claude-code-best-practice#how-to-use" target="_blank" rel="noopener noreferrer" class="">README の How to Use セクション</a>)。本記事もそれに倣い、まず全体像を提示してから、章末ごとに <code>採用判断</code> ミニまとめを置いて「自分は何を試すか」を整理する形で進めます。最後の第 9 章にレベル別ロードマップをまとめてあるので、急ぐ方はそちらから読んでも構いません。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-1-章-リポジトリの正体--13-ディレクトリの役割">第 1 章: リポジトリの正体<span class="heading-subtitle">13 ディレクトリの役割</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-1-%E7%AB%A0-%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AE%E6%AD%A3%E4%BD%93--13-%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AE%E5%BD%B9%E5%89%B2" class="hash-link" aria-label="第-1-章-リポジトリの正体--13-ディレクトリの役割 への直接リンク" title="第-1-章-リポジトリの正体--13-ディレクトリの役割 への直接リンク" translate="no">​</a></h2>
<p>最初に全体像を把握します。リポジトリ直下には 13 の主要ディレクトリと 2 つの基幹ファイルがあります。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">shanraisshan/claude-code-best-practice/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── README.md                    ハブ。585 行に全分類表が集約</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── CLAUDE.md                    このリポ自身の Claude Code 設定 (自己言及)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── best-practice/    (8 件)     概念ごとの仕様カタログ</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── implementation/   (6 件)     実際に動く実装例</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── orchestration-workflow/      ショーケース (Command → Agent → Skill)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── agent-teams/                 Agent Teams 実装例</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── development-workflows/       cross-model + RPI ワークフロー</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── reports/         (12 件)     深掘り解析レポート</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── tips/            (10 件)     Boris / Thariq 等の発言集</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── videos/           (8 件)     ポッドキャスト / 動画解説</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── tutorial/                    day0 / day1 セットアップ手順</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── changelog/                   更新追跡</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">├── presentation/                GDG Kolachi 等の登壇資料</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">└── .claude/                     このリポ自身が使う agents/commands/skills/hooks</span><br></div></code></pre></div></div>
<p>このリポジトリの特徴は <strong>4 つの情報軸が役割分担している</strong> 点にあります。</p>
<table><thead><tr><th>軸</th><th>配置</th><th>用途</th><th>読者にとっての価値</th></tr></thead><tbody><tr><td><strong>Concepts</strong></td><td><code>best-practice/</code></td><td>各機能の仕様カタログ</td><td>「この機能は何か」を 5 分で把握</td></tr><tr><td><strong>Workflows</strong></td><td><code>development-workflows/</code>, <code>orchestration-workflow/</code>, ECC/Superpowers 等の外部リポへのリンク</td><td>機能を組み合わせた方法論</td><td>他人がどう組み合わせているかを学ぶ</td></tr><tr><td><strong>Tips</strong></td><td><code>tips/</code> + <code>README</code> の 83 件</td><td>開発者発言の集約</td><td>自分が次に試すべきことを発見</td></tr><tr><td><strong>Reports</strong></td><td><code>reports/</code> (12 件)</td><td>比較分析・深掘り論考</td><td>「なぜそうするか」の理屈を理解</td></tr></tbody></table>
<p>どこから読み始めるかは、自分の Claude Code 経験レベルで決まります。インストールが終わったばかりなら <code>tutorial/</code> から始めて Tips の入門カテゴリへ。基本機能は動かしたことがあるなら <code>best-practice/</code> の 8 ファイルを順に読みつつ Workflows を 1 つ試す。すでに自前で <code>.claude/</code> を育てている人は Reports と Workflows の比較で次の改善ヒントを探す、という具合です。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>ディレクトリ図を「目次」としてブックマーク。1 セッションで全部読もうとせず、章別に拾い読みする方が頭に残ります。第 9 章のロードマップを先に眺めてから戻るのも1つの読み方。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-2-章-30-の-claude-code-概念カタログ">第 2 章: 30+ の Claude Code 概念カタログ<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-2-%E7%AB%A0-30-%E3%81%AE-claude-code-%E6%A6%82%E5%BF%B5%E3%82%AB%E3%82%BF%E3%83%AD%E3%82%B0" class="hash-link" aria-label="第 2 章: 30+ の Claude Code 概念カタログ への直接リンク" title="第 2 章: 30+ の Claude Code 概念カタログ への直接リンク" translate="no">​</a></h2>
<p>リポジトリの中心になるのが、README 冒頭の <code>🧠 CONCEPTS</code> 表と <code>🔥 Hot</code> 表です。Anthropic が Claude Code に追加した機能を網羅し、各機能について「公式ドキュメント」「リポ内の best-practice 解説」「実装例」「コミュニティの実装例」へのリンクを付けています。</p>
<p>ここでは機能を <strong>役割で再グルーピング</strong> して紹介します。各機能に <strong>採用 difficulty ラベル</strong> を付けてあります: ★ は設定 1 ファイル / 学習 30 分以内で導入可能、★★ は数ファイル + 1-2 時間の学習が必要、★★★ は複数機能の組み合わせ / 半日以上の理解が必要です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-中核プリミティブ-4">2.1 中核プリミティブ (4)<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#21-%E4%B8%AD%E6%A0%B8%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96-4" class="hash-link" aria-label="2.1 中核プリミティブ (4) への直接リンク" title="2.1 中核プリミティブ (4) への直接リンク" translate="no">​</a></h3>
<p>Claude Code を「ただのチャットボット以上」にする 4 つの基本機能です。</p>
<p><strong>Subagents</strong> ★★：<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-subagents.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-subagents.md</a> には 16 個の frontmatter (= ファイル冒頭に書く YAML メタデータ) フィールドが表形式で解説されています。フィールドは name / description / tools / model / permissionMode / skills / mcpServers / hooks / memory / effort / isolation / initialPrompt / color などです。Anthropic 公式ビルトインも 5 種類用意されていて、<code>general-purpose</code> (汎用), <code>Explore</code> (haiku でコードベース探索), <code>Plan</code> (read-only な実装計画), <code>statusline-setup</code>, <code>claude-code-guide</code> という構成です。実装例は <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/implementation/claude-subagents-implementation.md" target="_blank" rel="noopener noreferrer" class="">implementation/claude-subagents-implementation.md</a> にまとまっています。</p>
<p>設計の中核にあるのは <strong>Generator-Evaluator 分離</strong> という原則です。これは「作る側 (Generator)」と「評価する側 (Evaluator)」を別 subagent に分けて、self-evaluation bias (= 自分が作ったものを自分で評価すると品質を過大評価しやすい認知バイアス) を回避する考え方です。Boris Cherny も繰り返し言及しています。</p>
<p><strong>Commands</strong> ★：slash コマンドの作り方は <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-commands.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-commands.md</a> に解説があります。<code>.claude/commands/&lt;name&gt;.md</code> に Markdown を置くだけで <code>/&lt;name&gt;</code> として呼び出せるようになる、もっとも導入コストが低い拡張ポイントです。Boris Cherny の Tip でも「1 日 1 回以上やる作業は command か skill にする」と繰り返し強調されています。</p>
<p><strong>Skills</strong> ★★：<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-skills.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-skills.md</a> では 15 個の frontmatter フィールドに加え、<strong>9 個の公式 bundled skill</strong> が表になっています。まず <code>simplify</code> (重複削除リファクタ), <code>batch</code> (一括コマンド), <code>debug</code> (失敗調査), <code>loop</code> (定期実行), <code>claude-api</code> (Anthropic SDK 補助) の 5 つです。残る 4 つは <code>fewer-permission-prompts</code> (allowlist 自動生成), <code>run</code> (実アプリ起動), <code>verify</code> (動作検証), <code>run-skill-generator</code> (起動レシピ作成) です。v2.1.145 で <code>/run</code>, <code>/verify</code>, <code>/run-skill-generator</code> の 3 つが追加された点が特に新しいです。</p>
<p>Skill 設計の重要概念に <strong>Progressive disclosure</strong> (段階的開示) があります。1 つの SKILL.md ファイルに全情報を詰め込むのではなく、フォルダ内に <code>references/</code> <code>scripts/</code> <code>examples/</code> を配置し、必要になった時だけ Claude が読み込む構造にします。こうすることで context window (一度に処理できるトークン枠) を圧迫しないという設計指針です。</p>
<p><strong>Hooks</strong> ★★★：<code>PreToolUse</code> / <code>PostToolUse</code> / <code>Stop</code> / <code>SessionStart</code> などライフサイクルイベントで shell コマンドを実行できる機構。リポ内では別リポ <a href="https://github.com/shanraisshan/claude-code-hooks" target="_blank" rel="noopener noreferrer" class="">shanraisshan/claude-code-hooks</a> として独立しており、サウンド通知 / 自動フォーマット / 危険コマンドのブロック等の実装例が揃っています。Boris の発言「PostToolUse hook で auto-format を入れると Claude が生成するコードの最後 10% を仕上げてくれて CI failure を防げる」が代表的な活用例です。</p>
<p>Hook の役割を整理する枠組みとして <strong>Computational Sensor / Inferential Guide の二分法</strong> があります。Computational Sensor は「コードで自動検出する deterministic (決定的) な仕組み」です (lint / CI / hook の suppression 検出など)。Inferential Guide は「LLM 自身に判断させる確率的な仕組み」です (CLAUDE.md / rules / skills の指示文)。Hook は前者を担当し、rules や CLAUDE.md だけでは LLM が確率的に違反してしまう規範を deterministic に強制する役割を持ちます。</p>
<p>ここまでで Subagent / Command / Skill / Hook の 4 つを見てきましたが、初学者が最も混乱しやすい <strong>Subagent vs Skill vs Command の境界</strong> を 1 つの表に集約しておきます (Hook はライフサイクル機構のため別軸として整理)。</p>
<table><thead><tr><th>機能</th><th>役割</th><th>起動方法</th></tr></thead><tbody><tr><td>Command</td><td>エントリポイント / ワークフロー指揮</td><td><code>/&lt;name&gt;</code> をユーザーが打鍵</td></tr><tr><td>Agent (Subagent)</td><td>ドメイン作業の主役 (別 context window で動く)</td><td>Command または上位 agent から呼出</td></tr><tr><td>Skill</td><td>成果物生成 / 参照知識の単位</td><td>trigger 発話で自動起動、または Skill tool から呼出</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は <code>~/.claude/agents/</code> に <strong>7 件の subagent</strong> を配置し、<strong>Generator-Evaluator 分離</strong> を意図的に組んでいます。内訳は architect / code-reviewer / debugger / implementer / refactorer / test-strategist / infra-reviewer の 7 つです。</p><ul>
<li class=""><strong>Generator (作る側)</strong>: implementer (実装専任) / refactorer (リファクタ専任) / debugger (バグ調査 + 修正)</li>
<li class=""><strong>Evaluator (評価する側)</strong>: architect (設計レビュー) / code-reviewer (コードレビュー) / test-strategist (テスト戦略) / infra-reviewer (TF/IaC レビュー)</li>
</ul><p>Evaluator 側の 4 件は <code>disallowedTools: Write, Edit</code> で <strong>read-only を強制</strong> しており、評価担当が誤って実装に手を出すことを構造的に防いでいます。命名も <code>general qa</code> のような汎用名ではなく、Boris 推奨の <strong>feature 特化</strong> (具体的な役割名) に統一しています。</p><p>具体的には、subagent 定義ファイルの frontmatter は以下のような YAML 形式で書きます (筆者の <code>code-reviewer.md</code> 抜粋)。</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">reviewer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">description</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ペアプログラミング用コードレビュアー。コード変更後のレビューとガイダンスを提供</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">model</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> opus</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">effort</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> max</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">tools</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Read</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Grep</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Glob</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Bash</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> WebSearch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mcp__context7__query</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">docs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">disallowedTools</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Write</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Edit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">isolation</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> worktree</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Instruction 本文...</span><br></div></code></pre></div></div><p><code>tools</code> で許可ツール、<code>disallowedTools</code> で禁止ツールを明示することで、評価専任エージェントが誤って実装に手を出すリスクを deterministic に排除できます。</p><p><strong>Commands は 0 件で運用</strong> しており、用途は全て skill に統合しています。理由は、skill は frontmatter に書いた trigger 発話 (例: 「レビュー」「バグ」) で自動起動できるため、<code>/command-name</code> を毎回打つ手間が省けるからです。Boris の「1 日 1 回以上やる作業は command か skill にせよ」は、筆者環境では「全部 skill にせよ」に翻訳されています。</p><p><strong>Skills は 38 フォルダ</strong> (propose / debug / review / explain / compare / learn / next / checkpoint / spec-driven-development / verify ほか) を運用。代表的な trigger 発話の対応は以下のとおりです。</p><table><thead><tr><th>Skill</th><th>trigger 発話</th><th>役割</th></tr></thead><tbody><tr><td><code>/propose</code></td><td>"提案して", "設計案"</td><td>敵対的自己批判付きで実装方針を提案 (最大 2 反復)</td></tr><tr><td><code>/debug</code></td><td>"バグ", "動かない"</td><td>Investigate → Fix → Verify を最大 3 反復で自律解決</td></tr><tr><td><code>/review</code></td><td>"レビュー", "チェック"</td><td>7 観点 (正確性 / 可読性 / パフォーマンス / セキュリティ / 保守性 / 規約 / デグレ) でコードレビュー</td></tr><tr><td><code>/explain</code></td><td>"説明して", "なぜ"</td><td>コード・概念を段階的に説明</td></tr><tr><td><code>/checkpoint</code></td><td>"checkpoint", "区切って"</td><td>現セッション整理 + 次セッション用 prompt 生成</td></tr></tbody></table><p>Progressive disclosure 構造 (<code>references/</code> <code>scripts/</code> <code>examples/</code> を持つフォルダ型) は 2 件のみ (skill-creator / react-view-transitions)。他の 36 件は SKILL.md 単体 + metadata で済ませており、必要に応じて段階導入する方針です。</p><p><strong>Hooks は実体スクリプト 3 件</strong> を運用しています。</p><ul>
<li class=""><code>~/.claude/hooks/check-protected-files.sh</code> (PreToolUse)：Write/Edit 実行前に <code>.env</code> 等の保護ファイルへの書き込みを block</li>
<li class=""><code>~/.claude/hooks/check-suppression.sh</code> (PostToolUse)：Write/Edit の出力に suppression が新規追加されていないか deterministic に検証。対象は <code>// eslint-disable-next-line</code> や <code>// @ts-ignore</code> 等</li>
<li class=""><code>~/.claude/hooks/check-plain-question.sh</code> (Stop)：応答終了時にテキスト出力を grep し、「〜ましょうか」「〜ですか」等の平文質問パターンを検出。exit 2 で Claude に自己修正を促す (AskUserQuestion ツール使用を強制する Sensor)</li>
</ul><p>Boris の「PostToolUse hook で auto-format」を、筆者は <strong>「format」ではなく「suppression 検出」</strong> に転用しているのが意図的な差分です。format は CI で十分カバーできますが、suppression は LLM が確率的に挿入する anti-pattern で、CI 通過後に永続化されると harness が空洞化するため、PostToolUse hook で実行直後に即時 block します。</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-統合機構-5">2.2 統合機構 (5)<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#22-%E7%B5%B1%E5%90%88%E6%A9%9F%E6%A7%8B-5" class="hash-link" aria-label="2.2 統合機構 (5) への直接リンク" title="2.2 統合機構 (5) への直接リンク" translate="no">​</a></h3>
<p>外部世界やプロジェクト設定とつなぐためのレイヤです。</p>
<table><thead><tr><th>機能</th><th>難易度</th><th>リポ内リファレンス</th></tr></thead><tbody><tr><td><strong>MCP Servers</strong></td><td>★★</td><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-mcp.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-mcp.md</a>：context7 / aws-knowledge / playwright 等を Claude Code から呼べるプロトコル</td></tr><tr><td><strong>Plugins</strong></td><td>★★</td><td><a href="https://code.claude.com/docs/en/plugins" target="_blank" rel="noopener noreferrer" class="">公式 docs</a>：配布可能パッケージ。marketplace を立てる方法もリンクあり</td></tr><tr><td><strong>Settings</strong></td><td>★</td><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-settings.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-settings.md</a>：<code>.claude/settings.json</code> の全フィールド網羅、permission / model / output style 等</td></tr><tr><td><strong>Status Line</strong></td><td>★</td><td><a href="https://github.com/shanraisshan/claude-code-status-line" target="_blank" rel="noopener noreferrer" class="">shanraisshan/claude-code-status-line</a>：context 使用量 / cost の可視化</td></tr><tr><td><strong>Memory</strong></td><td>★★</td><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-memory.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-memory.md</a> + 深掘り <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-agent-memory.md" target="_blank" rel="noopener noreferrer" class="">reports/claude-agent-memory.md</a>：CLAUDE.md / .claude/rules / auto-memory の階層</td></tr></tbody></table>
<p>ここで <strong>MCP (Model Context Protocol)</strong> について少し補足しておきます。これは Anthropic が策定した「外部のツールやデータソースを Claude に呼び出させるための標準プロトコル」です。対応する MCP server を導入すれば、Claude が外部機能を tool として呼べるようになります。例えばライブラリドキュメント取得 (context7) / AWS 公式 docs 取得 (aws-knowledge) / ブラウザ自動化 (playwright / chrome-devtools) / コードベース symbol 検索 (serena) などです。WebSearch だけでは取りこぼす一次情報を確実に取得できるため、技術的判断の精度が上がります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-セッション制御-3">2.3 セッション制御 (3)<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#23-%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%B6%E5%BE%A1-3" class="hash-link" aria-label="2.3 セッション制御 (3) への直接リンク" title="2.3 セッション制御 (3) への直接リンク" translate="no">​</a></h3>
<p>長時間セッションや複雑タスクで Claude Code が「賢く動き続ける」ための機構です。</p>
<ul>
<li class=""><strong>Checkpointing</strong> ★：ファイル編集を自動追跡し、<code>/rewind</code> で過去状態に戻れる。失敗した試行で context を汚すより rewind した方が良い、という Thariq の Tip と組み合わせると価値が出ます。</li>
<li class=""><strong>CLI Startup Flags</strong> ★：<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-cli-startup-flags.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-cli-startup-flags.md</a> に主要フラグの一覧。<code>--permission-mode</code>, <code>--agent</code>, <code>--bg</code>, <code>--worktree</code> 等。</li>
<li class=""><strong>Auto Mode / Plan Mode</strong> ★★：<code>Shift+Tab</code> で Ask → Plan → Auto のモードを循環。Auto Mode はモデルベース classifier (= 小型モデルでコマンドの安全性を自動分類する分類器) が各コマンドの安全性を判定して自動承認する仕組みで、Boris は「dangerously-skip-permissions の代わりにこれを使え」と推奨しています。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24--hot-機能群-新機能優先度高">2.4 🔥 Hot 機能群 (新機能、優先度高)<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#24--hot-%E6%A9%9F%E8%83%BD%E7%BE%A4-%E6%96%B0%E6%A9%9F%E8%83%BD%E5%84%AA%E5%85%88%E5%BA%A6%E9%AB%98" class="hash-link" aria-label="2.4 🔥 Hot 機能群 (新機能、優先度高) への直接リンク" title="2.4 🔥 Hot 機能群 (新機能、優先度高) への直接リンク" translate="no">​</a></h3>
<p>2026 年に入って急速に追加された機能群です。一部は beta 表記がついていて仕様変更の可能性がありますが、業務での活用余地が大きいものを中心に紹介します。</p>
<table><thead><tr><th>機能</th><th>難易度</th><th>用途</th></tr></thead><tbody><tr><td><strong>Ultrareview</strong> ★★</td><td>beta</td><td><code>/ultrareview</code> で multi-agent な PR レビュー。GitHub PR# を指定して走らせると複数視点でレビューが並列実行される</td></tr><tr><td><strong>Ultraplan</strong> ★★</td><td>beta</td><td><code>/ultraplan</code> で大規模な計画立案。Plan mode の上位版</td></tr><tr><td><strong>Auto Mode</strong> ★</td><td>beta</td><td>上記 §2.3 参照。classifier が安全なコマンドを自動承認</td></tr><tr><td><strong>Power-ups</strong> ★</td><td>—</td><td><code>/powerup</code> (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-power-ups.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-power-ups.md</a>)</td></tr><tr><td><strong>Fast Mode</strong> ★</td><td>beta</td><td><code>/fast</code> で出力高速化 (モデルはダウングレードしない、Opus 4.6/4.7 で利用可)</td></tr><tr><td><strong>Chrome</strong> ★★</td><td>beta</td><td><code>--chrome</code> で Claude 専用 Chrome 拡張連携。Payload admin 等のログイン状態を共有して操作</td></tr><tr><td><strong>Code Review</strong> ★</td><td>beta</td><td>GitHub App として managed 提供。Boris は「Greptile / CodeRabbit / Cursor BugBot を置き換える」と言及</td></tr><tr><td><strong>GitHub Actions</strong> ★★</td><td>—</td><td><code>.github/workflows/</code> で claude を呼び出し。GitLab CI/CD も対応</td></tr><tr><td><strong>Remote Control</strong> ★★</td><td>—</td><td><code>/remote-control</code> / <code>/rc</code> で headless (= GUI なしで実行する) 操作</td></tr><tr><td><strong>Agent Teams</strong> ★★★</td><td>beta</td><td>env var で起動、複数 agent を並列実行 (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/implementation/claude-agent-teams-implementation.md" target="_blank" rel="noopener noreferrer" class="">implementation/claude-agent-teams-implementation.md</a>)</td></tr><tr><td><strong>Scheduled Tasks</strong> ★★</td><td>—</td><td><code>/loop</code> (local 最大 7 日) と <code>/schedule</code> (cloud cron) (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/implementation/claude-scheduled-tasks-implementation.md" target="_blank" rel="noopener noreferrer" class="">implementation/claude-scheduled-tasks-implementation.md</a>)</td></tr><tr><td><strong>Routines</strong> ★★</td><td>beta</td><td>cloud で動く scheduled agent。マシンがオフでも実行される</td></tr><tr><td><strong>Goal</strong> ★★</td><td>—</td><td><code>/goal &lt;condition&gt;</code> で「条件達成までターン自動反復」 (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/implementation/claude-goal-implementation.md" target="_blank" rel="noopener noreferrer" class="">implementation/claude-goal-implementation.md</a>)</td></tr><tr><td><strong>Voice Dictation</strong> ★</td><td>beta</td><td><code>/voice</code> で音声プロンプト</td></tr><tr><td><strong>Computer Use</strong> ★★★</td><td>beta</td><td><code>computer-use</code> MCP server。デスクトップ操作の自動化</td></tr><tr><td><strong>Git Worktrees</strong> ★★</td><td>—</td><td><code>--worktree</code> / <code>-w</code> で並列開発。Boris は 5 通りの使い方を <a href="https://x.com/bcherny/status/2025007393290272904" target="_blank" rel="noopener noreferrer" class="">紹介</a></td></tr><tr><td><strong>Simplify &amp; Batch</strong> ★</td><td>—</td><td><code>/simplify</code> / <code>/batch</code> の bundled skill</td></tr><tr><td><strong>Ralph Wiggum Loop</strong> ★★★</td><td>—</td><td>プラグイン。長時間自律ループ。実装は <a href="https://github.com/shanraisshan/ralph-wiggum-self-evolving-loop" target="_blank" rel="noopener noreferrer" class="">別リポ</a> で公開</td></tr></tbody></table>
<p>なお、表中の <strong>Git Worktrees</strong> は git 標準機能で「同じリポジトリを別ディレクトリにチェックアウトして並列作業できる仕組み」のことです (<code>git worktree add ../feature-x feature-x</code> のように使います)。Claude Code の <code>--worktree</code> フラグや subagent の <code>isolation: worktree</code> 設定は、この機能を使って main workspace から作業を隔離します。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は <code>--worktree</code> フラグそのものは多用していませんが、<strong>subagent (debugger / refactorer / code-reviewer) を <code>isolation: worktree</code> で起動するパターン</strong><sup><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#user-content-fn-worktree-isolation-5e45be" id="user-content-fnref-worktree-isolation-5e45be" data-footnote-ref="true" aria-describedby="footnote-label" class="anchorTargetStickyNavbar_Vzrq">1</a></sup> を採用しています。長時間 / 破壊的になりやすいレビューやリファクタを main workspace から隔離し、失敗時の巻き戻しを安全にしています。</p><p><strong>Auto Mode</strong> は permission prompt 削減効果を活用しています。Plan mode 承認後の自動実装で classifier が block するケースには、AskUserQuestion ツールで筆者に明示認可を求める動線を組んでいます (Phase 13 W1-1 ratchet)。この動線は <code>~/.claude/rules/ask-user-question.md</code> の規範によるものです。Auto Mode の便利さと破壊的操作の認可ギャップを harness で吸収する設計です。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>迷ったらまず ★ から導入します。Plan Mode、Commands、Settings あたりは初日から効きます。★★ の Subagents / Skills / Hooks は次の週末で 1 つ作ってみると進歩が大きい領域。★★★ (Agent Teams / Computer Use / Hooks 自作 / Ralph Wiggum) は基本機能を組み合わせて手応えを得てから挑む順番が安全です。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-3-章-orchestration-workflow-ショーケース">第 3 章: Orchestration Workflow ショーケース<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-3-%E7%AB%A0-orchestration-workflow-%E3%82%B7%E3%83%A7%E3%83%BC%E3%82%B1%E3%83%BC%E3%82%B9" class="hash-link" aria-label="第 3 章: Orchestration Workflow ショーケース への直接リンク" title="第 3 章: Orchestration Workflow ショーケース への直接リンク" translate="no">​</a></h2>
<p>このリポの中央に置かれているのが <strong>Command → Agent → Skill</strong> という 3 層オーケストレーションです。作者はこれを <code>/weather-orchestrator</code> という気象データ取得ワークフローで実演しています (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/orchestration-workflow/orchestration-workflow.md" target="_blank" rel="noopener noreferrer" class="">orchestration-workflow.md</a>)。</p>
<!-- -->
<p>この showcase が伝える設計の要点は 2 つあります。</p>
<p>1 つ目は <strong>2 つの Skill パターン</strong> の使い分けです。<code>weather-fetcher</code> は <code>weather-agent</code> の frontmatter に <code>skills:</code> フィールドで指定され、agent が起動した瞬間に context へ全文 inject されます (<strong>Agent Skills パターン</strong>)。一方 <code>weather-svg-creator</code> は preload されておらず、command が <code>Skill</code> tool を呼び出した時点で初めて発火します (<strong>Skill パターン</strong>)。前者は agent に「ドメイン知識を骨格として持たせる」用途、後者は「成果物を作る」用途、と覚えれば整理がつきます。</p>
<p>2 つ目は <strong>役割分担の徹底</strong> です。Command がワークフローを指揮し、Agent がデータ取得などのドメイン作業を担い、Skill が成果物を出力する。3 層がそれぞれ単一責任で並んでいるため、後から「fetcher を OpenWeather に差し替える」「SVG ではなく PNG を出す」といった変更が局所化されます。実装ファイルは <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/.claude/commands/weather-orchestrator.md" target="_blank" rel="noopener noreferrer" class="">.claude/commands/weather-orchestrator.md</a> と <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/.claude/agents/weather-agent.md" target="_blank" rel="noopener noreferrer" class="">.claude/agents/weather-agent.md</a> で直接参照できます。skill 側は <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/.claude/skills/weather-fetcher/SKILL.md" target="_blank" rel="noopener noreferrer" class="">.claude/skills/weather-fetcher/SKILL.md</a> と <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/.claude/skills/weather-svg-creator/SKILL.md" target="_blank" rel="noopener noreferrer" class="">.claude/skills/weather-svg-creator/SKILL.md</a> です。</p>
<p>リポを clone して <code>/weather-orchestrator</code> を実行すれば、上記の流れがそのまま手元で再現できます。3 層構造を学ぶ題材としては、このリポで一番手を動かしやすい素材です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は weather example と同じ「3 層オーケストレーション」の発想で、<code>~/.claude/teams/multi-perspective-review/</code> という team 構成を組んでいます。違いは weather example が「Command → Agent → Skill」の <strong>異種役割の直列実行</strong> であるのに対し、こちらは <strong>同種 Evaluator agent の並列実行</strong> という点です。</p><ul>
<li class=""><strong>Team 構成</strong>: <code>code-reviewer</code> + <code>architect</code> + <code>test-strategist</code> の 3 つの Evaluator subagent を並列起動</li>
<li class=""><strong>trigger 発話</strong>: 「複数視点でレビュー」「総合レビュー」「3 視点」(10 ファイル以上の変更時に推奨)</li>
<li class=""><strong>統合</strong>: 各 agent が独立に出した judgment を main agent が統合し、重複指摘を整理した上で筆者に提示</li>
</ul><p>設計目的は「単一 Evaluator では観点漏れが発生する大規模変更を、独立した 3 視点で冗長検証する」ことです。weather example の「定型タスクを直列で組み立てる」用途とは対照的に、こちらは「重要判断を並列で冗長化する」用途で、3 層構造の応用範囲の広さを示す事例として参考にできます。</p><p>なお Agent Teams 機能が無効な環境でも、Task tool での fallback 経路 (main agent が 3 subagent を逐次呼ぶ) を team.md に記載しており、機能変動に対する耐性も組み込んでいます。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>このパターンを理解するには、weather example をそのまま clone して動かすのが手っ取り早いです。自分の業務でも「定型レポート生成」「データ取得 + 可視化」のような 3 ステップで終わる作業を 1 つだけ、この 3 層構造で書き直してみると pattern が身につきます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-4-章-12-の開発ワークフローを比較する">第 4 章: 12 の開発ワークフローを比較する<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-4-%E7%AB%A0-12-%E3%81%AE%E9%96%8B%E7%99%BA%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%95%E3%83%AD%E3%83%BC%E3%82%92%E6%AF%94%E8%BC%83%E3%81%99%E3%82%8B" class="hash-link" aria-label="第 4 章: 12 の開発ワークフローを比較する への直接リンク" title="第 4 章: 12 の開発ワークフローを比較する への直接リンク" translate="no">​</a></h2>
<p><code>README</code> の <code>⚙️ DEVELOPMENT WORKFLOWS</code> セクションは、コミュニティで公開されている主要ワークフロー 12 件を star 数順に並べて比較しています。各ワークフローは「複数の slash command やスキルを連鎖させて開発を進める方法論」で、それぞれ思想と粒度が異なります。</p>
<table><thead><tr><th>ワークフロー</th><th style="text-align:right">★</th><th>流れの特徴</th><th>構成数 (A / C / S)</th><th>向き</th></tr></thead><tbody><tr><td><a href="https://github.com/obra/superpowers" target="_blank" rel="noopener noreferrer" class="">Superpowers</a></td><td style="text-align:right">200k</td><td>brainstorming → worktrees → writing-plans → subagent-driven → TDD → code-review → verification → finishing</td><td>0 / 0 / 14</td><td>TDD + 並列開発を重視する人</td></tr><tr><td><a href="https://github.com/affaan-m/everything-claude-code" target="_blank" rel="noopener noreferrer" class="">Everything Claude Code (ECC)</a></td><td style="text-align:right">188k</td><td><code>/ecc:plan</code> → <code>/tdd</code> → <code>/code-review</code> → <code>/security-scan</code> → <code>/e2e</code> → merge</td><td>48 / 143 / 230</td><td>商用プロダクトでフルセット導入</td></tr><tr><td><a href="https://github.com/github/spec-kit" target="_blank" rel="noopener noreferrer" class="">Spec Kit</a></td><td style="text-align:right">104k</td><td><code>/speckit.constitution</code> → <code>.specify</code> → <code>.clarify</code> → <code>.plan</code> → <code>.tasks</code> → <code>.implement</code></td><td>0 / 9 / 0</td><td>仕様駆動の最小構成を体験</td></tr><tr><td><a href="https://github.com/garrytan/gstack" target="_blank" rel="noopener noreferrer" class="">gstack</a></td><td style="text-align:right">100k</td><td><code>/office-hours</code> → 3 種類の <code>/plan-*-review</code> → <code>/design-shotgun</code> → <code>/review</code> → <code>/ship</code> → <code>/retro</code></td><td>0 / 0 / 48</td><td>CEO/Eng/Design の多角レビューを回したい</td></tr><tr><td><a href="https://github.com/mattpocock/skills" target="_blank" rel="noopener noreferrer" class="">Matt Pocock Skills</a></td><td style="text-align:right">97k</td><td><code>/grill-with-docs</code> → <code>/to-prd</code> → <code>/to-issues</code> → <code>/triage</code> → TDD → <code>/diagnose</code> → <code>/improve-codebase-architecture</code></td><td>0 / 0 / 28</td><td>要件詰めから PRD 化までを丁寧に</td></tr><tr><td><a href="https://github.com/gsd-build/get-shit-done" target="_blank" rel="noopener noreferrer" class="">Get Shit Done</a></td><td style="text-align:right">63k</td><td><code>/gsd-new-project</code> → <code>discuss</code> → <code>plan</code> → <code>execute</code> → <code>verify</code> → <code>ship</code> → <code>complete-milestone</code></td><td>33 / 67 / 0</td><td>個人開発で速度を求める</td></tr><tr><td><a href="https://github.com/Fission-AI/OpenSpec" target="_blank" rel="noopener noreferrer" class="">OpenSpec</a></td><td style="text-align:right">50k</td><td><code>/opsx:propose</code> → <code>:apply</code> → <code>:archive</code> の 3 段</td><td>0 / 11 / 0</td><td>3 段だけのミニマル仕様駆動</td></tr><tr><td><a href="https://github.com/bmad-code-org/BMAD-METHOD" target="_blank" rel="noopener noreferrer" class="">BMAD-METHOD</a></td><td style="text-align:right">48k</td><td>product-brief → PRD → architecture → epics-and-stories → sprint-planning → story-by-story 実装</td><td>0 / 0 / 42</td><td>PdM/PM と協働するチーム</td></tr><tr><td><a href="https://github.com/Yeachan-Heo/oh-my-claudecode" target="_blank" rel="noopener noreferrer" class="">oh-my-claudecode</a></td><td style="text-align:right">34k</td><td><code>/deep-interview</code> → <code>/team</code> → 5 段 team フロー → <code>/ralph</code> → merge</td><td>19 / 0 / 39</td><td>Team モードと Ralph ループを使いたい</td></tr><tr><td><a href="https://github.com/addyosmani/agent-skills" target="_blank" rel="noopener noreferrer" class="">agent-skills</a></td><td style="text-align:right">27k</td><td><code>/spec</code> → <code>/plan</code> → <code>/build</code> → <code>/test</code> → <code>/review</code> → <code>/ship</code></td><td>3 / 7 / 21</td><td>王道 6 段、迷ったらここから</td></tr><tr><td><a href="https://github.com/EveryInc/compound-engineering-plugin" target="_blank" rel="noopener noreferrer" class="">Compound Engineering</a></td><td style="text-align:right">17k</td><td><code>/ce-ideate</code> → <code>brainstorm</code> → <code>plan</code> → <code>work</code> → <code>code-review</code> → <code>debug</code> → <code>optimize</code> → <code>compound</code></td><td>49 / 4 / 38</td><td>アイデア発散段階から伴走させたい</td></tr><tr><td><a href="https://github.com/humanlayer/humanlayer" target="_blank" rel="noopener noreferrer" class="">HumanLayer</a></td><td style="text-align:right">11k</td><td><code>/create_plan</code> → <code>/validate_plan</code> → <code>/implement_plan</code> → <code>/iterate_plan</code> → <code>/local_review</code> → <code>/commit</code></td><td>6 / 27 / 0</td><td>Plan 検証と iteration を厳密に分けたい</td></tr></tbody></table>
<p>A = Agent / C = Command / S = Skill の数。表中の略語は以下のとおりです。<strong>TDD</strong> = Test-Driven Development (テスト駆動開発、テストを先に書いてから実装する手法)。<strong>PRD</strong> = Product Requirement Document (製品要件定義書)。<strong>RPI</strong> = Research-Plan-Implement (調査 → 計画 → 実装の 3 段階パターン)。</p>
<p>12 件の流れを並べて眺めると、構成要素の粒度や名前は違っても <strong>どれもほぼ同じパターンに収束している</strong> ことが分かります: <strong>Research → Plan → Execute → Review → Ship</strong>。これは作者本人が README で強調しているポイントで、ベテランほどこの 5 段に自然と寄っていく、という観察的事実とも一致します。</p>
<p>ワークフローを選ぶ際の見方をいくつか挙げます。<strong>Spec Kit</strong> は 6 個の slash command だけで完結する最小構成で、まず「仕様駆動の流れ」を体験するのに適します。<strong>Everything Claude Code (ECC)</strong> は 48 agents + 143 commands + 230 skills という超大規模で、商用プロダクトでフルセット導入したい人向け。<strong>BMAD-METHOD</strong> は PRD → architecture → sprint planning という「マネジメントの言葉」で構成されているので、PdM や PM と協働するチームに馴染みやすい。<strong>Matt Pocock Skills</strong> は <code>/grill-with-docs</code> で要件を詰めてから PRD 化する流れが特徴的で、Matt 氏自身の YouTube 解説と組み合わせれば理解が早いです (リポ内 <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/videos/claude-matt-pocock-24-apr-26.md" target="_blank" rel="noopener noreferrer" class="">videos/claude-matt-pocock-24-apr-26.md</a>)。</p>
<p>リポ独自の方法論として、<code>development-workflows/</code> ディレクトリには <strong>Cross-Model Workflow</strong> (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/development-workflows/cross-model-workflow/cross-model-workflow.md" target="_blank" rel="noopener noreferrer" class="">cross-model-workflow.md</a>) と <strong>RPI Workflow</strong> (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/development-workflows/rpi/rpi-workflow.md" target="_blank" rel="noopener noreferrer" class="">rpi-workflow.md</a>) が独自に整備されています。RPI は Plan agent と 8 種類の専門 agent を組み合わせた research-plan-implement パターンの実装です。専門 agent は constitutional-validator / requirement-parser / ux-designer / senior-software-engineer 等です。リポ内 <code>.claude/agents/</code> に実体ファイルがあるため、そのまま参照できます。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は表中の 12 ワークフローのどれも <strong>丸ごとは採用していません</strong>。代わりに、Plan-and-Solve (ACL 2023) / Plan-and-Act (ICML 2025) の論文エビデンスを根拠に、<strong>plan-then-execute Workflow Gate</strong> という規範を採用しています。これは <code>~/.claude/rules/plan-then-execute.md</code> に harness 規範化したものです。「3 ファイル以上 / 不慣れ領域 / アーキテクチャ判断 / 不可逆変更を伴うタスクは、実装着手前に必ず Plan フェーズを分離する」というシンプルな gate です。</p><p>SPECIFY → PLAN → TASKS → IMPLEMENT の 4-phase 構造そのものは、表中 12 件には含まれていない <strong>addyosmani/agent-skills</strong> (MIT) の <code>spec-driven-development</code> skill で運用しています。この skill は <code>~/.claude/skills/spec-driven-development/</code> に取り込み済みです。Skill が trigger 発話 (「仕様駆動」「spec から進めて」) で自動起動するため、Spec Kit のように <code>/specify.constitution</code> から手動で打鍵する必要がありません。</p><p>ワークフローを「外部からまるごと導入する」のではなく <strong>「個別 skill 単位で取り込んで自前 rule と組み合わせる」</strong> のが筆者方針で、これは表中のどのワークフローとも違う独自スタイルです。BMAD-METHOD の PRD → architecture の流れや Get Shit Done の <code>discuss → plan → execute</code> 等は、参考にした部分があれば個別の skill description にコメントを残す運用にしています。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>1 つだけ採用するなら <strong>Spec Kit</strong> (シンプル、6 コマンドだけ) か <strong>ECC</strong> (網羅型、商用想定) を推奨。チームに導入するなら BMAD-METHOD、個人で速度を求めるなら Get Shit Done あたりも候補。複数試してみて、しっくり来るものを 1 つだけ残す方が定着します。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-5-章-skill--agent-コレクション--ライブラリを借りる">第 5 章: Skill / Agent コレクション<span class="heading-subtitle">ライブラリを借りる</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-5-%E7%AB%A0-skill--agent-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3--%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E5%80%9F%E3%82%8A%E3%82%8B" class="hash-link" aria-label="第-5-章-skill--agent-コレクション--ライブラリを借りる への直接リンク" title="第-5-章-skill--agent-コレクション--ライブラリを借りる への直接リンク" translate="no">​</a></h2>
<p>ワークフローではなく、個別の SKILL.md / agent.md を「ライブラリ」として配布しているリポも多数あります。README の <code>🧰 SKILL COLLECTIONS</code> と <code>🤖 AGENT COLLECTIONS</code> セクションがこれを整理しています。</p>
<p><strong>Skill コレクション (8 件、star 数順)</strong>:</p>
<table><thead><tr><th>リポ</th><th style="text-align:right">★</th><th style="text-align:right">Skill 数</th></tr></thead><tbody><tr><td><a href="https://github.com/anthropics/skills" target="_blank" rel="noopener noreferrer" class="">anthropics/skills</a></td><td style="text-align:right">138k</td><td style="text-align:right">17</td></tr><tr><td><a href="https://github.com/mattpocock/skills" target="_blank" rel="noopener noreferrer" class="">mattpocock/skills</a></td><td style="text-align:right">97k</td><td style="text-align:right">24</td></tr><tr><td><a href="https://github.com/wshobson/agents" target="_blank" rel="noopener noreferrer" class="">wshobson/agents</a></td><td style="text-align:right">36k</td><td style="text-align:right">155</td></tr><tr><td><a href="https://github.com/pbakaus/impeccable" target="_blank" rel="noopener noreferrer" class="">impeccable</a></td><td style="text-align:right">27k</td><td style="text-align:right">1 (デザイン 7 ドメイン参照)</td></tr><tr><td><a href="https://github.com/addyosmani/agent-skills" target="_blank" rel="noopener noreferrer" class="">addyosmani/agent-skills</a></td><td style="text-align:right">27k</td><td style="text-align:right">21</td></tr><tr><td><a href="https://github.com/K-Dense-AI/scientific-agent-skills" target="_blank" rel="noopener noreferrer" class="">scientific-agent-skills</a></td><td style="text-align:right">25k</td><td style="text-align:right">138</td></tr><tr><td><a href="https://github.com/VoltAgent/awesome-agent-skills" target="_blank" rel="noopener noreferrer" class="">awesome-agent-skills</a></td><td style="text-align:right">22k</td><td style="text-align:right">1,100+ (キュレーション一覧)</td></tr><tr><td><a href="https://github.com/alirezarezvani/claude-skills" target="_blank" rel="noopener noreferrer" class="">claude-skills</a></td><td style="text-align:right">15k</td><td style="text-align:right">246 (9 ドメイン横断)</td></tr></tbody></table>
<p><strong>Agent コレクション (2 件)</strong>:</p>
<table><thead><tr><th>リポ</th><th style="text-align:right">★</th><th style="text-align:right">Agent 数</th></tr></thead><tbody><tr><td><a href="https://github.com/msitarzewski/agency-agents" target="_blank" rel="noopener noreferrer" class="">msitarzewski/agency-agents</a></td><td style="text-align:right">105k</td><td style="text-align:right">144</td></tr><tr><td><a href="https://github.com/VoltAgent/awesome-claude-code-subagents" target="_blank" rel="noopener noreferrer" class="">VoltAgent/awesome-claude-code-subagents</a></td><td style="text-align:right">20k</td><td style="text-align:right">151</td></tr></tbody></table>
<p>選び方のポイントは 2 つあります。1 つは <strong>ドメイン特化か汎用か</strong>。デザイン業務なら <a href="https://github.com/pbakaus/impeccable" target="_blank" rel="noopener noreferrer" class="">impeccable</a>、研究用途なら <a href="https://github.com/K-Dense-AI/scientific-agent-skills" target="_blank" rel="noopener noreferrer" class="">scientific-agent-skills</a> が個別ドメインに最適化された SKILL.md を提供します。汎用なら Anthropic 公式の <a href="https://github.com/anthropics/skills" target="_blank" rel="noopener noreferrer" class="">anthropics/skills</a> と Matt Pocock 氏の <a href="https://github.com/mattpocock/skills" target="_blank" rel="noopener noreferrer" class="">mattpocock/skills</a> を入れておけば日常作業はカバーできます。</p>
<p>もう 1 つは <strong>メンテナンス頻度</strong>。Claude Code 自体が頻繁にアップデートされるので、半年以上更新が止まっているリポは frontmatter フィールドが古い可能性があります。README の表は star 数だけでなく更新時期も把握できるよう作られているので、コレクションを選ぶ際は元リポの commit 履歴も合わせて確認すると安全です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は外部コレクションを <strong>丸ごとは採用せず、自前で 38 skills + 7 agents + 1 team</strong> を運用しています。理由は以下の 2 点です。</p><ul>
<li class=""><strong>trigger 発話のカスタマイズが必要</strong>: 各 skill の自動起動条件 (「レビューして」「バグ」等の日本語発話) を自分のタイピング習慣に合わせて細かく設定したいため、外部 skill を入れると description 全件を再編集することになりがち</li>
<li class=""><strong>Generator-Evaluator 分離の意図的設計</strong>: 第 2.1 章で述べたとおり、agent 群は「作る側 3 / 評価する側 4」で role-based 分離を意図的に組んでいる。外部 agent コレクションは「全部 generalist」「全部 specialist」のどちらかに寄っているケースが多く、設計思想ごと取り込めない</li>
</ul><p>ただし「自前 vs 外部」は二項対立ではなく <strong>個別 skill 単位で取捨選択</strong> しています。例えば前章で触れた <code>spec-driven-development</code> skill は <a href="https://github.com/addyosmani/agent-skills" target="_blank" rel="noopener noreferrer" class="">addyosmani/agent-skills</a> (MIT) から取り込んだもので、自前 rule (<code>~/.claude/rules/plan-then-execute.md</code>) と組み合わせて使っています。コレクション全体を導入するのではなく「気に入った skill だけ 1 つずつ取り込み、自前環境と整合させる」運用が現実的です。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>ドメイン特化が要るなら専用 collection (impeccable / scientific-agent-skills 等) を、そうでなければ <strong>anthropics/skills + mattpocock/skills の 2 つだけ</strong> で十分実用的。awesome-agent-skills は 1,100+ のキュレーション一覧なので「他に何があるか」を探す入口として使います。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-6-章-cross-model-workflows--claude-code-に他モデルを混ぜる">第 6 章: Cross-Model Workflows<span class="heading-subtitle">Claude Code に他モデルを混ぜる</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-6-%E7%AB%A0-cross-model-workflows--claude-code-%E3%81%AB%E4%BB%96%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E6%B7%B7%E3%81%9C%E3%82%8B" class="hash-link" aria-label="第-6-章-cross-model-workflows--claude-code-に他モデルを混ぜる への直接リンク" title="第-6-章-cross-model-workflows--claude-code-に他モデルを混ぜる への直接リンク" translate="no">​</a></h2>
<p><code>README</code> の <code>🔀 CROSS-MODEL WORKFLOWS</code> セクションでは、Claude Code を Codex / Gemini / GPT / Kimi / DeepSeek / local モデル等と組み合わせる方法が <strong>3 つの方式</strong> に分類されています。</p>
<table><thead><tr><th>方式</th><th>概念</th><th>代表リポ</th></tr></thead><tbody><tr><td><strong>Plugin</strong></td><td>他モデルの CLI を Claude Code 内から呼び出す (<code>/codex:review</code> 等)</td><td><a href="https://github.com/openai/codex-plugin-cc" target="_blank" rel="noopener noreferrer" class="">openai/codex-plugin-cc</a> (18k★)</td></tr><tr><td><strong>MCP</strong></td><td>Claude Code が他モデルを「tool」として呼ぶ</td><td><a href="https://github.com/BeehiveInnovations/pal-mcp-server" target="_blank" rel="noopener noreferrer" class="">BeehiveInnovations/pal-mcp-server</a> (12k★、Gemini / OpenAI / Azure / Grok / Ollama / OpenRouter 50+ モデル対応)</td></tr><tr><td><strong>Router</strong></td><td>Claude Code の API endpoint を別 provider に振り替え</td><td><a href="https://github.com/musistudio/claude-code-router" target="_blank" rel="noopener noreferrer" class="">musistudio/claude-code-router</a> (34k★)、<a href="https://github.com/router-for-me/CLIProxyAPI" target="_blank" rel="noopener noreferrer" class="">router-for-me/CLIProxyAPI</a> (32k★)</td></tr></tbody></table>
<p>リポ独自の方法論 <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/development-workflows/cross-model-workflow/cross-model-workflow.md" target="_blank" rel="noopener noreferrer" class="">development-workflows/cross-model-workflow/cross-model-workflow.md</a> は、Plan を Claude Code で、QA-Review を Codex で行う「手動 2-terminal フロー」を推奨しています。これは Plugin / MCP / Router いずれにも依存せず、すぐ試せる構成です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は cross-model を <strong>意図的に採用していません</strong>。Anthropic 内のモデル群 (Opus 4.7 / Sonnet 4.6 / Haiku 4.5) を subagent の <code>model:</code> フィールドと Fast Mode (<code>/fast</code>) で使い分けます。さらに adaptive thinking (effort level low/medium/high/xhigh/max) も併用すれば、必要な推論深さとコストのバランスは取れているという判断です。</p><p>cross-provider router (musistudio/claude-code-router 等) を導入すると、料金体系の二重管理 / Anthropic 公式機能との互換性のリグレッション追跡 / 用途別の品質差検証 / API key 管理の複雑化など、運用負荷が大きく上がります。これに見合うリターン (例: DeepSeek でコスト 1/10 化など) が筆者業務では明確でないため、現時点では Anthropic 単独運用を続けています。</p><p>将来採用するとしたら、第一候補は <strong>router 方式</strong> (<code>claude-code-router</code> 経由で OpenRouter にバックアップを通す) です。Plugin 方式 (<code>openai/codex-plugin-cc</code> で Codex を <code>/codex:review</code> として呼ぶ) は cross-model QA レビューが必要な場面が増えれば検討、という温度感です。</p><p>なお Anthropic 内ルーティングについては、<code>effortLevel: xhigh</code> を default にしています。<code>architect</code> / <code>debugger</code> / <code>propose</code> 等 28 件の subagent / skill では frontmatter で <code>effort: max</code> を上書き指定する、という細かい使い分けを harness で組んでいます。詳細は第 7 章の Workflows Advanced カテゴリで触れます。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>主要モデルを Claude Code 内に統合したいなら <strong>Plugin</strong> (<code>openai/codex-plugin-cc</code>) です。コスト最適化したいなら <strong>Router</strong> (<code>musistudio/claude-code-router</code> で OpenRouter / DeepSeek / Ollama に振り分け)。Claude を主役のまま他モデルに QA レビューさせたいなら <strong>MCP</strong> (<code>pal-mcp-server</code>) という棲み分けで考えると選びやすいです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-7-章-83-個の-tips-を-15-カテゴリで概観する">第 7 章: 83 個の Tips を 15 カテゴリで概観する<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-7-%E7%AB%A0-83-%E5%80%8B%E3%81%AE-tips-%E3%82%92-15-%E3%82%AB%E3%83%86%E3%82%B4%E3%83%AA%E3%81%A7%E6%A6%82%E8%A6%B3%E3%81%99%E3%82%8B" class="hash-link" aria-label="第 7 章: 83 個の Tips を 15 カテゴリで概観する への直接リンク" title="第 7 章: 83 個の Tips を 15 カテゴリで概観する への直接リンク" translate="no">​</a></h2>
<p><code>README</code> の <code>💡 TIPS AND TRICKS (83)</code> セクションは、開発陣やコミュニティから集められた Tips を <strong>15 カテゴリ</strong> に分けて表化したものです。出典は Boris Cherny (Claude Code 開発者) / Thariq (Anthropic) / Cat Wu / Dex Horthy / Lydia Hallie / コミュニティ等です。各 Tip には元ツイートや元動画への直リンクが付いていて、文脈ごと辿れるようになっています。</p>
<p>カテゴリと件数、そして象徴的な Tip を 1-2 個ずつ取り上げます。</p>
<table><thead><tr><th>カテゴリ</th><th style="text-align:right">件数</th><th>象徴的な Tip</th></tr></thead><tbody><tr><td><strong>Prompting</strong></td><td style="text-align:right">3</td><td>"grill me on these changes — PR を作る前に Claude にテストさせろ" (Boris)</td></tr><tr><td><strong>Planning/Specs</strong></td><td style="text-align:right">7</td><td>"always start with plan mode" (Boris), "AskUserQuestion で Claude にインタビューさせて仕様を引き出す" (Thariq)</td></tr><tr><td><strong>Context</strong></td><td style="text-align:right">5</td><td>"context rot は ~300-400k tokens 周辺で発生" / "dumb zone は ~40% context — 新人は 40%、ベテランは 30% 以下を死守" (Thariq / Dex)</td></tr><tr><td><strong>Session Management</strong></td><td style="text-align:right">6</td><td>"new task = new session" / "/compact vs /clear はトレードオフ" (Thariq)</td></tr><tr><td><strong>CLAUDE.md + rules</strong></td><td style="text-align:right">8</td><td>"CLAUDE.md は 200 行以下、humanlayer は 60 行" / "paths: frontmatter で lazy-load"</td></tr><tr><td><strong>Agents</strong></td><td style="text-align:right">4</td><td>"feature 特化型 subagent を作れ、general qa とか general backend とかは作るな" (Boris)</td></tr><tr><td><strong>Commands</strong></td><td style="text-align:right">3</td><td>"1 日 1 回以上やる作業は skill か command にせよ" (Boris)</td></tr><tr><td><strong>Skills</strong></td><td style="text-align:right">9</td><td>"skills are folders, not files — references/, scripts/, examples/ で progressive disclosure" / "Gotchas section は最重要、Claude の失敗ポイントを蓄積せよ" (Thariq)</td></tr><tr><td><strong>Hooks</strong></td><td style="text-align:right">5</td><td>"PostToolUse hook で auto-format — Claude が生成するコードの最後 10% を仕上げて CI failure を防ぐ" (Boris)</td></tr><tr><td><strong>Workflows</strong></td><td style="text-align:right">5</td><td>"thinking mode true + Output Style Explanatory で意思決定が見える" (Boris)</td></tr><tr><td><strong>Workflows Advanced</strong></td><td style="text-align:right">9</td><td>"/sandbox で permission prompt -84%" / "Opus 4.7 の effort level を low / medium / high / xhigh / max で動的調整" (Boris)</td></tr><tr><td><strong>Git / PR</strong></td><td style="text-align:right">5</td><td>"PR の p50 は 118 行 — 141 PR / 45K 行/日 のうち中央値" / "always squash merge" (Boris)</td></tr><tr><td><strong>Debugging</strong></td><td style="text-align:right">6</td><td>"screenshot を Claude に渡せ" / "agentic search (glob + grep) beats RAG — Claude Code は vector DB を試して破棄した" (Boris)</td></tr><tr><td><strong>Utilities</strong></td><td style="text-align:right">5</td><td>"iTerm / Ghostty / tmux &gt; IDE" (Boris)</td></tr><tr><td><strong>Daily</strong></td><td style="text-align:right">2</td><td>"Claude Code を毎日アップデート" / "朝、CHANGELOG を読む"</td></tr></tbody></table>
<p>各カテゴリの右側には Boris / Thariq の元投稿リンクが付いていて、原文の文脈を確認できます。特に <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tips/claude-boris-6-tips-16-apr-26.md" target="_blank" rel="noopener noreferrer" class="">tips/claude-boris-6-tips-16-apr-26.md</a> (Opus 4.7 関連の最新 6 Tips) には、2026 年時点の Claude Code 運用に直結する内容がまとまっています。<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tips/claude-thariq-tips-16-apr-26.md" target="_blank" rel="noopener noreferrer" class="">tips/claude-thariq-tips-16-apr-26.md</a> (Session Management と 1M context の使いこなし) も同様です。</p>
<p>Tips の中には <code>🚫👶</code> (do-not-babysit) というマークが付いているものがあります。これは「Claude に細かく口出しせず任せろ」系の Tip を区別するためのアイコンです。<code>grill me</code>、<code>fix</code> だけ言う、<code>use subagents to throw more compute at it</code>、Code Review の自動化、<code>@claude</code> でレビュー lint rule を自動生成、等が代表例です。「Claude を信頼して手放す」段階に進む人向けの位置づけです。</p>
<p>CLI binary から抽出された hidden Tips は <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-spinner-verbs-and-tips.md" target="_blank" rel="noopener noreferrer" class="">reports/claude-spinner-verbs-and-tips.md</a> にまとめられています。こちらでは、公式 README には載っていないものの CLI 内部で使われているスピナー verb (<code>Pondering</code>, <code>Cogitating</code>, <code>Synthesizing</code> 等) と関連 Tips も拾えます。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>主要 5 カテゴリの Tips に対する筆者の実装を対比表にまとめます。</p><table><thead><tr><th>カテゴリ</th><th>shanraisshan の象徴的 Tip</th><th>筆者の実装 (<code>~/.claude/</code>)</th></tr></thead><tbody><tr><td>CLAUDE.md + rules</td><td>"CLAUDE.md は 200 行以下"</td><td><strong>436 行</strong> (推奨の 2 倍超だが意図的)。詳細は path-scoped rule 20 件 + <code>references/</code> 30 件に分散。常時 load は 10 件 / 722 行に抑制</td></tr><tr><td>Skills</td><td>"Gotchas section は最重要"</td><td><code>doubt-driven-development</code> / <code>mcp-builder</code> / <code>grill-me</code> 等の SKILL.md に "When to Use" / "Loading Constraints" セクションを実装</td></tr><tr><td>Hooks</td><td>"PostToolUse hook で auto-format"</td><td>format ではなく <strong>suppression 検出</strong> (<code>check-suppression.sh</code>) + 平文質問検出 (<code>check-plain-question.sh</code>) に転用</td></tr><tr><td>Agents</td><td>"feature 特化型 subagent を作れ"</td><td>architect / code-reviewer / debugger 等 7 件すべて特化命名、generalist 系ゼロ</td></tr><tr><td>Workflows Advanced</td><td>"effort level を低 / 中 / 高 / xhigh / max で動的調整"</td><td><code>~/.claude/settings.json</code> で <code>effortLevel: xhigh</code> を default 化、frontmatter で skill 別に <code>effort: max</code> (architect / debugger / propose 等 28 ファイル) で上書き</td></tr></tbody></table><p>ここで補足を 4 つ追加します。</p><p><strong>Gotchas / Loading Constraints セクション</strong> が Tips で「最重要」と言われている理由は、SKILL.md の本文で「この skill が失敗しやすい条件 / 使ってはいけない場面」を明示すると、Claude が起動判断を間違えなくなるからです。筆者の <code>~/.claude/skills/grill-me/SKILL.md</code> から実例を抜粋すると、以下のような構成になっています。</p><div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">name</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> grill</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml">me</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">description</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> 要件・設計を厳しい質問で詰めて曖昧性を排除する skill</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">...</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> When to Use</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> タスク着手前の要件解像度向上</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> 仕様が曖昧な状態で実装すると手戻りが大きい場面</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Gotchas (使ってはいけない場面)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> 既に明示指示済みの作業継続中 → 過剰な質問になる</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> 純粋な事実確認 (Read / Grep で解決可) → AskUserQuestion を 1 回だけで十分</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Loading Constraints</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> context が 50k tokens 超のときは skip 推奨 (詰問が context を圧迫する)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> 他 skill が in_progress の場合は割り込まない</span><br></div></code></pre></div></div><p>このように <code>## Gotchas</code> と <code>## Loading Constraints</code> をセクション化しておくと、Claude が「今この skill を起動すべきか」を構造的に判断できます。逆にこれが無いと、不適切な場面で skill が暴発しやすくなります。</p><p><strong>Path-scoped rule (lazy-load)</strong> とは、rule ファイルの frontmatter に <code>paths: ["**/*.ts"]</code> のような glob を書いておく方式です。その glob にマッチするファイルを Claude が Read した時だけ、rule が自動 load されます。frontmatter は以下のような形になります。</p><div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">paths</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">[</span><span class="token front-matter-block front-matter yaml language-yaml string" style="color:#e3116c">"**/*.ts"</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml string" style="color:#e3116c">"**/*.tsx"</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">]</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">#</span><span class="token title important"> TypeScript Strict Mode 規範</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token code-snippet code keyword" style="color:#00009f">`any`</span><span class="token plain"> 型は禁止</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> ...</span><br></div></code></pre></div></div><p>常時 load にすると context window を圧迫しますが、path-scoped にすればフロントエンド作業中はフロントエンド rule だけ、infra 作業中は terraform rule だけ、と必要なものだけが context に注入されます。筆者は 31 件中 20 件 (64%) を path-scoped 化して 200 行制約を実質的に回避しています。</p><p><strong>Effort level</strong> は Opus 4.7 で推論の深さを動的に切り替えるパラメータ (low / medium / high / xhigh / max)。Anthropic 公式では coding/agentic タスクで <code>xhigh</code> が default 推奨です。<code>max</code> は overthinking (考えすぎ) + diminishing returns (= 投入量を増やしても得られる効果が頭打ちになる現象) リスクのため、日常 default にしないとされています。筆者は重要判断 (architect / debugger / propose 等) のみ frontmatter で <code>max</code> に上書きし、コスト効率を保ちつつ深い推論が必要な場面で精度を出しています。</p><p><strong>Ratchet</strong> という用語が Tips に頻出します。これは「同じ失敗を 2 度起こしたら必ず rule / hook / learning のいずれかに永続化して、再発を構造的に不可能にする」原則のことで、筆者 harness では <code>~/.claude/rules/ratchet-workflow.md</code> として規範化しています。Claude が同じミスを繰り返したら、その都度 prompt で叱るのではなく、deterministic な sensor (hook) や inferential guide (rule) に組み込んで「次は仕組みで防ぐ」という発想です。</p><p>なお <code>~/.claude/learnings/</code> には、core / frontend-vue / frontend-react / infra のカテゴリ別に 14 ファイル / 4,363 行の知見が蓄積されています。<code>/learn</code> skill の trigger 発話 (「追記して」「覚えといて」) で、1 件単位で即座に永続化する運用です。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>全 83 件を一度に読まず、<strong>直近で困っているカテゴリ</strong> だけ拾います。Context や Session Management で苦戦している人は Thariq の 16/Apr/26 まとめだけで方針が立ちます。Opus 4.7 や Auto Mode を試したい人は Boris の 16/Apr/26 まとめを優先。<code>🚫👶</code> マークの Tips は中級者以上向けと捉えてください。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-8-章-12-本の-reports--深掘り資料の地図">第 8 章: 12 本の Reports<span class="heading-subtitle">深掘り資料の地図</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-8-%E7%AB%A0-12-%E6%9C%AC%E3%81%AE-reports--%E6%B7%B1%E6%8E%98%E3%82%8A%E8%B3%87%E6%96%99%E3%81%AE%E5%9C%B0%E5%9B%B3" class="hash-link" aria-label="第-8-章-12-本の-reports--深掘り資料の地図 への直接リンク" title="第-8-章-12-本の-reports--深掘り資料の地図 への直接リンク" translate="no">​</a></h2>
<p><code>reports/</code> ディレクトリには 12 本の深掘り解析が格納されています。Tips が「箇条書きで覚える知識」だとすれば、Reports は「文章で理屈を理解する読み物」です。</p>
<table><thead><tr><th>Report</th><th>用途</th><th>こんな読者に</th></tr></thead><tbody><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/why-harness-is-important.md" target="_blank" rel="noopener noreferrer" class="">why-harness-is-important</a></td><td>「features は prompt と同じ」論への反論 (10 capability 表)</td><td>harness の価値を疑っている人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-agent-memory.md" target="_blank" rel="noopener noreferrer" class="">claude-agent-memory</a></td><td>memory の 3 scope (user/project/local) を詳細解説</td><td>セッション跨ぎ知識を永続化したい人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-global-vs-project-settings.md" target="_blank" rel="noopener noreferrer" class="">claude-global-vs-project-settings</a></td><td>設定階層 6 段 + Tasks system</td><td>個人 / チーム / 組織の設定境界を整理したい人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-agent-command-skill.md" target="_blank" rel="noopener noreferrer" class="">claude-agent-command-skill</a></td><td>3 機能 (agent / command / skill) の境界線</td><td>「どれを使うべきか」迷う人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-skills-for-larger-mono-repos.md" target="_blank" rel="noopener noreferrer" class="">claude-skills-for-larger-mono-repos</a></td><td>monorepo での skill 配置戦略</td><td>複数 package を扱う人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-in-chrome-v-chrome-devtools-mcp.md" target="_blank" rel="noopener noreferrer" class="">claude-in-chrome-v-chrome-devtools-mcp</a></td><td>ブラウザ MCP (Claude in Chrome vs Chrome DevTools) の使い分け</td><td>frontend / E2E で Claude にブラウザ操作させたい人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/llm-day-to-day-degradation.md" target="_blank" rel="noopener noreferrer" class="">llm-day-to-day-degradation</a></td><td>モデル「劣化」現象の追跡と検証</td><td>「最近 Claude が頭悪い気がする」と感じる人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-usage-and-rate-limits.md" target="_blank" rel="noopener noreferrer" class="">claude-usage-and-rate-limits</a></td><td>rate limit の構造とプラン別上限</td><td>業務利用でコスト管理する人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-advanced-tool-use.md" target="_blank" rel="noopener noreferrer" class="">claude-advanced-tool-use</a></td><td>tool use の高度パターン</td><td>カスタム tool を実装する人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-agent-sdk-vs-cli-system-prompts.md" target="_blank" rel="noopener noreferrer" class="">claude-agent-sdk-vs-cli-system-prompts</a></td><td>Agent SDK vs CLI の system prompt 差分 (110+ fragments)</td><td>SDK で独自 agent を組む人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/claude-spinner-verbs-and-tips.md" target="_blank" rel="noopener noreferrer" class="">claude-spinner-verbs-and-tips</a></td><td>CLI binary から抽出した hidden tips</td><td>Claude Code を deep dive したい人</td></tr><tr><td><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/learning-journey-weather-reporter-redesign.md" target="_blank" rel="noopener noreferrer" class="">learning-journey-weather-reporter-redesign</a></td><td>weather example の設計再考過程</td><td>orchestration pattern を学ぶ人</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-本だけ深掘り-why-harness-is-importantmd">1 本だけ深掘り: why-harness-is-important.md<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#1-%E6%9C%AC%E3%81%A0%E3%81%91%E6%B7%B1%E6%8E%98%E3%82%8A-why-harness-is-importantmd" class="hash-link" aria-label="1 本だけ深掘り: why-harness-is-important.md への直接リンク" title="1 本だけ深掘り: why-harness-is-important.md への直接リンク" translate="no">​</a></h3>
<p>12 本の中で最初に読むべき 1 本を選ぶなら <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/why-harness-is-important.md" target="_blank" rel="noopener noreferrer" class="">why-harness-is-important.md</a> です。これは「skills / commands / subagents / hooks も結局モデルに渡る prompt なんだから、強い prompt を 1 本書けば同じ結果になる」という reduction (還元論) への反論を、表形式で整理した論考です。</p>
<p>論旨の中核を要約すると次のようになります。</p>
<ul>
<li class="">単発タスク (例:「再帰版 flatten 関数を書け」) では確かに <code>Output quality ≈ Prompt quality</code> が成立する。これがチャットボットの世界。</li>
<li class="">だが本物のソフトウェア開発では <code>Output quality = f(effective context, model capability, iteration loop)</code> という関係になる。<strong>effective context</strong> (モデルが推論時に見るもの) はユーザーが打った prompt よりはるかに大きい。</li>
<li class="">harness の役割は、ユーザーが打った 6 トークンの prompt を、推論時には 5,000〜50,000 トークンの effective context に変換することにある。</li>
<li class="">harness にしかできない capability が 10 個ある。例えば context isolation (subagent が別 window で動く) や、harness-enforced tool restrictions (<code>disallowedTools</code> 等の deny 設定でモデルが tool を使う前にブロック)。lazy-loaded rules (<code>paths:</code> frontmatter)、hooks の deterministic な execution、<code>model:</code> による model routing、subagent の parallelism も挙がる。さらに memory による cross-session persistence、modular system prompt (CLI 内部で 110+ fragments を条件付き load) も該当する。最後が skill preloading と auto モードの permission classifier だ。これらは strong wording で代替できない。</li>
</ul>
<p>ここで重要概念の <strong>Effective context</strong> について少し補足しておきます。これは「ユーザーが打鍵した prompt そのもの」ではなく、「harness が裏で注入する CLAUDE.md / rules / skills / agents 定義 / memory / MCP 出力をすべて合計した、推論時に実際にモデルが見る情報の総体」のことです。6 トークンの prompt が 5,000〜50,000 トークンの effective context に膨らむのが harness の役割です。これが「rules を強い文言で書けば済む」という reduction (還元論) では代替できない理由になっています。</p>
<p>このレポートを先に読んでおくと、第 2-4 章で紹介した機能群がなぜ存在するのかを理屈として捉えやすくなります。残り 11 本は自分の興味に応じて選ぶ形で問題ありません。memory に困っているなら <code>claude-agent-memory</code>、settings 階層を整理したいなら <code>claude-global-vs-project-settings</code> を選びます。ブラウザ自動化を考えているなら <code>claude-in-chrome-v-chrome-devtools-mcp</code>、という具合です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の実装例 (<code>~/.claude/</code>)</div><div class="admonitionContent_BuS1"><p>筆者は <code>~/.claude/</code> を <strong>Phase 0-13</strong> の段階的進化として育ててきました。Phase は筆者固有の用語で、harness を一気に完成形にせず「節目ごとに rules / hooks / skills / agents を追加して必要性を検証する」運用の単位です。代表的な節目:</p><ul>
<li class=""><strong>Phase 0</strong>: harness engineering 概念導入 (Guides × Sensors 二分法)</li>
<li class=""><strong>Phase 1</strong>: Computational Guards 実装 (<code>check-suppression.sh</code> / <code>check-protected-files.sh</code> 等の hook)</li>
<li class=""><strong>Phase 6</strong>: 4 指標 (Task Resolution Rate / Code Churn Rate / Verification Tax / Defect Escape Rate) による health check 計測</li>
<li class=""><strong>Phase 11</strong>: rules の path-scoped 化と token reduction (常時 load を 10 件 / 722 行に圧縮)</li>
<li class=""><strong>Phase 13</strong>: 持ち越し吸収 + Notification Sensor 整備 (最新の transitional)</li>
</ul><p>各 Phase の意思決定は <strong>ADR (Architecture Decision Record)</strong> として <code>~/.claude/docs/adr/</code> に 16 件記録しています。ADR は「重要な設計判断を 1 件 1 ファイルで markdown 記録する慣習」で、「なぜそうしたか」を後から辿れます。代表的な ADR:</p><ul>
<li class=""><strong>ADR-0001</strong>: harness self-review を月次 / 四半期 cron 化</li>
<li class=""><strong>ADR-0004</strong>: Phase 進化ロードマップ (Foundation 0-6 / Consolidation 7-9 / Next-Gen 10+)</li>
<li class=""><strong>ADR-0011</strong>: Opus 4.7 の <code>effortLevel</code> を env 撤去 + settings.json field に移行</li>
<li class=""><strong>ADR-0013</strong>: Plan-then-Execute を Workflow Gate として規範化</li>
</ul><p><code>why-harness-is-important.md</code> で紹介されている harness の 10 capability は、筆者環境では Phase 0-13 の追加ロードマップとほぼ対応しています。10 capability の内訳は context isolation / harness-enforced tool restrictions / lazy-loaded rules / hooks の deterministic execution / model routing です。残りが subagent parallelism / memory cross-session persistence / modular system prompt / skill preloading / Auto Mode classifier です。harness は「完成」ではなく「継続的に Ratchet を積み上げる」性質のもの、というのが Phase 運用から得た実感です。</p><p>なお Phase の詳細な背景理論や規範は別記事 <a class="" href="https://notes.rewheel.dev/blog/harness-engineering">harness-engineering</a> でより深く扱っているので、harness の枠組み自体に興味がある方はそちらを参照してください。</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>まず <code>why-harness-is-important.md</code> 1 本だけ読んで「features の価値」を腑に落とす。残り 11 本は「いま自分が困っているテーマ」のものだけ 1-2 本選んで読みます。全部読破しようとすると 30 分以上かかるので、必要な時に必要な分だけ。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-9-章-取捨選択ロードマップ--自分のレベルで何を選ぶか">第 9 章: 取捨選択ロードマップ<span class="heading-subtitle">自分のレベルで何を選ぶか</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-9-%E7%AB%A0-%E5%8F%96%E6%8D%A8%E9%81%B8%E6%8A%9E%E3%83%AD%E3%83%BC%E3%83%89%E3%83%9E%E3%83%83%E3%83%97--%E8%87%AA%E5%88%86%E3%81%AE%E3%83%AC%E3%83%99%E3%83%AB%E3%81%A7%E4%BD%95%E3%82%92%E9%81%B8%E3%81%B6%E3%81%8B" class="hash-link" aria-label="第-9-章-取捨選択ロードマップ--自分のレベルで何を選ぶか への直接リンク" title="第-9-章-取捨選択ロードマップ--自分のレベルで何を選ぶか への直接リンク" translate="no">​</a></h2>
<p>ここまでで紹介してきた全要素から、<strong>自分のレベルに合うものだけ選ぶ</strong> ためのロードマップを 4 種類提示します。複数を中途半端に走らせるより、1 つを完走してから次に進む方が定着するため、まず自分の現在地に近いものを 1 つだけ選んでください。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="91-初心者ロードマップ--まず-claude-code-を動かせるようになる">9.1 初心者ロードマップ<span class="heading-subtitle">まず Claude Code を動かせるようになる</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#91-%E5%88%9D%E5%BF%83%E8%80%85%E3%83%AD%E3%83%BC%E3%83%89%E3%83%9E%E3%83%83%E3%83%97--%E3%81%BE%E3%81%9A-claude-code-%E3%82%92%E5%8B%95%E3%81%8B%E3%81%9B%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%AA%E3%82%8B" class="hash-link" aria-label="91-初心者ロードマップ--まず-claude-code-を動かせるようになる への直接リンク" title="91-初心者ロードマップ--まず-claude-code-を動かせるようになる への直接リンク" translate="no">​</a></h3>
<p>Claude Code をインストールしたばかり、または使い始めて 1 週間以内の段階を想定。</p>
<ol>
<li class=""><strong>セットアップ</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tutorial/day0/README.md" target="_blank" rel="noopener noreferrer" class="">tutorial/day0/README.md</a> を読み、OS 別の手順 (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tutorial/day0/linux.md" target="_blank" rel="noopener noreferrer" class="">linux</a> / <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tutorial/day0/mac.md" target="_blank" rel="noopener noreferrer" class="">mac</a> / <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tutorial/day0/windows.md" target="_blank" rel="noopener noreferrer" class="">windows</a>) でインストール</li>
<li class=""><strong>初プロジェクト</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tutorial/day1/README.md" target="_blank" rel="noopener noreferrer" class="">tutorial/day1/README.md</a> を辿って Claude Code を使った最初のコード生成を体験</li>
<li class=""><strong>Tips 入門</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/tips/claude-boris-13-tips-03-jan-26.md" target="_blank" rel="noopener noreferrer" class="">Boris 13 Tips (03/Jan/26)</a> を一読</li>
<li class=""><strong>実践</strong>: Tips の <code>Prompting</code> / <code>Planning/Specs</code> カテゴリだけ実践 (plan mode から始める / AskUserQuestion で仕様を引き出す / spec を書く)</li>
</ol>
<p>この段階では subagent や hooks に手を出さず、まずは Claude Code 単体での会話と plan mode に慣れることに集中します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="92-中級者ロードマップ--基本機能を組み合わせる">9.2 中級者ロードマップ<span class="heading-subtitle">基本機能を組み合わせる</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#92-%E4%B8%AD%E7%B4%9A%E8%80%85%E3%83%AD%E3%83%BC%E3%83%89%E3%83%9E%E3%83%83%E3%83%97--%E5%9F%BA%E6%9C%AC%E6%A9%9F%E8%83%BD%E3%82%92%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%82%8B" class="hash-link" aria-label="92-中級者ロードマップ--基本機能を組み合わせる への直接リンク" title="92-中級者ロードマップ--基本機能を組み合わせる への直接リンク" translate="no">​</a></h3>
<p>Claude Code は使えているが、自分で <code>.claude/</code> を育てるのはこれから、という段階を想定。</p>
<ol>
<li class=""><strong>概念理解</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-subagents.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-subagents.md</a> + <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-commands.md" target="_blank" rel="noopener noreferrer" class="">claude-commands.md</a> + <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-skills.md" target="_blank" rel="noopener noreferrer" class="">claude-skills.md</a> を順に通読</li>
<li class=""><strong>showcase 実行</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/orchestration-workflow/orchestration-workflow.md" target="_blank" rel="noopener noreferrer" class="">orchestration-workflow.md</a> を clone して <code>/weather-orchestrator</code> を動かす</li>
<li class=""><strong>Tips の応用</strong>: <code>CLAUDE.md</code>, <code>Skills</code>, <code>Hooks</code> カテゴリを実践 (CLAUDE.md を 200 行以下に整理、自分の skill を 1 個作る、PostToolUse hook で auto-format を仕込む)</li>
<li class=""><strong>自作</strong>: 自分の業務で 1 日 1 回以上やる作業を題材に、slash command を 1-2 個作る</li>
</ol>
<p>この段階で自分の <code>.claude/commands/</code> が 1 個でも増えれば、Claude Code との関係が変わってきます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="93-上級者ロードマップ--harness-を設計する">9.3 上級者ロードマップ<span class="heading-subtitle">harness を設計する</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#93-%E4%B8%8A%E7%B4%9A%E8%80%85%E3%83%AD%E3%83%BC%E3%83%89%E3%83%9E%E3%83%83%E3%83%97--harness-%E3%82%92%E8%A8%AD%E8%A8%88%E3%81%99%E3%82%8B" class="hash-link" aria-label="93-上級者ロードマップ--harness-を設計する への直接リンク" title="93-上級者ロードマップ--harness-を設計する への直接リンク" translate="no">​</a></h3>
<p>すでに自分の <code>.claude/</code> を運用していて、次の改善方向を探している段階を想定。</p>
<ol>
<li class=""><strong>理屈の整理</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/reports/why-harness-is-important.md" target="_blank" rel="noopener noreferrer" class="">why-harness-is-important.md</a> で harness の 10 capability を再確認</li>
<li class=""><strong>大規模ワークフローの精読</strong>: <a href="https://github.com/obra/superpowers" target="_blank" rel="noopener noreferrer" class="">Superpowers</a> (14 skills) と <a href="https://github.com/affaan-m/everything-claude-code" target="_blank" rel="noopener noreferrer" class="">ECC</a> (48 agents + 143 commands + 230 skills) のソースを読む</li>
<li class=""><strong>cross-model 導入</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/development-workflows/cross-model-workflow/cross-model-workflow.md" target="_blank" rel="noopener noreferrer" class="">cross-model-workflow.md</a> を参考に Codex で QA レビューを 2-terminal で試す</li>
<li class=""><strong>並列化</strong>: agent teams / git worktrees / scheduled tasks の実装例を 1 つ試す</li>
<li class=""><strong>継続観察</strong>: Boris / Thariq の最新 tips を継続購読 (README の <code>🔔 SUBSCRIBE</code> セクションに集約)</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>筆者の現在地</div><div class="admonitionContent_BuS1"><p>参考までに、筆者の <code>~/.claude/</code> は 2026 年 5 月時点で <strong>Phase 13</strong> (持ち越し吸収 + Notification Sensor 整備) を完了しています。現在は <strong>Phase 14</strong> (XML 全展開 + Six Sigma 統計手法 + Tree-of-Thoughts 推論パターン導入) に着手中です。第 8 章で書いたとおり Phase は筆者固有の節目単位で、ADR-0004 に進化ロードマップが残してあります。</p><p>実感としては、harness は <strong>「完成して安定運用に入る」性質のものではなく、「四半期ごとに新しい Ratchet を積み上げる」性質のもの</strong> です。Claude Code 本体が頻繁にアップデートされる以上、当面はこの「継続的に harness を更新する」モードが続く見込みです。</p><p>並列化系 (agent teams / worktree / scheduled tasks) は導入済みですが、cross-model はまだ導入していません (第 6 章で書いた理由)。本格採用は claude-code-router 経由の router 方式から、というのが現時点の見立てです。</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="94-3-つだけ採用するなら--時間がない人向け">9.4 「3 つだけ」採用するなら<span class="heading-subtitle">時間がない人向け</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#94-3-%E3%81%A4%E3%81%A0%E3%81%91%E6%8E%A1%E7%94%A8%E3%81%99%E3%82%8B%E3%81%AA%E3%82%89--%E6%99%82%E9%96%93%E3%81%8C%E3%81%AA%E3%81%84%E4%BA%BA%E5%90%91%E3%81%91" class="hash-link" aria-label="94-3-つだけ採用するなら--時間がない人向け への直接リンク" title="94-3-つだけ採用するなら--時間がない人向け への直接リンク" translate="no">​</a></h3>
<p>このリポを読む時間がなく、最も効くものを 3 つだけ採用したい場合の選び方。</p>
<ol>
<li class=""><strong>Plan Mode を必ず使う</strong> (Tips - Planning/Specs カテゴリ): 全ての複雑タスクで plan mode から入る。実装に飛びつかない。</li>
<li class=""><strong>CLAUDE.md を 200 行以下に保つ</strong> (Tips - CLAUDE.md カテゴリ): プロジェクト指示書が肥大化すると Claude が無視し始める。簡潔に。</li>
<li class=""><strong>feature 特化型 subagent を作る</strong> (<a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/best-practice/claude-subagents.md" target="_blank" rel="noopener noreferrer" class="">best-practice/claude-subagents.md</a>): <code>general qa</code> や <code>backend engineer</code> のような汎用名は避ける。<code>payment-flow-verifier</code> のような具体名の subagent を skill 付きで作る。</li>
</ol>
<p>この 3 つだけでも Claude Code の出力の安定感は変わってきます。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>自分のレベルに最も近いロードマップを 1 つだけ選んで、それを完走してから次のレベルへ。並走させると認知負荷が上がって全部中途半端になるので、順序を守ることをおすすめします。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-10-章-注意点と-open-questions">第 10 章: 注意点と Open Questions<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-10-%E7%AB%A0-%E6%B3%A8%E6%84%8F%E7%82%B9%E3%81%A8-open-questions" class="hash-link" aria-label="第 10 章: 注意点と Open Questions への直接リンク" title="第 10 章: 注意点と Open Questions への直接リンク" translate="no">​</a></h2>
<p>最後に、このリポを参考にする際の注意点と、作者が読者に投げかけている open question を確認します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="101-鵜呑みにしない-5-つの注意点">10.1 鵜呑みにしない 5 つの注意点<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#101-%E9%B5%9C%E5%91%91%E3%81%BF%E3%81%AB%E3%81%97%E3%81%AA%E3%81%84-5-%E3%81%A4%E3%81%AE%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="10.1 鵜呑みにしない 5 つの注意点 への直接リンク" title="10.1 鵜呑みにしない 5 つの注意点 への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><strong>更新頻度の早さ</strong>: リポは Claude Code v2.1.145 (2026-05-19 リリース) ベース。Anthropic 側の機能名や仕様は頻繁に変わるため、重要な判断をする時は <a href="https://code.claude.com/docs/" target="_blank" rel="noopener noreferrer" class="">公式 docs</a> で必ず再確認してください。</li>
<li class=""><strong>英語前提</strong>: 元 Tip の出典 (X tweet / YouTube / Reddit) は全て英語。リポ内の解説も英語のため、翻訳しながら読むか機械翻訳を併用する場面が出てきます。</li>
<li class=""><strong>コピペで動くわけではない</strong>: 多くは「実装ヒント」であり、<code>.claude/agents/&lt;name&gt;.md</code> の中身までは書かれていない場合があります。自分のプロジェクトに合わせた調整が前提です。</li>
<li class=""><strong>業界トップの運用 ≠ 個人プロジェクトの最適解</strong>: Anthropic 社内や Boris 個人の workflow を、そのままチーム導入しても合わないことが多いです。「なぜそうしているのか」の理屈だけ抜き出して自分の状況に翻訳する姿勢が安全です。</li>
<li class=""><strong>コストへの注意</strong>: Cross-model workflow、Agent Teams の並列実行、Routines (cloud cron)、Computer Use 等の機能は API クレジット消費が大きく増えます。Anthropic Console で spending limit を必ず設定しておきましょう。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="102-作者からの-open-questions--billion-dollar-questions">10.2 作者からの Open Questions<span class="heading-subtitle">Billion-Dollar Questions</span><a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#102-%E4%BD%9C%E8%80%85%E3%81%8B%E3%82%89%E3%81%AE-open-questions--billion-dollar-questions" class="hash-link" aria-label="102-作者からの-open-questions--billion-dollar-questions への直接リンク" title="102-作者からの-open-questions--billion-dollar-questions への直接リンク" translate="no">​</a></h3>
<p><code>README</code> 末尾には <a href="https://github.com/shanraisshan/claude-code-best-practice#billion-dollar-questions" target="_blank" rel="noopener noreferrer" class="">Billion-Dollar Questions</a> というセクションがあり、作者自身が「自分にも答えがないが、解いた人がいたら教えてほしい」と公開している 13 個の未解決問題が並んでいます。代表的な 3 カテゴリを紹介します。</p>
<p><strong>Memory &amp; Instructions に関する問い</strong> (4 問):</p>
<ul>
<li class="">CLAUDE.md に何を入れて何を入れないか</li>
<li class="">既に CLAUDE.md がある状態で、別に <code>constitution.md</code> や <code>rules.md</code> は必要か</li>
<li class="">CLAUDE.md はどれくらいの頻度で更新すべきで、古くなったとどう判別するか</li>
<li class="">なぜ Claude は MUST と全大文字で書いても CLAUDE.md の指示を無視するのか (<a href="https://reddit.com/r/ClaudeCode/comments/1qn9pb9/claudemd_says_must_use_agent_claude_ignores_it_80/" target="_blank" rel="noopener noreferrer" class="">reddit 投稿: 80% 無視される事例</a>)</li>
</ul>
<p><strong>Agents, Skills &amp; Workflows に関する問い</strong> (6 問):</p>
<ul>
<li class="">command vs agent vs skill はいつ使い分けるか — そして「vanilla な Claude Code」だけで十分なケースはどれか</li>
<li class="">agents / commands / workflows はモデル進化のたびに更新が必要か</li>
<li class="">generalist subagent vs feature/role 特化型 subagent はどちらが良いか</li>
<li class="">Claude Code 標準の plan mode を使うか、それともチームの workflow を強制する自前の plan command/agent を作るか</li>
<li class="">個人 skill (例: 自分のコーディングスタイル付き <code>/implement</code>) とコミュニティ skill (例: <code>/simplify</code>) はどう共存させるか</li>
<li class="">既存 codebase を spec markdown に変換 → コード削除 → AI に spec から再生成させて元と同じコードが復元できる時代は来るか</li>
</ul>
<p><strong>Specs &amp; Documentation に関する問い</strong> (3 問):</p>
<ul>
<li class="">全 feature に spec markdown が必要か</li>
<li class="">spec の更新頻度はどう保つか</li>
<li class="">新機能実装で他 spec への波及をどう管理するか</li>
</ul>
<p>これらは「正解」を作者が知っているわけではなく、読者がそれぞれの現場で答えを見つける性質の問いです。本記事を読み終えた後の探求テーマとして覚えておくと、自分の運用が深まります。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>採用判断</div><div class="admonitionContent_BuS1"><p>5 つの注意点を踏まえつつ、Open Questions は「自分の現場ではどう答えるか」を考えながら採用判断する。注意点 3 (コピペで動かない) と 4 (業界トップの運用が自分に合うとは限らない) は特に意識して、リポは「写経対象」ではなく「思考の材料」として扱うのがおすすめです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第-11-章-まとめと関連リンク">第 11 章: まとめと関連リンク<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E7%AC%AC-11-%E7%AB%A0-%E3%81%BE%E3%81%A8%E3%82%81%E3%81%A8%E9%96%A2%E9%80%A3%E3%83%AA%E3%83%B3%E3%82%AF" class="hash-link" aria-label="第 11 章: まとめと関連リンク への直接リンク" title="第 11 章: まとめと関連リンク への直接リンク" translate="no">​</a></h2>
<p><a href="https://github.com/shanraisshan/claude-code-best-practice" target="_blank" rel="noopener noreferrer" class="">shanraisshan/claude-code-best-practice</a> は、Claude Code をめぐる公式機能とコミュニティ実践を一次情報リンク付きで集約したキュレーション集です。本記事では 13 ディレクトリ構成、30+ の概念カタログ、12 の開発ワークフロー、8 + 2 のコレクション、3 種の cross-model 方式、83 個の Tips、12 本のレポートを概観し、レベル別の 4 ロードマップで取捨選択の道筋をまとめました。</p>
<p>このリポは「読んで終わり」というより、自分の <code>.claude/</code> を育てる際の参照先として手元に置いておく類のものです。気になった概念があれば本記事のリンクから該当ファイルへ直接飛んで原文を読む、という使い方が向いています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="関連リンク">関連リンク<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#%E9%96%A2%E9%80%A3%E3%83%AA%E3%83%B3%E3%82%AF" class="hash-link" aria-label="関連リンク への直接リンク" title="関連リンク への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><strong>元リポジトリ</strong>: <a href="https://github.com/shanraisshan/claude-code-best-practice" target="_blank" rel="noopener noreferrer" class="">shanraisshan/claude-code-best-practice</a></li>
<li class=""><strong>作者</strong>: Shayan Raisshan (<a href="https://github.com/shanraisshan" target="_blank" rel="noopener noreferrer" class="">GitHub</a>)</li>
<li class=""><strong>作者の他リポジトリ</strong>:<!-- -->
<ul>
<li class=""><a href="https://github.com/shanraisshan/claude-code-hooks" target="_blank" rel="noopener noreferrer" class="">claude-code-hooks</a>：hook 実装集 (サウンド通知 / auto-format / 危険コマンドブロック)</li>
<li class=""><a href="https://github.com/shanraisshan/claude-code-status-line" target="_blank" rel="noopener noreferrer" class="">claude-code-status-line</a>：context 使用量 / cost の可視化</li>
<li class=""><a href="https://github.com/shanraisshan/codex-cli-best-practice" target="_blank" rel="noopener noreferrer" class="">codex-cli-best-practice</a>：Codex CLI 版</li>
<li class=""><a href="https://github.com/shanraisshan/codex-cli-hooks" target="_blank" rel="noopener noreferrer" class="">codex-cli-hooks</a>：Codex CLI 用 hook</li>
<li class=""><a href="https://github.com/shanraisshan/gemini-cli-best-practice" target="_blank" rel="noopener noreferrer" class="">gemini-cli-best-practice</a>：Gemini CLI 版</li>
<li class=""><a href="https://github.com/shanraisshan/gemini-cli-hooks" target="_blank" rel="noopener noreferrer" class="">gemini-cli-hooks</a>：Gemini CLI 用 hook</li>
<li class=""><a href="https://github.com/shanraisshan/ralph-wiggum-self-evolving-loop" target="_blank" rel="noopener noreferrer" class="">ralph-wiggum-self-evolving-loop</a>：Ralph Wiggum ループの実装</li>
</ul>
</li>
<li class=""><strong>Anthropic 公式</strong>:<!-- -->
<ul>
<li class=""><a href="https://code.claude.com/docs/" target="_blank" rel="noopener noreferrer" class="">Claude Code docs</a></li>
<li class=""><a href="https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md" target="_blank" rel="noopener noreferrer" class="">Claude Code CHANGELOG</a></li>
<li class=""><a href="https://github.com/anthropics/skills" target="_blank" rel="noopener noreferrer" class="">Anthropic Skills</a></li>
</ul>
</li>
<li class=""><strong>コミュニティ subscribe 先</strong> (README の <code>🔔 SUBSCRIBE</code> セクションより抜粋):<!-- -->
<ul>
<li class="">Reddit: <a href="https://www.reddit.com/r/ClaudeAI/" target="_blank" rel="noopener noreferrer" class="">r/ClaudeAI</a>, <a href="https://www.reddit.com/r/ClaudeCode/" target="_blank" rel="noopener noreferrer" class="">r/ClaudeCode</a>, <a href="https://www.reddit.com/r/Anthropic/" target="_blank" rel="noopener noreferrer" class="">r/Anthropic</a></li>
<li class="">X: <a href="https://x.com/bcherny" target="_blank" rel="noopener noreferrer" class="">Boris Cherny</a>, <a href="https://x.com/trq212" target="_blank" rel="noopener noreferrer" class="">Thariq</a>, <a href="https://x.com/_catwu" target="_blank" rel="noopener noreferrer" class="">Cat Wu</a>, <a href="https://x.com/lydiahallie" target="_blank" rel="noopener noreferrer" class="">Lydia Hallie</a></li>
<li class="">YouTube: <a href="https://www.youtube.com/@anthropic-ai" target="_blank" rel="noopener noreferrer" class="">Anthropic</a>, <a href="https://www.youtube.com/@LennysPodcast" target="_blank" rel="noopener noreferrer" class="">Lenny's Podcast</a>, <a href="https://www.youtube.com/@pragmaticengineer" target="_blank" rel="noopener noreferrer" class="">The Pragmatic Engineer</a></li>
</ul>
</li>
<li class=""><strong>動画解説</strong> (リポ内 <code>videos/</code> ディレクトリより主要 3 本):<!-- -->
<ul>
<li class=""><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/videos/claude-karpathy-ai-engineer-02-may-26.md" target="_blank" rel="noopener noreferrer" class="">Andrej Karpathy: From Vibe Coding to Agentic Engineering (2026-05-02, AI Engineer)</a></li>
<li class=""><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/videos/claude-matt-pocock-24-apr-26.md" target="_blank" rel="noopener noreferrer" class="">Matt Pocock: Full Walkthrough — Workflow for AI Coding (2026-04-24)</a></li>
<li class=""><a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/videos/claude-boris-pragmatic-engineer-04-mar-26.md" target="_blank" rel="noopener noreferrer" class="">Boris Cherny: Building Claude Code (2026-03-04, The Pragmatic Engineer)</a></li>
</ul>
</li>
</ul>
<p>最後に、このリポジトリは継続的にアップデートされています。本記事で紹介した数字 (445 ファイル / 83 Tips / 12 Workflows 等) は 2026 年 5 月 25 日時点のスナップショットです。最新の状態は元 <a href="https://github.com/shanraisshan/claude-code-best-practice/blob/main/README.md" target="_blank" rel="noopener noreferrer" class="">README</a> で確認してください。</p>
<!-- -->
<section data-footnotes="true" class="footnotes"><h2 class="anchor anchorTargetStickyNavbar_Vzrq sr-only" id="footnote-label">Footnotes<a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#footnote-label" class="hash-link" aria-label="Footnotes への直接リンク" title="Footnotes への直接リンク" translate="no">​</a></h2>
<ol>
<li class="anchorTargetStickyNavbar_Vzrq" id="user-content-fn-worktree-isolation-5e45be">
<p><code>isolation: worktree</code> は subagent の frontmatter で指定する設定で、起動時に一時的な git worktree (作業用ディレクトリ) を自動生成し、変更を main workspace から隔離します。subagent が作業完了 (または失敗) すると、変更がなければ worktree は自動削除され、変更があれば path と branch が返ります。リファクタや大規模 debug など「失敗時に丸ごと破棄したい」作業で、main workspace を汚さずに済むのが利点です。 <a href="https://notes.rewheel.dev/blog/claude-code-best-practice-guide#user-content-fnref-worktree-isolation-5e45be" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <category>ai-agent</category>
            <category>claude-code</category>
            <category>best-practice</category>
            <category>curation</category>
        </item>
        <item>
            <title><![CDATA[ブログのドメインを notes.rewheel.dev に変更しました]]></title>
            <link>https://notes.rewheel.dev/blog/rewheel-naming</link>
            <guid>https://notes.rewheel.dev/blog/rewheel-naming</guid>
            <pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[個人技術ブログのドメインを notes.rasshii.dev から notes.rewheel.dev に変更しました。「車輪の再発明から学ぶ」というコンセプトに合わせた命名と、検討プロセスを共有します。]]></description>
            <content:encoded><![CDATA[<p>このブログのドメインを <strong><code>notes.rasshii.dev</code></strong> から <strong><code>notes.rewheel.dev</code></strong> に変更しました。あわせてサイト名も「Tech Notes」から <strong>「Reinvent Notes」</strong> に、タグラインを <strong>「車輪を再発明して学ぶ」</strong> に改名しています。</p>
<p>ドメインを変えるたびに「なんで変えたんだっけ」「なんでこの名前にしたんだっけ」を忘れてしまうので、決定までの経緯と検討プロセスを記録に残しておきます。同じように「個人ブログのドメインを変えたい」と思っている方のヒントになれば嬉しいです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="旧-notesrasshiidev-の何が気に入らなかったか">旧 <code>notes.rasshii.dev</code> の何が気に入らなかったか<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E6%97%A7-notesrasshiidev-%E3%81%AE%E4%BD%95%E3%81%8C%E6%B0%97%E3%81%AB%E5%85%A5%E3%82%89%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%8B" class="hash-link" aria-label="旧-notesrasshiidev-の何が気に入らなかったか への直接リンク" title="旧-notesrasshiidev-の何が気に入らなかったか への直接リンク" translate="no">​</a></h2>
<p><code>rasshii.dev</code> は僕の GitHub / X ハンドル名から取った apex ドメインで、技術ブログ用にサブドメイン <code>notes.rasshii.dev</code> を切って運用していました。1 ヶ月ほど運用してみて、以下が引っかかりました。</p>
<ul>
<li class=""><strong>個人ハンドル名依存</strong>: ブログのコンセプトとハンドル名が結びつくと、サイトの方針を変えづらい</li>
<li class=""><strong>apex の用途が決まらない</strong>: <code>rasshii.dev</code> 自体をポートフォリオに使うつもりだったが、「個人ハンドル = ポートフォリオ」だと特徴が出にくい</li>
<li class=""><strong>読者目線で意味が伝わらない</strong>: <code>rasshii.dev</code> を初見で見ても、サイトの内容が想像できない</li>
</ul>
<p>要するに、<strong>ブログのアイデンティティをハンドル名から独立させたい</strong> と感じはじめました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンセプトから決める">コンセプトから決める<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E3%82%B3%E3%83%B3%E3%82%BB%E3%83%97%E3%83%88%E3%81%8B%E3%82%89%E6%B1%BA%E3%82%81%E3%82%8B" class="hash-link" aria-label="コンセプトから決める への直接リンク" title="コンセプトから決める への直接リンク" translate="no">​</a></h2>
<p>ドメイン名を考えるとき、いきなり候補を並べるよりも <strong>「サイトの軸となるコンセプトは何か」</strong> から決めたほうが筋がいいです。</p>
<p>このブログで僕がやりたいのは、要するに <strong>「既存ツールや概念を、自分で作り直したり整理しなおしたりして理解を深める」</strong> ことです。最近書いた <a class="" href="https://notes.rewheel.dev/blog/harness-engineering">ハーネスエンジニアリング入門</a> もまさにそれで、Anthropic / Hashimoto らが提唱する harness engineering の概念を、自分の <code>~/.claude/</code> 構成例とともに自分の言葉で再構築したものでした。</p>
<p>このパターンを英語のフレーズで言うと、ぴったりなのが <strong>"reinventing the wheel"</strong> (車輪の再発明) です。本来は <strong>「すでにあるものを無駄に作り直す」</strong> という否定的なイディオムですが、AI エージェントが何でも作ってくれる時代に <strong>「あえて自分で作り直して理解する」</strong> こと自体が学習価値を持つ、と考えると、この皮肉なフレーズをポジティブに反転させて使えそうだと思いました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ドメイン名の候補出し">ドメイン名の候補出し<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E5%90%8D%E3%81%AE%E5%80%99%E8%A3%9C%E5%87%BA%E3%81%97" class="hash-link" aria-label="ドメイン名の候補出し への直接リンク" title="ドメイン名の候補出し への直接リンク" translate="no">​</a></h2>
<p>「車輪の再発明 = reinventing the wheel」をテーマに、候補を出してみました。</p>
<table><thead><tr><th>候補</th><th>カテゴリ</th><th>コメント</th></tr></thead><tbody><tr><td><code>reinventingthewheel.com</code></td><td>フル</td><td>元フレーズ忠実だが 24 文字、Slack で誤記しやすい</td></tr><tr><td><code>reinvent.dev</code></td><td>短縮 (既存単語)</td><td>強い、<code>reinvent</code> 単体で意味成立</td></tr><tr><td><code>rewheel.dev</code></td><td>造語</td><td>7 文字、短く印象的、<code>re + wheel</code> で意味推測可能</td></tr><tr><td><code>wheelnotes.dev</code></td><td>合成造語</td><td><code>tech-notes</code> リポ名との親和性、ノート感</td></tr><tr><td><code>wheellab.dev</code></td><td>合成造語</td><td>「車輪の実験室」、試行錯誤のニュアンス</td></tr><tr><td><code>whyreinvent.com</code></td><td>反転問いかけ</td><td>「なぜ再発明するか」、攻めたトーン</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="seo-観点での検討">SEO 観点での検討<a href="https://notes.rewheel.dev/blog/rewheel-naming#seo-%E8%A6%B3%E7%82%B9%E3%81%A7%E3%81%AE%E6%A4%9C%E8%A8%8E" class="hash-link" aria-label="SEO 観点での検討 への直接リンク" title="SEO 観点での検討 への直接リンク" translate="no">​</a></h2>
<p>最初は「SEO は気にしないから何でもいい」と思っていましたが、念のため検討してみました。結論として <strong>現代の SEO では造語が有利になりやすい</strong> です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="既存フレーズ系-reinventingthewheelcom-など-の問題">既存フレーズ系 (<code>reinventingthewheel.com</code> など) の問題<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E6%97%A2%E5%AD%98%E3%83%95%E3%83%AC%E3%83%BC%E3%82%BA%E7%B3%BB-reinventingthewheelcom-%E3%81%AA%E3%81%A9-%E3%81%AE%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="既存フレーズ系-reinventingthewheelcom-など-の問題 への直接リンク" title="既存フレーズ系-reinventingthewheelcom-など-の問題 への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><strong>Exact Match Domain ペナルティ</strong>: Google は 2012 年に「キーワードを丸ごとドメインに入れたサイト」の評価を下げる仕様変更を入れました。コンテンツの質が低いと、むしろ逆効果になります</li>
<li class=""><strong>検索競合多数</strong>: 「reinventing the wheel」は一般的なイディオムなので、上位表示は難しい</li>
<li class=""><strong>覚えにくい / 綴り間違いリスク</strong>: 長いフレーズはシェア時に誤記されやすい</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="造語-rewheeldev-など-の強み">造語 (<code>rewheel.dev</code> など) の強み<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E9%80%A0%E8%AA%9E-rewheeldev-%E3%81%AA%E3%81%A9-%E3%81%AE%E5%BC%B7%E3%81%BF" class="hash-link" aria-label="造語-rewheeldev-など-の強み への直接リンク" title="造語-rewheeldev-など-の強み への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><strong>ブランド検索に強い</strong>: 「rewheel」を検索したときに <strong>上位を取りやすい</strong></li>
<li class=""><strong>競合が少ない</strong>: 同じドメイン名がなければ SEO 競争が起きにくい</li>
<li class=""><strong>短くシェアしやすい</strong>: 7 文字なら口頭・Slack での誤記リスクが低い</li>
<li class=""><strong>大手の前例</strong>: Google / Spotify / Vercel：すべて造語または独自綴り</li>
</ul>
<p>「ブランド検索の独占」は思った以上に強い武器で、現代の SEO は「内容で勝負 + ブランドで集客」という構造になっています。造語ドメインはこの両方を加速させます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="結局-rewheeldev-に決めた">結局 <code>rewheel.dev</code> に決めた<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E7%B5%90%E5%B1%80-rewheeldev-%E3%81%AB%E6%B1%BA%E3%82%81%E3%81%9F" class="hash-link" aria-label="結局-rewheeldev-に決めた への直接リンク" title="結局-rewheeldev-に決めた への直接リンク" translate="no">​</a></h2>
<p>候補の中から <strong><code>rewheel.dev</code></strong> を選びました。決め手は:</p>
<ol>
<li class=""><strong>7 文字と短い</strong>: Slack や口頭で URL を伝えるときに最短</li>
<li class=""><strong>意味が推測できる造語</strong>: <code>re + wheel</code> で「車輪を再び」というコンセプトが伝わる</li>
<li class=""><strong>SEO 競争がゼロ</strong>: ブランド検索の独占ができる</li>
<li class=""><strong><code>.dev</code> TLD</strong>: Google が運営する技術者向け TLD、HTTPS 必須でクリーン</li>
<li class=""><strong>発音しやすい</strong>: 「リホイール」とそのまま読める</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サブドメイン構成にした理由">サブドメイン構成にした理由<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E3%82%B5%E3%83%96%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E6%A7%8B%E6%88%90%E3%81%AB%E3%81%97%E3%81%9F%E7%90%86%E7%94%B1" class="hash-link" aria-label="サブドメイン構成にした理由 への直接リンク" title="サブドメイン構成にした理由 への直接リンク" translate="no">​</a></h2>
<p><code>rewheel.dev</code> を Vercel Domains で取得した後、最初は apex (<code>rewheel.dev</code>) でこのブログを公開していました。が、すぐに <strong>サブドメイン (<code>notes.rewheel.dev</code>) に変更</strong> しました。理由は:</p>
<ul>
<li class=""><strong>apex を将来の用途に温存</strong>: ポートフォリオ / landing / 別のサイトに <code>rewheel.dev</code> apex を使いたくなったときに切り替えやすい</li>
<li class=""><strong>過去パターンとの一貫性</strong>: 旧 <code>notes.rasshii.dev</code> と同じ「<code>notes.&lt;brand&gt;.&lt;tld&gt;</code>」の形なので、URL パターンとして読者にも自然</li>
<li class=""><strong>拡張性</strong>: 将来 <code>blog.rewheel.dev</code> / <code>lab.rewheel.dev</code> / <code>api.rewheel.dev</code> のような形でサブサイトを増やせる</li>
</ul>
<p><code>rasshii.dev</code> を取得したときと同じ反省で、<strong>apex は気軽に他用途に切り替えられないので、明確な目的が決まるまで残しておく</strong> のが個人的な定石になりました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サイト名タグライントップページ説明文も刷新">サイト名・タグライン・トップページ説明文も刷新<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E3%82%B5%E3%82%A4%E3%83%88%E5%90%8D%E3%82%BF%E3%82%B0%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%88%E3%83%83%E3%83%97%E3%83%9A%E3%83%BC%E3%82%B8%E8%AA%AC%E6%98%8E%E6%96%87%E3%82%82%E5%88%B7%E6%96%B0" class="hash-link" aria-label="サイト名・タグライン・トップページ説明文も刷新 への直接リンク" title="サイト名・タグライン・トップページ説明文も刷新 への直接リンク" translate="no">​</a></h2>
<p>ドメインを変えるなら、ついでにサイト名やタグラインも揃えたほうが一貫性が出ます。</p>
<table><thead><tr><th>項目</th><th>Before</th><th>After</th></tr></thead><tbody><tr><td>サイト名</td><td>Tech Notes</td><td><strong>Reinvent Notes</strong></td></tr><tr><td>タグライン</td><td>個人的な技術発信</td><td><strong>車輪を再発明して学ぶ</strong></td></tr><tr><td>説明文</td><td>フロントエンド・バックエンド・インフラ・AI・学習ログなどを記録する個人的な技術発信サイトです。</td><td><strong>車輪の再発明は無駄と言われますが、あえて作り直すことで仕組みを深く理解できます。フロントエンド・バックエンド・インフラ・AI などを再発明しながら学んだことを記録する個人技術ノートです。</strong></td></tr></tbody></table>
<p>当初はサイト名を URL そのもの (<code>notes.rewheel.dev</code>) にしようと考えました。<strong>ブランドと URL を一体化させる</strong> ことで覚えやすさを狙う、Vercel や Linear のようなプロダクト系サイトでよく見るパターンです。ただ、いざ navbar に置いてみると <strong>コンセプト (車輪の再発明から学ぶ) が初見の人に伝わらない</strong> ことに気付き、より直接的に意味が伝わる <strong>「Reinvent Notes」</strong> に落ち着きました。<code>re-</code> (再び) + <code>invent</code> (発明する) + <code>notes</code> (記録) の 3 要素を合わせた構造で、ドメインを知らない人にも一目でサイトの性質が伝わります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="旧ドメイン-notesrasshiidev-はどうする">旧ドメイン (<code>notes.rasshii.dev</code>) はどうする?<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E6%97%A7%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3-notesrasshiidev-%E3%81%AF%E3%81%A9%E3%81%86%E3%81%99%E3%82%8B" class="hash-link" aria-label="旧ドメイン-notesrasshiidev-はどうする への直接リンク" title="旧ドメイン-notesrasshiidev-はどうする への直接リンク" translate="no">​</a></h2>
<p>しばらく <strong>削除せずに残します</strong> (1-2 週間程度)。理由は:</p>
<ul>
<li class="">ブックマークしてくれている方の URL が即死しない</li>
<li class="">Slack や Twitter でシェアした古い URL がしばらく動く</li>
<li class="">万一新ドメインで問題が出たときに旧 URL に戻せる</li>
</ul>
<p>SEO 観点では「旧 URL → 新 URL の 301 リダイレクト」を設定するのが王道ですが、このブログは SEO を主目的にしていないので、シンプルに <strong>両方並列で配信</strong> する運用にしました。新ドメインが安定したら旧ドメインを削除する予定です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://notes.rewheel.dev/blog/rewheel-naming#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ドメイン変更は地味な作業ですが、<strong>サイトのアイデンティティを 1 つの単語に圧縮する</strong> プロセスとして面白い経験でした。技術選定と同じで、複数候補を並べてトレードオフを検討するクセを付けると、後から振り返って意思決定の理由を再現できます。</p>
<ul>
<li class="">旧 URL: <code>https://notes.rasshii.dev</code> (しばらく並列で動きます)</li>
<li class="">新 URL: <strong><code>https://notes.rewheel.dev</code></strong></li>
</ul>
<p>ブックマークしてくださっている方は、お時間あるときに新 URL に切り替えていただければ嬉しいです。これからも「車輪の再発明から学ぶ」記録を続けていきます。</p>]]></content:encoded>
            <category>meta</category>
            <category>branding</category>
        </item>
        <item>
            <title><![CDATA[ハーネスエンジニアリング入門: AI エージェントの信頼性は周囲の設計で決まる]]></title>
            <link>https://notes.rewheel.dev/blog/harness-engineering</link>
            <guid>https://notes.rewheel.dev/blog/harness-engineering</guid>
            <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[AI エージェント (Claude Code 含む) の信頼性はモデル本体ではなく harness の設計品質に依存する。中核等式 Agent = Model + Harness、二分法フレーム、Phase 0-6 の段階的導入を ~/.claude/ 実例で整理。]]></description>
            <content:encoded><![CDATA[<p>「モデルを最新版に上げたのに、結局同じ間違いを繰り返す」「プロンプトを工夫してもどこかで頭打ちになる」。AI エージェントを業務に組み込もうとすると、必ずこの壁に当たります。これは多くの場合モデル本体の問題ではなく、<strong>モデルを取り囲む環境 (= harness) の設計品質</strong>に起因します。本記事では 2026 年 2 月以降に急速に体系化された <strong>ハーネスエンジニアリング (Harness Engineering)</strong> という考え方を、自分の <code>~/.claude/</code> 配下の構成例とあわせて整理します。</p>
<p>なお、用語の語源について軽く触れておきます。「harness」は『装備・馬具・(動詞で) 力を制御し活用する』意味を持つ英単語で、AI 文脈では Mitchell Hashimoto (HashiCorp 共同創設者) が 2026 年 2 月に「モデルを支える環境一式」の意味で導入しました。中核にある等式はとてもシンプルです。</p>
<blockquote>
<p><strong>Agent = Model + Harness</strong></p>
</blockquote>
<p>この等式を出発点に、harness とは何か、どう設計するか、どう段階的に育てるかを見ていきます。</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>本記事の読み方</div><div class="admonitionContent_BuS1"><p>本記事は <strong>公式 / 業界共通の概念</strong> と <strong>自分の運用例</strong> が混在しています。後者は <code>:::info 自分の運用例 :::</code> のブロックで囲んで明示しているので、「あなたの Claude Code にもそのまま当てはまるか」「rasshii さん独自の運用か」を判別しながら読み進めてください。コード雛形もすべて自分の運用例ブロック内に置いています。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハーネスエンジニアリングとは">ハーネスエンジニアリングとは<a href="https://notes.rewheel.dev/blog/harness-engineering#%E3%83%8F%E3%83%BC%E3%83%8D%E3%82%B9%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF" class="hash-link" aria-label="ハーネスエンジニアリングとは への直接リンク" title="ハーネスエンジニアリングとは への直接リンク" translate="no">​</a></h2>
<p>ハーネスエンジニアリングとは、AI エージェント (Claude Code / Codex / Cursor などの coding agent) が動作する <strong>全環境を設計する実践</strong> を指します。Mitchell Hashimoto が 2026 年 2 月に個人ブログで概念を提唱し、数日後に OpenAI の Ryan Lopopolo がフィールドレポートで追従しました。Lopopolo のチームは 5 ヶ月で約 100 万行のコードベースを <strong>手書き 0 行 / 1500 自動 PR</strong> で構築したと報告しており、これが業界の注目を集めるきっかけになりました。その後 Anthropic / Augment Code / Birgitta Böckeler (Martin Fowler のサイト掲載) / SIG など複数社がそれぞれの語彙で体系化し、2026 年 5 月現在は業界共通の用語として定着しつつあります。</p>
<p>ハーネスエンジニアリングは、これまでの「プロンプトエンジニアリング」「コンテキストエンジニアリング」と階層的に区別される概念です。</p>
<table><thead><tr><th>層</th><th>問い</th><th>スコープ</th></tr></thead><tbody><tr><td>Prompt Engineering</td><td>何を尋ねるか</td><td>単一インタラクション</td></tr><tr><td>Context Engineering</td><td>何を見せるか</td><td>1 つの context window 内</td></tr><tr><td>Harness Engineering</td><td>環境全体をどう設計するか</td><td>複数セッション横断 / 環境設計 / phase gate</td></tr></tbody></table>
<p>プロンプトエンジニアリングが「単発の指示文を磨く」のに対し、ハーネスエンジニアリングは <strong>エージェントが今後ずっと動く環境全体</strong> を対象にします。同じモデルでも、harness の設計品質次第でエージェントの信頼性は大きく変わる、というのが主張です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="中核等式-agent--model--harness">中核等式: Agent = Model + Harness<a href="https://notes.rewheel.dev/blog/harness-engineering#%E4%B8%AD%E6%A0%B8%E7%AD%89%E5%BC%8F-agent--model--harness" class="hash-link" aria-label="中核等式: Agent = Model + Harness への直接リンク" title="中核等式: Agent = Model + Harness への直接リンク" translate="no">​</a></h2>
<p>もう一度等式を書きます。</p>
<blockquote>
<p><strong>Agent = Model + Harness</strong></p>
</blockquote>
<p>ここでいう <strong>Model</strong> は Claude Opus / Sonnet / Haiku、GPT、Gemini といった LLM 本体です。<strong>Harness</strong> はそれ以外のすべてを指します。具体的には次のようなものが含まれます。</p>
<ul>
<li class="">システムプロンプト</li>
<li class="">常時注入される指示書 (Claude Code でいう CLAUDE.md)</li>
<li class="">ルール / スキル / サブエージェントの定義</li>
<li class="">MCP (Model Context Protocol) サーバー / 各種 tool / permissions</li>
<li class="">フック (PreToolUse / PostToolUse / Stop など、ツール実行前後の特定タイミングで発火する shell 連携。詳細は後述)</li>
<li class="">メモリ (会話を跨いだ永続化)</li>
<li class="">コンテキスト管理 (compaction [会話履歴の圧縮] や reset の戦略)</li>
<li class="">検証ループ (CI / lint / type-check / 自動レビュー)</li>
</ul>
<p>つまり「エージェントが世界とやり取りする経路」と「エージェントが自分を律する仕組み」をまとめて harness と呼びます。モデル単体の精度より、これらが噛み合っているかどうかが、業務利用に耐える信頼性を左右します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二分法フレームワーク-guides--sensors">二分法フレームワーク: Guides × Sensors<a href="https://notes.rewheel.dev/blog/harness-engineering#%E4%BA%8C%E5%88%86%E6%B3%95%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF-guides--sensors" class="hash-link" aria-label="二分法フレームワーク: Guides × Sensors への直接リンク" title="二分法フレームワーク: Guides × Sensors への直接リンク" translate="no">​</a></h2>
<p>harness の設計を整理する上で最も実用的なフレームが、Birgitta Böckeler（Thoughtworks Distinguished Engineer、Martin Fowler のサイトで公開）による <strong>Guides + Sensors</strong> の二分法です。</p>
<ul>
<li class=""><strong>Guides (feedforward)</strong>: 行動「前」のステアリング。失敗が起きる前に方向付けする</li>
<li class=""><strong>Sensors (feedback)</strong>: 行動「後」の自己修正。間違いを検出して直す</li>
</ul>
<p>さらにそれぞれを、<strong>Computational</strong> か <strong>Inferential</strong> かで十字に分類します。Computational は deterministic、つまり機械的に毎回同じ結果が返る検証 (lint / 型チェック / テストなど) です。Inferential は LLM-based、すなわち LLM の推論に依存する確率的な検証 (レビュー agent / 提案 skill など) です。すると 4 象限になります。</p>
<!-- -->
<p>このフレームの重要な含意は、<strong>どちらか片方だけでは harness は機能しない</strong> ことです。</p>
<ul>
<li class=""><strong>Feedback だけ (Sensors のみ)</strong>: エージェントは同じミスを何度も繰り返した後でしか修正できない</li>
<li class=""><strong>Feedforward だけ (Guides のみ)</strong>: ルールを書いても LLM は確率的に違反するため、効果を検証する手段がない</li>
<li class=""><strong>Computational だけ</strong>: 機械的な lint や型は通っても、設計の妥当性や仕様適合は見抜けない</li>
<li class=""><strong>Inferential だけ</strong>: LLM のレビューは確率的で、<code>// eslint-disable</code> のような明確な違反すら見落とすことがある</li>
</ul>
<p>良い harness は <strong>4 象限すべてで何か手を打ってある</strong> 状態を目指します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="claudemd--mcp--memory-harness-の入口">CLAUDE.md / MCP / memory: harness の入口<a href="https://notes.rewheel.dev/blog/harness-engineering#claudemd--mcp--memory-harness-%E3%81%AE%E5%85%A5%E5%8F%A3" class="hash-link" aria-label="CLAUDE.md / MCP / memory: harness の入口 への直接リンク" title="CLAUDE.md / MCP / memory: harness の入口 への直接リンク" translate="no">​</a></h2>
<p><code>~/.claude/</code> 配下の細かいディレクトリに入る前に、Claude Code 標準で用意されている <strong>harness の入口</strong> にあたる 3 要素を押さえておきます。これらは Anthropic が公式に提供している機能で、誰の Claude Code でも同じように使えます。</p>
<ul>
<li class=""><strong>CLAUDE.md</strong>: プロジェクトルートや <code>~/.claude/</code> 直下に置く Markdown 指示書。起動時に常時 context に注入されます。<code>@path/to/file</code> 構文で他ファイルを auto-load (自動読み込み) できるため、ハブ的な位置づけになります</li>
<li class=""><strong>MCP (Model Context Protocol)</strong>: 外部ツール / データソースへのアクセス手段。Claude Code に対するプラグインのようなものです。例: context7 (ライブラリの最新 docs)、aws-knowledge (AWS 公式 docs)、terraform、sequential-thinking (構造化推論)、serena (LSP ベースのコード検索) など</li>
<li class=""><strong>auto-memory</strong>: 会話を跨いで <code>~/.claude/projects/&lt;project&gt;/memory/</code> に永続化される情報 (<code>&lt;project&gt;</code> は git リポジトリから派生)。ビルドコマンド、デバッグ知見、アーキテクチャメモ、コーディング規約の好み、ワークフロー習慣などが蓄積されます</li>
</ul>
<p>これら 3 つは harness の「外周の入口」にあたります。CLAUDE.md がエージェントに「今回も守ってほしいこと」を毎回伝え、MCP が「最新の一次情報にアクセスする手段」を提供し、memory が「前回までに学んだことを思い出させる」役割を担います。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p>僕は CLAUDE.md の Quality &amp; Accuracy Policy セクションに「MCP 第一選択 → WebSearch fallback」と明記しています。これは、WebSearch だけに頼ると古い情報を引きやすいためで、ライブラリ仕様は context7 / AWS は aws-knowledge / Terraform は terraform MCP / 複雑判断は sequential-thinking、と用途別に MCP を割り当てています。さらに、特定キーワード (例: 「ultrathink」「アーキテクチャ設計」) が発話に含まれたら sequential-thinking を自動起動するように CLAUDE.md でトリガーを定義しています。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="claude-配下の構成-全体マップ">~/.claude/ 配下の構成 (全体マップ)<a href="https://notes.rewheel.dev/blog/harness-engineering#claude-%E9%85%8D%E4%B8%8B%E3%81%AE%E6%A7%8B%E6%88%90-%E5%85%A8%E4%BD%93%E3%83%9E%E3%83%83%E3%83%97" class="hash-link" aria-label="~/.claude/ 配下の構成 (全体マップ) への直接リンク" title="~/.claude/ 配下の構成 (全体マップ) への直接リンク" translate="no">​</a></h2>
<p>ここから先は具体的なディレクトリ構成の例として、自分の <code>~/.claude/</code> 配下を題材に紹介します。Claude Code 標準の機能と自分の独自運用を 2 列で対比した表が以下です。</p>
<table><thead><tr><th>ディレクトリ</th><th>Claude Code 標準機能</th><th>自分の運用</th></tr></thead><tbody><tr><td><code>rules/</code></td><td>✅ Memory システムの一部 (auto-load 対象)</td><td>29 ファイルに分割、frontmatter <code>paths:</code> で path-scoped 化</td></tr><tr><td><code>skills/</code></td><td>✅ Skill システム (発話 trigger で起動)</td><td>23 個、Step 0 で MCP 起動を強制</td></tr><tr><td><code>agents/</code></td><td>✅ Subagent システム (隔離 context で起動)</td><td>7 個、Generator (implementer) と Evaluator (code-reviewer / architect / test-strategist) を意図的に分離</td></tr><tr><td><code>hooks/</code></td><td>✅ PreToolUse / PostToolUse / Stop などの hook システム</td><td>2 個、suppression 検出 / 保護ファイル防御</td></tr><tr><td><code>teams/</code></td><td>⚠️ 公式機能ではなく自分で定義した meta orchestration パターン</td><td>multi-perspective-review 等、複数 agent を main agent から spawn (起動)</td></tr><tr><td><code>references/</code></td><td>❌ 公式機能ではなく自分の命名</td><td>progressive disclosure 用、on-demand Read で context 節約</td></tr><tr><td><code>scripts/</code></td><td>❌ 公式機能ではなく自分の運用</td><td>月次 cron で consistency check / self-test</td></tr><tr><td><code>commands/</code></td><td>✅ Custom slash command システム</td><td>軽量な汎用コマンド (init / review / security-review など)</td></tr><tr><td><code>output-styles/</code></td><td>✅ Output style システム</td><td>レスポンス調整用、最小限のみ</td></tr></tbody></table>
<p>「✅」が付いているディレクトリは Claude Code 公式機能の格納先で、誰でも同じ位置に置けば同じように動きます。「⚠️ / ❌」は個人的に切り出した運用上の慣習で、別に <code>notes/</code> でも <code>meta/</code> でも構わない名前です。読者の Claude Code でこれをそのまま真似する必要はありませんが、<strong>なぜそういうディレクトリを切ったのか</strong> の理由を以降の章で紹介します。</p>
<p>全体のレイヤ構造は、入口 (CLAUDE.md / MCP / memory) から Inferential Guides (rules / skills)、Generator/Evaluator (agents) へと進みます。その先に Computational Sensors (hooks)、Orchestration (teams)、補助 (references / scripts) が続く構成です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-inferential-guides-の中核">rules/: Inferential Guides の中核<a href="https://notes.rewheel.dev/blog/harness-engineering#rules-inferential-guides-%E3%81%AE%E4%B8%AD%E6%A0%B8" class="hash-link" aria-label="rules/: Inferential Guides の中核 への直接リンク" title="rules/: Inferential Guides の中核 への直接リンク" translate="no">​</a></h2>
<p>Claude Code には Memory システムという仕組みがあり、<code>~/.claude/rules/*.md</code> (および各リポジトリの <code>CLAUDE.md</code>) を起動時に context に取り込みます。常時ロードされる rule と、frontmatter (Markdown 冒頭の <code>---</code> 区切りメタデータ部) に <code>paths:</code> を持つ <strong>path-scoped rule</strong> (特定パスのファイルを読んだときだけロード) の 2 種類が公式にサポートされています。</p>
<p>rule は「LLM に向けた規範指示」なので、二分法フレームでいう <strong>Inferential Guide</strong> に分類されます。コンパイラや lint と違って違反を機械的に止められるわけではありませんが、エージェントに「こうあってほしい」を伝える最も基本的な手段です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p>僕は <code>~/.claude/rules/</code> を 29 ファイルに分割して運用しています。1 ファイルに詰め込みすぎると context window (LLM が一度に扱える情報量) を圧迫し、かつ「いつでも全部読まれる」状態になるためです。</p><ul>
<li class=""><strong>常時ロード</strong>: コーディング規約 (<code>code-style.md</code>)、Git ワークフロー (<code>git-workflow.md</code>)、harness 概念 (<code>harness-engineering.md</code>) など、どのタスクでも参照したいもの</li>
<li class=""><strong>path-scoped</strong>: Terraform 規約 (<code>terraform.md</code>、<code>paths: ["**/*.tf"]</code>)、Ratchet ワークフロー (<code>ratchet-workflow.md</code>、関連 path Read 時のみ) など、特定ドメインに入ったときだけ必要なもの</li>
</ul><p>各 rule の構造はだいたい次のような Markdown です。</p><div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">name</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> code</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml">style</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">description</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> コード可読性 / 命名規則 / コメント / シークレット管理</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">#</span><span class="token title important"> Code Style</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Priority: readability &gt; maintainability &gt; performance &gt; brevity</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> 変数・関数・クラス名: English / コメント: Japanese ("why" を説明)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Documentation format: JSDoc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Credentials via environment variables only, never hardcoded</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Example (Good)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token code punctuation" style="color:#393A34">```</span><span class="token code code-language">ts</span><span class="token code"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code"></span><span class="token code code-block language-ts">const MAX_RETRY_COUNT = 3;</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-ts">function groupUsersByRole(users: User[]): Map&lt;Role, User[]&gt; { /* ... */ }</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-ts">const apiKey = process.env.MY_SERVICE_API_KEY;</span><span class="token code"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code"></span><span class="token code punctuation" style="color:#393A34">```</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Example (Bad)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token code punctuation" style="color:#393A34">```</span><span class="token code code-language">ts</span><span class="token code"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code"></span><span class="token code code-block language-ts">function f(u: any[]) { /* ... */ }                // 命名違反</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-ts">const apiKey = "hardcoded-value-here";            // ハードコード禁止</span><span class="token code"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code"></span><span class="token code punctuation" style="color:#393A34">```</span><br></div></code></pre></div></div><div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>一般的な TypeScript コード品質の話題 (上記 <code>code-style.md</code> の harness 版ではなく、コードそのものをどう書くか) は本サイトの Docs に <a class="" href="https://notes.rewheel.dev/docs/clean-code">Clean Code (TypeScript)</a> として体系化しています。命名・関数設計・SOLID 等は そちらを参照してください。</div></div></div></div>
<p>一方、特定の拡張子やパスでのみ参照したい規約は、frontmatter に <code>paths:</code> (minimatch グロブ) を追加するだけで <strong>path-scoped rule</strong> になります。Terraform 規約ならこんな感じです:</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">name</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> terraform</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">description</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> Terraform のコーディング規約 (provider バージョン / リソース命名 / シークレット管理)</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">paths</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml">  </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml string" style="color:#e3116c">"**/*.tf"</span><span class="token front-matter-block front-matter yaml language-yaml"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml">  </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml"> </span><span class="token front-matter-block front-matter yaml language-yaml string" style="color:#e3116c">"**/*.tfvars"</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">#</span><span class="token title important"> Terraform</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Provider バージョンは </span><span class="token code-snippet code keyword" style="color:#00009f">`~&gt; X.Y`</span><span class="token plain"> 形式で固定する</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> リソース命名: </span><span class="token code-snippet code keyword" style="color:#00009f">`&lt;service&gt;_&lt;purpose&gt;`</span><span class="token plain"> のスネークケース</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> シークレットは AWS Secrets Manager / SSM Parameter Store 経由 (ハードコード禁止)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> module は input/output を明示し、可能な限り公式 Verified module を優先する</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Example (Good)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token code punctuation" style="color:#393A34">```</span><span class="token code code-language">hcl</span><span class="token code"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code"></span><span class="token code code-block language-hcl">provider "aws" {</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl">  region  = "ap-northeast-1"</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl">  version = "~&gt; 5.0"</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl">resource "aws_s3_bucket" "app_logs" {</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl">  bucket = "${var.project_name}-app-logs"</span><br></div><div class="token-line" style="color:#393A34"><span class="token code code-block language-hcl">}</span><span class="token code"></span><br></div><div class="token-line" style="color:#393A34"><span class="token code"></span><span class="token code punctuation" style="color:#393A34">```</span><br></div></code></pre></div></div>
<p>この rule は <code>.tf</code> / <code>.tfvars</code> ファイルを Read したときだけ context に追加されます。フロントエンドだけ触っている日はロードされず context window が空くので、ドメイン別に分割しておくと効きます。</p>
<p>どちらの形式でも、Good / Bad の Example を必ず添えるようにしています。LLM は説明文だけより、具体例から「型」を学ぶほうが安定するためです。</p>
<p>:::</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="skills-発話駆動スキル">skills/: 発話駆動スキル<a href="https://notes.rewheel.dev/blog/harness-engineering#skills-%E7%99%BA%E8%A9%B1%E9%A7%86%E5%8B%95%E3%82%B9%E3%82%AD%E3%83%AB" class="hash-link" aria-label="skills/: 発話駆動スキル への直接リンク" title="skills/: 発話駆動スキル への直接リンク" translate="no">​</a></h2>
<p>Skill は Claude Code が公式に提供する仕組みで、特定の発話 (たとえば「レビューして」「提案して」「デバッグして」) で起動するワークフローを <code>~/.claude/skills/&lt;name&gt;/SKILL.md</code> に記述します。SKILL.md の frontmatter で発話トリガー / 利用可能ツール / effort などを宣言し、本文で実行手順を記述する形です。</p>
<p>二分法フレームでいうと、<code>/review</code> や <code>/debug</code> のように出力後に検査・修正するものは <strong>Inferential Sensor</strong>、<code>/propose</code> のように事前ステアリングするものは <strong>Inferential Guide</strong> として機能します。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p>僕は 23 個の skill を運用しており、主なものは <code>/propose</code> / <code>/review</code> / <code>/debug</code> / <code>/learn</code> / <code>/explain</code> / <code>/compare</code> / <code>/tec-doc</code> / <code>/grill-me</code> / <code>/next</code> / <code>/checkpoint</code> などです。共通の運用ポイントとして:</p><ul>
<li class=""><strong>Step 0 で MCP 起動を強制</strong>: 複雑判断は sequential-thinking、ライブラリ仕様は context7、AWS は aws-knowledge を必ずロードするよう SKILL.md に書く</li>
<li class=""><strong>反復プロトコル</strong>: <code>/review</code> は最大 3 反復で Critical / Major を 0 に収束、<code>/propose</code> は敵対的自己批判で最大 2 反復、<code>/debug</code> は Investigate → Fix → Verify を最大 3 反復、というように skill ごとに反復回数を決める</li>
</ul><p>SKILL.md の雛形例:</p><div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">name</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> review</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">description</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> コード変更を 7 観点 (正確性 / 可読性 / パフォーマンス / セキュリティ / 保守性 / 規約 / デグレ) で重要度付きレビュー</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">when_to_use</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> 「レビュー」「review」「見て」「チェック」発話で起動</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">allowed-tools</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> Read</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Grep</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Glob</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Bash</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> WebSearch</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">effort</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> max</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Step 0: MCP 起動</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> sequential-thinking (複雑判断時に強制)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> context7 (FW 用法検証時)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Step 1: 変更範囲の把握</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git status / git diff で対象ファイルを取得。</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Step 2-3: 7 観点レビュー → 重要度付け</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Critical / Major / Minor / Nit の 4 段階で指摘。</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> 終了条件</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Critical / Major が 0 になるか、最大 3 反復に到達したら終了。</span><br></div></code></pre></div></div></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="agents-subagent-generator-evaluator-分離">agents/: Subagent (Generator-Evaluator 分離)<a href="https://notes.rewheel.dev/blog/harness-engineering#agents-subagent-generator-evaluator-%E5%88%86%E9%9B%A2" class="hash-link" aria-label="agents/: Subagent (Generator-Evaluator 分離) への直接リンク" title="agents/: Subagent (Generator-Evaluator 分離) への直接リンク" translate="no">​</a></h2>
<p>Claude Code には Subagent (サブエージェント) という公式機能があり、隔離された context で独立した役割を持つエージェントを起動できます。<code>~/.claude/agents/&lt;name&gt;.md</code> に定義し、main agent から <code>Agent</code> ツール (subagent を起動するための main agent 側の tool) 経由で呼び出します。</p>
<p>ここで harness engineering の重要原則のひとつ、<strong>Generator-Evaluator 分離</strong> が登場します。Anthropic や業界共通の知見として「実装した agent が自分で評価すると self-evaluation bias が働き、品質を過大評価する」ことが分かっています。実装役 (Generator) と評価役 (Evaluator) は別の agent にする、というのが要点です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p>自分の <code>~/.claude/agents/</code> には 7 個の subagent を定義しています。</p><ul>
<li class=""><strong>Generator 系</strong>: <code>implementer</code> (TDD [テスト駆動開発] + 型安全)、<code>refactorer</code> (振る舞い保存のリファクタ)、<code>debugger</code> (バグ自律解決)</li>
<li class=""><strong>Evaluator 系</strong>: <code>code-reviewer</code> (7 観点レビュー、read-only [読み取り専用])、<code>architect</code> (設計レビュー + 敵対的検証)、<code>test-strategist</code> (テスト戦略)、<code>infra-reviewer</code> (TF / IaC レビュー)</li>
</ul><p>Evaluator 系は <code>disallowedTools: Write, Edit</code> を設定して、編集権限を持たない read-only な役割に固定しています。これで「レビュアーが勝手にコードを書き換える」事故を構造的に防げます。</p><p>subagent 定義の雛形:</p><div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">name</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> code</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml">reviewer</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">description</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> ペアプログラミング用コードレビュアー。コード変更後のレビューとガイダンスを提供</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">tools</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> Read</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Grep</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Glob</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Bash</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> WebSearch</span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">disallowedTools</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> Write</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">,</span><span class="token front-matter-block front-matter yaml language-yaml"> Edit</span><span class="token front-matter-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> Step 0: MCP 起動 (必須)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> sequential-thinking (複数仮説の検証で強制)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> context7 (FW 用法検証時)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> レビュー観点</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">正確性 / 可読性 / パフォーマンス / セキュリティ / 保守性 / 規約 / デグレ</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token title important punctuation" style="color:#393A34">##</span><span class="token title important"> 出力フォーマット</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Critical / Major / Minor / Nit で重要度付け</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> ファイル名 + 行番号で指摘箇所を明示</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> 修正案を提示 (具体的な diff か命令で)</span><br></div></code></pre></div></div></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hooks-computational-sensors">hooks/: Computational Sensors<a href="https://notes.rewheel.dev/blog/harness-engineering#hooks-computational-sensors" class="hash-link" aria-label="hooks/: Computational Sensors への直接リンク" title="hooks/: Computational Sensors への直接リンク" translate="no">​</a></h2>
<p>Claude Code の Hook システムは、特定のイベント (<strong>PreToolUse</strong>: ツール実行前、<strong>PostToolUse</strong>: ツール実行後、<strong>Stop</strong>: 応答終了時、<strong>UserPromptSubmit</strong>: ユーザー入力受信時 など) で shell コマンドを実行できる公式機能です。<code>~/.claude/settings.json</code> で <code>hooks</code> フィールドに登録します。</p>
<p>二分法フレームでいうと、hook は典型的な <strong>Computational</strong> 層: deterministic な仕組みで、入力と出力が毎回同じ shell スクリプトに従います。Guide 用途 (実行前ブロック) と Sensor 用途 (実行後検出) の両方に使えます。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p>僕は 2 個の hook を運用しています。</p><ul>
<li class=""><strong><code>check-suppression.sh</code></strong> (PostToolUse): <code>// eslint-disable</code> / <code>@ts-ignore</code> / <code># type: ignore</code> / <code># noqa</code> などの <strong>lint 抑制コメントの新規追加を検出</strong> します。検出時は exit 2 で Claude にフィードバックを返し、根本原因の修正を強制します (編集そのものを未然に止めたいなら PreToolUse を使います)</li>
<li class=""><strong><code>check-protected-files.sh</code></strong> (PreToolUse): <code>.env</code> / 秘密鍵 / 認証情報ファイルへの書き込みを未然にブロック</li>
</ul><p><code>check-suppression.sh</code> の最小実装例:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token shebang important">#!/bin/bash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># ~/.claude/hooks/check-suppression.sh</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># PostToolUse hook: lint suppression コメントの新規追加をブロック</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">set</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-eu</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 緊急時の override (環境変数で skip 可能)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">${CLAUDE_HOOK_SKIP_SUPPRESSION</span><span class="token string variable operator" style="color:#393A34">:-</span><span class="token string variable" style="color:#36acaa">0}</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">exit</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># hook 入力 (JSON) から新規追加コンテンツを取り出す</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">INPUT</span><span class="token operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable function" style="color:#d73a49">cat</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">NEW_CONTENT</span><span class="token operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable builtin class-name" style="color:#36acaa">echo</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">"</span><span class="token variable string variable" style="color:#36acaa">$INPUT</span><span class="token variable string" style="color:#e3116c">"</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable operator" style="color:#393A34">|</span><span class="token variable" style="color:#36acaa"> jq </span><span class="token variable parameter variable" style="color:#36acaa">-r</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'.tool_input.new_string // .tool_input.content // empty'</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 抑制パターンの検出</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$NEW_CONTENT</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">grep</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-qE</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'(eslint-disable|@ts-ignore|# type: ignore|# noqa)'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ERROR: Suppression comment is not allowed. Fix the root cause instead."</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token file-descriptor important">&amp;2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin class-name">exit</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># PostToolUse の exit 2 は Claude に feedback として返る</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fi</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">exit</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><br></div></code></pre></div></div><p>settings.json への登録例 (<code>matcher</code> フィールドで発火対象のツール名を正規表現で絞れます):</p><div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"hooks"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"PostToolUse"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"matcher"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Edit|Write"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"hooks"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"command"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"command"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"~/.claude/hooks/check-suppression.sh"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div><p>ポイントは <strong>「rules で『any 禁止』と書くだけでは LLM は確率的に違反する」</strong> という前提です。重要規範ほど deterministic な hook で機械的に止める、というのが harness engineering の中核哲学のひとつになります。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="補助ディレクトリ-teams--references--scripts">補助ディレクトリ (teams / references / scripts)<a href="https://notes.rewheel.dev/blog/harness-engineering#%E8%A3%9C%E5%8A%A9%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA-teams--references--scripts" class="hash-link" aria-label="補助ディレクトリ (teams / references / scripts) への直接リンク" title="補助ディレクトリ (teams / references / scripts) への直接リンク" translate="no">​</a></h2>
<p>ここからは Claude Code 標準機能ではない、自分の運用上の慣習として切り出したディレクトリです。読者がそのまま真似する必要はありませんが、なぜこういう箱が欲しくなるのかを共有しておきます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="teams-multi-agent-orchestration">teams/: Multi-agent Orchestration<a href="https://notes.rewheel.dev/blog/harness-engineering#teams-multi-agent-orchestration" class="hash-link" aria-label="teams/: Multi-agent Orchestration への直接リンク" title="teams/: Multi-agent Orchestration への直接リンク" translate="no">​</a></h3>
<p>複数の subagent を協調動作させたいケースがあります。たとえば「10 ファイル以上の変更を、code-reviewer (品質) + architect (設計) + test-strategist (テスト) の 3 視点で並列レビューしたい」など。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p><code>~/.claude/teams/multi-perspective-review/team.md</code> に、main agent が 3 subagent を並列 spawn して結果を統合する手順を Markdown で記述しています。発話に「複数視点」「総合レビュー」「3 視点」が含まれたら main agent が team.md を Read して実行する、という運用です。</p><p>これは Claude Code 公式機能ではなく、subagent 機能の上に乗せた meta orchestration (複数 agent を束ねる統制) パターンです。</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="references-詳細プロトコル-progressive-disclosure">references/: 詳細プロトコル (progressive disclosure)<a href="https://notes.rewheel.dev/blog/harness-engineering#references-%E8%A9%B3%E7%B4%B0%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB-progressive-disclosure" class="hash-link" aria-label="references/: 詳細プロトコル (progressive disclosure) への直接リンク" title="references/: 詳細プロトコル (progressive disclosure) への直接リンク" translate="no">​</a></h3>
<p>rules/ に詳細な手順 / テンプレート / 適用例まで書き込むと、auto-load されるたびに context を圧迫します。そこで rules には <strong>規範ルール (要約)</strong> だけ書き、詳細プロトコルは別の場所に置いて、必要時のみ Read する、という形で分離します (これを <strong>progressive disclosure</strong> [段階的開示] と呼びます)。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p><code>~/.claude/references/</code> に 25 ファイルの詳細プロトコルを置いています。rules 側から「詳細プロトコル: <code>~/.claude/references/xxx.md</code> (必要時 Read)」と参照する形です。これで context window を節約しつつ、深掘りしたいときは詳細にアクセスできます。</p><p>これも公式機能ではなく、命名も任意です。<code>docs/</code> でも <code>manuals/</code> でも構いません。</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scripts-計測検証">scripts/: 計測・検証<a href="https://notes.rewheel.dev/blog/harness-engineering#scripts-%E8%A8%88%E6%B8%AC%E6%A4%9C%E8%A8%BC" class="hash-link" aria-label="scripts/: 計測・検証 への直接リンク" title="scripts/: 計測・検証 への直接リンク" translate="no">​</a></h3>
<p>harness 自体の整合性を機械的にチェックする shell スクリプト置き場です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p><code>~/.claude/scripts/</code> に内部整合性チェック (<code>check-internal-consistency.sh</code>) と self-test (<code>self-test.sh</code>) を置き、月次 cron (定期実行ジョブ) で実行しています。「rules 内のクロスリンクが壊れていないか」「skill の発話トリガーが衝突していないか」など、harness そのものの drift を検出する Computational Sensor です。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="phase-0-6-段階的導入パス">Phase 0-6 段階的導入パス<a href="https://notes.rewheel.dev/blog/harness-engineering#phase-0-6-%E6%AE%B5%E9%9A%8E%E7%9A%84%E5%B0%8E%E5%85%A5%E3%83%91%E3%82%B9" class="hash-link" aria-label="Phase 0-6 段階的導入パス への直接リンク" title="Phase 0-6 段階的導入パス への直接リンク" translate="no">​</a></h2>
<p>ここまで個別のディレクトリを紹介してきましたが、これらを <strong>どういう順番で導入していくか</strong> が次の論点になります。「29 個の rules / 23 個の skills / 7 個の agents / 2 個の hooks」を一気に書こうとすると、context window が溢れ、規範が形骸化し、違反が量産されて基準そのものが崩壊します。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Phase 番号は自分で付けた整理軸です</div><div class="admonitionContent_BuS1"><p>以下の Phase 0-6 という段階分けは僕が自分の harness を整理するために便宜的に付けたラベルで、公式・業界標準の区分ではありません。ただし、各 Phase で扱う <strong>個別の概念</strong> (Computational Guards / Architecture Fitness / Generator-Evaluator 分離 / Spec-First / Ratchet / Metrics) は僕のオリジナルではありません。Hashimoto / Anthropic / Neal Ford / Birgitta Böckeler などの公式・業界由来です。</p></div></div>
<table><thead><tr><th>Phase</th><th>名称</th><th>主な成果物</th><th>概念の由来</th></tr></thead><tbody><tr><td>0</td><td>概念整理</td><td>harness 規範 rule の文書化</td><td>Hashimoto / Lopopolo / Böckeler</td></tr><tr><td>1</td><td>Computational Guards</td><td>hook (suppression 検出 / protected files) + pre-commit</td><td>Anthropic / 業界共通</td></tr><tr><td>2</td><td>Architecture Fitness</td><td>dependency-cruiser / Conftest / tflint で構造制約検証</td><td>Neal Ford (Building Evolutionary Architectures)</td></tr><tr><td>3</td><td>Generator-Evaluator 分離</td><td>subagent (implementer / code-reviewer / architect ...)</td><td>Anthropic</td></tr><tr><td>4</td><td>Spec-First Pipeline</td><td>CONTEXT.md (用語) / docs/adr/ (決定) / docs/specs/ (仕様)</td><td>Hashimoto</td></tr><tr><td>5</td><td>Ratchet Workflow</td><td>失敗 → 永続修正への昇格手順</td><td>Hashimoto の原則 + 自分の運用整理</td></tr><tr><td>6</td><td>Metrics &amp; Health Check</td><td>4 Quality metrics の月次集計スクリプト</td><td>Hashimoto / Lopopolo</td></tr></tbody></table>
<p>進化の流れを図にすると以下です。</p>
<!-- -->
<p>各 Phase の狙いは次のとおりです。</p>
<ul>
<li class=""><strong>Phase 0</strong>: まず harness とは何かを文書化する。<code>rules/harness-engineering.md</code> のような規範文書を書き、その後の Phase で何を強制したいかの <strong>語彙</strong> を揃える</li>
<li class=""><strong>Phase 1</strong>: rule (Inferential Guide) で書いた規範のうち、機械化できるものを hook / lint / type-check で deterministic に止める。LLM の probabilistic compliance を補完する最初の砦</li>
<li class=""><strong>Phase 2</strong>: 単一ファイルではなくファイル間関係 (循環依存 / レイヤ違反 / <strong>cross-feature 依存</strong> [feature 間の不要な依存関係]) を CI で検出する。具体的には <strong>dependency-cruiser</strong> (JS/TS の依存方向検証) / <strong>Conftest</strong> (OPA Rego ベースの policy 検証) / <strong>tflint</strong> (Terraform linter) などのツールを使う。Neal Ford の Architecture Fitness Functions が出典</li>
<li class=""><strong>Phase 3</strong>: 実装役と評価役を別 subagent に分ける。Generator (implementer) と Evaluator (code-reviewer / architect / test-strategist) を意図的に分離し、self-evaluation bias を排除する</li>
<li class=""><strong>Phase 4</strong>: 用語定義 (CONTEXT.md) / 重要設計決定 (ADR) / 機能仕様 (docs/specs) を harness の必須入力にする。Spec を読まずに実装させない</li>
<li class=""><strong>Phase 5</strong>: 失敗パターンを rule / hook / fitness function に永続化して再発を構造的に不可能にする (Hashimoto の原則がベース。詳細は後述)。僕は中間置き場として <code>learnings/</code> を使い、繰り返した失敗だけを昇格させる運用</li>
<li class=""><strong>Phase 6</strong>: harness 自体の信頼性を 4 Quality metrics (後述) で計測し、月次 cron で health check する</li>
</ul>
<p>要点は「いきなり Phase 6 から始めない」ことです。Phase 0 と Phase 1 だけでも、ほとんどのプロジェクトで効果が出ます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ratchet-workflow-失敗を永続修正に変える">Ratchet Workflow: 失敗を永続修正に変える<a href="https://notes.rewheel.dev/blog/harness-engineering#ratchet-workflow-%E5%A4%B1%E6%95%97%E3%82%92%E6%B0%B8%E7%B6%9A%E4%BF%AE%E6%AD%A3%E3%81%AB%E5%A4%89%E3%81%88%E3%82%8B" class="hash-link" aria-label="Ratchet Workflow: 失敗を永続修正に変える への直接リンク" title="Ratchet Workflow: 失敗を永続修正に変える への直接リンク" translate="no">​</a></h2>
<p>Phase 5 で登場した <strong>Ratchet</strong> の考え方は、harness engineering の中でも特に「これだけ覚えて帰っていい」レベルの中核原則なので、独立した節として取り上げます。土台にあるのは Mitchell Hashimoto がブログで示した原則で、原文に沿って要約すると次のとおりです。</p>
<blockquote>
<p>agent がミスをしたと気付いたら、その都度、同じミスが二度と起きないような解決を engineering する。</p>
</blockquote>
<p>僕はこの原則を <strong>Ratchet (ラチェット)</strong> と呼び、「同じ失敗が <strong>2 度</strong> 観測されたら、必ず rules / hooks / fitness function / learnings のいずれかに <strong>永続的修正</strong> を加え、再発を <strong>構造的に不可能</strong> にする」という形で運用に落とし込んでいます。「ratchet」という呼び名、「2 度」という閾値、修正先の 4 分類は、いずれも Hashimoto の記事そのものにはない僕自身の運用整理です。</p>
<p>ratchet (ラチェット) は工具用語で「一方向にしか回らない歯車」を指します。一度進んだら戻らない、という性質を harness の学習ループに重ねた比喩です。「もう気をつけます」「次から注意します」では同じ失敗を必ず繰り返すので、<strong>気をつけなくても発生しない仕組み</strong> に組み込む、というのが要点です。</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>自分の運用例</div><div class="admonitionContent_BuS1"><p>僕は <code>/learn</code> skill で会話中に得た知見をその場で <code>~/.claude/learnings/&lt;category&gt;/&lt;topic&gt;.md</code> に追記し、セッション終了時には <code>/record-learnings</code> skill で一括抽出しています。さらに、同じ失敗が複数回観測されたら learnings から rule や hook に昇格させます。たとえば「型違反を見落としがち」という learnings が積み重なれば、<code>rules/typescript-strict.md</code> に項目を足したり、TypeScript 設定を strict 寄りに更新したりします。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-つの規範ルール">8 つの規範ルール<a href="https://notes.rewheel.dev/blog/harness-engineering#8-%E3%81%A4%E3%81%AE%E8%A6%8F%E7%AF%84%E3%83%AB%E3%83%BC%E3%83%AB" class="hash-link" aria-label="8 つの規範ルール への直接リンク" title="8 つの規範ルール への直接リンク" translate="no">​</a></h2>
<p>ハーネスエンジニアリングの実践で守りたい主な規範を 8 つにまとめます。出典は公式 / 業界共通のものと、自分の運用方針が混在しています。</p>
<ol>
<li class=""><strong>Anything not in context doesn't exist</strong> (Hashimoto / Lopopolo): agent に伝えたい情報は repository-local かつ versioned な artifact に置く。Slack や口頭、個人の頭の中は agent から見えない</li>
<li class=""><strong>Probabilistic compliance を信用しない</strong> (業界共通): rule で「any 禁止」と書いても LLM は確率的に違反する。重要規範は computational sensor (lint / hook / CI) で deterministic に強制する</li>
<li class=""><strong>Generator と Evaluator は分離する</strong> (Anthropic): 実装した agent に自己評価させない。別の subagent でレビューする</li>
<li class=""><strong>Inline-disable suppression を許さない</strong> (業界共通): <code>// eslint-disable-next-line</code> / <code>@ts-ignore</code> で sensor を抑制するのは harness を空洞化させる最大の anti-pattern。修正で対応する</li>
<li class=""><strong>Ratchet principle</strong> (Hashimoto の原則に基づく自分の運用整理): 同じ失敗を 2 度起こしたら永続的修正を加える。再発を構造的に不可能にする</li>
<li class=""><strong>Volume metrics を主指標にしない</strong> (Hashimoto / Lopopolo): 「AI が書いた行数」「採用された提案数」は harness 信頼性を反映しない。後述の Quality metrics を使う</li>
<li class=""><strong>Context Resets &gt; Compaction</strong> (Anthropic / Hashimoto): 長期タスクを compaction で延命させず、構造化ハンドオフで完全リセットして新規 agent を起こす</li>
<li class=""><strong>Spec drift を sensor で検出する</strong> (Hashimoto): CONTEXT.md / ADR / docs/specs と実装の乖離は下流テストでは捕捉できない。Evaluator agent が仕様準拠で判定する</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アンチパターン">アンチパターン<a href="https://notes.rewheel.dev/blog/harness-engineering#%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3" class="hash-link" aria-label="アンチパターン への直接リンク" title="アンチパターン への直接リンク" translate="no">​</a></h2>
<p>harness を組むときによく起きる失敗パターンも併せて挙げておきます。</p>
<ul>
<li class=""><strong>One-shot completion</strong>: 複雑タスクを一発完了させようとする。途中で hand-off (別 agent / 次セッションへの引き継ぎ) / 反復を入れない</li>
<li class=""><strong>Rules files alone</strong>: rule だけ書いて computational sensor を併設しない。Inferential Guide のみだと違反は確率的に発生する</li>
<li class=""><strong>Over-constraining</strong>: 厳しすぎる制約で legitimate な refactoring まで阻害する。「全部禁止」は誰も従わなくなる</li>
<li class=""><strong>完了の早期宣言</strong>: verification なしで「完了」と言う。テスト・型・lint の確認なし</li>
<li class=""><strong>部分実装の放置</strong>: 途中状態のままハンドオフ。次セッションが拾えない</li>
<li class=""><strong>Self-evaluation only</strong>: Generator agent に自己評価させる。code-reviewer / architect など別 agent を介さない</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-quality-metrics">4 Quality metrics<a href="https://notes.rewheel.dev/blog/harness-engineering#4-quality-metrics" class="hash-link" aria-label="4 Quality metrics への直接リンク" title="4 Quality metrics への直接リンク" translate="no">​</a></h2>
<p>最後に、harness 自体の品質を測る指標を整理します。コードの「行数」「採用率」のような Volume metrics ではなく、信頼性に直結する Quality metrics を主指標にすべき、というのが Hashimoto / Lopopolo の主張です。</p>
<table><thead><tr><th>指標</th><th>内容</th><th>なぜ大事か</th></tr></thead><tbody><tr><td>Task Resolution Rate</td><td>自動テストで検証された正解率</td><td>エージェントが「自称完了」したものの実際の合格率</td></tr><tr><td>Code Churn Rate</td><td>2 週間以内に破棄されたコードの割合</td><td>短期で書き直された量。harness が安定設計を導けたかの指標</td></tr><tr><td>Verification Tax</td><td>AI 生成コード監査に費やした時間</td><td>人間のレビュー負荷。これが下がるほど harness が信頼できる</td></tr><tr><td>Defect Escape Rate</td><td>本番に到達した欠陥率</td><td>最終的なユーザー影響。harness 全体の総合スコア</td></tr></tbody></table>
<p>Volume metrics との対比は次のとおりです。</p>
<ul>
<li class="">Volume metrics は「使用量」を測る (e.g. AI が書いた行数、受諾された提案数): エージェントがどれだけ働いたか</li>
<li class="">Quality metrics は「信頼性」を測る: エージェントがどれだけ役に立ったか</li>
</ul>
<p>harness の良し悪しは Quality 側で評価しないと、「たくさん書いたけど 2 週間後に全部書き直し」が高得点になってしまいます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめの一歩-実践指南">はじめの一歩 (実践指南)<a href="https://notes.rewheel.dev/blog/harness-engineering#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AE%E4%B8%80%E6%AD%A9-%E5%AE%9F%E8%B7%B5%E6%8C%87%E5%8D%97" class="hash-link" aria-label="はじめの一歩 (実践指南) への直接リンク" title="はじめの一歩 (実践指南) への直接リンク" translate="no">​</a></h2>
<p>ここまで読んで「全部一気にやるのは無理だ」と感じたら、それで正解です。ハーネスエンジニアリングは段階的に育てるものです。読者が今日から始められる最小ステップを 3 つ挙げておきます。</p>
<ol>
<li class=""><strong>CLAUDE.md を 1 ファイル書く</strong>: プロジェクトルートか <code>~/.claude/CLAUDE.md</code> に「このプロジェクトで守ってほしいこと」を Markdown で 5-10 行。「型は any 禁止」「コメントは日本語」「テストを書いてから実装」など、自分が普段口頭で伝えていることを文字に起こすだけで十分です</li>
<li class=""><strong>失敗したら learnings に記録</strong>: 同じミスが 2 回出たら、CLAUDE.md か別の rule ファイルに永続化する (Ratchet Workflow)。「気をつける」ではなく「次から発生しない仕組み」に倒す</li>
<li class=""><strong>重要なものは Computational Sensor で deterministic 化</strong>: 型安全 (TypeScript strict) や lint (ESLint) は LLM の意思にかかわらず違反を弾けます。一番効くのは pre-commit hook (Git コミット前に走るチェック) と CI</li>
</ol>
<p>「いきなり 29 個の rules / 23 個の skills を書かない」が最大の落とし穴回避策です。1 ファイルから始めて、必要が出るたびに増やすほうが、長期的には強い harness になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="出典参考リンク">出典・参考リンク<a href="https://notes.rewheel.dev/blog/harness-engineering#%E5%87%BA%E5%85%B8%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF" class="hash-link" aria-label="出典・参考リンク への直接リンク" title="出典・参考リンク への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="用語提唱中核概念">用語提唱・中核概念<a href="https://notes.rewheel.dev/blog/harness-engineering#%E7%94%A8%E8%AA%9E%E6%8F%90%E5%94%B1%E4%B8%AD%E6%A0%B8%E6%A6%82%E5%BF%B5" class="hash-link" aria-label="用語提唱・中核概念 への直接リンク" title="用語提唱・中核概念 への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://mitchellh.com/writing/my-ai-adoption-journey" target="_blank" rel="noopener noreferrer" class="">Mitchell Hashimoto, "My AI Adoption Journey"</a>: harness engineering の用語提唱 + Ratchet の土台となる「ミスを二度と起きなくする」原則 (2026 年 2 月)</li>
<li class=""><a href="https://openai.com/index/harness-engineering/" target="_blank" rel="noopener noreferrer" class="">Ryan Lopopolo (OpenAI), "Harness engineering: leveraging Codex in an agent-first world"</a>: 100 万行 / 1500 自動 PR のフィールドレポート</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="anthropic-公式-long-running-agents">Anthropic 公式 (Long-Running Agents)<a href="https://notes.rewheel.dev/blog/harness-engineering#anthropic-%E5%85%AC%E5%BC%8F-long-running-agents" class="hash-link" aria-label="Anthropic 公式 (Long-Running Agents) への直接リンク" title="Anthropic 公式 (Long-Running Agents) への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents" target="_blank" rel="noopener noreferrer" class="">Anthropic, "Effective harnesses for long-running agents"</a>: Context Resets / initializer + coding agent パターン</li>
<li class=""><a href="https://www.anthropic.com/engineering/harness-design-long-running-apps" target="_blank" rel="noopener noreferrer" class="">Anthropic, "Harness design for long-running application development"</a>: planner / generator / evaluator の 3 層アーキテクチャ</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="二分法フレームワーク">二分法フレームワーク<a href="https://notes.rewheel.dev/blog/harness-engineering#%E4%BA%8C%E5%88%86%E6%B3%95%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF" class="hash-link" aria-label="二分法フレームワーク への直接リンク" title="二分法フレームワーク への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://martinfowler.com/articles/harness-engineering.html" target="_blank" rel="noopener noreferrer" class="">Birgitta Böckeler, "Harness engineering for coding agent users"</a>: Guides + Sensors / Computational + Inferential の十字分類の詳細</li>
<li class=""><a href="https://martinfowler.com/articles/exploring-gen-ai/harness-engineering-memo.html" target="_blank" rel="noopener noreferrer" class="">Birgitta Böckeler, "Harness Engineering - first thoughts"</a>: Hashimoto への注釈と概念整理（martinfowler.com 掲載）</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="architecture-fitness-functions">Architecture Fitness Functions<a href="https://notes.rewheel.dev/blog/harness-engineering#architecture-fitness-functions" class="hash-link" aria-label="Architecture Fitness Functions への直接リンク" title="Architecture Fitness Functions への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://www.oreilly.com/library/view/building-evolutionary-architectures/9781492097538/" target="_blank" rel="noopener noreferrer" class="">Building Evolutionary Architectures (O'Reilly)</a>: Neal Ford / Rebecca Parsons / Patrick Kua (Fitness Functions の元ネタ)</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="claude-code-公式-docs">Claude Code 公式 docs<a href="https://notes.rewheel.dev/blog/harness-engineering#claude-code-%E5%85%AC%E5%BC%8F-docs" class="hash-link" aria-label="Claude Code 公式 docs への直接リンク" title="Claude Code 公式 docs への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://code.claude.com/" target="_blank" rel="noopener noreferrer" class="">Claude Code 公式 docs</a>: Memory / Skill / Subagent / Hook の公式仕様</li>
<li class=""><a href="https://code.claude.com/docs/en/memory" target="_blank" rel="noopener noreferrer" class="">How Claude remembers your project (Memory)</a>: auto-memory の公式仕様 (v2.1.59 以降デフォルト有効)</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ツール公式">ツール公式<a href="https://notes.rewheel.dev/blog/harness-engineering#%E3%83%84%E3%83%BC%E3%83%AB%E5%85%AC%E5%BC%8F" class="hash-link" aria-label="ツール公式 への直接リンク" title="ツール公式 への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/sverweij/dependency-cruiser" target="_blank" rel="noopener noreferrer" class="">dependency-cruiser</a>: JS/TS の依存方向検証</li>
<li class=""><a href="https://www.conftest.dev/" target="_blank" rel="noopener noreferrer" class="">Conftest</a>: OPA Rego ベースの policy 検証</li>
<li class=""><a href="https://github.com/terraform-linters/tflint" target="_blank" rel="noopener noreferrer" class="">tflint</a>: Terraform linter</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="語源">語源<a href="https://notes.rewheel.dev/blog/harness-engineering#%E8%AA%9E%E6%BA%90" class="hash-link" aria-label="語源 への直接リンク" title="語源 への直接リンク" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://www.etymonline.com/word/harness" target="_blank" rel="noopener noreferrer" class="">Harness: Etymology, Origin &amp; Meaning (etymonline)</a>: 古フランス語 <em>harnois</em> 起源</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://notes.rewheel.dev/blog/harness-engineering#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>最後にもう一度、中核等式を確認します。</p>
<blockquote>
<p><strong>Agent = Model + Harness</strong></p>
</blockquote>
<p>モデルを最新版に上げても精度が頭打ちなら、harness を疑う。harness は Guides (feedforward) と Sensors (feedback) の 2 軸、それぞれを Computational / Inferential で十字分類して設計する。一気に全部入れず、Phase 0 (概念整理) と Phase 1 (Computational Guards) から始める。失敗は気をつけるのではなく Ratchet で構造的に不可能にする。</p>
<p>本記事を読み終えたあなたが「自分のプロジェクトでも、まず CLAUDE.md を 1 ファイル書いてみよう」と感じたなら、この記事は十分に役目を果たしたと思います。</p>]]></content:encoded>
            <category>ai-agent</category>
            <category>claude-code</category>
            <category>harness-engineering</category>
        </item>
        <item>
            <title><![CDATA[Welcome to Reinvent Notes]]></title>
            <link>https://notes.rewheel.dev/blog/welcome</link>
            <guid>https://notes.rewheel.dev/blog/welcome</guid>
            <pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Reinvent Notes ブログ開設のお知らせ。Docusaurus + Vercel で個人的な技術発信を続けていきます。]]></description>
            <content:encoded><![CDATA[<p>このブログを始めました。Docusaurus + Vercel で個人的な技術発信を続けていきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="このサイトの方向性">このサイトの方向性<a href="https://notes.rewheel.dev/blog/welcome#%E3%81%93%E3%81%AE%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AE%E6%96%B9%E5%90%91%E6%80%A7" class="hash-link" aria-label="このサイトの方向性 への直接リンク" title="このサイトの方向性 への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>ブログ (このページ)</strong>: 学んだこと・調査メモを時系列で発信</li>
<li class=""><strong><a class="" href="https://notes.rewheel.dev/docs/intro">Docs</a></strong>: 体系的にまとめたリファレンスやガイドを蓄積</li>
<li class=""><strong>スコープ</strong>: フロントエンド・バックエンド・インフラ・AI・学習ログ等</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-docusaurus-か">なぜ Docusaurus か<a href="https://notes.rewheel.dev/blog/welcome#%E3%81%AA%E3%81%9C-docusaurus-%E3%81%8B" class="hash-link" aria-label="なぜ Docusaurus か への直接リンク" title="なぜ Docusaurus か への直接リンク" translate="no">​</a></h2>
<p>技術ノートを発信するうえで、ブログとドキュメントを 1 つのサイトに併設できるのが Docusaurus の強みです。
Markdown で書けて、タグ・RSS が標準で備わっているのも嬉しいところ (検索もプラグインで簡単に追加できます)。</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Sample code block</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> greeting</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Hello, Reinvent Notes!'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">greeting</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="数式テスト">数式テスト<a href="https://notes.rewheel.dev/blog/welcome#%E6%95%B0%E5%BC%8F%E3%83%86%E3%82%B9%E3%83%88" class="hash-link" aria-label="数式テスト への直接リンク" title="数式テスト への直接リンク" translate="no">​</a></h2>
<p>インラインで <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E = mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0576em">E</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8141em"></span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span>、ブロックで:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msubsup><mo>∫</mo><mn>0</mn><mi mathvariant="normal">∞</mi></msubsup><msup><mi>e</mi><mrow><mo>−</mo><msup><mi>x</mi><mn>2</mn></msup></mrow></msup><mi>d</mi><mi>x</mi><mo>=</mo><mfrac><msqrt><mi>π</mi></msqrt><mn>2</mn></mfrac></mrow><annotation encoding="application/x-tex">\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.3262em;vertical-align:-0.9119em"></span><span class="mop"><span class="mop op-symbol large-op" style="margin-right:0.4445em;position:relative;top:-0.0011em">∫</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4143em"><span style="top:-1.7881em;margin-left:-0.4445em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.8129em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">∞</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9119em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0369em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em"><span style="top:-2.931em;margin-right:0.0714em"><span class="pstrut" style="height:2.5em"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span class="mord mathnormal">d</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.1633em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4773em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">2</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8003em"><span class="svg-align" style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord" style="padding-left:0.833em"><span class="mord mathnormal" style="margin-right:0.0359em">π</span></span></span><span style="top:-2.7603em"><span class="pstrut" style="height:3em"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2397em"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="図表テスト">図表テスト<a href="https://notes.rewheel.dev/blog/welcome#%E5%9B%B3%E8%A1%A8%E3%83%86%E3%82%B9%E3%83%88" class="hash-link" aria-label="図表テスト への直接リンク" title="図表テスト への直接リンク" translate="no">​</a></h2>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="これから書きたいこと">これから書きたいこと<a href="https://notes.rewheel.dev/blog/welcome#%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E6%9B%B8%E3%81%8D%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8" class="hash-link" aria-label="これから書きたいこと への直接リンク" title="これから書きたいこと への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">開発環境のセットアップ手順</li>
<li class="">AWS/Terraform の運用 Tips</li>
<li class="">AI/LLM (Claude) を活用した開発フロー</li>
<li class="">読書メモやカンファレンス参加記</li>
</ul>
<p>よろしくお願いします。</p>]]></content:encoded>
            <category>meta</category>
        </item>
    </channel>
</rss>