Strengths — Asymmetric Grid
A strengths block that lists several reasons without falling back to an even grid. On a 12-column track the heading:body ratio rotates 7:5 → 5:5 (indented by one column) → 4:7 → 7:5, stepping the left edge so that hairline rules alone create rhythm. A vertical Latin mini-label stands in the left gutter, and the rail below it doubles as the list's left boundary — a compositional role, not decoration (it flips back to horizontal at 375px). Because reasons carry no inherent order, there are no 01/02 numeric markers; short Japanese kickers characterise each row instead. Grouped with hairline rules and negative space rather than cards, with rules drawn in via an origin-left scale-x wipe. Zero images, zero dependencies. A deliberate counterpart to strength-stat (BLK-05), which instead blows up a single KPI.
- Added:
- 2026-08-09
- Dependencies:
- None
- tags
- #block #strengths #reasons #asymmetric-grid #editorial #vertical-label #hairline #no-image #no-deps
Preview
BLK-16 — Strengths / 選ばれる理由
同じ型で、同じものをつくらない。
制作会社は数あれど、選ばれる理由は会社ごとに違います。First CH が中小企業のWebで選ばれてきた理由を、飾らずに4つ並べます。
- 体制
通しで、ひとりが見る
ヒアリングから情報設計、デザイン、実装、公開後の改善までを同じ担当が通して受け持ちます。工程をまたぐたびに意図が薄まることがありません。
- 設計
成果から、逆算する
好みの色や流行りの動きからではなく、問い合わせ・採用応募・資料請求といった「起こしたい行動」から構成を決めます。公開後に数字で振り返るところまでが制作です。
- 納品後
渡したあと、動かせる
自分で更新できる範囲と手順を、編集マップと操作マニュアルにして納品時にお渡しします。文字ひとつ直すたびに見積もりが要る状態にはしません。
- 費用
見積もりに、あとから足さない
対応範囲と修正回数を、契約前に文章で確定させます。範囲外の追加が必要になった場合は、着手する前に金額をお出しします。
ご相談の段階で費用は発生しません。要件が固まっていない状態でも、整理からお手伝いします。
"use client";
import { useEffect, useRef, useState } from "react";
import { Montserrat } from "next/font/google";
const montserrat = Montserrat({
weight: ["500", "600", "700"],
subsets: ["latin"],
display: "swap",
});
/**
* 選ばれる理由・強み(非対称グリッド)
* 参考: BERG ONISHI INC. の非対称2カラム+脇に添える縦組み英字ミニラベルの構図を
* First CH ブランドで再構築(コピーではない・素材/文言は全て自社オリジナル)。
*
* 汎用技法メモ(web-design-playbook 還流用):
* - 非対称グリッド(PT-LAYOUT-001): 12カラム上で「見出し:本文」の比率を 7:5 → 5:5(インデント)
* → 4:7 → 7:5 と**2〜3パターンでローテーション**する。全行を同じ比率にすると結局は均等グリッドに
* 戻り、1行ごとにランダムにすると読み順が崩れる。col-span だけでなく col-start をずらして
* 左端を階段状にすると、罫線だけで「リズムのある表」に見える。
* - 縦組み英字ミニラベル(PT-TYPE-007): [writing-mode:vertical-rl] で本のページの背表紙のように
* 立てる。**構図上の役割を持たせるのが条件**で、ここではラベル+その下のレールが理由リストの
* 左境界そのものを兼ねる(飾りとして置かない)。375px では横組みに戻す(縦組みのまま残すと読めない)。
* - 脱カード: 各理由を箱で囲まず border-t の hairline と広い余白だけで区切る。
* 罫線は origin-left の scale-x ワイプで引くと「線を引いていく」動きになり、
* opacity フェードだけより素材が少ないまま情報が立ち上がる(transform のみなので合成が軽い)。
* - 番号マーカー(01/02/03)は使わない: 「選ばれる理由」は順序を持たないため。
* 代わりに和文のミニラベル(体制/設計/納品後/費用)で行の性格を示す。
*/
type Reason = {
/** 行の性格を示す和文ミニラベル(順序を持たないので番号は振らない) */
kicker: string;
title: string;
body: string;
/** 12カラム上の非対称配置。行ごとに比率をローテーションする */
titleCol: string;
bodyCol: string;
};
const reasons: Reason[] = [
{
kicker: "体制",
title: "通しで、ひとりが見る",
body: "ヒアリングから情報設計、デザイン、実装、公開後の改善までを同じ担当が通して受け持ちます。工程をまたぐたびに意図が薄まることがありません。",
// 7:5
titleCol: "md:col-span-7",
bodyCol: "md:col-span-5",
},
{
kicker: "設計",
title: "成果から、逆算する",
body: "好みの色や流行りの動きからではなく、問い合わせ・採用応募・資料請求といった「起こしたい行動」から構成を決めます。公開後に数字で振り返るところまでが制作です。",
// 5:5(左を1カラム分インデントして階段をつくる)
titleCol: "md:col-span-5 md:col-start-2",
bodyCol: "md:col-span-5 md:col-start-8",
},
{
kicker: "納品後",
title: "渡したあと、動かせる",
body: "自分で更新できる範囲と手順を、編集マップと操作マニュアルにして納品時にお渡しします。文字ひとつ直すたびに見積もりが要る状態にはしません。",
// 5:6(本文側を優勢にして重心を反転させる。見出しが折れない下限が5カラム)
titleCol: "md:col-span-5",
bodyCol: "md:col-span-6 md:col-start-7",
},
{
kicker: "費用",
title: "見積もりに、あとから足さない",
body: "対応範囲と修正回数を、契約前に文章で確定させます。範囲外の追加が必要になった場合は、着手する前に金額をお出しします。",
// 7:5(1行目の比率へ戻して締める)
titleCol: "md:col-span-7",
bodyCol: "md:col-span-5",
},
];
export default function StrengthAsymmetric() {
const rootRef = useRef<HTMLElement>(null);
const [active, setActive] = useState(false);
// 画面内に入ったらリビール開始(IO 非対応環境は即開始)
useEffect(() => {
const el = rootRef.current;
if (!el) return;
if (typeof IntersectionObserver === "undefined") {
const id = requestAnimationFrame(() => setActive(true));
return () => cancelAnimationFrame(id);
}
const io = new IntersectionObserver(
(entries) => {
if (entries.some((e) => e.isIntersecting)) {
setActive(true);
io.disconnect();
}
},
{ threshold: 0.15 }
);
io.observe(el);
return () => io.disconnect();
}, []);
const reveal = `transition-[opacity,transform] duration-700 ease-out motion-reduce:transition-none ${
active
? "translate-y-0 opacity-100"
: "translate-y-6 opacity-0 motion-reduce:translate-y-0 motion-reduce:opacity-100"
}`;
// 罫線は「引かれる」動き(transform のみ)
const rule = `origin-left transition-transform duration-[900ms] ease-out motion-reduce:transition-none ${
active ? "scale-x-100" : "scale-x-0 motion-reduce:scale-x-100"
}`;
return (
<section
ref={rootRef}
aria-labelledby="strength-asymmetric-heading"
className="w-full max-w-5xl bg-white px-1"
>
{/* ヘッダー(7:5 の非対称。見出しは左、リードは右へ寄せる) */}
<header className="grid grid-cols-1 gap-6 md:grid-cols-12 md:gap-10">
<div className="md:col-span-7">
<p
className={`${montserrat.className} text-xs font-bold tracking-[0.25em] text-amber-600 uppercase ${reveal}`}
style={{ transitionDelay: "0ms" }}
>
BLK-16 — Strengths / 選ばれる理由
</p>
<h2
id="strength-asymmetric-heading"
className={`mt-4 text-[clamp(1.75rem,3.6vw,2.5rem)] leading-[1.3] font-bold tracking-tight text-gray-900 ${reveal}`}
style={{ transitionDelay: "80ms" }}
>
<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>
</div>
<div className="md:col-span-5 md:self-end">
<p
className={`text-[15px] leading-[1.95] font-medium text-gray-600 ${reveal}`}
style={{ transitionDelay: "160ms" }}
>
制作会社は数あれど、選ばれる理由は会社ごとに違います。First CH が中小企業のWebで選ばれてきた理由を、飾らずに4つ並べます。
</p>
</div>
</header>
{/* 縦組みラベル(スパイン)+ 理由リスト */}
<div className="mt-12 grid grid-cols-1 gap-4 md:mt-16 md:grid-cols-[auto_1fr] md:gap-8">
{/* スパイン: モバイルは横組み、md 以上で縦組みに切り替える(PT-TYPE-007) */}
<div
className={`flex items-center gap-3 md:flex-col md:items-center md:gap-4 md:pt-1 ${reveal}`}
style={{ transitionDelay: "220ms" }}
>
<span
className={`${montserrat.className} shrink-0 text-[10px] font-semibold tracking-[0.3em] whitespace-nowrap text-gray-400 uppercase md:[writing-mode:vertical-rl]`}
>
Why First CH
</span>
{/* レール: リスト左端の境界そのものを兼ねる(飾りではない) */}
<span aria-hidden="true" className="flex flex-1 items-center gap-0 md:w-px md:flex-col">
<span className="h-px w-10 bg-amber-600 md:h-10 md:w-px" />
<span className="h-px flex-1 bg-gray-200 md:w-px" />
</span>
</div>
{/* 理由リスト(カードで囲まず hairline と余白でグルーピング) */}
<div className="relative">
<ul className="grid grid-cols-1">
{reasons.map((r, i) => (
<li
key={r.title}
className="group relative grid grid-cols-1 gap-3 py-8 md:grid-cols-12 md:gap-8 md:py-10"
>
{/* 上端の hairline(origin-left の scale-x で「引かれる」動きにする) */}
<span
aria-hidden="true"
className={`absolute inset-x-0 top-0 h-px bg-gray-200 ${rule}`}
style={{ transitionDelay: `${260 + i * 110}ms` }}
/>
{/* hover 時にアンバーの線が重なって引かれる(transform のみ) */}
<span
aria-hidden="true"
className="absolute inset-x-0 top-0 h-px origin-left scale-x-0 bg-amber-600 transition-transform duration-500 ease-out group-hover:scale-x-100 motion-reduce:transition-none"
/>
<div className={`flex flex-col ${r.titleCol}`}>
<span className="text-[11px] font-bold tracking-[0.18em] text-amber-700">
{r.kicker}
</span>
<h3 className="mt-2.5 text-[clamp(1.1rem,2.4vw,1.4rem)] leading-[1.55] font-bold tracking-tight text-gray-900">
{r.title}
</h3>
</div>
<div className={`flex items-start gap-3 md:pt-1 ${r.bodyCol}`}>
<span
aria-hidden="true"
className="mt-[0.95em] h-px w-4 flex-none bg-gray-300 transition-colors duration-300 group-hover:bg-amber-600 motion-reduce:transition-none"
/>
<p className="text-[13.5px] leading-[2] text-gray-600">{r.body}</p>
</div>
</li>
))}
</ul>
{/* 表を閉じる最終行の罫線(リスト項目にしないよう ul の外へ置く) */}
<span
aria-hidden="true"
className={`absolute inset-x-0 bottom-0 h-px bg-gray-200 ${rule}`}
style={{ transitionDelay: `${260 + reasons.length * 110}ms` }}
/>
</div>
</div>
{/* フッター(7:5 の非対称。CTA は左、補足は右へ) */}
<div className="mt-10 grid grid-cols-1 gap-4 md:grid-cols-12 md:gap-8">
<div className={`md:col-span-7 ${reveal}`} style={{ transitionDelay: "760ms" }}>
<a
href="#"
className="group inline-flex items-center gap-2 text-sm font-semibold text-gray-900 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-amber-600"
>
<span className="relative">
進め方を詳しく見る
<span className="absolute -bottom-1 left-0 h-px w-full origin-left scale-x-100 bg-gray-900 transition-transform duration-300 ease-out group-hover:scale-x-0 motion-reduce:transition-none" />
<span className="absolute -bottom-1 left-0 h-px w-full origin-right scale-x-0 bg-amber-600 transition-transform delay-150 duration-300 ease-out group-hover:origin-left group-hover:scale-x-100 motion-reduce:transition-none" />
</span>
<span
aria-hidden="true"
className="inline-block transition-transform duration-300 ease-out group-hover:translate-x-1 motion-reduce:transition-none"
>
→
</span>
</a>
</div>
<p
className={`text-[12.5px] leading-[1.9] text-gray-500 md:col-span-5 md:self-center ${reveal}`}
style={{ transitionDelay: "840ms" }}
>
ご相談の段階で費用は発生しません。要件が固まっていない状態でも、整理からお手伝いします。
</p>
</div>
</section>
);
}
Add to your project via shadcn CLI
npx shadcn@latest add https://designs.first-ch.com/r/strength-asymmetric.json