BLK-60Blocks
特大英字ウォーターマーク付きセクション見出し
節の扉に置く見出しブロック。背面に版面幅いっぱいの特大英字を約4%の薄グレーで敷き、前景の和文見出し・リード・「この節で扱うこと」4行の索引と重ねる。透かしはインラインSVGのtextLengthで幅を固定するため、フォントが変わっても横スクロールが出ない。前景は一切アニメーションせず、索引は注目した行以外を薄くして視線だけを動かす(JS不要のサーバーコンポーネント)。
- 追加日:
- 2026-09-14
- 依存:
- なし
- tags
- #heading #section #typography #watermark #svg #index #no-image
プレビュー
BLK-60節の扉方針
4 項目
03つくる前に決めること
速さより、迷わなさ
作り直しの多くは、手を動かす前の合意不足から起きます。この節では、着手前にそろえておく4つの軸と、それぞれで何を決めるのかを先に並べます。
この節で扱うこと
- 01前提をそろえる誰に何を届けるかを1枚に書き切る合意
- 02順番を決める出せる範囲と時期で並べ替える優先
- 03やめる線を引く伸びない施策を止める条件を先に書く撤退
- 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