このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

View in English Always switch to English

border-shape による不規則なナビゲーションメニューの作成

CSS の border-shape プロパティを要素に適用することで、吹き出しや抽象的なツールチップのデザインなど、精密な形状のコンテナーを生成できます。これにより、回避策やハックに頼ることなく、多くの新しいアイディアを実際に実装することが可能になります。このプロパティに関する詳細や、基本的な動作例については、プロパティリファレンスページでご確認いただけます。

このガイドでは、border-shape を使用して、それぞれのナビゲーションアイテムがジグソーパズルのピースのような形をした、不規則なアニメーション付きナビゲーションメニューを作成する方法をご紹介します。

ナビゲーションメニューのマークアップの定義

この HTML は、ナビゲーションメニューとしてはごく一般的なもので、リンクのリストとなっています。

html
<ul>
  <li><a href="#">One</a></li>
  <li><a href="#">Two</a></li>
  <li><a href="#">Three</a></li>
  <li><a href="#">Four</a></li>
</ul>

基本的なページスタイルの設定

基本的なページ設定のスタイルは以下の通りです。基本的なフォントスタイルを適用し、height を使用して <body> がビューポートいっぱいに表示されるようにし、フレックスボックスを使ってナビゲーションメニューを画面の中央に配置します。

css
* {
  box-sizing: border-box;
}

html {
  height: 100%;
  font-family: "Helvetica", "Arial";
  font-size: 1.2rem;
}

body {
  margin: 0;
  height: inherit;
  display: flex;
  justify-content: center;
  align-items: center;
}

一般的なナビゲーションスタイルの設定

まず、<ul> のスタイルを設定します。デフォルトの list-style-type と padding を除去し、display の値を flex に設定して、内部の <li> 要素を一列に配置します。次に、gap の値を 0 に設定し、transition を適用します。これにより、<ul> の状態が変化した際、gap の値の変化が滑らかにアニメーション表示されるようになります。

css
ul {
  list-style-type: none;
  padding: 0;
  display: flex;
  gap: 0;
  transition: gap 0.6s;
}

次に、<li> 要素にスタイルを設定します。それぞれのナビゲーションアイテムを正方形にしたいので、width と height を同じ値に設定します。

css
li {
  width: 160px;
  height: 160px;
}

次に、リストアイテム内の <a> 要素にスタイルを適用します。まず、デフォルトの text-decoration を除去し、color が black に設定されます。次に、<a> 要素が <li> 要素の領域全体を埋め尽くすように、width と height を 100% に設定し、フレックスボックスを使用して、テキストを水平方向および垂直方向に中央揃えにします。

さらに、リンクに box-shadow および text-shadow プロパティを設定し、要素の状態が変化した際にプロパティ値の変更が滑らかにアニメーションされるよう transition を追加します。

css
a {
  text-decoration: none;
  color: black;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;

  box-shadow:
    2px 0px 2px rgb(0 0 0 / 0.5),
    inset 3px 3px 3px rgb(255 255 255 / 0.5);
  text-shadow: 1px 1px 1px rgb(0 0 0 / 0.5);
  transition: all 0.6s;
}

次に、ジグソーパズルの各ピースにそれぞれ異なる色を割り当てます。

css
li:nth-child(1) a {
  background-color: #2de1fc;
}

li:nth-child(2) a {
  background-color: #2afc98;
}

li:nth-child(3) a {
  background-color: #09e85e;
}

li:nth-child(4) a {
  background-color: #16c172;
}

境界線の図形の処理

それでは、目指しているジグソーパズルのピースのような外観を実現するために、それぞれのナビゲーションアイテムの border-shape を設定しましょう。変化をつけるため、奇数番目の <a> 要素は下向きのジグソーパズルのピースのような形に、偶数番目の <a> 要素は上向きのジグソーパズルのピースのような形に設定しました。

css
li:nth-child(even) a {
  border-shape: shape(
      from 0% 0%,
      hline to 33%,
      arc by 33% 0% of 16% 20% small cw,
      hline to 100%,
      line to 100% 33%,
      arc by 0% 33% of 20% 16% small cw,
      line to 100% 100%,
      hline to 66%,
      arc by -33% 0% of 16% 20% small ccw,
      hline to 0%,
      line to 0% 66%,
      arc by 0% -33% of 20% 16% small ccw,
      close
    )
    content-box;
}

li:nth-child(odd) a {
  border-shape: shape(
      from 0% 0%,
      hline to 33%,
      arc by 33% 0% of 16% 20% small ccw,
      hline to 100%,
      line to 100% 33%,
      arc by 0% 33% of 20% 16% small cw,
      line to 100% 100%,
      hline to 66%,
      arc by -33% 0% of 16% 20% small cw,
      hline to 0%,
      line to 0% 66%,
      arc by 0% -33% of 20% 16% small ccw,
      close
    )
    content-box;
}

これにより、すぐに課題が発生します。元の <a> 領域からはみ出しているジグソーパズルのピースの切り欠き部分が、<a> 要素の背景色で塗りつぶされないのです。

この課題には解決策があります。前の 2 つのルールでは、意図的に content-box <geometry-box> の値をそれぞれの shape() 関数の後に記載しています。このことによって、図形は要素のコンテンツボックスを基準に描画され、適用された padding は図形の内部には設定されません。代わりに、パディングは図形の外側に配置されるため、図形が小さくなり、背景色が切り欠き部分を埋めるようになります。

必要な padding は同様に設定します。

css
a {
  padding: 24px;
}

メモ: ブラウザーの開発者ツールでライブサンプルを確認し、<a> 要素に適用されている padding を無効にすることで、この問題の背景がどのようなものかを確認できます。

padding を設定するとジグソーパズルのピースが小さくなり、ピース間に隙間が発生してしまいます。最初はピース同士がくっついている状態にしたいので、それぞれのリストアイテムに大きな負の margin-right 値を設定して、ピース同士を寄せ合わせます。

css
li {
  margin-right: -47px;
}

この margin-right の設定による副作用として、すべての <li> 要素が右側に移動してしまうため、ナビゲーションメニューが水平方向に中央揃えできなくなることです。これを修正するために、相対位置指定を使用して、<ul> を左側に戻します。

css
ul {
  position: relative;
  right: 23.5px;
}

最後に、:hover および :focus に対してスタイルの更新を適用します。これにより、先ほど設定した transition プロパティと相まって、ナビゲーションアイテムとの操作時にアニメーション効果が生まれます。<ul> のフレックスボックスレイアウトに設定されている gap を、ホバー時やフォーカス時に拡大します。フォーカス状態を処理するために、:has 擬似クラスを使用し、内部の <a> のいずれかがフォーカスされた際に <ul> 全体を選択します。

css
ul:hover,
ul:has(a:focus) {
  gap: 30px;
}

次に、<a> 要素自体に対して、ホバー時やフォーカス時に brightness filter や scale の値を大きくし、外側の box-shadow を適用することで、操作時に要素がより明るく現れ、浮き上がったような効果を出しています。

css
a:hover,
a:focus {
  filter: brightness(1.2);
  scale: 1.1;
  box-shadow:
    5px 0px 10px rgb(0 0 0 / 0.5),
    inset 3px 3px 3px rgb(255 255 255 / 0.5);
}

結果

ナビゲーションアイテムにカーソルを合わせたり、フォーカスを当てたりすると、アニメーション効果が表示されます。適用されたさまざまな効果が、border-shape の値と自然に調和してい様子に注目してください。

関連情報