🙏

【CSS】ナビゲーションで隣り合うタブの枠線の重なりを直す

2024/08/14に公開

はじめに

みなさんはCSSでナビゲーションを実装する際、隣り合うタブの枠線が重なり、にごって見えてしまった経験はありますか?

上の例では"gnaviの"と"枠線が"の間にある線の太さが2倍分になっていますし、"枠線が"と"にごる"の間も色がにごって見えています

枠線の太さも揃えたいし、色味もにごらないのが理想ですよね!

この記事ではなんとも言い表せないモヤモヤを解消するために格闘した話を解説しています

対象読者

ナビゲーションのCSS記述に悩む方

モヤモヤするコード

まずは現状のHTMLとCSSを確認しましょう

HTML
<nav class="gnavi">
  <ul>
    <li>
      <a>
        gnaviの
      </a>
    </li>
    <li>
      <a>
        枠線が
      </a>
    </li>
    <li class="gnavi__active">
      <a>
        にごる
      </a>
    </li>
  </ul>
</nav>
CSS
.gnavi {
  ul {
    display: flex;
    list-style: none;
  }
  
  li { 
    &.gnavi__active {
      color: #6dbeaf;

      a {
        border: #3EA8FF 4px solid;
        color: #000000D1;
      }
    }
  }

  a {
    display: flex;
    justify-content: center;
    align-items: center;
    border: #65717B 4px solid;
    height: 60px;
    width:100px;
    font-weight: 700;
    color: #65717B;

    &:hover {
      color: #000000D1;
    }
  }
}

border-leftborder-rightが隣り合っているため、左右の枠線だけ2倍の太さに見えたり、にごって見えてしまう原因となっています
それではこの問題を解決していきましょう

とりあえず思いつくことをやってみる

いちばん右端の要素となるlast-child以外のタブのborder-rightを消せば問題を解決できそう…!
右端の要素だけはlast-childborder-rightを使って右の枠線が描かれるようにしましょう
そして、aタグにはborder-right: none;を書き足してみましょう!

CSS
.gnavi {
  ul {
    display: flex;
    list-style: none;
  }

  li {
+   &:last-child a {
+     border-right: #65717B 4px solid;
+   }
 
    &.gnavi__active {
      color: #6dbeaf;

      a {
        border: #3EA8FF 4px solid;
        color: #000000D1;
      }
    }
  }

  a {
    display: flex;
    justify-content: center;
    align-items: center;
    border: #65717B 4px solid;
+   border-right: none;
    height: 60px;
    width:100px;
    font-weight: 700;
    color: #65717B;

    &:hover {
      color: #000000D1;
    }
  }
}

表示結果もスッキリしていい感じですね!
別のタブでも確認してみましょうか

ん…?
拡大してみてみましょう

"枠線が"と"にごる"の間も色がにごって見えているし右の枠線の太さは2倍のまま
gnavi__activeであるタブの右隣だけ問題が残っている
あぁ〜!
これはいけない
まだ問題は解決していませんね

gnavi__activeクラスが当たっているタブの右隣だけ問題が残るのであれば、右隣のCSSも表示内容が変わるように書かなければいけませんね
このような実装はJavaScriptを使わないと難しいか…

次兄弟結合子

なんとCSSだけで実装する方法がありました!
次兄弟結合子を使用することでgnavi__activeの次のタブをborder-left: none;にするといった、JavaScriptを使わないと実装できなさそうな記述がCSSだけで実装できます

そう言われても次兄弟結合子っていったい何なんだと思われるかもしれません
次兄弟結合子mdn web docsでは次のように説明されていました

次兄弟結合子 (next-sibling combinator, +) は 2 つのセレクターを接続し、 2 つ目の要素が 1 つ目の要素の 直後 にあって、両者が同じ親要素の子同士である場合に一致します。

うーん、説明を読んでもパッと頭に入ってきませんね…
今回の場合はborder-left: none;にしたいタブ要素はgnavi__activeの直後の要素だし、この2つのタグはどちらもliタグであり同じ親を持つ子要素です
次兄弟結合子が使えそうです!
さっそく実装してみましょう

CSS
.gnavi {
  ul {
    display: flex;
    list-style: none;

  }
  li {
    &:last-child a {
      border-right: #65717B 4px solid;
    }
 
    &.gnavi__active {
      color: #6dbeaf;

      a {
        border: #3EA8FF 4px solid;
        color: #000000D1;
      }
 
+     &+li a {
+     border-left: none;
+     }
    }
  }

  a {
    display: flex;
    justify-content: center;
    align-items: center;
    border: #65717B 4px solid;
    border-right: none;
    height: 60px;
    width:100px;
    font-weight: 700;
    color: #65717B;

    &:hover {
      color: #000000D1;
    }
  }
}

どのように反映されているかも確認しましょう

…いい感じですね!
どのタブを選択しても期待通りのデザインであることを確認できました!
次兄弟結合子、スゴ技じゃないですか!
え?次兄弟結合子は万能じゃない?

次兄弟結合子でできないこと

同じ親を持つ要素のにある要素を変更できるなら、の要素も変更できるのでは?
なんて思いましたが、どうやらそれは出来ないそうです
これは記述したCSSが前の兄弟要素に遡求して変更を与えるとなると、プロパティが子要素や次の兄弟へと受け継がれるというCSSそのものの設計思想に反するためではないかと考えられます

おわりに

今回の記事ではなんとも言い表せないモヤモヤを解消するために格闘した話を解説しました!
ナビゲーションのCSSを書くために次兄弟結合子を使って問題を解決しましたが、デザイン的にこだわりたいポイントでないようであれば、ulタグにborderをあて、liタグのafter要素でz-index: -1;となる右枠の線を記述する方法もあると思います(そっちの方が楽)
また、zennのナビゲーションUIのように、選択されているタブにのみborder-bottomをあてる方法もよいと思います(もっと簡単)

また、次兄弟結合子と似たような構文で後継兄弟結合子なるものも存在するそうです
こちらはmdn web docsでは次のように説明されていました

後続兄弟結合子 (subsequent-sibling combinator, ~) は 2 個のセレクターを結びつけ、 1 つ目の要素の後に 2 つ目の要素があり(直後である必要はない)、かつ両者が同じ親要素の子であるすべてのパターンに一致します。

うーん、次兄弟結合子と比べても使用条件がかなりトリッキーで使いどころが難しそうですね…
いい使い方を知っている方は教えてください!
読んでいただきありがとうございました🙌

Discussion