First CH Designs
BLK-16Blocks

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つ並べます。

Why First CH
  • 体制

    通しで、ひとりが見る

    ヒアリングから情報設計、デザイン、実装、公開後の改善までを同じ担当が通して受け持ちます。工程をまたぐたびに意図が薄まることがありません。

  • 設計

    成果から、逆算する

    好みの色や流行りの動きからではなく、問い合わせ・採用応募・資料請求といった「起こしたい行動」から構成を決めます。公開後に数字で振り返るところまでが制作です。

  • 納品後

    渡したあと、動かせる

    自分で更新できる範囲と手順を、編集マップと操作マニュアルにして納品時にお渡しします。文字ひとつ直すたびに見積もりが要る状態にはしません。

  • 費用

    見積もりに、あとから足さない

    対応範囲と修正回数を、契約前に文章で確定させます。範囲外の追加が必要になった場合は、着手する前に金額をお出しします。

ご相談の段階で費用は発生しません。要件が固まっていない状態でも、整理からお手伝いします。

"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