ウェブサイトデザインシステム
見た目と視覚的なパフォーマンスを担当するレイヤーです。モバイルグラフィック、ダークモード、テーマトークンを管理します。BRAND.md(テンプレート:new-websiteスキルのtemplates/brand.md)と、そのスキルのtemplates/astro/src/styles/global.css内のトークンブロックを出力・維持します。画像ルールは以下の通り(Astroのastro:assetsによるWebP/AVIF対応)。images.spec.tsテストでルールが強制されます。画像アセットの生成にはimageスキルを使用してください。
ダークモード(ファーストクラス)
- トークン、ハードコードされた色は使用しません。 すべての色はCSSカスタムプロパティを通して流れます。ライトモードは
:rootのデフォルト、ダークモードは:root[data-theme="dark"]で上書きし、さらにJavaScript無効の訪問者向けに@media (prefers-color-scheme: dark)ブロックも用意します。スターターキットはこの正確な構造を備えています。 - FOUC防止スクリプト を
<head>に配置し、localStorage.themeまたはシステム設定から最初の描画前にdata-themeを設定します(Base.astroに既に組み込み済み)。トグルはlocalStorage.themeに書き込みます(スターターキットにはトグルは含まれていません。追加する場合は、プライバシーページのセクション3にその旨を記載してください)。 - 両テーマのコントラスト比を確認: すべてのテキスト/背景の組み合わせがWCAG AA(本文4.5:1、大文字/UI 3:1)を満たします。ライトモードで合格する色でもダークモードでは不合格になることがよくあります。アクセシビリティテストはライト+ダークの全組み合わせで実行されるため、両方を確認してください。
モバイル&レスポンシブグラフィック
- モバイルファースト: 360pxからデザイン。タップターゲットは44px以上。水平スクロールは禁止。360 / 768 / 1280pxで確認してください(
preview_resize)。 - モダンなフォーマット: 写真にはJPEG/PNGではなくWebP(またはAVIF)を使用します。
- 表示サイズに合わせ、元画像に合わせない。 80pxのスロットに2000pxの画像を配信しないでください。幅のバリエーションを生成し、
srcset/sizes(または<picture>)を使用します。 - レイアウトシフトを防ぐ: 明示的な
width/height属性を設定し、画像読み込み前にボックスを確保します(CLS / LCPを保護)。Astroの<Image>は自動的に設定します。images.spec.tsでは属性の存在が必須です(CSSのみのaspect-ratioではテストに合格しません)。 - 遅延読み込み vs 即時読み込み: ファーストビュー以下の画像は
loading="lazy"。LCP/ヒーロー画像はfetchpriority="high"で即時読み込みします(遅延読み込みはしないでください)。 - すべての画像にaltテキスト(純粋に装飾的な場合のみ
alt="")。 - フォント: woff2をセルフホストし、
font-display: swapを設定。ファーストビューで使用する1~2種類のフォントのみプリロードします。フォントの遅延読み込みによるレイアウトシフトを避けます。
テキスト長のレスポンシブ対応(翻訳コピー)
- 翻訳テキストは長くなります。 ドイツ語(および他の多くの言語)は、英語の同等コピーよりも一般的に20~35%長くなります。英語のプレースホルダーテキストに合わせてサイズ設定されたボタン、ナビリンク、またはカードは、実際の翻訳コピーに置き換えられた瞬間に静かにオーバーフローしたり、折り返しが醜くなったり、切り詰められたりします。
- フレキシブルな幅を優先します。 翻訳テキストを保持する要素には、英語の文字列に合わせた固定
widthではなく、min-width/ flex / gridのauto/frサイジングを使用します。 - オーバーフロー ≠ 幅: フレキシブルなサイジングだけでは、単一の長い複合名詞(例:
Datenschutzerklärung)が狭いカラムからはみ出すのを防げません。そのためには、hyphens: auto(lang属性により言語ごとに正しく適用。Base.astroで既に設定済み)に加え、フォールバックとしてoverflow-wrap: break-wordが必要です。スターターキットのglobal.cssは両方をbodyに設定しています。テーブルのセルではハイフネーションをオフにします(テーブルを参照)。 - ナビリンクやボタンラベルの処理として、
white-space: nowrap+text-overflow: ellipsisだけに依存しないでください。 ナビリンクを切り詰めることは、通常許容されるUXではありません(訪問者はどこに移動するのかわからなくなります)。切り詰めが許容されるトレードオフとなるコンテンツ(例:テーブルセル)には問題ありませんが、ナビゲーションには適していません。 - 英語のプレースホルダーではなく、実際の翻訳ラベルで確認してください。 サイトが
astro-i18n-setupのLanguageSwitcher.astroやバイリンガル/マルチリンガルナビを出荷する場合、英語のプレースホルダーだけでなく、すべての言語で実際に出荷されたコピーを使用して、360/768/1280(preview_resize)でレイアウトを確認してください。
テーブル
すべてのテーブルに1つのスタイル。ページはテーブル用のレイアウトCSSを持ちません。スターターキットのsrc/styles/tables.css(Base.astroによってglobal.cssの後にインポートされる)に存在し、tests/tables.spec.tsがそれをチェックします。このセクションより前に作られたサイトは、tables.cssをコピーし、レイアウトにインポート行を追加し、最初に各ページの独自のテーブルCSSを削除します。
セルの内容によって区別される2種類があります。
stack(文章): 40rem以上のテーブル。40rem以下では各行が積み重なり、行ヘッダーがタイトル、各セルがカラム名の下に配置されます。4カラムの散文は320pxを共有できません。figures(短い数字、数値セルにclass="num"を付与): スマートフォンでもテーブルを維持し、320pxに収まる必要があります。収まらなくなった場合はstackにします。スクロールバーは修正策ではありません。stack figuresを組み合わせると、積み上げレイアウトでのフィギュアの見え方になります。
すべてのテーブルのマークアップ:
<div class="table-wrap stack" role="region" aria-label="テーブルの説明" tabindex="0">
<table role="table">
<thead role="rowgroup"><tr role="row">
<th scope="col" role="columnheader">{cols[0]}</th><th scope="col" role="columnheader">{cols[1]}</th>
</tr></thead>
<tbody role="rowgroup">{rows.map((r) => (
<tr role="row"><th scope="row" role="rowheader">{r.item}</th><td role="cell" data-label={cols[1]}>{r.text}</td></tr>
))}</tbody>
</table>
</div>
- カラム名はリストに1度だけ記述し、ヘッダーとすべての
data-labelに使用します。スタックされたセルのラベルはそのカラムのヘッダーと等しくなければならず、テストで両者が比較されます。figuresテーブルには、セルごとのロールやdata-labelは必要ありません。 - スタックされたテーブルはもはや
display: tableではありません。ロールは、レイアウトが変更されたときにブラウザがテーブルをドロップした場合のフォールバックです。実際にそれらが機能するかどうかはチェックされません。Chromiumでは、実際のテーブルで測定したところ、アクセシビリティツリーはロールがなくてもテーブルを維持していました。 - セル内に単独で存在するリンクに
class="tap"を付けると、スマートフォン上で44pxのタップターゲットになります。nowrapは短い値(1, 3, 6など)を1行に保持します。long-head(stackと併用)は、行ヘッダーが文章の場合に最初のカラムを広げます。 - セル内の単語はハイフネーションされません。bodyの
hyphens: autoは狭い最初のカラムを断片("An-thropic")に圧縮するため、ブロックはそこにhyphens: manualを設定します。 - 外部から取得したテーブルを含むページで、マークアップを適用できない場合は、理由とともに
TABLE_EXEMPTに追加します。PAGESは変更しないでください。サイトマップとllms.txtのチェックでは、すべてのライブルートがそれに含まれている必要があります。
tables.spec.tsがチェックする内容:すべてのテーブルは、いずれかの種類の.table-wrap内にあり、そのリージョン、ラベル、tabindexを持ち、すべてのヘッダーセルにscope="col"があり、すべての行の最初のセルとして<th scope="row">があり、残りは<td>であること。スタックされたテーブルにはロールと、各セルにヘッダーと等しいラベルがあること。360pxおよび320pxで、どのページもテーブルも横スクロールしないこと。641px以上では、スタックされたテーブルは再びテーブルになり、ヘッダー行が表示され、単語が分割されていないこと(これはoverflow-wrap: anywhereのようにカラムが単語を圧縮できるようにするルールをキャッチします)。どのセルにもhyphens: autoがないこと。また、ホームページに追加するフィクスチャでCSSをテストし、両方のテーマでaxeを使用するため、テーブルがなくてもブロックが機能することを証明します。ヘッドレスChromiumはハイフネーションしないため、ハイフンガードは計算されたスタイルを読み取り、レイアウトは読み取りません。SafariとVoiceOverはチェックされません。
メールアドレスの難読化(クローラーからアドレスを隠す)
プレーンテキストのメールアドレスやmailto:your@addrをHTMLソースに決して配置しないでください。スクレイパーがそれを収集し、数時間以内にスパムに使用します。すべてのアドレスは、**EmailLink.astro**コンポーネント(new-websiteスキルのtemplates/astro/内のsrc/components/EmailLink.astro、およびそのlib/obfuscate.tsヘルパー)を通してレンダリングします。アドレスはビルド時に反転されbase64エンコードされ、ブラウザで再構築されるため、提供されるHTMLには読み取り可能なアドレスは含まれません。JavaScript無効の訪問者には、引き続き使用可能な「name [at] domain [dot] com」ヒントが表示されます。
<EmailLink address="[email protected]" subject="お問い合わせ" text="メールを送信" />
ルール:ビルドされたHTML内に生のmailto:リンクとリテラルの@アドレスをゼロにする — website-qaがこれをチェックします。収集されたくない電話番号についても同様の考え方を適用します。
ブランドトークン
BRAND.mdのパレットをトークンブロックに反映し、同期を維持します。以下を定義します:ブランド(固定)、およびテーマごとのheading/ink/muted/accent/link/line/bg/bg-soft/surface。アクセントカラーとリンクカラーは、両方のテーマで背景に対してAAに合格する必要があります。そのため、各テーマは独自のものを持っています。オプションのwebsite-brand-kitはチェックとともにミラーリングを行います。その入力はBRAND.mdのテーブルを読み取り、すべてのテキストカラーを両テーマのすべての背景に対して4.5:1に保ち、値をglobal.cssの3つのすべてのカラーブロック(ライト、ダーク、JavaScript無効の訪問者向けのダークブロック)に書き込みます。手動で行う場合も、3つのブロックすべてを一緒に変更してください。
完了条件
BRAND.mdが記入され、トークンブロックと一致していること。画像はWebP形式で、サイズ設定され、遅延/即時読み込みが正しく行われ、alt属性があること。アクセシビリティテスト(ライト+ダーク)がパスしていること。レイアウトが360/768/1280pxでシフトなく保持されていること。すべてのテーブルがstackまたはfiguresであり、tables.spec.tsがパスしていること。パフォーマンス数値(FCP/LCP)がwebsite-qaで確認されていること。レイアウトは、デフォルトのロケールだけでなく、出荷されるすべての言語の実際の(プレースホルダーではない)コピーでも、360/768/1280pxで保持されていること。