<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>yuheijotaki.com</title><description>主にウェブデザインや技術についてのブログサイトです。</description><link>https://yuheijotaki.com/</link><language>ja</language><item><title>Claude Code Action で PR を自動レビューしてみる</title><link>https://yuheijotaki.com/blog/2026051901_claude-code-action-pr-review/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2026051901_claude-code-action-pr-review/</guid><description>このリポジトリに Claude Code Action を導入して、@claude メンション応答と PR 自動レビューを設定した記事です。</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;このリポジトリに &lt;a href=&quot;https://github.com/anthropics/claude-code-action&quot;&gt;anthropics/claude-code-action&lt;/a&gt; を導入して、PR の自動レビューと &lt;code&gt;@claude&lt;/code&gt; メンションへの応答が動くようにしてみる。&lt;/p&gt;
&lt;h2&gt;2 つのワークフローの役割&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.github/workflows/&lt;/code&gt; に 2 つのワークフローを置く。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/blob/main/.github/workflows/claude.yml&quot;&gt;&lt;code&gt;claude.yml&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Issue / PR / レビューコメントの本文に &lt;code&gt;@claude&lt;/code&gt; を含む投稿があったとき&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/blob/main/.github/workflows/claude-code-review.yml&quot;&gt;&lt;code&gt;claude-code-review.yml&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;PR が &lt;code&gt;opened&lt;/code&gt; / &lt;code&gt;synchronize&lt;/code&gt; されたとき&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;paths フィルタで対象を絞る&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pull_request&lt;/code&gt; の &lt;code&gt;paths&lt;/code&gt; で、コードとビルド系の変更だけに絞る。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;paths:
  - &apos;src/**&apos;
  - &apos;api/**&apos;
  - &apos;astro.config.mjs&apos;
  - &apos;package.json&apos;
  - &apos;package-lock.json&apos;
  - &apos;tsconfig.json&apos;
  - &apos;eslint.config.mjs&apos;
  - &apos;playwright.config.ts&apos;
  - &apos;.github/workflows/**&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;動作確認&lt;/h2&gt;
&lt;p&gt;それぞれテスト用に Issue / PR を作って動かしてみた。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@claude&lt;/code&gt; メンション:
&lt;ul&gt;
&lt;li&gt;本文に &lt;code&gt;@claude&lt;/code&gt; を含む &lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/issues/7&quot;&gt;Issue #7&lt;/a&gt; を立てると &lt;code&gt;claude.yml&lt;/code&gt; がトリガーされ、コメントが返ってきた&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PR 自動レビュー:
&lt;ul&gt;
&lt;li&gt;コメント 1 行を追加した &lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/pull/8&quot;&gt;PR #8&lt;/a&gt; を作って、&lt;code&gt;claude-code-review.yml&lt;/code&gt; が走り PR コメントで指摘が返ってきた&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;実行コスト&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;claude-code-action&lt;/code&gt; はジョブの末尾に実行結果を JSON 形式で出力する。動作確認用 PR（&lt;code&gt;src/pages/index.astro&lt;/code&gt; にコメント 1 行追加だけ）でのレビュー 1 回の実測値を、デフォルトの Opus 4.7 と、&lt;code&gt;claude_args&lt;/code&gt; に &lt;code&gt;--model claude-sonnet-4-6&lt;/code&gt; を渡して指定した Sonnet 4.6 で比較した。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;Opus 4.7&lt;/th&gt;
&lt;th&gt;Sonnet 4.6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Duration&lt;/td&gt;
&lt;td&gt;約 76 秒&lt;/td&gt;
&lt;td&gt;約 59 秒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Turns&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total cost&lt;/td&gt;
&lt;td&gt;$0.37&lt;/td&gt;
&lt;td&gt;$0.17&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Anthropic API は入力トークン / 出力トークン / プロンプトキャッシュの読み書きそれぞれに単価が設定されていて、消費したトークン量 × モデルの単価で課金される（&lt;a href=&quot;https://platform.claude.com/docs/ja/about-claude/pricing&quot;&gt;料金 - Claude API Docs&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;claude-code-action&lt;/code&gt; は 1 回の起動の中で複数回 API リクエストを走らせる（Opus 4.7 は 13 turns、Sonnet 4.6 は 11 turns）ので、&lt;code&gt;total_cost_usd&lt;/code&gt; はそれらの合計値。同じく 1 行追加だけの PR でも、Sonnet 4.6 に切り替えるとレビュー 1 回あたり半分以下のコストに収まった。&lt;/p&gt;
&lt;h2&gt;所感&lt;/h2&gt;
&lt;p&gt;CLAUDE.md にコーディング規約・コミット規約を書いておけば、レビューでもその文脈を踏まえた指摘が返ってくるのは便利だがコストとの兼ね合いかな。&lt;/p&gt;
</content:encoded></item><item><title>Astro Fonts API を試してみる</title><link>https://yuheijotaki.com/blog/2026051501_astro-fonts/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2026051501_astro-fonts/</guid><description>Astro の Fonts API について、Google プロバイダーで Noto Sans JP を読み込んだときに何が起きるかと、日本語フォントのサブセット周りを試した記事です。</description><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Astro Fonts API とは&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;astro.config.mjs&lt;/code&gt; の &lt;code&gt;fonts&lt;/code&gt; 配列でフォントを宣言し、&lt;code&gt;&amp;lt;Font /&amp;gt;&lt;/code&gt; コンポーネントを &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; に置くと &lt;code&gt;@font-face&lt;/code&gt; と preload link が出力される。&lt;/p&gt;
&lt;p&gt;取得したフォントはローカル化されて、fallback フォントもメトリクス込みで自動生成される。組み込みプロバイダーは &lt;code&gt;adobe&lt;/code&gt; / &lt;code&gt;bunny&lt;/code&gt; / &lt;code&gt;fontshare&lt;/code&gt; / &lt;code&gt;fontsource&lt;/code&gt; / &lt;code&gt;google&lt;/code&gt; / &lt;code&gt;googleicons&lt;/code&gt; / &lt;code&gt;local&lt;/code&gt; / &lt;code&gt;npm&lt;/code&gt; の 8 種。&lt;/p&gt;
&lt;p&gt;今回は Google プロバイダーで Noto Sans JP を読み込んで、生成される &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; と日本語フォントのサブセットがどう扱われるかを見ていく。動作確認は &lt;code&gt;astro@6.3.3&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;設定&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig, fontProviders } from &apos;astro/config&apos;;

export default defineConfig({
  fonts: [
    {
      provider: fontProviders.google(),
      name: &apos;Noto Sans JP&apos;,
      cssVariable: &apos;--font-noto-sans-jp&apos;,
      weights: [400, 700],
      subsets: [&apos;japanese&apos;],
    },
  ],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 側：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import { Font } from &apos;astro:assets&apos;;
---

&amp;lt;Font cssVariable=&quot;--font-noto-sans-jp&quot; preload /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;生成された出力&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt; 後の &lt;code&gt;dist/client/_astro/fonts/&lt;/code&gt; を見ると、woff2 が &lt;strong&gt;120 ファイル / 合計 5.2 MB&lt;/strong&gt;。&lt;code&gt;weights&lt;/code&gt; 2 種 × &lt;code&gt;unicode-range&lt;/code&gt; で 60 分割されている計算で各ファイルは 12〜88KB。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; には &lt;code&gt;unicode-range&lt;/code&gt; 付きの &lt;code&gt;@font-face&lt;/code&gt; が並ぶ。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;style&amp;gt;
  @font-face {
    font-family: &apos;Noto Sans JP-86c17494f12e7c6c&apos;;
    src: url(&apos;/_astro/fonts/5bcf9a2144f25cf1.woff2&apos;) format(&apos;woff2&apos;);
    font-display: swap;
    unicode-range: U+25ee8, U+25f23, U+25f5c, U+25fd4, /* ... 多数 ... */;
    font-weight: 400;
    font-style: normal;
  }
  /* ...同じ要領で 120 個続く... */
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;font-family 名にハッシュのサフィックスが付いて、同じファミリー名が複数の &lt;code&gt;&amp;lt;Font /&amp;gt;&lt;/code&gt; 設定で衝突しないようになっている。&lt;/p&gt;
&lt;p&gt;fallback も自動で出力される。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@font-face {
  font-family: &apos;Noto Sans JP-86c17494f12e7c6c fallback: Arial&apos;;
  src: local(&apos;Arial&apos;);
  font-display: swap;
  size-adjust: 197.1733%;
  ascent-override: 58.8315%;
  descent-override: 14.6064%;
  line-gap-override: 0%;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;src: local(&apos;Arial&apos;)&lt;/code&gt; でローカルにある Arial を使い、&lt;code&gt;size-adjust&lt;/code&gt; と &lt;code&gt;ascent-override&lt;/code&gt; 等でメトリクスを Noto Sans JP に合わせて、ロード前後の見た目のズレを抑えてくれる。&lt;/p&gt;
&lt;p&gt;preload link は &lt;code&gt;&amp;lt;Font preload /&amp;gt;&lt;/code&gt; のままだと &lt;strong&gt;120 個ぜんぶ&lt;/strong&gt; に &lt;code&gt;&amp;lt;link rel=&quot;preload&quot;&amp;gt;&lt;/code&gt; が並ぶ。日本語のように分割数が多いフォントではここを絞らないと過剰。&lt;/p&gt;
&lt;h2&gt;内部の挙動&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;preload&lt;/code&gt; プロパティは boolean だけでなく filter を渡せる。&lt;a href=&quot;https://github.com/withastro/astro/blob/main/packages/astro/src/assets/fonts/core/filter-preloads.ts&quot;&gt;&lt;code&gt;filter-preloads.ts&lt;/code&gt;&lt;/a&gt; を読むと、&lt;code&gt;weight&lt;/code&gt; / &lt;code&gt;style&lt;/code&gt; / &lt;code&gt;subset&lt;/code&gt; で絞り込めるのが分かる。&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;コードを開閉する&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function filterPreloads(
  data: Array&amp;lt;PreloadData&amp;gt;,
  preload: PreloadFilter,
): Array&amp;lt;PreloadData&amp;gt; | null {
  if (!preload) {
    return null;
  }
  if (preload === true) {
    return data;
  }
  return data.filter(({ weight, style, subset }) =&amp;gt;
    preload.some((p) =&amp;gt; {
      if (
        p.weight !== undefined &amp;amp;&amp;amp;
        weight !== undefined &amp;amp;&amp;amp;
        !checkWeight(p.weight.toString(), weight)
      ) {
        return false;
      }
      if (p.style !== undefined &amp;amp;&amp;amp; p.style !== style) {
        return false;
      }
      if (p.subset !== undefined &amp;amp;&amp;amp; p.subset !== subset) {
        return false;
      }
      return true;
    }),
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;Font preload={[{ weight: 400, subset: &apos;japanese&apos; }]} /&amp;gt;&lt;/code&gt; のように書くと、特定の組み合わせだけに preload を絞れる。日本語フォントでは全 chunk を preload するのは過剰なので、こうした filter で絞る前提になりそう。&lt;/p&gt;
&lt;h2&gt;日本語フォントとサブセット&lt;/h2&gt;
&lt;p&gt;Noto Sans JP のフルセットは数 MB あって、何の工夫もなしに読ませると重い。Google プロバイダーで &lt;code&gt;subsets: [&apos;japanese&apos;]&lt;/code&gt; を指定した場合、Google Fonts の CSS2 API がそもそも &lt;code&gt;unicode-range&lt;/code&gt; で 60 分割した CSS を返してくる。Astro はそれをそのまま読んで、各 chunk の woff2 をローカルへコピーしている。つまり、Astro 自身が分割しているわけではなく、Google 側の分割をそのまま使っているということ。ブラウザは表示する文字に応じて必要な chunk だけ取りに行く動きになる。&lt;/p&gt;
&lt;h2&gt;所感&lt;/h2&gt;
&lt;p&gt;同一オリジンから配信してくれる、且つサブセット分割なども処理されるので、要件でリソースはセルフホストが必要な場合などに使えそう。&lt;/p&gt;
</content:encoded></item><item><title>WCAG 1.4.13 からホバーUIを考える</title><link>https://yuheijotaki.com/blog/2026051101_wcag-1-4-13-hover-focus/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2026051101_wcag-1-4-13-hover-focus/</guid><description>WCAG 1.4.13 をもとにホバーやフォーカスで表示されるコンテンツの実装ポイントを整理した記事です。</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;WCAG 2.2 の達成基準 &lt;a href=&quot;https://waic.jp/translations/WCAG22/Understanding/content-on-hover-or-focus.html&quot;&gt;1.4.13 ホバー又はフォーカスで表示されるコンテンツ&lt;/a&gt; について、ホバーUIを実装するときに何を気にすべきか考えてみる。&lt;/p&gt;
&lt;p&gt;アクセシビリティの文脈で「なるべくホバーは使わないほうがよい」と聞くことがあるが、ホバー自体が禁止されているというより、ホバーしないと情報が得られない、またはホバーで出た情報をうまく扱えない状態が問題になる認識。&lt;/p&gt;
&lt;h2&gt;WCAG 1.4.13 とは&lt;/h2&gt;
&lt;p&gt;ポインタホバー、またはキーボードフォーカスによって追加コンテンツが表示される場合の達成基準。&lt;/p&gt;
&lt;p&gt;対象になるものは次のようなUI。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;サブメニュー、メガメニュー&lt;/li&gt;
&lt;li&gt;ツールチップ&lt;/li&gt;
&lt;li&gt;ホバー時にだけ表示されるカード内の詳細情報&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;逆に、ボタンの背景色が変わるなど追加コンテンツが表示されない単なる視覚的フィードバックは、この達成基準の主な対象ではない。&lt;/p&gt;
&lt;h2&gt;3つの要件&lt;/h2&gt;
&lt;p&gt;WCAG 1.4.13 では大きく3つの要件が示されている。&lt;/p&gt;
&lt;h3&gt;非表示にできる（Dismissible）&lt;/h3&gt;
&lt;p&gt;追加コンテンツが他のコンテンツを覆う場合、ポインタやフォーカスを動かさずに非表示にできる必要がある。&lt;/p&gt;
&lt;p&gt;実装としては &lt;code&gt;Esc&lt;/code&gt; キーで閉じられるようにするのが分かりやすい。特に拡大表示している場合、画面内に見えている範囲が狭くなるため、ホバーで出たコンテンツが邪魔になってもマウスを動かすと再度表示される、という状態が起こる。&lt;/p&gt;
&lt;h3&gt;ホバーできる（Hoverable）&lt;/h3&gt;
&lt;p&gt;ホバーで出た追加コンテンツに対して、その追加コンテンツ上へポインタを移動できる必要がある。&lt;/p&gt;
&lt;p&gt;例えばツールチップが大きい場合や、マウスカーソルを大きくしている場合、ポインタ自体がコンテンツを隠してしまうことがある。そのときにトリガー要素から追加コンテンツへポインタを移動できないと、内容を確認できない。&lt;/p&gt;
&lt;p&gt;CSSだけで実装する場合、トリガーと追加コンテンツの間に隙間があると、移動中に &lt;code&gt;:hover&lt;/code&gt; が外れて消えてしまうので注意が必要。&lt;/p&gt;
&lt;h3&gt;表示が継続される（Persistent）&lt;/h3&gt;
&lt;p&gt;追加コンテンツは、ホバーやフォーカスが外れる、ユーザーが閉じる、または内容が無効になるまで表示され続ける必要がある。&lt;/p&gt;
&lt;p&gt;一定時間で勝手に消えるツールチップなどは避ける。読む速度や拡大表示、ポインタ操作の正確さはユーザーによって異なるため、表示時間を実装側で短く決めてしまうのは危うい。&lt;/p&gt;
&lt;h2&gt;他社ガイドラインの例&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://a11y-guidelines.ameba.design/1/4/13/&quot;&gt;Ameba Accessibility Guidelines&lt;/a&gt; でも、画面を拡大して閲覧している場合に追加コンテンツを知覚できなかったり、閲覧中のコンテンツが覆い隠されたりする例が挙げられている。ホバーUIは、見えている範囲やポインタ操作のしやすさによって使いやすさが大きく変わる。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://smarthr.design/products/components/tooltip/&quot;&gt;SmartHR Design System の Tooltip&lt;/a&gt; では、Tooltip はユーザーが能動的に表示しなければならないことや、拡大表示時に領域外に表示される課題があるため、安易な使用は勧められていない。操作に必要な情報は Tooltip ではなく常に表示すること、モバイルでは Tooltip を使わないことも示されている。&lt;/p&gt;
&lt;h2&gt;ホバーUIの扱い方&lt;/h2&gt;
&lt;p&gt;自分の中では「ホバーを使わない」というより「ホバーでしか成立しない情報設計を避ける」と考えるのがしっくりきた。実装可否よりも、情報の重要度や表示される文脈で判断するのがよい。&lt;/p&gt;
&lt;p&gt;避けたほうがよいもの。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;フォームの入力要件やエラーメッセージをホバー時にだけ表示する&lt;/li&gt;
&lt;li&gt;ホバーで表示された中に操作ボタンやリンクがある&lt;/li&gt;
&lt;li&gt;拡大表示時に画面の大部分を覆う&lt;/li&gt;
&lt;li&gt;ポインタを少し動かしただけで消える&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使ってもよいもの。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;すでに本文やラベルで分かる情報の補足&lt;/li&gt;
&lt;li&gt;アイコンだけのボタンに対する補助的なラベル&lt;/li&gt;
&lt;li&gt;省略されたテキストの全文表示&lt;/li&gt;
&lt;li&gt;フォーカス、クリック、タップでも同じ内容にアクセスでき、&lt;code&gt;Esc&lt;/code&gt; など閉じる操作が用意されている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ホバーはPCでは自然な操作だが、キーボードやタッチでは別の操作になる。なので、ホバーを入口にする場合でも、フォーカスやクリックでも同じ状態にできるようにしておくのが基本になる。&lt;/p&gt;
&lt;h2&gt;所感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:hover&lt;/code&gt; だけで表示するのではなく、&lt;code&gt;:focus&lt;/code&gt; や &lt;code&gt;:focus-within&lt;/code&gt;、クリック/タップでも同じ内容にアクセスできるようにする&lt;/li&gt;
&lt;li&gt;追加コンテンツを読ませたい場合は、&lt;code&gt;pointer-events: none;&lt;/code&gt; にせず、トリガーから追加コンテンツ上へポインタを移動できる構造にする&lt;/li&gt;
&lt;li&gt;重要な情報は最初から表示するか、明示的に開けるUIにする&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus.html&quot;&gt;Content on Hover or Focus | Understanding WCAG 2.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/&quot;&gt;Tooltip Pattern | WAI-ARIA Authoring Practices Guide (APG)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Vertex AI Search でサイト内検索を実装してみる</title><link>https://yuheijotaki.com/blog/2026022001_vertex-ai-search/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2026022001_vertex-ai-search/</guid><description>Google Cloud の Vertex AI Search（Discovery Engine API）を使い、Astro でサイト内検索デモを実装してみる。</description><pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;Google Cloud の &lt;a href=&quot;https://docs.cloud.google.com/generative-ai-app-builder/docs?hl=ja&quot;&gt;Vertex AI Search&lt;/a&gt;（Discovery Engine API）を使ったサイト内検索を作ってみた。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/demo/vertex-ai-search/&quot;&gt;デモ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/commit/ceb6c4df2c8c68f3cc17414659a2a7bb164b572b&quot;&gt;コミット&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;構成&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;フロント
&lt;ul&gt;
&lt;li&gt;Astro SSG のページで &lt;code&gt;/api/search&lt;/code&gt; を叩く&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;バックエンド
&lt;ul&gt;
&lt;li&gt;Vercel Functions（&lt;code&gt;api/search.ts&lt;/code&gt;） が認証トークンを取得して Discovery Engine API に POST&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;認証
&lt;ul&gt;
&lt;li&gt;Google Cloud サービスアカウントの JSON 鍵を Vercel 環境変数に保存し、&lt;code&gt;google-auth-library&lt;/code&gt; でアクセストークンを取得&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Google Cloud 側の準備&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Discovery Engine API を有効化する
&lt;ul&gt;
&lt;li&gt;Google Cloud コンソールの API ライブラリから有効化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;データストアを作成する
&lt;ul&gt;
&lt;li&gt;ウェブサイトクロール型を選択し、クロール対象を &lt;code&gt;yuheijotaki.com/*&lt;/code&gt; に設定&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;検索アプリを作成する
&lt;ul&gt;
&lt;li&gt;データストアと接続し、プロジェクト ID を控えておく&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;サービスアカウントを作成する
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;roles/discoveryengine.viewer&lt;/code&gt; ロールを付与し、JSON 鍵をダウンロード&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Vercel 環境変数に登録する
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GOOGLE_SERVICE_ACCOUNT_KEY&lt;/code&gt;（JSON 鍵の中身）と &lt;code&gt;VERTEX_AI_SEARCH_ENGINE_ID&lt;/code&gt;（プロジェクト ID）を設定&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;所感など&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ウィジェット埋め込みと API 2種類の統合形式がある&lt;/li&gt;
&lt;li&gt;ウェブクロール後（インデックス作成後）に検索が可能となる。だいたい8時間ほどかかる&lt;/li&gt;
&lt;li&gt;ページの取得件数は上限は100件まで（&lt;a href=&quot;https://docs.cloud.google.com/retail/docs/reference/rest/v2/projects.locations.catalogs.placements/predict#request-body&quot;&gt;参考&lt;/a&gt;）
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; 使えば特定階層のみに絞ったクエリも投げれそう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ページの取得順はデフォルトで関連度順になる。ランク付けをしたりすることも可能&lt;/li&gt;
&lt;li&gt;サイト内検索の場合の料金（&lt;a href=&quot;https://cloud.google.com/generative-ai-app-builder/pricing?hl=ja&quot;&gt;参考&lt;/a&gt;）
&lt;ul&gt;
&lt;li&gt;データストアの容量は月 10 GiB まで無料&lt;/li&gt;
&lt;li&gt;Standard Edition ではなく Enterprise Edition（$4.00 / 1,000 クエリ）が必須。月 10,000 クエリまで無料&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;データソースは今回はウェブサイトを選択しているが、BigQuery や Cloud Storage も選択可能で、データソースの種類に合わせてAIを使った検索や会話機能を作れるAPIといったものらしい&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>pitcms を試してみる</title><link>https://yuheijotaki.com/blog/2026020901_pitcms/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2026020901_pitcms/</guid><description>pitcms を試した雑感です。</description><pubDate>Mon, 09 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;pitcms とは&lt;/h2&gt;
&lt;p&gt;GitHub と連携してコンテンツを管理できる日本製ヘッドレスCMS&lt;br /&gt;
公式サイト：&lt;a href=&quot;https://pitcms.net/&quot;&gt;pitcms&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;導入から設定まで&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pitcms.net/docs/getting-started/installation&quot;&gt;導入する | pitcms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pitcms.net/docs/getting-started/project&quot;&gt;各機能の設定 | pitcms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;入稿から反映まで&lt;/h2&gt;
&lt;p&gt;今回はデプロイ先、プレビュー環境を Cloudflare Pages にした。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;pitcms の管理画面で「編集セッション」を作成&lt;/li&gt;
&lt;li&gt;コレクション内の記事を編集&lt;/li&gt;
&lt;li&gt;記事の保存後にプレビューURLが発行されるので確認（ &lt;code&gt;https://pitcms-***.***.pages.dev/&lt;/code&gt; など ）&lt;/li&gt;
&lt;li&gt;確認後、編集セッションの「変更を反映」&lt;/li&gt;
&lt;li&gt;本番環境にデプロイされる&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;git の動きとしては、3. の段階でプレビューブランチが作成、5.の段階で main ブランチへマージされる&lt;/p&gt;
&lt;h2&gt;デモ&lt;/h2&gt;
&lt;p&gt;Astro の Content Collections（SSG）と組み合わせて使ってみた。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;デモサイト：&lt;a href=&quot;https://pitcms-sample.pages.dev/&quot;&gt;pitcms-sample.pages.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;リポジトリ：&lt;a href=&quot;https://github.com/yuheijotaki/pitcms-sample&quot;&gt;yuheijotaki/pitcms-sample&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;分かったこと・所感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;コレクションのスキーマはルートに置く pitcms.jsonc ファイルで定義する&lt;/li&gt;
&lt;li&gt;プレビュー環境の対応は Cloudflare Pages の他に、Vercel、Netlify、Cloudflare Workers が選択できる。1クリックで設定できるので楽&lt;/li&gt;
&lt;li&gt;GitHub のコンテンツをデータソースとするため、例えばローカルで記事編集してプッシュすると pitcms の管理画面に反映される仕組み&lt;/li&gt;
&lt;li&gt;編集セッションの概念が最初はとっつきにくいが、使って理解すると理にかなっているように思えた&lt;/li&gt;
&lt;li&gt;Astro の Content Collections との相性がよさそう&lt;/li&gt;
&lt;li&gt;TinaCMS（TinaCloud）に近い感じがする&lt;/li&gt;
&lt;li&gt;プランはコレクション（API）3つ、メンバー3人などの制限がある Free プラン、それらの数が無制限の Proプラン（¥980/月）がある&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Astro Live Content Collections を試してみる</title><link>https://yuheijotaki.com/blog/2026011501_live-content-collections/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2026011501_live-content-collections/</guid><description>Astro 5.10 で追加された Live Content Collections の実験的機能を試した雑感です。</description><pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;Astro 5.10 で追加された &lt;a href=&quot;https://docs.astro.build/en/reference/experimental-flags/live-content-collections/&quot;&gt;Live Content Collections&lt;/a&gt; を試してみた。Live Content Collections はビルド時ではなくリクエスト時にデータを取得できる機能（v5 系だと要 experimental フラグ）。&lt;/p&gt;
&lt;h2&gt;Live Content Collections とは&lt;/h2&gt;
&lt;h3&gt;従来の Content Collections&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;データはビルド時に取得される&lt;/li&gt;
&lt;li&gt;データストアに保存される&lt;/li&gt;
&lt;li&gt;静的なコンテンツに最適&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Live Content Collections&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;データはリクエスト時に取得される&lt;/li&gt;
&lt;li&gt;データストアには保存されない&lt;/li&gt;
&lt;li&gt;常に最新のデータを取得できる&lt;/li&gt;
&lt;li&gt;頻繁に更新されるコンテンツに最適&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;詳細： &lt;a href=&quot;https://astro.build/blog/live-content-collections-deep-dive/&quot;&gt;Live Content Collections: A Deep Dive | Astro&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;環境構築&lt;/h2&gt;
&lt;p&gt;SSR アダプターとして今回は Vercel を使用する。&lt;/p&gt;
&lt;h3&gt;設定&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;astro.config.mjs&lt;/code&gt; に実験的フラグを追加する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default defineConfig({
  adapter: vercel(),
  experimental: {
    liveContentCollections: true,
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/blob/main/src/live.config.ts&quot;&gt;src/live.config.ts&lt;/a&gt; にライブコレクションを定義する。（従来の &lt;code&gt;src/content.config.ts&lt;/code&gt; とは別ファイル）&lt;/p&gt;
&lt;p&gt;ポイントいくつか&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;defineLiveCollection()&lt;/code&gt; を使用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type: &apos;live&apos;&lt;/code&gt; を指定&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loader&lt;/code&gt; に &lt;code&gt;loadCollection&lt;/code&gt; と &lt;code&gt;loadEntry&lt;/code&gt; を実装&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Content Collections と Live Content Collections の比較&lt;/h2&gt;
&lt;p&gt;今回は従来のビルド時 Content Collections と Live Content Collections の両方を実装して比較した。&lt;/p&gt;
&lt;h3&gt;デモページ、ソースコード&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;静的版（従来の Content Collections）
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/demo/live-content-collections/static/&quot;&gt;デモページ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/blob/main/src/content/config.ts&quot;&gt;src/content.config.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/tree/main/src/pages/demo/live-content-collections/static&quot;&gt;src/pages/demo/live-content-collections/static/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;動的版（Live Content Collections）
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/demo/live-content-collections/dynamic/&quot;&gt;デモページ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/blob/main/src/live.config.ts&quot;&gt;src/live.config.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/tree/main/src/pages/demo/live-content-collections/dynamic&quot;&gt;src/pages/demo/live-content-collections/dynamic/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;静的版（Content Collections）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/content.config.ts&lt;/code&gt; で定義する。ページコンポーネントでは &lt;code&gt;getCollection()&lt;/code&gt; を使用。&lt;/p&gt;
&lt;h3&gt;動的版（Live Content Collections）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/live.config.ts&lt;/code&gt; で定義する。ページコンポーネントでは &lt;code&gt;getLiveCollection()&lt;/code&gt; を使用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
export const prerender = false;

import { getLiveCollection } from &apos;astro:content&apos;;
const { entries, error } = await getLiveCollection(&apos;liveBlog&apos;);
---
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;従来の fetch（SSR）との比較&lt;/h2&gt;
&lt;h3&gt;従来の fetch&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;手動で型の定義が必要&lt;/li&gt;
&lt;li&gt;バリデーションは自分で実装&lt;/li&gt;
&lt;li&gt;エラーハンドリングも自分で実装&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Live Content Collections&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;スキーマから自動的に型が推論される&lt;/li&gt;
&lt;li&gt;Zod による自動バリデーション&lt;/li&gt;
&lt;li&gt;エラーハンドリングが統一される&lt;/li&gt;
&lt;li&gt;ローダーとして再利用や配布可能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Live Content Collections は SSR の上に構築された抽象化レイヤーという扱いらしい（よく分からない）&lt;/p&gt;
&lt;h2&gt;所感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;割とシンプルなデータでも Live Content Collections のページは重いので使い所は気をつけないと&lt;/li&gt;
&lt;li&gt;記事のプレビュー環境で使うのとかはいいかも&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Astro で CSS ファイル名を固定する</title><link>https://yuheijotaki.com/blog/2025112306_css-filename/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2025112306_css-filename/</guid><description>Astro で生成される CSS ファイル名を固定する方法を試した雑感です。</description><pubDate>Sun, 23 Nov 2025 00:00:05 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://zenn.dev/ymnkx/articles/12b45b8e06e5eb&quot;&gt;ビルド後に組み込み作業アリ用のAstro開発環境&lt;/a&gt; という記事で Astro の CSS ファイル名を固定する方法が紹介されていた。CSS ファイル名の固定はできないと思ってたので実際に試してみた。&lt;/p&gt;
&lt;h2&gt;Astro での CSS ファイル名の扱い&lt;/h2&gt;
&lt;p&gt;通常 Astro でビルドすると CSS ファイルにハッシュが付与される。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_astro/_slug_.a1Wq-uq3.css
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ハッシュはファイルの内容が変わるたびに異なる値になる。キャッシュ戦略として有効だがバックエンドへの組み込み作業がある場合やクライアントのガイドラインでファイル名が制限される場合など固定のファイル名にしたいケースがある。&lt;/p&gt;
&lt;p&gt;固定化すると以下のようなファイル名になる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_astro/common.css
_astro/styles.css
_astro/styles2.css
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CSS カスタムプロパティを使った固定化&lt;/h2&gt;
&lt;h3&gt;仕組み&lt;/h3&gt;
&lt;p&gt;Vite の &lt;code&gt;rollupOptions.output.assetFileNames&lt;/code&gt; で CSS のソースコードから特定のカスタムプロパティを抽出してそれをファイル名として使用する。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zenn.dev/ymnkx/articles/12b45b8e06e5eb&quot;&gt;ビルド後に組み込み作業アリ用のAstro開発環境&lt;/a&gt; では以下の2つのカスタムプロパティが使われている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--output-file-name-important&lt;/code&gt; - 優先度が高い（全ページ共通 CSS やページ固有 CSS で使用）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--output-file-name&lt;/code&gt; - 優先度が低い（複数ページ用 CSS で使用）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;設定方法&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;astro.config.mjs&lt;/code&gt; に以下を追加する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig } from &apos;astro/config&apos;;

export default defineConfig({
  build: {
    inlineStylesheets: &apos;never&apos;,
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          assetFileNames: (assetInfo) =&amp;gt; {
            if (assetInfo.name &amp;amp;&amp;amp; assetInfo.name.endsWith(&apos;.css&apos;)) {
              // --output-file-name-important: filename.css を優先
              const importantMatch = String(assetInfo.source).match(
                /--output-file-name-important:\s*([^;}\s]+)/,
              );
              if (importantMatch) {
                return `_astro/${importantMatch[1].trim()}`;
              }

              // --output-file-name: filename.css を次に確認
              const matches = [
                ...String(assetInfo.source).matchAll(/--output-file-name:\s*([^;}\s]+)/g),
              ];
              if (matches.length &amp;gt; 0) {
                const fileName =
                  matches.map((m) =&amp;gt; m[1].trim().replace(/\.css$/, &apos;&apos;)).join(&apos;-&apos;) + &apos;.css&apos;;
                return `_astro/${fileName}`;
              }

              // カスタムプロパティがない場合はデフォルト名
              return &apos;_astro/styles.css&apos;;
            }
            return &apos;_astro/[name].[hash][extname]&apos;;
          },
        },
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;build.inlineStylesheets: &apos;never&apos;&lt;/code&gt; - CSS をインライン展開せず外部ファイルとして出力&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assetFileNames&lt;/code&gt; 関数内で &lt;code&gt;assetInfo.source&lt;/code&gt; から正規表現でカスタムプロパティを抽出&lt;/li&gt;
&lt;li&gt;CSS ファイル以外のアセット（画像など）はハッシュ付きのままにする&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CSS への記述&lt;/h3&gt;
&lt;p&gt;Astro コンポーネントの style タグに &lt;code&gt;is:global&lt;/code&gt; を付けて、カスタムプロパティを記述する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;style is:global&amp;gt;
  :root {
    --output-file-name-important: common.css;
  }
&amp;lt;/style&amp;gt;

&amp;lt;style lang=&quot;scss&quot;&amp;gt;
  /* 通常のスタイル */
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;実際に試してみる&lt;/h2&gt;
&lt;p&gt;全ページで使われる Layout.astro などのコンポーネントに &lt;code&gt;--output-file-name-important: common.css&lt;/code&gt; を追加する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;style is:global&amp;gt;
  :root {
    --output-file-name-important: common.css;
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ビルド結果&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;_astro/common.css
_astro/styles.css
_astro/styles2.css
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;common.css&lt;/code&gt; が生成された。中身を確認すると Layout コンポーネントと全ページで使われている他のコンポーネントの CSS がまとめられていた。&lt;/p&gt;
&lt;h2&gt;Astro の CSS 分割の仕組み&lt;/h2&gt;
&lt;p&gt;Astro はコンポーネントの使用状況に応じて CSS を自動的に分割する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全ページで使われるコンポーネント - 1つの CSS ファイルにまとめられる&lt;/li&gt;
&lt;li&gt;一部のページでのみ使われるコンポーネント - 別の CSS ファイルとして分割される&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;複数のコンポーネントが1つの CSS ファイルにまとめられる場合そのファイル内には複数の &lt;code&gt;--output-file-name&lt;/code&gt; カスタムプロパティが含まれる可能性がある。その場合は優先度の高い &lt;code&gt;--output-file-name-important&lt;/code&gt; が使用される。&lt;/p&gt;
&lt;h2&gt;Vite と Rollup の役割&lt;/h2&gt;
&lt;p&gt;Astro はビルドツールとして Vite を使用しており CSS の出力は Rollup によって処理される。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vite - モジュールのバンドルと最適化を担当&lt;/li&gt;
&lt;li&gt;Rollup - &lt;code&gt;output.assetFileNames&lt;/code&gt; で出力されるアセットファイルの命名規則を制御&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://docs.astro.build/en/reference/configuration-reference/#vite&quot;&gt;Vite Configuration Reference&lt;/a&gt; / &lt;a href=&quot;https://rollupjs.org/configuration-options/#output-assetfilenames&quot;&gt;Rollup - output.assetFileNames&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;assetInfo.source&lt;/code&gt; には CSS の内容が文字列として渡される。この文字列から正規表現でカスタムプロパティを抽出することでファイル名を制御できる。&lt;/p&gt;
&lt;h2&gt;その他の固定化方法&lt;/h2&gt;
&lt;p&gt;CSS カスタムプロパティを使う以外にも設定ファイルでマッピングを定義する方法がある。&lt;/p&gt;
&lt;h3&gt;設定ファイルでのマッピング&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;astro.config.mjs&lt;/code&gt; で明示的にファイル名のマッピングを定義する方法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const cssMapping = {
  Layout: &apos;common.css&apos;,
  Header: &apos;common.css&apos;,
  Footer: &apos;common.css&apos;,
  index: &apos;top.css&apos;,
  about: &apos;about.css&apos;,
};

export default defineConfig({
  build: {
    inlineStylesheets: &apos;never&apos;,
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          assetFileNames: (assetInfo) =&amp;gt; {
            if (assetInfo.name &amp;amp;&amp;amp; assetInfo.name.endsWith(&apos;.css&apos;)) {
              // names から元のファイル名を推測
              const baseName = assetInfo.names[0];
              const mappedName = cssMapping[baseName] || &apos;styles.css&apos;;
              return `_astro/${mappedName}`;
            }
            return &apos;_astro/[name].[hash][extname]&apos;;
          },
        },
      },
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;デメリット&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Astro が複数のコンポーネントの CSS を1つのファイルに最適化する際 &lt;code&gt;assetInfo.names&lt;/code&gt; に含まれる情報が限定的で元のファイルを正確に特定できないことがある&lt;/li&gt;
&lt;li&gt;マッピングの管理が煩雑になる（コンポーネントが増えると設定も増える）&lt;/li&gt;
&lt;li&gt;CSS とマッピング定義が離れているためメンテナンス性が低い&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CSS カスタムプロパティを使う方法は CSS の中で完結してコンポーネント単位で管理できる点で優れている。&lt;/p&gt;
&lt;h2&gt;分かったこと・所感&lt;/h2&gt;
&lt;p&gt;バックエンドへの組み込みなどに有効だがフレームワークの設計を壊してる気もするので罪悪感が残る。&lt;/p&gt;
</content:encoded></item><item><title>Astro SVG components を試してみる</title><link>https://yuheijotaki.com/blog/2025112305_svg-components/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2025112305_svg-components/</guid><description>Astro の SVG components 機能を試した雑感です。</description><pubDate>Sun, 23 Nov 2025 00:00:04 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;Astro 5.0 以降で追加された SVG components 機能を使って従来の astro-icon との違いを試してみた。&lt;/p&gt;
&lt;h2&gt;SVG components とは&lt;/h2&gt;
&lt;p&gt;SVG ファイルを直接インポートして Astro コンポーネントとして使える機能。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import StarIcon from &apos;./icons/star.svg&apos;;
---

&amp;lt;StarIcon width=&quot;48&quot; height=&quot;48&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このようにインポートすると、SVG の内容が HTML にインラインで埋め込まれる。&lt;/p&gt;
&lt;p&gt;公式ドキュメント：&lt;a href=&quot;https://docs.astro.build/en/guides/images/#svg-components&quot;&gt;SVG components - Astro Docs&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SVG components の処理の仕組み&lt;/h3&gt;
&lt;p&gt;Astro は SVG ファイルをインポートするとビルド時に props を受け取れる Astro コンポーネントに変換する。&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://github.com/withastro/astro/blob/main/packages/astro/src/assets/utils/svg.ts#L1-L50&quot;&gt;astro/packages/astro/src/assets/utils/svg.ts#L1-L50&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;変換処理では SVG ファイルの内容を読み取って &lt;code&gt;Astro.props&lt;/code&gt; で属性を受け取れる形に整形される。これにより SVG がそのままインラインで HTML に展開される。&lt;/p&gt;
&lt;h3&gt;生成される HTML&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- SVG components の生成HTML --&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 24 24&quot; width=&quot;48&quot; height=&quot;48&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot;&amp;gt;
  &amp;lt;path
    d=&quot;M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z&quot; /&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同じアイコンを複数回使うと、その都度 SVG がインラインで展開される。&lt;/p&gt;
&lt;h2&gt;astro-icon との比較&lt;/h2&gt;
&lt;h3&gt;astro-icon の仕組み&lt;/h3&gt;
&lt;p&gt;astro-icon は SVG sprite を生成して最適化する仕組み。同じアイコンを複数回使う場合は &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt; と &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; で参照する形になる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import { Icon } from &apos;astro-icon/components&apos;;
---

&amp;lt;Icon name=&quot;star&quot; width=&quot;48&quot; height=&quot;48&quot; class=&quot;custom-class&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://github.com/natemoo-re/astro-icon/blob/main/packages/core/src/loaders/loadLocalCollection.ts&quot;&gt;astro-icon/packages/core/src/loaders/loadLocalCollection.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SVG ファイルを読み込む際 &lt;code&gt;@iconify/tools&lt;/code&gt; を使って SVG の最適化や色の変換（&lt;code&gt;currentColor&lt;/code&gt; への変換など）を行う。同じアイコンが複数回使われる場合は &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt; 定義を一度だけ生成して &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; で参照することで HTML サイズを削減する。&lt;/p&gt;
&lt;h3&gt;生成される HTML&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- astro-icon の生成HTML --&amp;gt;
&amp;lt;svg data-icon=&quot;star&quot; width=&quot;48&quot; height=&quot;48&quot; class=&quot;custom-class&quot;&amp;gt;
  &amp;lt;symbol id=&quot;ai:local:star&quot; viewBox=&quot;0 0 24 24&quot;&amp;gt;
    &amp;lt;path
      d=&quot;M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z&quot; /&amp;gt;
  &amp;lt;/symbol&amp;gt;
  &amp;lt;use href=&quot;#ai:local:star&quot;&amp;gt;&amp;lt;/use&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;主な違い&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;機能&lt;/th&gt;
&lt;th&gt;SVG components&lt;/th&gt;
&lt;th&gt;astro-icon&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;インポート&lt;/td&gt;
&lt;td&gt;&lt;code&gt;import Icon from &apos;./icon.svg&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Icon name=&quot;icon&quot; /&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML出力&lt;/td&gt;
&lt;td&gt;インラインSVG&lt;/td&gt;
&lt;td&gt;SVG sprite（symbol + use）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;最適化&lt;/td&gt;
&lt;td&gt;なし&lt;/td&gt;
&lt;td&gt;同じアイコンの重複を自動削減&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ファイル管理&lt;/td&gt;
&lt;td&gt;SVGファイルを直接管理&lt;/td&gt;
&lt;td&gt;アイコンディレクトリで一元管理&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;デモ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/demo/svg-components/&quot;&gt;デモ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yuheijotaki/yuheijotaki.com/blob/main/src/pages/demo/svg-components.astro&quot;&gt;コード&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;デモでは astro-icon と SVG components を並べて、生成される HTML の違いを確認できる。&lt;/p&gt;
&lt;h2&gt;分かったこと・所感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SVG components はシンプルな実装で追加のライブラリなしで SVG を扱える&lt;/li&gt;
&lt;li&gt;どちらも props（width や height や class など）は渡せる&lt;/li&gt;
&lt;li&gt;SVG components はインライン展開されるため同じアイコンを複数回使う場合は HTML サイズは増えそう&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>使っていいのか迷う機能 2025</title><link>https://yuheijotaki.com/blog/2025112304_new-features-2025/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2025112304_new-features-2025/</guid><description>新しい Web 標準機能のブラウザ対応状況を整理した雑感です。</description><pubDate>Sun, 23 Nov 2025 00:00:03 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;新しい CSS や HTML の機能が次々と追加されているが実際に実務で使っていいのか判断に迷う。ここ1〜2年で使えるようになったものとまだ使えないものを整理してみた。&lt;/p&gt;
&lt;h2&gt;判断基準&lt;/h2&gt;
&lt;h3&gt;Baseline とは&lt;/h3&gt;
&lt;p&gt;Baseline は主要なブラウザで機能が利用可能になったことを示す指標。MDN や web.dev で表示される。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Newly available - すべての主要ブラウザで利用可能になってから30ヶ月未満&lt;/li&gt;
&lt;li&gt;Widely available - すべての主要ブラウザで利用可能になってから30ヶ月以上経過&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;クライアントワークでの基準&lt;/h3&gt;
&lt;p&gt;Web 制作会社でのクライアントワークでは Chrome、Safari、Firefox、Edge、iOS Safari、Android Chrome の全てで利用可能でないと「使えない」という基準で判断する。&lt;/p&gt;
&lt;p&gt;Baseline の「Newly available」であれば基本的には使えるが一部のブラウザでのみ実装されている機能はフォールバックを用意しない限り使用を控える。&lt;/p&gt;
&lt;h2&gt;使える機能&lt;/h2&gt;
&lt;p&gt;全ブラウザで利用可能になった機能。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/CSS_nesting&quot;&gt;CSS Nesting&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sass や SCSS で使われていたネスト構文がネイティブ CSS で利用可能になった。&lt;/p&gt;
&lt;p&gt;Baseline: Widely available（2023年8月）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/API/Popover_API&quot;&gt;Popover API&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;JavaScript を使わずに HTML 属性だけでポップオーバーを実装できる。Anchor Positioning と組み合わせることで柔軟な配置が可能だが Anchor Positioning が未対応のブラウザでは位置調整が必要。&lt;/p&gt;
&lt;p&gt;Baseline: Newly available（2024年4月）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/@starting-style&quot;&gt;@starting-style&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;display: none から display: block への切り替え時にトランジションを適用できる。これまで opacity や transform のトランジションは可能だったが display の変更時にはトランジションが効かなかった。&lt;/p&gt;
&lt;p&gt;Baseline: Newly available（2024年5月）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/color_value/light-dark&quot;&gt;light-dark() 関数&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ライトモードとダークモードで異なる色を1行で指定できる。prefers-color-scheme のメディアクエリを書く必要がなくなった。&lt;/p&gt;
&lt;p&gt;Baseline: Newly available（2024年3月）&lt;/p&gt;
&lt;h2&gt;まだ使えない機能&lt;/h2&gt;
&lt;p&gt;一部のブラウザでのみ実装されているか実装途中の機能。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/CSS_anchor_positioning&quot;&gt;CSS Anchor Positioning&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ツールチップやポップオーバーをアンカー要素に対して正確に配置する機能。Chrome と Edge のみ対応で Safari と Firefox は未対応。Popover API と組み合わせて使うことが多いが Anchor Positioning が使えないブラウザでは JavaScript での位置調整が必要になる。&lt;/p&gt;
&lt;p&gt;対応状況: Chrome 125+, Edge 125+（Safari と Firefox 未対応）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/API/View_Transitions_API&quot;&gt;View Transitions API&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ページ遷移時にスムーズなアニメーションを実現する API。Chrome と Edge のみ対応。SPA だけでなく MPA（マルチページアプリケーション）でも使える。&lt;/p&gt;
&lt;p&gt;対応状況: Chrome 111+, Edge 111+（Safari と Firefox 未対応）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/CSS_scroll-driven_animations&quot;&gt;Scroll-driven Animations&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;スクロール位置に連動したアニメーションを CSS だけで実装できる。Safari が未対応。&lt;/p&gt;
&lt;p&gt;対応状況: Chrome 115+, Edge 115+, Firefox 114+（Safari 未対応）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/field-sizing&quot;&gt;field-sizing&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;textarea や input 要素のサイズを内容に応じて自動調整する機能。Chrome のみ対応。&lt;/p&gt;
&lt;p&gt;対応状況: Chrome 123+（他ブラウザ未対応）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/interpolate-size&quot;&gt;interpolate-size&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;height: auto や width: auto でもアニメーションを可能にする機能。Chrome のみ対応。これまで height: 0 から height: auto へのトランジションができなかったがこの機能で可能になる。&lt;/p&gt;
&lt;p&gt;対応状況: Chrome 129+（他ブラウザ未対応）&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/reading-flow&quot;&gt;reading-flow&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CSS Grid や Flexbox で配置した要素の読み上げ順序を制御する機能。まだ実装されているブラウザはない。視覚的な順序とスクリーンリーダーでの読み上げ順序を一致させることができる。&lt;/p&gt;
&lt;p&gt;対応状況: すべてのブラウザで未対応（仕様策定中）&lt;/p&gt;
&lt;h2&gt;分かったこと・所感&lt;/h2&gt;
&lt;p&gt;CSS Nesting みたく使えるけど設計難しいとか &lt;code&gt;light-dark()&lt;/code&gt; みたく使えるけどどこでとか &lt;code&gt;field-sizing&lt;/code&gt; や &lt;code&gt;reading-flow&lt;/code&gt; みたく使えてもいやだなとか、そういうのも多いがまあ知っておくのは大事ということで。&lt;/p&gt;
</content:encoded></item><item><title>axe-core の Best Practice ルールを理解する</title><link>https://yuheijotaki.com/blog/2025112303_axe-best-practice/</link><guid isPermaLink="true">https://yuheijotaki.com/blog/2025112303_axe-best-practice/</guid><description>axe-core の Best Practice ルールについて調べた雑感です。</description><pubDate>Sun, 23 Nov 2025 00:00:02 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;axe DevTools で Web サイトのアクセシビリティチェックをしていると WCAG の達成基準とは別に「Best Practice」という項目が検出される。どのような基準で Best Practice なのか WCAG とどう違うのかを調べてみた。&lt;/p&gt;
&lt;h2&gt;axe-core の Best Practice ルールとは&lt;/h2&gt;
&lt;p&gt;axe-core の Best Practice ルールは WCAG の達成基準には直接該当しないがアクセシビリティ向上に寄与する推奨事項を含むルールセット。Deque Systems が独自に定義している。&lt;/p&gt;
&lt;p&gt;WCAG の達成基準は A、AA、AAA の3つのレベルに分類されるが Best Practice はこれらのレベルとは別にユーザビリティの向上を目的とした項目が含まれる。&lt;/p&gt;
&lt;h2&gt;主な Best Practice ルール&lt;/h2&gt;
&lt;p&gt;axe-core の Best Practice タグで検出される主な項目：&lt;/p&gt;
&lt;h3&gt;ページ構造&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;page-has-heading-one&lt;/code&gt; - ページに h1 見出しが存在するか&lt;/li&gt;
&lt;li&gt;&lt;code&gt;region&lt;/code&gt; - コンテンツがランドマークに含まれているか&lt;/li&gt;
&lt;li&gt;&lt;code&gt;landmark-one-main&lt;/code&gt; - メインランドマークが1つだけ存在するか&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;見出し構造&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;heading-order&lt;/code&gt; - 見出しレベルが正しい順序で使われているか（h1 の次が h3 ではなく h2 など）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;リンク&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;link-name&lt;/code&gt; - リンクにアクセシブルな名前が付いているか&lt;/li&gt;
&lt;li&gt;&lt;code&gt;link-in-text-block&lt;/code&gt; - テキストブロック内のリンクが識別可能か&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;画像&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;image-redundant-alt&lt;/code&gt; - 画像の alt テキストが冗長でないか（「画像」「写真」などの不要なテキストを含まない）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;フォーム&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;label&lt;/code&gt; - フォーム要素に適切なラベルが付いているか（これは WCAG の達成基準にも含まれることがある）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;WCAG との関係&lt;/h2&gt;
&lt;p&gt;Best Practice ルールは WCAG の達成基準とは独立している。WCAG の達成基準を満たしていても Best Practice で指摘されることがある。&lt;/p&gt;
&lt;p&gt;例えば h1 見出しの存在は WCAG の達成基準には含まれていないがページの主要なトピックを明確にするために推奨される。&lt;/p&gt;
&lt;p&gt;axe-core ではテスト時にタグを指定することで実行するルールを制御できる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// WCAG 2.1 レベル A, AA のみをテスト
.withTags([&apos;wcag2a&apos;, &apos;wcag2aa&apos;, &apos;wcag21a&apos;, &apos;wcag21aa&apos;])

// Best Practice も含めてテスト
.withTags([&apos;wcag2a&apos;, &apos;wcag2aa&apos;, &apos;wcag21a&apos;, &apos;wcag21aa&apos;, &apos;best-practice&apos;])
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practice の位置づけ&lt;/h2&gt;
&lt;p&gt;WCAG の達成基準はアクセシビリティの最低限または推奨される基準を定めている。Best Practice はそれらを補完する形でより良いユーザー体験を提供するための指針となる。&lt;/p&gt;
&lt;p&gt;WCAG レベルでいうと：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;レベル A - 達成が比較的容易で重篤な問題を引き起こしやすい基準&lt;/li&gt;
&lt;li&gt;レベル AA - より高度なアクセシビリティを実現する基準（多くの組織が目指すレベル）&lt;/li&gt;
&lt;li&gt;レベル AAA - 最も厳格な基準&lt;/li&gt;
&lt;li&gt;Best Practice - WCAG の達成基準とは独立したユーザビリティ向上のための推奨事項&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;実務での扱い方&lt;/h2&gt;
&lt;p&gt;axe DevTools でチェックする際 Best Practice の項目は Issue として検出されるが WCAG 違反とは別に考える必要がある。&lt;/p&gt;
&lt;p&gt;例えば WCAG レベル AA の準拠を目指すプロジェクトであれば Best Practice の項目は必須ではない。ただし Best Practice の項目を改善することでユーザー体験の向上につながることが多い。&lt;/p&gt;
&lt;p&gt;また Best Practice で指摘された項目が実際にはプロジェクトの要件や文脈では適切でない場合もある。例えばランディングページではメインコンテンツをランドマークで囲まない設計も考えられる。&lt;/p&gt;
&lt;h2&gt;分かったこと・所感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Best Practice は WCAG の達成基準とは独立したユーザビリティ観点でのチェック項目&lt;/li&gt;
&lt;li&gt;ページ構造（h1 やランドマーク）や見出し順序などコンテンツの構造に関する項目が多い&lt;/li&gt;
&lt;li&gt;WCAG の達成基準を満たしていても Best Practice で指摘されることがある&lt;/li&gt;
&lt;li&gt;必須ではないが改善することでユーザー体験の向上が期待できる&lt;/li&gt;
&lt;li&gt;プロジェクトの要件や文脈によっては Best Practice の項目を対応しないという判断もある&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考記事&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://accessible-usable.net/2022/07/entry_220701.html&quot;&gt;axe-core ルールと WCAG 2.1 達成基準の対照表 | Accessible &amp;amp; Usable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://azukiazusa.dev/blog/axe-core-playwright/&quot;&gt;@axe-core/playwright によるアクセシビリティテストの自動化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dequelabs/axe-core-npm/tree/master/packages/playwright&quot;&gt;axe-core-npm/packages/playwright at master · dequelabs/axe-core-npm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.deque.com/axe/core-documentation/api-documentation/&quot;&gt;axe-core Rules | Deque University&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>