BLK-10Blocks
Split-Band CTA
A wide horizontal CTA band to drop into the flow of a page: Japanese label, heading and a short lead on the left, primary/secondary buttons on the right, split by a single hairline rule rather than two boxes. The faint amber wash (bg-amber-50/60) is applied only to the action column so colour marks function, not decoration. The rule-based counterpart to cta-banner (BLK-01), which is a centred gradient panel. Zero image assets.
- Added:
- 2026-08-04
- Dependencies:
- None
- tags
- #cta #section #block #split #hairline #brand #motion #no-image
Preview
"use client";
import { useEffect, useRef, useState } from "react";
import { Montserrat } from "next/font/google";
const montserrat = Montserrat({
weight: ["600", "700"],
subsets: ["latin"],
display: "swap",
});
/**
* CTA(スプリット帯) / Split-Band CTA
* 参考: 他社コーポレートサイトに見られる下部コンバージョン帯(横長・左右分割・面ではなく罫線で切る)。
* 構図だけ借りて First CH のブランド(白 + アンバー #d97706 / Montserrat + Noto Sans JP)で
* 再構築したもので、見た目のコピーではない。画像素材ゼロ。
*
* 既存 cta-banner(BLK-01)との対: あちらは「面」(アンバーのグラデ面・中央寄せ・縦積み)。
* こちらは「罫線」(白地・左右分割・横長帯)。同じCTAでもページ内での役割が違う:
* 中央寄せの面は"締め"の1枚、横長の帯は本文の流れを止めずに差し込む"通過点"に効く。
*
* 汎用技法メモ(web-design-playbook 還流用):
* - 分割は「箱を2つ並べる」のではなく「1本の縦罫で切る」: 左右それぞれに border / bg / radius を
* 与えるとカード2枚に見え、CTAの一体感が消える。外枠だけを持ち、内側は右カラムの
* border-l(SPでは border-t)1本で切る。ブレークポイントで罫の向きだけ入れ替える。
* - 極薄アンバーは「行動する側」にだけ敷く: 帯の全面に色を敷くと cta-banner と同じ"面"になる。
* bg-amber-50/60 を右カラム(ボタン側)だけに限定すると、色が装飾ではなく
* 「ここが行動の場所」という機能の印になる。左は白のまま余白で押す。
* - 見出しの中の1語だけをアンバーの下線でワイプする: 見出し全体を色付けせず、判断の核になる
* 語(ここでは「何を直すべきか」)に h-[3px] の下線を敷き、リビール時に origin-left の
* scale-x 0→1 で引く。下線は absolute なので語が折り返すと崩れる= whitespace-nowrap 必須。
* - 和文見出しの改行は <br> で決める: 和文は語境界で折り返さず幅いっぱいまで貪欲に詰めるため、
* 自然改行に任せると「を、」のような助詞が次行の頭に落ちる。各行を最も狭い環境(≒220px)でも
* 収まる長さに割ってから <br> を入れる。text-wrap: balance は和文では効きが読めないので使わない。
* - 小さな英語ラベルを既定にしない: 帯の頭は和文の短いラベル + 4px のアンバー角にする
* ("CONTACT" 等は別業種のサイトへそのまま移植でき、構図上の役割を持たない)。
* - 矢印は「送り出して、入れ替える」: size-4 の overflow-hidden な箱に矢印を2つ重ね、
* hover で1つ目を translate-x で外へ送り、-translate-x-4 に待たせた2つ目を 0 へ戻す。
* opacity を触らないので下地のアンバーに濁りが出ない。
* - 分割の罫線に「入場のとっかかり」を1つだけ置く: 罫の起点に w-10(SP)/ h-10(PC)の
* アンバー片を重ね、origin-left / origin-top で伸ばす。動かすのは transform と opacity だけ。
*
* 実案件への移植:
* <section> をそのまま本文セクションの間か記事末に置く(デモシェルは同梱していない)。
* ページ末の"締め"に使うなら cta-banner(BLK-01)、本文の途中に差し込むならこちら。
*/
export default function CtaSplitBand() {
const rootRef = useRef<HTMLElement>(null);
const [inView, setInView] = useState(false);
// 画面に入ったら1度だけリビール(reduced-motion 時は motion-reduce: クラスで即時表示)
useEffect(() => {
const el = rootRef.current;
if (!el) return;
const io = new IntersectionObserver(
(entries) => {
if (entries.some((e) => e.isIntersecting)) {
setInView(true);
io.disconnect();
}
},
{ threshold: 0.2 },
);
io.observe(el);
return () => io.disconnect();
}, []);
// translate-y + opacity のスタッガー(transform / opacity のみ)
const rise = (delay: number) => ({
className: `transition-[opacity,transform] duration-700 ease-out motion-reduce:transition-none ${
inView
? "translate-y-0 opacity-100"
: "translate-y-2 opacity-0 motion-reduce:translate-y-0 motion-reduce:opacity-100"
}`,
style: { transitionDelay: inView ? `${delay}ms` : "0ms" },
});
const label = rise(0);
const heading = rise(110);
const lead = rise(220);
const actions = rise(320);
return (
<section
ref={rootRef}
aria-labelledby="cta-split-band-heading"
className="w-full max-w-4xl overflow-hidden rounded-2xl border border-gray-200 bg-white"
>
<div className="flex flex-col lg:flex-row lg:items-stretch">
{/* 左: 見出しと短い説明。色も面も使わず余白で押す */}
<div className="flex flex-col justify-center px-6 py-9 sm:px-9 sm:py-11 lg:flex-1 lg:px-10 lg:py-12">
<p
className={`flex items-center gap-2 text-[11px] font-bold tracking-[0.14em] text-gray-900 ${label.className}`}
style={label.style}
>
<span
aria-hidden="true"
className="inline-block size-1 shrink-0 bg-amber-600"
/>
サイト改善のご相談
</p>
{/* 和文は語境界で折り返さないため、行の切れ目は <br> で決める。
1行が最も狭い環境(≒220px)でも収まる長さに割ってある */}
<h2
id="cta-split-band-heading"
className={`mt-4 text-xl leading-[1.5] font-bold tracking-tight text-gray-900 sm:text-2xl ${heading.className}`}
style={heading.style}
>
<span className="relative inline-block whitespace-nowrap">
直す順番
<span
aria-hidden="true"
className={`absolute -bottom-0.5 left-0 h-[3px] w-full origin-left bg-amber-600/70 transition-transform duration-700 ease-out motion-reduce:transition-none ${
inView ? "scale-x-100" : "scale-x-0 motion-reduce:scale-x-100"
}`}
style={{ transitionDelay: inView ? "430ms" : "0ms" }}
/>
</span>
だけ、
<br />
<span className={`${montserrat.className} tabular-nums`}>30</span>
分で決めませんか。
</h2>
<p
className={`mt-4 max-w-md text-[13px] leading-[1.9] text-gray-600 ${lead.className}`}
style={lead.style}
>
制作のご依頼が前提でなくて構いません。現在のアクセス・問い合わせ導線・更新体制を伺い、
先に手を付けるべき箇所をその場でお伝えします。
</p>
</div>
{/* 右: 行動の場所。極薄アンバーはここだけに敷く */}
<div className="relative flex shrink-0 flex-col justify-center border-t border-gray-200 bg-amber-50/60 px-6 py-8 sm:px-9 lg:w-[300px] lg:border-t-0 lg:border-l lg:px-8 lg:py-12">
{/* 分割の罫線に重ねるアンバー片(SP=横 / PC=縦。入場で起点から伸びる) */}
<span
aria-hidden="true"
className={`absolute -top-px left-6 h-px w-10 origin-left bg-amber-600 transition-transform duration-700 ease-out motion-reduce:transition-none sm:left-9 lg:hidden ${
inView ? "scale-x-100" : "scale-x-0 motion-reduce:scale-x-100"
}`}
/>
<span
aria-hidden="true"
className={`absolute top-0 -left-px hidden h-10 w-px origin-top bg-amber-600 transition-transform duration-700 ease-out motion-reduce:transition-none lg:block ${
inView ? "scale-y-100" : "scale-y-0 motion-reduce:scale-y-100"
}`}
/>
<div
className={`flex flex-col gap-3 ${actions.className}`}
style={actions.style}
>
{/* Primary: 矢印を送り出して入れ替える */}
<a
href="#"
className="group flex items-center justify-between gap-3 rounded-lg bg-amber-600 px-5 py-3.5 text-sm font-bold text-white transition-transform duration-300 ease-out hover:-translate-y-0.5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600 motion-reduce:transition-none motion-reduce:hover:translate-y-0"
>
無料相談を予約する
<span
aria-hidden="true"
className="relative block size-4 shrink-0 overflow-hidden"
>
<span className="absolute inset-0 flex items-center justify-center transition-transform duration-300 ease-out group-hover:translate-x-4 group-focus-visible:translate-x-4 motion-reduce:transition-none">
→
</span>
<span className="absolute inset-0 flex -translate-x-4 items-center justify-center transition-transform duration-300 ease-out group-hover:translate-x-0 group-focus-visible:translate-x-0 motion-reduce:transition-none">
→
</span>
</span>
</a>
{/* Secondary: 白地 + 罫線。主従が読めるよう font-weight まで落とす */}
<a
href="#"
className="group flex items-center justify-between gap-3 rounded-lg border border-gray-300 bg-white px-5 py-3 text-sm font-semibold text-gray-800 transition-colors hover:border-gray-900 hover:text-gray-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-600"
>
制作実績を見る
<span
aria-hidden="true"
className="inline-block shrink-0 transition-transform duration-300 ease-out group-hover:translate-x-1 group-focus-visible:translate-x-1 motion-reduce:transition-none"
>
→
</span>
</a>
</div>
<p
className={`mt-5 flex flex-col items-start gap-1 text-[11px] text-gray-500 sm:flex-row sm:flex-wrap sm:items-center sm:gap-x-2.5 ${actions.className}`}
style={{ transitionDelay: inView ? "400ms" : "0ms" }}
>
<span>ご返信は1営業日以内</span>
{/* 区切りの縦罫は横並びのときだけ。縦積みで出すと行末に線が余る */}
<span
aria-hidden="true"
className="hidden h-2.5 w-px shrink-0 bg-gray-300 sm:block"
/>
<span>
平日{" "}
<span className={`${montserrat.className} tabular-nums`}>
10:00–19:00
</span>
</span>
</p>
</div>
</div>
</section>
);
}
Add to your project via shadcn CLI
npx shadcn@latest add https://designs.first-ch.com/r/cta-split-band.json