ベントー型メガメニュー

様式名: ベントーグリッド / ナビゲーション

ナビメガメニューグリッドベントー
⧉ 別タブで開く

「壊れた例」を押すと、この様式が成立しなくなる条件を実物で見られます。

特徴 — なぜ効くか

  • ナビの上段バー自体は高さ固定の1行なので、bentoの生命線である『行スパンによる大小』が使えない。だからbento化はドロップダウンのパネル側だけに限定した。パネルは開いた瞬間に自由な2次元space(幅×高さ)を持つため、そこで初めて大小=重要度の文法が効く
  • 特集セル(2列×3行)・カテゴリセル(1列×2行)・補助セル(1列×1行)の3段階で、メガメニューにありがちな『リンクの壁』を避ける。見出しを追加しなくても、セルの大きさだけで『まず見るべきもの』が伝わる
  • カテゴリセルはリンク数がまちまち(3〜4本)でも row:span 2で吸収できる。1列グリッドの単純な段組みだと、リストが長いカテゴリだけ隣より縦に伸びて揃わないが、bentoは元々『揃わない前提』の様式なのでこの不揃いが破綻に見えない

実装メモ — 踏んだ罠

  • パネルは position:absolute で上段バーの下に重ねる。position:static で通常フローに置くとバーの下の本文を押し下げてしまい、開閉のたびにページ全体が跳ねる
  • 特集セルにグラデーションで『写真っぽさ』を足そうとしたが、白文字が下部に来る設計だと重ねる位置によって実効コントラストが変わる。08_neo_brutalist_navで踏んだのと同じ罠なので、今回は最初から半透明を使わず、ベタ塗り#18181b+アクセント色の小さな四角(::before)だけで『特集感』を出した
  • role="region" + aria-label をパネルに付け、開閉は<button aria-expanded>で行う。<a>のhoverだけで開閉するCSS onlyの実装も試したが、タッチ端末はhoverが発火しないため『サービス』配下のリンクに一切到達できなくなる。クリック/Enterで開くbutton方式なら入力方法を問わない
  • 外側クリックで閉じる処理は、トリガー自身のクリックをtoggle側で既に処理しているため、document側のclickリスナーでtrigger自身を除外しないと『開いた瞬間に自分のクリックで即閉じる』二重発火になる。最初 e.target!==trigger で判定していたが、トリガー内の矢印svgアイコンを直接クリックするとe.targetがsvg/pathになりtrigger自身とは一致しないため、外側クリック扱いで即座に閉じてしまっていた。trigger.contains(e.target) に直して解決した
  • SPでは.megaを1カラムに落とし、span を全部autoに戻す。01_bento_grid と同じ理由で、span を残したまま1カラムにすると4カラム分の幅を前提にしたレイアウトが崩れる。あわせて上段の.linksをdisplay:noneにし、SP用のハンバーガー等別のナビに切り替える前提とした(このデモにはハンバーガー実装までは含めていない)
  • 最初 grid-auto-rows:42px / gap:10px で組んだところ、カテゴリセル(row:span 2=94px、内側パディング差引で約70px)に3本のリンクが収まらず、.cellのoverflow:hiddenで3本目が見えないまま切れていた。スクリーンショットで実際に見て気付いた不具合で、コードの見た目上は『崩れていない』ので目視しないと発見できない。auto-rowsを50px・gapを8pxに広げて解決した

使いどころ / 使ってはいけない場面

壊れる条件

コントラスト実測

対象文字色背景実効背景判定
特集セル本文(白/太字)#ffffff#18181b#18181b17.72:1AAA 合格
特集セル見出し(PICK UP)#d4d4d8#18181b#18181b11.99:1AAA 合格
補助セル(すべて見る、マルーン地)#ffffff#7b1418#7b141810.74:1AAA 合格
カテゴリ見出し(白セル)#71717a#ffffff#ffffff4.83:1AA 合格(余裕なし)
カテゴリ本文リンク(白セル)#3f3f46#ffffff#ffffff10.44:1AAA 合格
上段バーのロゴ/トリガー文字#18181b#ffffff#ffffff17.72:1AAA 合格

アクセシビリティ

スマホへの落とし方

関連するパーツ

コードを見る
<!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>
出典・参考: STYLE_AXIS.md の様式定義(07 ベントーグリッド=大きさの違うカードを隙間なく敷き詰める)を、01_bento_gridが扱わなかった『ナビゲーション(メガメニュー)』部位に適用した自作実装