media-object
fandhe-frontend-pre-styled-ui の item / image 部品を合成した、画像(またはアイコン)と見出し・説明文を横並びにする media object の整列パターン集です。Blocks セクションは新規部品を追加するものではなく、既存の Themes/Primitives 部品を組み合わせた実例集であることに注意してください(主参照は対応表 ID R1062、集約元は R1063〜R1069。出典の固有名・ファイル名は記載しません)。
上揃え(基準形)を軸に、縦中央揃え・下揃え・画像を行高いっぱいに伸ばす・画像を右側に配置・狭幅で縦積み・狭幅で画像を全幅化・本文内への小さい media object の入れ子という 7 通りの差分パターンを併記しています。狭幅パターン(縦積み・画像全幅化)は、ビューポート全体ではなく各パネルの実測幅で折り返しを切り替えるため @container を使い、パネルへ max-width: 20rem を与えて同一ビューポートのまま狭幅後の見た目を再現しています。
本 Demo は静的な整列パターン集であり、<form> 要素・ボタン・リンク・id 属性を一切持たず、データの取得・送信・状態管理を行いません。画像はすべて装飾用のプレースホルダ(alt="")で、見出し・説明文の文言はすべて独自に書いた架空のものであり、実企業名・実クレデンシャル・PII を含みません。
Demo
上揃え(基準形)
サイドバーと本文の比率を見直し、狭い画面での折り返しを改善しました。
縦中央揃え
メール通知とアプリ内通知を個別に切り替えられるようにしました。
下揃え
表示中のデータを CSV 形式でダウンロードできるようになりました。
画像を行高いっぱいに伸ばす
更新日時・関連度・名前の 3 種類から並べ替え基準を選べます。狭いカラムで折り返すことで、media が行高いっぱいに伸びる様子を確認できます。
画像を右側に配置
よく使う操作をキーボードだけで完結できるようにしました。
狭幅で縦積み
アイテムに自由なタグを付けて絞り込めるようになります。
狭幅で画像を全幅化
共有リンクに有効期限を設定し、期限切れ後は自動で無効になります。
本文内に小さい media object を入れ子
今回のリリースでは以下の変更を行いました。
同じ整列パターンを縮小して本文内に再利用した例です。
使用部品
Rust コード
use crate::blocks::dummy_assets;
use fandhe_frontend_core::{div, el, text, Node};
use fandhe_frontend_pre_styled_ui::image::{self, ImageProps};
use fandhe_frontend_pre_styled_ui::item::{self, ItemMediaVariant, ItemRootProps};
/// media(画像)パーツを組み立てる。`extra_attr` は `variant` 固有の
/// フック用 `data-*` 属性(例: `-media-nested`)。
fn media_image(extra_attr: (&'static str, &'static str)) -> Node {
item::media(
ItemMediaVariant::Image,
vec![extra_attr],
vec![image::image(
&ImageProps::new(dummy_assets::PRODUCT_SRC, ""),
vec![],
)],
)
}
/// content(見出し + 説明文)パーツを組み立てる。
fn body(title: &str, description: &str) -> Node {
body_with_extra(title, description, Vec::new())
}
/// content(見出し + 説明文 + 追加の子ノード)パーツを組み立てる。`nested`
/// インスタンスが入れ子の media object を本文末尾へ追加するために使う
/// ([`body`] はこの追加子ノードが空の特殊形)。
fn body_with_extra(title: &str, description: &str, extra: Vec<Node>) -> Node {
let mut children = vec![
item::title(vec![], vec![text(title)]),
item::description(vec![], vec![text(description)]),
];
children.extend(extra);
item::content(vec![], children)
}
/// パネル見出し(短い説明、[`el`] で `<p>` として直接組み立てる。見出し
/// 階層は docs ページ H1 → 生成 H2「Demo」の配下のため `h2`/`h3` を増やさず
/// `p` で十分)。
fn caption(text_content: &str) -> Node {
el(
"p",
vec![("class", "blocks-media-object-caption")],
vec![text(text_content)],
)
}
/// パネル外枠。`narrow` が `true` のパネルは
/// `data-blocks-media-object-narrow`([`LAYOUT_CSS`] の `max-width: 20rem`
/// 規則)を追加で持つ。`stack`/`stack-full` は `@container` 分岐の実測幅
/// 確保用、`stretch`(Bugbot 指摘、イシュー #3228 PR #3402)は説明文を
/// 折り返させて行高を media の既定 6rem 角より高くし、`align-items:
/// stretch` が実際に media を伸長させる様子を可視化する用途で使う
/// (デモ幅が広いままだと 1 行に収まり `top` と見分けがつかない)。
fn panel(variant: &'static str, caption_text: &str, narrow: bool, item_node: Node) -> Node {
let mut attrs: Vec<(&str, &str)> = vec![
("class", "blocks-media-object-panel"),
("data-blocks-media-object-variant", variant),
];
if narrow {
attrs.push(("data-blocks-media-object-narrow", ""));
}
div(attrs, vec![caption(caption_text), item_node])
}
/// `root`(item)パーツ。`variant` を `data-blocks-media-object-variant`
/// へ渡し、既定の整列(`top`)以外は [`LAYOUT_CSS`] の対応セレクタが上書く。
fn media_object_root(variant: &'static str, media: Node, content: Node) -> Node {
item::root(
ItemRootProps::default(),
vec![
("data-blocks-media-object-root", ""),
("data-blocks-media-object-variant", variant),
],
vec![media, content],
)
}
/// `nested`(R1069)の入れ子部分。外側の `nowrap`/`align-items` 規則を
/// 継承させないため `data-blocks-media-object-root` は付与せず、media のみ
/// `-media-nested` フックを持つ([`LAYOUT_CSS`] 参照)。
fn nested_media_object() -> Node {
item::root(
ItemRootProps::default(),
vec![],
vec![
media_image(("data-blocks-media-object-media-nested", "")),
body(
"関連するアップデート",
"同じ整列パターンを縮小して本文内に再利用した例です。",
),
],
)
}
/// `top`(R1062・主参照)インスタンス。
fn top_instance() -> Node {
panel(
"top",
"上揃え(基準形)",
false,
media_object_root(
"top",
media_image(("data-blocks-media-object-media", "")),
body(
"新しいダッシュボードのレイアウト",
"サイドバーと本文の比率を見直し、狭い画面での折り返しを改善しました。",
),
),
)
}
/// `center`(R1063)インスタンス。
fn center_instance() -> Node {
panel(
"center",
"縦中央揃え",
false,
media_object_root(
"center",
media_image(("data-blocks-media-object-media", "")),
body(
"通知設定の見直し",
"メール通知とアプリ内通知を個別に切り替えられるようにしました。",
),
),
)
}
/// `bottom`(R1064)インスタンス。
fn bottom_instance() -> Node {
panel(
"bottom",
"下揃え",
false,
media_object_root(
"bottom",
media_image(("data-blocks-media-object-media", "")),
body(
"エクスポート機能の追加",
"表示中のデータを CSV 形式でダウンロードできるようになりました。",
),
),
)
}
/// `stretch`(R1065)インスタンス。
fn stretch_instance() -> Node {
panel(
"stretch",
"画像を行高いっぱいに伸ばす",
true,
media_object_root(
"stretch",
media_image(("data-blocks-media-object-media", "")),
body(
"検索結果の並べ替え",
"更新日時・関連度・名前の 3 種類から並べ替え基準を選べます。狭いカラムで折り返すことで、media が行高いっぱいに伸びる様子を確認できます。",
),
),
)
}
/// `right`(R1066)インスタンス。
fn right_instance() -> Node {
panel(
"right",
"画像を右側に配置",
false,
media_object_root(
"right",
media_image(("data-blocks-media-object-media", "")),
body(
"キーボードショートカットの追加",
"よく使う操作をキーボードだけで完結できるようにしました。",
),
),
)
}
/// `stack`(R1067)インスタンス。狭幅パネル(`@container`)で縦積みに
/// 折り返す。
fn stack_instance() -> Node {
panel(
"stack",
"狭幅で縦積み",
true,
media_object_root(
"stack",
media_image(("data-blocks-media-object-media", "")),
body(
"タグ機能のプレビュー",
"アイテムに自由なタグを付けて絞り込めるようになります。",
),
),
)
}
/// `stack-full`(R1068)インスタンス。狭幅パネルで縦積み + 画像を全幅化。
fn stack_full_instance() -> Node {
panel(
"stack-full",
"狭幅で画像を全幅化",
true,
media_object_root(
"stack-full",
media_image(("data-blocks-media-object-media", "")),
body(
"共有リンクの有効期限",
"共有リンクに有効期限を設定し、期限切れ後は自動で無効になります。",
),
),
)
}
/// `nested`(R1069)インスタンス。本文内に小さい media object を入れ子。
fn nested_instance() -> Node {
panel(
"nested",
"本文内に小さい media object を入れ子",
false,
media_object_root(
"nested",
media_image(("data-blocks-media-object-media", "")),
body_with_extra(
"リリースノート 2026.9",
"今回のリリースでは以下の変更を行いました。",
vec![nested_media_object()],
),
),
)
}
/// `media-object` の Demo 本体。呼び出しごとに同一の `Node` を返す純関数。
/// 主参照(R1062・top)を先頭に、8 インスタンスを縦に並べる。
#[must_use]
pub fn demo() -> Node {
div(
vec![("class", "blocks-media-object-layout")],
vec![
top_instance(),
center_instance(),
bottom_instance(),
stretch_instance(),
right_instance(),
stack_instance(),
stack_full_instance(),
nested_instance(),
],
)
}原案差分メモ
- 主参照は R1062(画像 + 見出し + 説明文の横並び、上揃え基準形)です。集約元 7 件(R1063 縦中央揃え・R1064 下揃え・R1065 画像を行高いっぱいに伸ばす・R1066 画像を右側に配置・R1067 狭幅で縦積み・R1068 狭幅で画像を全幅化・R1069 本文内に小さい media object を入れ子)を、それぞれ
data-blocks-media-object-variantで区別した別パネルとして併記しています。 - 狭幅パターン(
stack/stack-full)はビューポート幅ではなくパネル自体の実測幅(@container)で折り返しを切り替えます。パネルへmax-width: 20remを与えることで、同一ビューポート・無 JS のまま狭幅後の見た目を静的に表現しています。 nestedパターンは本文(item::content)の末尾へ、より小さい media object(media2.5rem角)をそのまま入れ子にしています。入れ子側のitem::rootには外側専用のnowrap/align-items上書きフック(data-blocks-media-object-root)を付与せず、既定の折り返しレイアウトを継承させています。- 画像はすべて
dummy_assets::PRODUCT_SRC(モノトーン抽象図形の SVG)を使う装飾画像としてalt=""を付与し、意味を持つ画像として偽装していません。見出し・説明文はすべて独自に書いた架空データです。 - 参照元(
_/blocks-intake/)は本イシュー着手時点で worktree に存在せず、対応表 ID とイシュー記載の差分説明のみを根拠に実装しました(page-heading-avatarと同じ扱い)。 - ブラウザでの実機確認(狭幅パネルでの折り返し・ライト/ダーク両テーマ)はサンドボックス制約により未実施です。cargo test による出力検証のみで代替しました。