ハンドクラフト・手描き グローバルナビゲーション

様式名: ハンドクラフト・手描き / ナビゲーション

手描き下線現在地表示和文
⧉ 別タブで開く

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

特徴 — なぜ効くか

  • 現在地リンクの下線を feDisplacementMap で歪ませ、定規で引いた直線を避ける。手描き感の核はここ
  • 判子風ロゴと紙テクスチャで 19_handcraft_note と地続きの世界観を保ちつつ、部位をカード・バッジからナビへ広げる
  • 現在地表示は『手描き下線の見た目』と『aria-current="page"』の二重化。見た目だけに状態を語らせない
  • :hover だけに揺れを実装すると、キーボード操作・タッチでは他リンクの状態変化が一切見えなくなる。:focus-visible と [aria-current] にも同じ揺れを確実に発火させる

実装メモ — 踏んだ罠

  • 【最大の罠】SVG の <filter> は既定で region が x=-10% y=-10% width=120% height=120%(フィルタ対象のバウンディングボックス比)。下線のように細く横長な要素へ scale=5 の feDisplacementMap をかけると、上下方向の余白が足りず波の頂点が水平に切れ、片側だけ定規で切ったような下線になった。filter に x="-20%" y="-140%" width="140%" height="380%" を明示して広げて解決した
  • 同じ理由で、下線用(細く横長)と囲み枠用(太く矩形)で filter を分けた。1つを使い回すと、太い枠は綺麗に歪むのに細い下線だけ波が潰れる、という『見た目は同じはずなのに片方だけ壊れている』不具合になる
  • 揺れの発火条件は :hover, :focus-visible, [aria-current="page"] の3つすべてに揃える。:hover だけに実装すると、キーボードとタッチには『何も変わらないナビ』にしか見えない
  • 現在地の伝達を下線の視覚効果だけに頼らない。aria-current="page" を必ず併記する(下線の有無は支援技術には伝わらない)
  • 判子ロゴはこのデモではリンクにしていない(装飾)。ロゴをホームリンクにする場合は文字ラベルを併記する。19_handcraft_note で踏んだ『アイコンだけでアフォーダンス不明』の罠を再現しないため
  • 紙のグレイン・揺らぎのフィルタ層は pointer-events:none にする。ナビのクリック判定を吸わせない

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

壊れる条件

コントラスト実測

対象文字色背景実効背景判定
ナビ文字(紙地)#2b2622#f4ead8#f4ead812.55:1AA 合格
非現在地リンク(紙地)#6b5d4f#f4ead8#f4ead85.33:1AA 合格
現在地下線・CTA(紙地)#b23a2e#f4ead8#f4ead84.98:1AA 合格(余裕なし)
CTA文字(白 on 判子赤)#ffffff#b23a2e#b23a2e5.94:1AA 合格

アクセシビリティ

スマホへの落とし方

ブラウザ対応

関連するパーツ

コードを見る
<!doctype html>
<html lang="ja"><head><meta charset="utf-8"><title>ハンドクラフト・手描き ナビゲーション デモ</title><style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
  --paper:#f4ead8;   /* 生成りの紙。19_handcraft_note と同じ配色を流用(実測値も同じ) */
  --ink:#2b2622;     /* インク。紙地で 12.55:1 */
  --sub:#6b5d4f;     /* サブ。紙地で 5.33:1 */
  --stamp:#b23a2e;   /* 判子の赤。紙地で 4.98:1、白抜きで 5.94:1 */
}
body{background:var(--paper);color:var(--ink);min-height:340px;position:relative;overflow:hidden;
  font-family:"Yu Gothic","Hiragino Maru Gothic ProN",system-ui,sans-serif}
svg.filters{position:absolute;width:0;height:0}
/* 紙のグレイン。19 と同じ手法(feTurbulence の data URI)。装飾なのでクリックを吸わせない */
body::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.06;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E")}

.hcnav{position:relative;z-index:1;border-bottom:2.5px dashed var(--ink)}
.hc-inner{max-width:640px;margin:0 auto;padding:14px 24px;display:flex;align-items:center;gap:20px}

/* 判子風ロゴ。装飾なのでリンクにしない(19 で踏んだ『アイコンだけでアフォーダンス不明』を避ける) */
.brand{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:38px;height:38px;border:2.5px solid var(--stamp);border-radius:50%;color:var(--stamp);
  font-size:12px;font-weight:800;transform:rotate(-8deg);opacity:.9;filter:url(#roughbox)}

.links{list-style:none;display:flex;gap:22px;flex:1}
.links a{position:relative;display:inline-block;text-decoration:none;color:var(--sub);
  font-size:14px;font-weight:700;padding:6px 2px}
.links a:hover{color:var(--ink)}
.links a:focus-visible{outline:3px solid var(--ink);outline-offset:3px;border-radius:2px}
.links a[aria-current="page"]{color:var(--ink)}

/* 手描きの下線本体。text本体でなく専用の薄い要素に filter を掛けるため、
   要素の縦横比が極端(幅広・高さ4px)になり、既定 filter region では波が上下で切れた。
   region を広げた roughline を使うことで解消している */
.wobble-line{position:absolute;left:2px;right:2px;bottom:1px;height:4px;
  background:var(--stamp);filter:url(#roughline)}
/* 現在地以外は hover / focus-visible のときだけ、判子色の手描き囲みを見せる。
   :hover だけに実装すると、キーボードとタッチには何も変化が見えなくなる */
.links a:not([aria-current="page"]):hover::before,
.links a:not([aria-current="page"]):focus-visible::before{
  content:"";position:absolute;inset:-4px -8px;border:2px solid var(--stamp);
  border-radius:8px;filter:url(#roughbox);pointer-events:none}

.cta{margin-left:auto;flex:none;display:inline-block;background:var(--stamp);color:#fff;
  font-weight:800;font-size:13px;padding:9px 18px;border-radius:14px 9px 15px 8px;
  text-decoration:none;filter:url(#roughbox)}
.cta:focus-visible{outline:3px solid var(--ink);outline-offset:3px}

.page{position:relative;z-index:1;padding:34px 24px;max-width:640px;margin:0 auto}
.page .eyebrow{font-size:11px;letter-spacing:.12em;color:var(--sub);font-weight:700;margin-bottom:10px}
.page h1{font-size:clamp(24px,5.5vw,34px);line-height:1.2}
.page p{margin-top:10px;font-size:13px;line-height:1.85;color:var(--sub);max-width:30em}
</style></head><body>
<svg class="filters" aria-hidden="true">
  <!-- 下線用:幅広・高さ4pxの帯に合わせて region を大きく広げる(既定 -10%/-10%/120%/120% では波が切れる) -->
  <filter id="roughline" x="-20%" y="-140%" width="140%" height="380%">
    <feTurbulence type="fractalNoise" baseFrequency="0.05 0.9" numOctaves="2" result="n"/>
    <feDisplacementMap in="SourceGraphic" in2="n" scale="5"/>
  </filter>
  <!-- 囲み枠・ロゴ・CTA用:矩形でそこまで極端ではないが、同じ考え方で少し広げておく -->
  <filter id="roughbox" x="-15%" y="-30%" width="130%" height="160%">
    <feTurbulence type="fractalNoise" baseFrequency="0.02" numOctaves="3" result="n"/>
    <feDisplacementMap in="SourceGraphic" in2="n" scale="4"/>
  </filter>
</svg>

<nav class="hcnav" aria-label="グローバル">
  <div class="hc-inner">
    <span class="brand" aria-hidden="true">て</span>
    <ul class="links">
      <li><a href="#">ホーム</a></li>
      <li><a href="#">作品</a></li>
      <li><a href="#" aria-current="page">ものがたり<span class="wobble-line" aria-hidden="true"></span></a></li>
      <li><a href="#">教室</a></li>
    </ul>
    <a class="cta" href="#">お問い合わせ</a>
  </div>
</nav>

<div class="page">
  <p class="eyebrow">STORY</p>
  <h1>てしごとの、はじまり。</h1>
  <p>ひとつずつ手を動かしてつくる時間について、作り手たちの言葉を集めています。今日も誰かの手が、何かを形にしています。</p>
</div>
</body></html>
出典・参考: 自作の再現実装。SVG filter 要素の既定 region(x/y/width/height の初期値 -10%/-10%/120%/120%)は MDN(https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/filter)を参照し、下線が切れる挙動は自分で実機確認して region を広げて解消した。手描きのフィルタ技法自体は 19_handcraft_note の応用。コントラスト値は当該パーツの実測値を流用(同一配色)。