First CH Designs
BLK-60Blocks

特大英字ウォーターマーク付きセクション見出し

節の扉に置く見出しブロック。背面に版面幅いっぱいの特大英字を約4%の薄グレーで敷き、前景の和文見出し・リード・「この節で扱うこと」4行の索引と重ねる。透かしはインラインSVGのtextLengthで幅を固定するため、フォントが変わっても横スクロールが出ない。前景は一切アニメーションせず、索引は注目した行以外を薄くして視線だけを動かす(JS不要のサーバーコンポーネント)。

追加日:
2026-09-14
依存:
なし
tags
#heading #section #typography #watermark #svg #index #no-image

プレビュー

BLK-60節の扉方針

4 項目

03つくる前に決めること

速さより、迷わなさ

作り直しの多くは、手を動かす前の合意不足から起きます。この節では、着手前にそろえておく4つの軸と、それぞれで何を決めるのかを先に並べます。

この節で扱うこと

  1. 01前提をそろえる誰に何を届けるかを1枚に書き切る合意
  2. 02順番を決める出せる範囲と時期で並べ替える優先
  3. 03やめる線を引く伸びない施策を止める条件を先に書く撤退
  4. 04渡し方を決める公開後に運用側が触れる形にする継続

この4つが決まってから、はじめて制作に入ります。決め方そのものも、案件ごとに見直します

import { Montserrat } from "next/font/google";

const montserrat = Montserrat({ subsets: ["latin"], weight: ["600", "800"], display: "swap" });

/**
 * 特大英字ウォーターマーク付きセクション見出し(BLK-60)
 *
 * 差別化メモ: 既存の見出し・タイポ系は「前景の文字をどう動かすか」で分かれている(スクロール連動の
 * リビール / タブ切替の文字分解 / タイプライター+グリッチ / 1字ずつの揺れ)。素の見出しセットは
 * 英字キッカー+見出し+リードの3行で終わる。本ブロックは前景を一切動かさず(JSゼロ・イベントなし)、
 * 背面の幅ロック特大英字と「この節で扱うこと」4行の索引という**版面の組み方だけ**で節の扉を作る。
 * 節の定型も意図的に外した: 英字キッカーを廃して英字は背面のウォーターマークへ振り替え /
 * 和文見出しは「。」止めにしない / 角丸ゼロ / 末尾の矢印リンクなし / 出現アニメーションなし。
 *
 * 汎用技法メモ(web-design-playbook 還流用):
 * - **特大英字は vw ではなくインラインSVGで幅ロックする**: font-size を vw で与えても、実際の文字送りは
 *   フォントのメトリクス次第なので「版面幅にちょうど収まる」保証がない。webフォント読込前の
 *   フォールバックやフォント差し替えで簡単にはみ出し、そのまま横スクロールの発生源になる。
 *   `<text textLength="(viewBox幅)" lengthAdjust="spacingAndGlyphs">` は総送り量を数値で固定するので、
 *   どのフォントで描かれても幅が1pxもずれない。SVGは w-full h-auto で親幅に追従させるだけでよく、
 *   JSでの計測もリサイズ監視も要らない(=サーバーコンポーネントのまま実現できる)。
 * - **「ぶち抜き」は要素の overflow ではなく viewBox のクロップで作る**: 字面の高さより低い viewBox を
 *   与えると letters の上下が切れる。親の高さやスクロール位置に依存しないので、一覧の縮小プレビューでも
 *   詳細ページでも同じ絵になる。逆に「はみ出させて親で切る」設計にすると、縮小率が変わるだけで
 *   切れ方が変わってしまう。
 * - **文字数で字の大きさが決まる**: 幅Wへ n 文字を敷き詰めると大文字の高さは概ね W/n×1.3。特大に
 *   見せたいなら語を短くするのが唯一効く手で、濃度や色をいじっても大きさの印象は変わらない。
 * - **背面レイヤーは親に isolate、装飾に -z-10**: 親が独立した重なり文脈を作るので、-z-10 は
 *   「親の背景より上・本文より下」に必ず入り、ページ全体の重なり順へ漏れない。
 * - **透かしの濃度は 4%前後**: 1〜2%だと縮小プレビューやコントラスト強調環境で消え、6%を超えると
 *   前景の本文と読み合いになる。装飾なので aria-hidden とし、支援技術には一切読ませない。
 * - **索引は「注目した行以外を薄くする」**: hover した行を強調するのではなく兄弟を落とすと、
 *   行そのものは1pxも動かないまま視線だけが集まる(動かすのは opacity と、行頭レールの scaleY のみ)。
 *   キーボードでは :focus-within で同じ状態へ入る。
 * - **Tailwind v4 の scale-y-0 / translate-x-1 は transform ではなく scale / translate プロパティに出る**:
 *   transition-transform は v4 では transform,translate,scale,rotate を対象にするので動きは効くが、
 *   検証で getComputedStyle().transform を見ると "none" が返る。合否は .scale / .translate を見て採る。
 */

type IndexItem = {
  no: string;
  title: string;
  note: string;
  /** 行の性格を示す和文ミニラベル(英字ラベルは背面のウォーターマークへ譲る) */
  label: string;
};

const items: IndexItem[] = [
  { no: "01", title: "前提をそろえる", note: "誰に何を届けるかを1枚に書き切る", label: "合意" },
  { no: "02", title: "順番を決める", note: "出せる範囲と時期で並べ替える", label: "優先" },
  { no: "03", title: "やめる線を引く", note: "伸びない施策を止める条件を先に書く", label: "撤退" },
  { no: "04", title: "渡し方を決める", note: "公開後に運用側が触れる形にする", label: "継続" },
];

/** 背面の透かし。viewBox幅=textLength なので、どのフォントで描かれても版面幅ぴったりに収まる */
function Watermark({ word }: { word: string }) {
  return (
    // forced-colors: では透かしの色が潰れて本文の可読性を奪うので、丸ごと出さない
    <div
      aria-hidden="true"
      className="pointer-events-none absolute -right-6 -bottom-1 -left-6 -z-10 select-none forced-colors:hidden sm:-right-10 sm:-left-10"
    >
      {/* 高さ210の箱に字面約242を描く=上下が切れて「画面をぶち抜いた」見え方になる */}
      <svg viewBox="0 0 1000 210" className="h-auto w-full" focusable="false" role="presentation">
        <text
          x="0"
          y="228"
          textLength="1000"
          lengthAdjust="spacingAndGlyphs"
          fontSize="345"
          fontWeight="800"
          className={`${montserrat.className} fill-gray-900/[0.045]`}
        >
          {word}
        </text>
      </svg>
    </div>
  );
}

export default function WatermarkHeadingSection() {
  return (
    <section
      aria-labelledby="wmh-heading"
      className="relative isolate w-full max-w-5xl overflow-hidden border-y border-gray-200 bg-white px-6 py-12 sm:px-10 sm:py-16"
    >
      {/* 標本の見出し帯(英字キッカーの代わりに、節の位置だけを示す) */}
      <div className="flex items-baseline justify-between gap-4 border-b border-gray-200 pb-3">
        <p className={`${montserrat.className} text-[11px] font-semibold tracking-wide text-gray-500`}>
          BLK-60
          <span aria-hidden="true" className="mx-2 text-gray-300">
            /
          </span>
          <span className="font-sans font-bold text-gray-700">節の扉</span>
          <span aria-hidden="true" className="mx-2 text-gray-300">
            /
          </span>
          <span className="font-sans font-medium text-gray-400">方針</span>
        </p>
        <p className={`${montserrat.className} text-[11px] font-semibold text-gray-400 tabular-nums`}>
          4 項目
        </p>
      </div>

      {/* 透かしは padding の外まで伸ばす(切るのは section の overflow-hidden だけ) */}
      <header className="relative pt-8 pb-10 sm:pt-10 sm:pb-12">
        <Watermark word="AXIS" />

        <div className="flex items-center gap-3">
          <span className={`${montserrat.className} text-[13px] font-bold text-amber-600 tabular-nums`}>
            03
          </span>
          <span aria-hidden="true" className="h-px w-8 bg-amber-600" />
          <span className="text-[11px] font-bold tracking-[0.04em] text-gray-500">つくる前に決めること</span>
        </div>

        <h2
          id="wmh-heading"
          className="mt-5 max-w-2xl text-[clamp(1.8rem,4.2vw,2.7rem)] leading-[1.3] font-bold tracking-tight text-gray-900"
        >
          速さより、
          <span className="relative whitespace-nowrap">
            迷わなさ
            <span aria-hidden="true" className="absolute -bottom-1 left-0 h-[3px] w-full bg-amber-500/70" />
          </span>
        </h2>

        {/* 和文の段落はソース上でも改行しない(折ると「、 手」のように句読点の後だけ空く) */}
        <p className="mt-7 max-w-xl text-[15px] leading-[1.95] font-medium text-gray-600">
          作り直しの多くは、手を動かす前の合意不足から起きます。この節では、着手前にそろえておく4つの軸と、それぞれで何を決めるのかを先に並べます。
        </p>
      </header>

      <p className="text-[11px] font-bold tracking-[0.04em] text-gray-500">この節で扱うこと</p>

      <ol className="wmh-index mt-4 list-none">
        {items.map((item) => (
          <li
            key={item.no}
            className="wmh-row group relative border-t border-gray-200 transition-opacity duration-300 ease-out last:border-b motion-reduce:transition-none"
          >
            {/* 行頭のレール。行そのものは動かさず、これと不透明度だけで注目を作る */}
            <span
              aria-hidden="true"
              className="absolute top-0 left-0 h-full w-px origin-top scale-y-0 bg-amber-600 transition-transform duration-500 ease-out group-hover:scale-y-100 group-focus-within:scale-y-100 motion-reduce:transition-none"
            />
            <a
              href="#"
              className="grid grid-cols-[2.75rem_minmax(0,1fr)] items-baseline gap-x-4 gap-y-1.5 py-5 pl-3 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-amber-600 sm:grid-cols-[3.25rem_minmax(0,10.5rem)_minmax(0,1fr)_3rem] sm:gap-x-6"
            >
              <span className={`${montserrat.className} text-[12px] font-bold text-amber-600 tabular-nums`}>
                {item.no}
              </span>
              <span className="text-[15px] font-bold tracking-tight text-gray-900 transition-transform duration-300 ease-out group-hover:translate-x-1 group-focus-within:translate-x-1 motion-reduce:transition-none">
                {item.title}
              </span>
              <span className="col-start-2 text-[13px] leading-[1.9] text-gray-500 sm:col-start-3">
                {item.note}
              </span>
              <span className="col-start-2 text-[11px] font-medium text-gray-400 sm:col-start-4 sm:text-right">
                {item.label}
              </span>
            </a>
          </li>
        ))}
      </ol>

      <p className="mt-8 text-[12px] leading-[1.95] text-gray-500">
        この4つが決まってから、はじめて制作に入ります。決め方そのものも、案件ごとに見直します
      </p>

      <style>{`
        /* 注目した行を強めるのではなく、それ以外を落とす(行は1pxも動かない) */
        .wmh-index:hover .wmh-row { opacity: 0.42; }
        .wmh-index .wmh-row:hover,
        .wmh-index .wmh-row:focus-within { opacity: 1; }
      `}</style>
    </section>
  );
}

shadcn CLI でプロジェクトに追加

npx shadcn@latest add https://designs.first-ch.com/r/watermark-heading-section.json