デザインの様式を、動くデモ・効く理由・実装で踏んだ罠つきで解剖する図鑑。
様式名: ベントーグリッド / ナビゲーション
「壊れた例」を押すと、この様式が成立しなくなる条件を実物で見られます。
| 対象 | 文字色 | 背景 | 実効背景 | 比 | 判定 |
|---|---|---|---|---|---|
| 特集セル本文(白/太字) | #ffffff | #18181b | #18181b | 17.72:1 | AAA 合格 |
| 特集セル見出し(PICK UP) | #d4d4d8 | #18181b | #18181b | 11.99:1 | AAA 合格 |
| 補助セル(すべて見る、マルーン地) | #ffffff | #7b1418 | #7b1418 | 10.74:1 | AAA 合格 |
| カテゴリ見出し(白セル) | #71717a | #ffffff | #ffffff | 4.83:1 | AA 合格(余裕なし) |
| カテゴリ本文リンク(白セル) | #3f3f46 | #ffffff | #ffffff | 10.44:1 | AAA 合格 |
| 上段バーのロゴ/トリガー文字 | #18181b | #ffffff | #ffffff | 17.72:1 | AAA 合格 |
<!doctype html>
<html lang="ja"><head><meta charset="utf-8"><title>ベントー型メガメニュー デモ</title><style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,"Hiragino Kaku Gothic ProN",sans-serif;background:#fff;padding:16px}
.wrap{max-width:600px;margin:0 auto;position:relative}
/* 上段のバーは高さ固定の1行。ここは幅の強弱しか付けられないので、あえて普通のnavのまま */
.bar{display:flex;align-items:center;justify-content:space-between;gap:16px;
border-bottom:1px solid #dcdce1;padding:0 2px 14px}
.logo{font-weight:700;font-size:15px;letter-spacing:-.01em;color:#18181b}
.links{display:flex;align-items:center;gap:22px;list-style:none}
.links a{font-size:13px;color:#3f3f46;text-decoration:none}
.links a:focus-visible{outline:2px solid #7b1418;outline-offset:3px;border-radius:3px}
.trigger{font:inherit;font-size:13px;color:#18181b;background:none;border:0;cursor:pointer;
display:flex;align-items:center;gap:5px;padding:6px 2px}
.trigger:focus-visible{outline:2px solid #7b1418;outline-offset:3px;border-radius:3px}
.trigger svg{transition:transform .15s}
.trigger[aria-expanded="true"] svg{transform:rotate(180deg)}
/* ここから先だけベントー化する。パネルは2次元(幅×高さ)の空間を自由に使えるため、
大小=重要度という文法が初めて機能する */
.mega{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fff;
border:1px solid #dcdce1;border-radius:16px;box-shadow:0 16px 40px rgba(24,24,27,.14);
padding:14px;display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:50px;gap:8px;z-index:5}
.mega[hidden]{display:none}
.cell{border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;
border:1px solid #e4e4e7;overflow:hidden}
.cell h3{font-size:11px;letter-spacing:.04em;color:#71717a;font-weight:600}
.cell ul{list-style:none;margin-top:6px;display:flex;flex-direction:column;gap:5px}
.cell a{font-size:12.5px;color:#3f3f46;text-decoration:none}
.cell a:hover,.cell a:focus-visible{color:#18181b;text-decoration:underline}
.cell a:focus-visible{outline:2px solid #7b1418;outline-offset:2px;border-radius:2px}
/* 最大のセル=最重要(今回はピックアップ特集)。列2×行3で、幅と高さの両方を使って主役にする */
/* 半透明の重ねは使わない。文字の下は常にベタ塗り#18181bで、実効色が場所によって変わる罠を避ける */
.feature{grid-column:span 2;grid-row:span 3;background:#18181b;color:#fff;justify-content:flex-end;
position:relative}
.feature::before{content:"";position:absolute;top:12px;right:12px;width:26px;height:26px;
border-radius:8px;background:#7b1418}
.feature h3{color:#d4d4d8}
.feature p{font-size:15px;font-weight:700;line-height:1.4;margin:6px 0 10px}
.feature a{font-size:12.5px;color:#fff;font-weight:600}
/* 中間セル=カテゴリのリンク集。列1×行2で、特集より一段小さいと視覚的に分かる */
.cat{grid-column:span 1;grid-row:span 2}
/* 最小セル=補助リンク。列1×行1で、リストほどの分量が無いものをここに落とす */
.mini{grid-column:span 1;grid-row:span 1;justify-content:center}
.mini a{font-size:12.5px;font-weight:600}
.mini.accent{background:#7b1418;border-color:#7b1418}
.mini.accent a{color:#fff}
@media(max-width:480px){
/* SPは1カラムへ。span を戻し忘れると4カラム分の幅が無いまま詰まって崩れる */
.mega{grid-template-columns:1fr;grid-auto-rows:auto}
.feature,.cat,.mini{grid-column:auto;grid-row:auto}
.links{display:none}
}
</style></head><body>
<div class="wrap">
<div class="bar">
<span class="logo">STUDIO</span>
<ul class="links">
<li><a href="#">会社概要</a></li>
<li><a href="#">実績</a></li>
<li>
<button class="trigger" id="trigger" aria-expanded="true" aria-controls="mega">
サービス
<svg width="10" height="6" viewBox="0 0 10 6" aria-hidden="true"><path d="M1 1l4 4 4-4" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg>
</button>
</li>
<li><a href="#">お問い合わせ</a></li>
</ul>
</div>
<div class="mega" id="mega" role="region" aria-label="サービスメニュー">
<div class="cell feature">
<h3>PICK UP</h3>
<p>2026年の制作実績を<br>特集ページで公開</p>
<a href="#">特集を見る →</a>
</div>
<div class="cell cat">
<h3>デザイン</h3>
<ul>
<li><a href="#">ブランディング</a></li>
<li><a href="#">Webデザイン</a></li>
<li><a href="#">UI/UX</a></li>
</ul>
</div>
<div class="cell cat">
<h3>エンジニアリング</h3>
<ul>
<li><a href="#">フロントエンド</a></li>
<li><a href="#">CMS構築</a></li>
<li><a href="#">保守運用</a></li>
</ul>
</div>
<div class="cell mini"><a href="#">料金</a></div>
<div class="cell mini accent"><a href="#">すべて見る →</a></div>
</div>
</div>
<script>
const trigger=document.getElementById('trigger'), mega=document.getElementById('mega');
function isOpen(){ return trigger.getAttribute('aria-expanded')==='true'; }
function setOpen(v){
trigger.setAttribute('aria-expanded', v ? 'true' : 'false');
mega.hidden = !v;
}
trigger.addEventListener('click', ()=> setOpen(!isOpen()));
document.addEventListener('keydown', e=>{
if(e.key==='Escape' && isOpen()){ setOpen(false); trigger.focus(); }
});
// パネルの外をクリックしたら閉じる。トリガー自身のクリックはtoggle側で処理済みなので除外する
// (trigger.contains判定にしないと、内側のsvgアイコンをクリックした瞬間に開いて即閉じる)
document.addEventListener('click', e=>{
if(isOpen() && !mega.contains(e.target) && !trigger.contains(e.target)){ setOpen(false); }
});
setOpen(true); // デモは開いた状態で表示
</script>
</body></html>