Skip to content
fandhe-frontendcore v0.4.3
GitHub

incentives-inline-strip

fandhe-frontend-pre-styled-ui の icon / text / visually-hidden 部品を合成した、特典紹介を 1 行の帯で見せる実例です。Blocks セクションは新規部品を追加するものではなく、既存の Themes/Primitives 部品を組み合わせた実例集であることに注意してください(主参照は対応表 ID R1022、集約元は R0560。出典の固有名・ファイル名は記載しません)。

アイコンと短いタイトルの組を横一行に並べ、説明文は付けません。集約元の差分を 2 通りの例として並べています。

  • 例 A: 広い幅では 1 行中央寄せ、狭い幅では自然に折り返します
  • 例 B: 1 行のまま並べ、はみ出した分は横スクロールで見せます。横スクロール枠は tabindex="0" + role="region" + aria-label を持ち、マウスに依存せずキーボードの矢印キーでもスクロールできます

本 Demo は静的な表示例であり、<form> 要素も JavaScript も一切持ちません。帯に見える見出しがないため、各例の先頭にはスクリーンリーダー向けの導入文(visually-hidden)を置いています。文言はすべて独自に書いた架空のものであり、実企業名・実クレデンシャル・PII を含みません。

Demo

中央寄せ + 折り返し。広い幅では 1 行中央寄せ、狭い幅では自然に折り返す。

ご購入特典
  • 送料無料

  • 30 日間返品

  • 安全なお支払い

  • 24 時間サポート

  • ギフト包装

横スクロール 1 行。広い幅では 1 行に収まり、狭い幅でははみ出した分を横スクロールで見せる。

ご購入特典
  • 送料無料

  • 30 日間返品

  • 安全なお支払い

  • 24 時間サポート

  • ギフト包装

使用部品

Rust コード

Rust
use fandhe_frontend_core::{div, el, text, Node};
use fandhe_frontend_pre_styled_ui::icon::{icon, IconProps};
use fandhe_frontend_pre_styled_ui::text::{self as styled_text, TextProps, TextSize, TextWeight};
use fandhe_frontend_pre_styled_ui::visually_hidden;
use fandhe_frontend_pre_styled_ui::Size;

/// 特典 1 件分の架空データ(タイトルと自作アイコンのパス)。
struct Incentive {
    title: &'static str,
    icon_path_d: &'static str,
}

/// 特典 5 件(架空、実在の企業・製品とは無関係)。自作の単純な幾何パスのみ
/// を使い、lucide 等の著作物は複製しない。
const INCENTIVES: [Incentive; 5] = [
    Incentive {
        title: "送料無料",
        icon_path_d: "M3 7l9-4 9 4-9 4-9-4zM3 7v10l9 4 9-4V7M12 11v10",
    },
    Incentive {
        title: "30 日間返品",
        icon_path_d: "M4 4v6h6M4 10a8 8 0 1012-6",
    },
    Incentive {
        title: "安全なお支払い",
        icon_path_d: "M12 2l8 4v6c0 5-3.5 9-8 10-4.5-1-8-5-8-10V6z",
    },
    Incentive {
        title: "24 時間サポート",
        icon_path_d: "M12 21a9 9 0 100-18 9 9 0 000 18zM12 7v5l4 2",
    },
    Incentive {
        title: "ギフト包装",
        icon_path_d: "M3 9h18v4H3zM5 9v10h14V9M12 9v10M9 9c0-2 1-4 3-4s3 2 3 4M15 9c0-2-1-4-3-4",
    },
];

/// 装飾用の自作幾何アイコン(`fill="none"` + `stroke="currentColor"` の
/// 線画、`feature_three_column_icons::geo_icon` と同型の判断)。
fn geo_icon(path_d: &'static str) -> Node {
    icon(
        &IconProps {
            size: Size::Md,
            ..IconProps::default()
        },
        vec![("data-blocks-incentives-inline-strip-icon", "")],
        vec![el(
            "path",
            vec![
                ("d", path_d),
                ("fill", "none"),
                ("stroke", "currentColor"),
                ("stroke-width", "2"),
                ("stroke-linecap", "round"),
                ("stroke-linejoin", "round"),
            ],
            vec![],
        )],
    )
}

/// 特典 1 件(アイコン → タイトル)を `li` で組み立てる。
fn item(i: &Incentive) -> Node {
    el(
        "li",
        vec![("class", "blocks-incentives-inline-strip-item")],
        vec![
            geo_icon(i.icon_path_d),
            styled_text::text(
                &TextProps {
                    size: TextSize::Sm,
                    weight: TextWeight::Medium,
                    ..TextProps::default()
                },
                vec![("data-blocks-incentives-inline-strip-title", "")],
                vec![text(i.title)],
            ),
        ],
    )
}

/// 注記行(インスタンスの原案差分の要約)。
fn note(body: &'static str) -> Node {
    styled_text::text(
        &TextProps {
            size: TextSize::Xs,
            ..TextProps::default()
        },
        vec![("data-blocks-incentives-inline-strip-note", "")],
        vec![text(body)],
    )
}

/// 見出しのない帯の先頭に置く、スクリーンリーダー向け導入文 + 特典一覧
/// (`ul`)。`variant` が `Some("scroll")` のとき横スクロール 1 行構成
/// (インスタンス B)、`None` のとき中央寄せ・折り返し構成(インスタンス A)
/// になる。
fn strip(hidden_label: &'static str, variant: Option<&'static str>) -> Vec<Node> {
    let mut ul_attrs = vec![("class", "blocks-incentives-inline-strip-list")];
    if let Some(v) = variant {
        ul_attrs.push(("data-variant", v));
    }
    let list = el(
        "ul",
        ul_attrs,
        INCENTIVES.iter().map(item).collect::<Vec<Node>>(),
    );
    vec![
        visually_hidden::root(vec![], vec![text(hidden_label)]),
        list,
    ]
}

/// `incentives-inline-strip` の Demo 本体。呼び出しごとに同一の `Node` を
/// 返す純関数(他 block と同じ状態を持たない設計)。
#[must_use]
pub fn demo() -> Node {
    let mut instance_a_children = vec![note(
        "中央寄せ + 折り返し。広い幅では 1 行中央寄せ、狭い幅では自然に折り返す。",
    )];
    instance_a_children.extend(strip("ご購入特典", None));
    let instance_a = div(
        vec![("class", "blocks-incentives-inline-strip-instance")],
        instance_a_children,
    );

    let mut instance_b_children = vec![note(
        "横スクロール 1 行。広い幅では 1 行に収まり、狭い幅でははみ出した分を横スクロールで見せる。",
    )];
    let scroll_list = el(
        "div",
        vec![
            ("class", "blocks-incentives-inline-strip-scroller"),
            ("tabindex", "0"),
            ("role", "region"),
            ("aria-label", "特典一覧(横にスクロールできます)"),
        ],
        strip("ご購入特典", Some("scroll")),
    );
    instance_b_children.push(scroll_list);
    let instance_b = div(
        vec![("class", "blocks-incentives-inline-strip-instance")],
        instance_b_children,
    );

    div(
        vec![("class", "blocks-incentives-inline-strip-layout")],
        vec![instance_a, instance_b],
    )
}

原案差分メモ

  • 例 A は集約元 R0560 に対応し、広い幅では中央寄せ、収まらなければ flex-wrap: wrap で自然に折り返す構成を表します。
  • 例 B は主参照 R1022 に対応し、flex-wrap: nowrap + overflow-x: auto で 1 行のまま並べ、はみ出した分を横スクロールで見せる構成を表します。横スクロール枠には tabindex="0" + role="region" + aria-label を付け、キーボード操作でもスクロールできるようにしています。
  • 参照元が持つ装飾・配色・説明文は持ち込まず、アイコンは自作の単純な幾何パスのみを使い、タイトルも独自に書いた架空の文言(送料無料・30 日間返品・安全なお支払い・24 時間サポート・ギフト包装)に差し替えています。
  • 帯に見える h2/h3 等の見出しはないため、各例の先頭へ visually-hidden でスクリーンリーダー向けの導入文(「ご購入特典」)を置いています。
  • 狭い幅での折り返し・横スクロールの切り替えは無 JS のため CSS のみで表現しています。実際のブラウザでの表示確認は本 Demo では行っていません。

関連情報: Icon / Text / Visually Hidden