FAB メニュー
縮めたメインアクションボタンとして描くトグルから、関連するアクションの短いリストを開きます。リストが出ている間、トグルはそのリストを閉じるボタンになります。
- カテゴリ
- アクション
- トークン
comp.fab-menu- レベル
- 基礎
- Web
tanrei-fab-menuschedule - SwiftUI
TanreiFabMenuschedule - Jetpack Compose
TanreiFabMenuschedule - Flutter
TanreiFabMenuschedule - React Native
FabMenuschedule
生成元 docs/status/status.json, specs/fab-menu.spec.json
すべてのデザインシステムにあるわけではありません
m3-expressive-like, m3-like にあり、@tanrei/web/m3-expressive-like, @tanrei/web/m3-like が登録します。
tanrei, cupertino にはありません。 このコンポーネントは m3-like レイヤーにあり、これらのシステムはそのレイヤーを継いでいません。 A system outside this layer draws no floating action button that opens a list: its main action button opens a bottom sheet that lists the related actions. 代わりに Main action button, Bottom sheet を使います。
これを持たないシステムの上のプロダクトでも、自分のレイヤーに取り込み(自分のデザインシステムを作る)、コンポーネント自身のモジュールを読み込めば使えます。そのときは自分のシステムのトークンで描かれます。
"components": {
"include": [{ "name": "fab-menu", "from": "m3-like" }]
}import '@tanrei/web/components/fab-menu';閉じた状態と開いた状態
m3-like で表示開閉のボタン(トグル)は、ラベルとアイコンを持ち、縮めたメインアクションボタンとして描きます。項目はアイコンとラベルを持つアクションで、各プラットフォームが繰り返しの子要素を受け取るいつものやり方で渡します。メインアクションボタンと同じく、FAB メニューは自分では位置を決めません。トグルを置くのはレイアウトの役目で、ふつうは末尾側の下の角に置きます。項目はその上に、プラットフォームのオーバーレイ層に表示されます。
<tanrei-fab-menu class="primary-action" label="作成">
<span slot="icon">add</span>
<tanrei-fab-menu-item label="ドキュメント" icon="description"></tanrei-fab-menu-item>
<tanrei-fab-menu-item label="フォルダー" icon="create_new_folder"></tanrei-fab-menu-item>
<tanrei-fab-menu-item label="アップロード" icon="upload"></tanrei-fab-menu-item>
</tanrei-fab-menu>
<style>
/* 要素は自分の位置を決めません。トグルを末尾側の下の角に置くのはページの役目です。 */
tanrei-fab-menu.primary-action {
position: fixed;
inset-inline-end: var(--tanrei-sys-space-space400);
inset-block-end: var(--tanrei-sys-space-space400);
}
</style>
<script type="module">
// FAB メニューは m3-like レイヤーにあります。m3-like 系のシステムのエントリーが、ほかのコンポーネントと一緒に登録します。
import '@tanrei/web/m3-like';
const fabMenu = document.querySelector('tanrei-fab-menu');
// トグルは開閉を求めるだけです。open を設定するのは利用者側です。
fabMenu.addEventListener('toggle', (e) => (fabMenu.open = e.detail.open));
// close には閉じる理由(escape、outside、tab、focus-out、item)が付きます。Web では back は送られません。
fabMenu.addEventListener('close', () => (fabMenu.open = false));
// 項目の click はバブリングし、close より先に届きます。
fabMenu.querySelector('[label="アップロード"]').addEventListener('click', upload);
</script>// FAB メニューは m3-like レイヤーにあります。それを持つシステム(m3-like、m3-expressive-like)のエントリーと /all にあります。
import { FabMenu, FabMenuItem } from '@tanrei/react/m3-like';
<FabMenu
className="primary-action"
label="作成"
open={open}
onToggle={(e) => setOpen(e.detail.open)}
onClose={() => setOpen(false)}
>
<span slot="icon">add</span>
<FabMenuItem label="ドキュメント" icon="description" onClick={newDocument} />
<FabMenuItem label="フォルダー" icon="create_new_folder" onClick={newFolder} />
<FabMenuItem label="アップロード" icon="upload" onClick={upload} />
</FabMenu>import Tanrei
// FAB と同じように置きます。バインディングには、トグルと項目の両方が書き込みます。
DocumentList()
.overlay(alignment: .bottomTrailing) {
TanreiFabMenu("作成", icon: "add", isPresented: $showsCreate) {
TanreiFabMenuItem("ドキュメント", icon: "description", action: newDocument)
TanreiFabMenuItem("フォルダー", icon: "create_new_folder", action: newFolder)
TanreiFabMenuItem("アップロード", icon: "upload", action: upload)
}
.padding()
}import io.github.chnotchy.tanrei.components.TanreiFabMenu
import io.github.chnotchy.tanrei.components.TanreiFabMenuItem
import io.github.chnotchy.tanrei.primitives.TanreiText
// 状態ホイスティングです。expanded は呼び出し側で持ちます。Scaffold の floatingActionButton スロットに渡すか、自分で配置します。
TanreiFabMenu(
expanded = expanded,
onExpandedChange = { expanded = it },
onDismissRequest = { expanded = false },
contentDescription = "作成",
icon = { TanreiText("add") },
) {
TanreiFabMenuItem(text = "ドキュメント", icon = "description", onClick = ::newDocument)
TanreiFabMenuItem(text = "フォルダー", icon = "create_new_folder", onClick = ::newFolder)
TanreiFabMenuItem(text = "アップロード", icon = "upload", onClick = ::upload)
}import 'package:tanrei/tanrei.dart';
// FAB メニューがコントローラーを通じて自分で開閉し、onToggle と onClose で知らせます。
Scaffold(
body: const DocumentList(),
floatingActionButton: TanreiFabMenu(
controller: createMenu,
semanticLabel: '作成',
icon: const Icon(AppSymbols.add),
items: <TanreiFabMenuItem>[
TanreiFabMenuItem(label: 'ドキュメント', icon: const Icon(AppSymbols.description), onPressed: newDocument),
TanreiFabMenuItem(label: 'フォルダー', icon: const Icon(AppSymbols.createNewFolder), onPressed: newFolder),
TanreiFabMenuItem(label: 'アップロード', icon: const Icon(AppSymbols.upload), onPressed: upload),
],
),
);import { View } from 'react-native';
import { FabMenu } from '@tanrei/react-native';
// 制御コンポーネントです。onToggle で visible を設定し、理由を受け取る onDismiss で false に戻します。
// トグルは自分の位置を決めません。style で配置します。
<View style={{ flex: 1 }}>
<DocumentList />
<FabMenu
icon="add"
accessibilityLabel="作成"
visible={open}
onToggle={setOpen}
onDismiss={() => setOpen(false)}
items={[
{ label: 'ドキュメント', icon: 'description', onPress: newDocument },
{ label: 'フォルダー', icon: 'create_new_folder', onPress: newFolder },
{ label: 'アップロード', icon: 'upload', onPress: upload },
]}
style={styles.primaryAction}
/>
</View>バリアント、サイズ、無効状態
m3-like で表示構成要素
プロパティ
| プロパティ | 型 | 既定値 | 説明 |
|---|---|---|---|
variant | filled | tonal | surface | "filled" | Emphasis, as the main action button's variants of the same names: it colors the toggle and the items. A design system may add values. |
size | small | medium | large | "medium" | The toggle's size (40 / 48 / 64, square), as the main action button's. The items keep one size. A design system may add values. |
open | boolean | false | Whether the items are shown. Where open is a plain value (web, Compose, React Native) the product sets it from toggle and sets it to false from close; where it is a binding or a controller (SwiftUI, Flutter) the FAB menu opens and closes itself through it (FBM-1, FBM-15). |
label | string | "" | The accessible name of the toggle and of the list ("Create"). Required: an icon alone is not a name. |
disabled | boolean | false | Prevents opening and removes the toggle from the focus order. Hiding a FAB menu whose actions are unavailable is usually better than disabling it. |
スロット
| スロット | 説明 |
|---|---|
icon | The toggle's glyph while closed (a glyph of the system's icon set). |
イベント
| イベント | 説明 |
|---|---|
toggle | The toggle asks to open or close the menu; carries the open value it asks for. |
close | The menu asks to be closed; carries the reason: escape, back, outside, tab, focus-out or item (in the platform's casing). |
アイテム
このコンポーネントが繰り返すアイテムの種類です。フィクスチャはアイテムを順に並べ、各プラットフォームはそれぞれの流儀で種類ごとに名前を付けます。
item
One action of the menu: an icon and a label on a raised container.
| プロパティ | 型 | 既定値 | 説明 |
|---|---|---|---|
label | string | "" | The item's text and its accessible name. Required. |
icon | string | "" | The name of the item's icon (a glyph of the system's icon set). Required. |
| イベント | 説明 |
|---|---|
click | The item was activated by pointer, touch, keyboard or assistive technology. |
| 仕様 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| アイテム | tanrei-fab-menu-item | TanreiFabMenuItem | TanreiFabMenuItem | TanreiFabMenuItem | FabMenuItem |
label · プロパティ | label | title | text | label | label |
icon · プロパティ | icon | icon | icon | icon | icon |
click · イベント | click | action | onClick | onPressed | onPress |
| 備考 | A child element: <tanrei-fab-menu-item label="Upload" icon="upload">; its click bubbles. | TanreiFabMenuItem(_ title:, icon:, action:) in the view builder. | TanreiFabMenuItem(text, icon, onClick) called in the content lambda. | TanreiFabMenuItem(label:, icon:, onPressed:) in the items: list. | The element type of the items array: { label, icon, onPress }, where icon is the glyph's name. |
状態
振る舞い
EARS 記法による要件です。各プラットフォームの実装は、この ID に対応するテストで検証されます。要件の原文は仕様ファイルのものをそのまま掲載しています。
| ID | 要件 |
|---|---|
FBM-1 | WHEN the user activates the toggle by pointer, touch, Enter or Space THE FAB menu SHALL emit toggle exactly once with the opposite of open, and WHEN the user presses Down Arrow or Up Arrow on the toggle while open is false it SHALL emit toggle with true; the menu SHALL then open or close through open: through its binding or controller where open is one (SwiftUI, Flutter), and by the product setting open where open is a plain value (web, Compose, React Native). |
FBM-2 | WHILE disabled is true THE FAB menu SHALL NOT emit toggle or close, SHALL NOT make the toggle focusable, SHALL expose the disabled state to assistive technology, and SHALL draw the toggle closed and present no items, whatever open says. |
FBM-3 | WHILE the toggle or an item is hovered, keyboard-focused or pressed THE FAB menu SHALL render its container, icon, label, elevation and opacity from the component tokens of its variant in its own highest-precedence active state — the toggle from its closed parts (comp.fab-menu.filled.container.color for filled, and so on) or its open parts (comp.fab-menu.filled.open.container.color, and so on), an item from the item parts (comp.fab-menu.filled.item.container.color, and so on) — with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state. |
FBM-4 | WHEN the toggle or an item receives keyboard focus THE FAB menu SHALL show the focus ring around that one's own shape, without animation; focus gained by pointer or touch SHALL NOT show it. |
FBM-5 | WHERE the toggle is smaller than the minimum target THE FAB menu SHALL extend its hit area on both axes to sys.target.touch for touch input and sys.target.pointer for fine pointers, without changing its visual size; THE FAB menu SHALL make each item at least that target tall, because stacked items cannot extend a hit area over each other, and each item's whole container SHALL be its hit area; a platform that cannot tell the input type SHALL use sys.target.touch. |
FBM-6 | WHILE disabled is true THE FAB menu SHALL render the toggle's container, icon, elevation and opacity from the disabled component tokens of its variant's closed parts. |
FBM-7 | WHEN variant or size names a value that the active design system does not have THE FAB menu SHALL render with the default value's tokens, and in debug builds report the unknown value. |
FBM-8 | WHEN the toggle is pressed by pointer, touch, Enter or Space THE toggle SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. |
FBM-9 | WHILE pressed THE toggle SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; icon, elevation, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.fab-menu.motion.feedback-duration with comp.fab-menu.motion.state-easing, fades over comp.fab-menu.motion.state-duration with comp.fab-menu.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. Each item SHALL answer its own press in the same way, within its own container and from its item parts: label, icon, elevation and opacity still come from its pressed tokens. |
FBM-10 | THE toggle SHALL ease its container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show its keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. Each item SHALL ease its corners and show its keyboard focus in the same way. |
FBM-11 | THE FAB menu SHALL draw the toggle as a square whose side is the container size of its size (comp.fab-menu.container.size.medium for medium, and so on), with the corners of its size (comp.fab-menu.container.shape.medium, and so on) while closed and comp.fab-menu.container.shape-open while open, and its icon centred in it at the icon size of its size (comp.fab-menu.icon.size.medium, and so on) through the icon helper, with the family and fill of an unselected icon, sys.icon.weight, and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max: the icon slot's glyph while closed, and the glyph close of the system's icon set while open, not mirrored when the layout direction is right-to-left. THE FAB menu SHALL draw no mark of its own (no path, shape or character of its own) for the close glyph. |
FBM-12 | THE FAB menu SHALL NOT position itself: the toggle SHALL lay out at its own size where the layout that holds it places it (normally the inline-end bottom corner of the screen, clear of any navigation bar), as a main action button does (MAB-18), and the items SHALL be presented above the page rather than laid out, so that they move no content. |
FBM-13 | WHEN open becomes true THE FAB menu SHALL present its items in the platform's overlay layer (the web's top layer through the popover API, SwiftUI's presentation as the menu's, Compose's Popup, Flutter's Overlay, React Native's overlay layer that TanreiProvider renders) as a column above the toggle, comp.fab-menu.list.offset from its top edge and comp.fab-menu.list.gap apart, the first item at the top and every item's end edge aligned with the toggle's, and SHALL move focus to the first item, or to the last (the one nearest the toggle) when Up Arrow opened it; WHEN open becomes false THE FAB menu SHALL remove the items and, if focus was in them, return focus to the toggle. The FAB menu is an operated overlay, so it takes focus and returns it although it is not modal. |
FBM-14 | WHILE open is true THE FAB menu SHALL NOT trap focus, SHALL NOT make the rest of the screen inert and SHALL draw no scrim: it is an operated overlay that is not modal. |
FBM-15 | WHEN the user presses Escape or the platform's back, presses outside the items and the toggle, presses Tab or Shift+Tab on an item, or moves focus out of the items and the toggle THE FAB menu SHALL emit close once with that reason (escape, back, outside, tab, focus-out) and SHALL be closed: through its binding or controller where open is one (SwiftUI, Flutter), and by the product setting open to false where open is a plain value (web, Compose, React Native); Tab and Shift+Tab SHALL then move focus on from the toggle as they would have from it. A press on the toggle while open is not a press outside: it SHALL emit toggle with false (FBM-1) and no close. Whether a press outside also reaches the element under it SHALL follow the platform, as the menu's does, and be stated in its README. |
FBM-16 | WHEN the user activates an item by pointer, touch, Enter or Space THE FAB menu SHALL emit the item's click once and then emit close with the reason item, in that order, and SHALL be closed as FBM-15 says. |
FBM-17 | WHILE open is true and an item has keyboard focus THE FAB menu SHALL move focus to the next item with Down Arrow and to the previous one with Up Arrow, wrapping at the ends, and to the first and last item with Home and End. |
FBM-18 | THE FAB menu SHALL draw each item as a container at least comp.fab-menu.item.height tall and as wide as its content, with corners comp.fab-menu.item.shape eased by sys.shape.corner-smoothing, holding its icon and then its label comp.fab-menu.item.gap apart, padded by comp.fab-menu.item.padding-inline-start at the start and comp.fab-menu.item.padding-inline-end at the end; the label SHALL be set in comp.fab-menu.item.label.typescale on one line while it fits and SHALL wrap rather than truncate, the item growing with it; the icon SHALL be drawn at comp.fab-menu.item.icon.size through the icon helper with the family and fill of an unselected icon, sys.icon.weight, and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max; and the item SHALL cast its item elevation (comp.fab-menu.filled.item.container.elevation for filled, and so on) outside its container without clipping it. |
FBM-19 | WHEN the FAB menu opens THE FAB menu SHALL change the toggle's colors and corners and cross-fade its icon to the glyph close (a cross-fade of two glyphs, neither drawn along a path) over comp.fab-menu.motion.open-duration with comp.fab-menu.motion.open-easing, and fade and grow each item in from its end edge over the same, starting with the item nearest the toggle and delaying each next one by comp.fab-menu.motion.stagger; WHEN it closes it SHALL reverse the toggle's change and fade all items out together over comp.fab-menu.motion.close-duration with comp.fab-menu.motion.close-easing; WHERE motion is reduced it SHALL change and show or hide everything at once. |
FBM-20 | THE FAB menu SHALL expose the toggle as a button named by label that opens a menu, with whether the menu is open where the platform has those properties, and while open SHALL expose the items as a menu named by label, each a menu item named by its own label with its position and the number of items; the icons SHALL be hidden. |
FBM-21 | THE FAB menu SHALL cast the toggle's elevation, of its variant and state (comp.fab-menu.filled.container.elevation for filled at rest, comp.fab-menu.filled.open.container.elevation while open, and so on), outside its container without clipping it, and WHEN the elevation or the colors of the toggle or an item change between interaction states SHALL animate the change over comp.fab-menu.motion.state-duration with comp.fab-menu.motion.state-easing, changing them at once where motion is reduced. |
FBM-22 | WHEN the layout direction is right-to-left THE FAB menu SHALL align the items' end edges with the toggle's left edge, mirror the order of each item's icon and label (a glyph itself only as FBM-26 says) and apply its paddings by logical start and end, and SHALL grow a ripple from the point of contact as in left-to-right. |
FBM-23 | WHILE label is not empty THE FAB menu SHALL expose it as the toggle's accessible name, over a name the product gives it on the element (aria-label or aria-labelledby). WHILE label is empty and the product gives the FAB menu a name of its own on the element — aria-label, or aria-labelledby naming an element — THE FAB menu SHALL expose that name as the toggle's accessible name and name the list by the toggle. WHILE label is empty and the product gives it none, an item's label or icon is empty, or the menu holds fewer than two or more than six items, THE FAB menu SHALL still render, and in debug builds report it. |
FBM-24 | IF the icon set cannot draw the close glyph yet (its font is still loading or failed to load) WHILE open is true THE FAB menu SHALL draw nothing in the toggle's icon box, never the glyph's name as text, SHALL keep the toggle's size, colors, corners, name and expanded state, and SHALL draw the glyph once the icon set can, without the cross-fade of FBM-19. |
FBM-25 | THE FAB menu SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the FAB menu is shown THE FAB menu SHALL draw itself for the new direction at once, without a motion of its own, keeping its open state and its focus. |
FBM-26 | WHERE the layout direction is right-to-left THE FAB menu SHALL draw the glyph of its icon slot while closed and the icon of each item mirrored left to right exactly when the system's icon set marks that glyph's name as mirrored or the product forces mirroring for that use, and unmirrored when the product forbids it: the icon helper mirrors the glyph inside its own box, about the box's vertical centre line, changing neither its place, size, color and fill nor the order FBM-22 gives. THE FAB menu SHALL NOT mirror a glyph itself, and SHALL NOT mirror content the product gives as an image or a view of its own rather than by a glyph's name. The close glyph of the open toggle is not marked and is never mirrored (FBM-11). |
FBM-27 | THE FAB menu SHALL lay out the label of each item as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right word, a number with its sign or unit, a telephone number and an address keep their own order in a right-to-left layout and reorder nothing beside them; THE FAB menu SHALL still place each label and align its lines by the layout direction, not by the text's own. |
FBM-28 | WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE FAB menu SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on the element that carries its toggle's role — an id reference by the elements it names in the host's tree or a tree around it, an error message as part of the description while the toggle is invalid — except those the FAB menu owns (aria-haspopup, aria-expanded, aria-controls, aria-disabled), which it SHALL move and not expose. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE FAB menu SHALL take it off that element. An item's host carries its menu item role itself (FBM-20), so what the product writes on it is not moved, but for a name: WHILE an item's label is not empty THE FAB menu SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is the item's whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written; WHILE an item's label is empty a name the product gives so is the item's name and SHALL NOT be reported as having none in debug builds (FBM-23); WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it to null, THE FAB menu SHALL take it away. WHEN the product sets an aria- attribute the FAB menu owns THE FAB menu SHALL report it in debug builds. |
プラットフォーム別の名前対応
パリティは API が同一であることを意味しません。同じ契約を、各プラットフォームの流儀で表現します。
| 仕様 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| コンポーネント | tanrei-fab-menu | TanreiFabMenu | TanreiFabMenu | TanreiFabMenu | FabMenu |
variant · プロパティ | variant | variant | variant | variant | variant |
size · プロパティ | size | size | size | size | size |
open · プロパティ | open | isPresented | expanded | controller | visible |
label · プロパティ | label | label | contentDescription | semanticLabel | accessibilityLabel |
disabled · プロパティ | disabled | disabled | enabled | enabled | disabled |
toggle · イベント | toggle | isPresented | onExpandedChange | onToggle | onToggle |
close · イベント | close | isPresented | onDismissRequest | onClose | onDismiss |
| 備考 | <tanrei-fab-menu label="Create" variant size open> with a slot="icon" glyph and tanrei-fab-menu-item children. The toggle is a <button> in the shadow root with aria-haspopup="menu", aria-expanded and aria-controls; the items are a manual popover in the top layer with role menu, placed with CSS anchor positioning and a script fallback, as the menu is. toggle is a CustomEvent whose detail.open is the value asked for (not the popover's ToggleEvent, which does not leave the shadow root); close carries detail.reason. The page positions the element. The inline attribute, for tests, draws the open items in place (FBM-13). The open toggle's glyph is the icon helper's close in the icon part (FBM-11). It lives in the m3-like layer: @tanrei/web/m3-like registers it (with @tanrei/web/components/fab-menu, its own module), and @tanrei/web does not; its tokens are in the stylesheet of the m3-like systems (@tanrei/web/systems/m3-like.css, m3-expressive-like.css) and of a product that includes it. | TanreiFabMenu(_ label:, icon:, variant:, size:, isPresented: Binding<Bool>) { TanreiFabMenuItem … }, placed by the caller as the main action button is (an .overlay(alignment: .bottomTrailing)). The items are presented as TanreiMenu presents its surface, with a clear background, so a press outside closes them. Writing the binding is toggle and close. disabled follows .disabled(_:). iOS has no menu role: the items are buttons in a container named by label. Without an installed icon font it strokes the close glyph as a shape of its own (TanreiChipGlyph(kind: .close)) instead of drawing nothing (FBM-11, FBM-24). The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. | TanreiFabMenu(expanded, onExpandedChange, onDismissRequest, contentDescription, icon, variant, size, enabled) { TanreiFabMenuItem(…) }, placed through Scaffold's floatingActionButton slot or by the caller; the items are shown in a Popup. State hoisting: the caller keeps expanded, sets it from onExpandedChange and to false from onDismissRequest, which carries the reason. Material 3's FloatingActionButtonMenu is not used. enabled is the inverse flag. Without an installed icon font it strokes the close glyph as a vector of its own (TanreiDrawnGlyph) instead of drawing nothing (FBM-11, FBM-24). The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. | TanreiFabMenu(controller:, semanticLabel:, icon:, variant:, size:, enabled:, items: [TanreiFabMenuItem(…)], onToggle:, onClose:), built with the widgets library and placed through Scaffold.floatingActionButton or by the caller; the items are shown in the Overlay. The controller is a TanreiMenuController (the Tanrei menu's own), passed or created internally; it is open, so the FAB menu opens and closes itself; onToggle and onClose report it. enabled is a flag here, because the toggle's press is the component's own. Lagging behind FBM-24: it writes the close glyph as the Material Symbols code point U+E5CD (tanreiCloseGlyph), and without the icon font it draws whatever the fallback face has there instead of nothing. The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. | <FabMenu icon="add" accessibilityLabel visible onToggle onDismiss items={FabMenuItem[]} variant size disabled>; the caller positions it. The items are presented on the overlay layer that TanreiProvider renders (not a Modal, which would make the rest of the screen unreachable) and placed by measuring the toggle (measureInWindow); Android's back button (BackHandler) is close. Controlled: the caller sets visible from onToggle and to false from onDismiss, which carries the reason. Lagging behind FBM-24: without the icon font the ligature close prints as a word. The FAB menu lives in the m3-like layer, and only the systems that descend from it have its tokens; whether this platform ships it in a package of its own is decided when the platform catches up with the web. |
トークン
| トークン | 参照先 | 解決値(ライト) |
|---|---|---|
--tanrei-comp-fab-menu-container-shape-open | sys.shape.corner.full | 9999px |
--tanrei-comp-fab-menu-container-shape-large | sys.shape.corner.extra-large | 28px |
--tanrei-comp-fab-menu-container-shape-medium | sys.shape.corner.large | 16px |
--tanrei-comp-fab-menu-container-shape-small | sys.shape.corner.medium | 12px |
--tanrei-comp-fab-menu-container-size-large | — | 96px |
--tanrei-comp-fab-menu-container-size-medium | — | 56px |
--tanrei-comp-fab-menu-container-size-small | — | 40px |
--tanrei-comp-fab-menu-disabled-opacity | — | 1 |
--tanrei-comp-fab-menu-filled-container-color | sys.color.primary | |
--tanrei-comp-fab-menu-filled-container-elevation | sys.elevation.level3 | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-disabled-container-color | — | |
--tanrei-comp-fab-menu-filled-disabled-container-elevation | — | |
--tanrei-comp-fab-menu-filled-disabled-icon-color | — | |
--tanrei-comp-fab-menu-filled-disabled-item-container-color | — | |
--tanrei-comp-fab-menu-filled-disabled-item-container-elevation | — | |
--tanrei-comp-fab-menu-filled-disabled-item-icon-color | — | |
--tanrei-comp-fab-menu-filled-disabled-item-label-color | — | |
--tanrei-comp-fab-menu-filled-disabled-item-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-disabled-open-container-color | — | |
--tanrei-comp-fab-menu-filled-disabled-open-container-elevation | — | |
--tanrei-comp-fab-menu-filled-disabled-open-icon-color | — | |
--tanrei-comp-fab-menu-filled-disabled-open-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-disabled-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-focused-container-color | — | |
--tanrei-comp-fab-menu-filled-focused-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-focused-icon-color | — | |
--tanrei-comp-fab-menu-filled-focused-item-container-color | — | |
--tanrei-comp-fab-menu-filled-focused-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-focused-item-icon-color | — | |
--tanrei-comp-fab-menu-filled-focused-item-label-color | — | |
--tanrei-comp-fab-menu-filled-focused-item-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-focused-open-container-color | — | |
--tanrei-comp-fab-menu-filled-focused-open-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-focused-open-icon-color | — | |
--tanrei-comp-fab-menu-filled-focused-open-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-focused-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-hovered-container-color | — | |
--tanrei-comp-fab-menu-filled-hovered-container-elevation | — | 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-hovered-icon-color | — | |
--tanrei-comp-fab-menu-filled-hovered-item-container-color | — | |
--tanrei-comp-fab-menu-filled-hovered-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-hovered-item-icon-color | — | |
--tanrei-comp-fab-menu-filled-hovered-item-label-color | — | |
--tanrei-comp-fab-menu-filled-hovered-item-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-hovered-open-container-color | — | |
--tanrei-comp-fab-menu-filled-hovered-open-container-elevation | — | 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-hovered-open-icon-color | — | |
--tanrei-comp-fab-menu-filled-hovered-open-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-hovered-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-icon-color | sys.color.on-primary | |
--tanrei-comp-fab-menu-filled-item-container-color | sys.color.primary | |
--tanrei-comp-fab-menu-filled-item-container-elevation | sys.elevation.level2 | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-item-icon-color | sys.color.on-primary | |
--tanrei-comp-fab-menu-filled-item-label-color | sys.color.on-primary | |
--tanrei-comp-fab-menu-filled-item-state-layer-color | comp.fab-menu.filled.item.label.color | |
--tanrei-comp-fab-menu-filled-open-container-color | sys.color.secondary-container | |
--tanrei-comp-fab-menu-filled-open-container-elevation | sys.elevation.level3 | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-open-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-fab-menu-filled-open-state-layer-color | comp.fab-menu.filled.open.icon.color | |
--tanrei-comp-fab-menu-filled-pressed-container-color | — | |
--tanrei-comp-fab-menu-filled-pressed-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-pressed-icon-color | — | |
--tanrei-comp-fab-menu-filled-pressed-item-container-color | — | |
--tanrei-comp-fab-menu-filled-pressed-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-pressed-item-icon-color | — | |
--tanrei-comp-fab-menu-filled-pressed-item-label-color | — | |
--tanrei-comp-fab-menu-filled-pressed-item-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-pressed-open-container-color | — | |
--tanrei-comp-fab-menu-filled-pressed-open-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-filled-pressed-open-icon-color | — | |
--tanrei-comp-fab-menu-filled-pressed-open-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-pressed-state-layer-color | — | |
--tanrei-comp-fab-menu-filled-state-layer-color | comp.fab-menu.filled.icon.color | |
--tanrei-comp-fab-menu-focused-opacity | — | 1 |
--tanrei-comp-fab-menu-hovered-opacity | — | 1 |
--tanrei-comp-fab-menu-icon-size-large | sys.icon.large | 36px |
--tanrei-comp-fab-menu-icon-size-medium | sys.icon.medium | 24px |
--tanrei-comp-fab-menu-icon-size-small | sys.icon.small | 24px |
--tanrei-comp-fab-menu-item-gap | sys.space.space100 | 8px |
--tanrei-comp-fab-menu-item-height | — | 56px |
--tanrei-comp-fab-menu-item-icon-size | sys.icon.small | 24px |
--tanrei-comp-fab-menu-item-padding-inline-end | sys.space.space250 | 20px |
--tanrei-comp-fab-menu-item-padding-inline-start | sys.space.space200 | 16px |
--tanrei-comp-fab-menu-item-shape | sys.shape.corner.large | 9999px |
--tanrei-comp-fab-menu-list-gap | sys.space.space100 | 4px |
--tanrei-comp-fab-menu-list-offset | sys.space.space100 | 8px |
--tanrei-comp-fab-menu-motion-close-duration | sys.motion.duration.short3 | 150ms |
--tanrei-comp-fab-menu-motion-close-easing | sys.motion.easing.emphasized-accelerate | cubic-bezier(0.3, 0, 0.8, 0.15) |
--tanrei-comp-fab-menu-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-fab-menu-motion-open-duration | sys.motion.duration.medium1 | 250ms |
--tanrei-comp-fab-menu-motion-open-easing | sys.motion.easing.emphasized-decelerate | cubic-bezier(0.05, 0.7, 0.1, 1) |
--tanrei-comp-fab-menu-motion-stagger | sys.motion.duration.stagger | 100ms |
--tanrei-comp-fab-menu-motion-state-duration | sys.motion.role.state.default | 150ms |
--tanrei-comp-fab-menu-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-fab-menu-pressed-opacity | — | 1 |
--tanrei-comp-fab-menu-surface-container-color | sys.color.surface-container-lowest | |
--tanrei-comp-fab-menu-surface-container-elevation | sys.elevation.level3 | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-disabled-container-color | — | |
--tanrei-comp-fab-menu-surface-disabled-container-elevation | — | |
--tanrei-comp-fab-menu-surface-disabled-icon-color | — | |
--tanrei-comp-fab-menu-surface-disabled-item-container-color | — | |
--tanrei-comp-fab-menu-surface-disabled-item-container-elevation | — | |
--tanrei-comp-fab-menu-surface-disabled-item-icon-color | — | |
--tanrei-comp-fab-menu-surface-disabled-item-label-color | — | |
--tanrei-comp-fab-menu-surface-disabled-item-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-disabled-open-container-color | — | |
--tanrei-comp-fab-menu-surface-disabled-open-container-elevation | — | |
--tanrei-comp-fab-menu-surface-disabled-open-icon-color | — | |
--tanrei-comp-fab-menu-surface-disabled-open-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-disabled-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-focused-container-color | — | |
--tanrei-comp-fab-menu-surface-focused-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-focused-icon-color | — | |
--tanrei-comp-fab-menu-surface-focused-item-container-color | — | |
--tanrei-comp-fab-menu-surface-focused-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-focused-item-icon-color | — | |
--tanrei-comp-fab-menu-surface-focused-item-label-color | — | |
--tanrei-comp-fab-menu-surface-focused-item-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-focused-open-container-color | — | |
--tanrei-comp-fab-menu-surface-focused-open-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-focused-open-icon-color | — | |
--tanrei-comp-fab-menu-surface-focused-open-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-focused-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-hovered-container-color | — | |
--tanrei-comp-fab-menu-surface-hovered-container-elevation | — | 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-hovered-icon-color | — | |
--tanrei-comp-fab-menu-surface-hovered-item-container-color | — | |
--tanrei-comp-fab-menu-surface-hovered-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-hovered-item-icon-color | — | |
--tanrei-comp-fab-menu-surface-hovered-item-label-color | — | |
--tanrei-comp-fab-menu-surface-hovered-item-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-hovered-open-container-color | — | |
--tanrei-comp-fab-menu-surface-hovered-open-container-elevation | — | 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-hovered-open-icon-color | — | |
--tanrei-comp-fab-menu-surface-hovered-open-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-hovered-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-icon-color | sys.color.primary | |
--tanrei-comp-fab-menu-surface-item-container-color | sys.color.surface-container-lowest | |
--tanrei-comp-fab-menu-surface-item-container-elevation | sys.elevation.level2 | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-item-icon-color | sys.color.primary | |
--tanrei-comp-fab-menu-surface-item-label-color | sys.color.on-surface | |
--tanrei-comp-fab-menu-surface-item-state-layer-color | comp.fab-menu.surface.item.label.color | |
--tanrei-comp-fab-menu-surface-open-container-color | sys.color.secondary-container | |
--tanrei-comp-fab-menu-surface-open-container-elevation | sys.elevation.level3 | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-open-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-fab-menu-surface-open-state-layer-color | comp.fab-menu.surface.open.icon.color | |
--tanrei-comp-fab-menu-surface-pressed-container-color | — | |
--tanrei-comp-fab-menu-surface-pressed-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-pressed-icon-color | — | |
--tanrei-comp-fab-menu-surface-pressed-item-container-color | — | |
--tanrei-comp-fab-menu-surface-pressed-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-pressed-item-icon-color | — | |
--tanrei-comp-fab-menu-surface-pressed-item-label-color | — | |
--tanrei-comp-fab-menu-surface-pressed-item-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-pressed-open-container-color | — | |
--tanrei-comp-fab-menu-surface-pressed-open-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-surface-pressed-open-icon-color | — | |
--tanrei-comp-fab-menu-surface-pressed-open-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-pressed-state-layer-color | — | |
--tanrei-comp-fab-menu-surface-state-layer-color | comp.fab-menu.surface.icon.color | |
--tanrei-comp-fab-menu-tonal-container-color | sys.color.secondary-container | |
--tanrei-comp-fab-menu-tonal-container-elevation | sys.elevation.level3 | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-disabled-container-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-container-elevation | — | |
--tanrei-comp-fab-menu-tonal-disabled-icon-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-item-container-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-item-container-elevation | — | |
--tanrei-comp-fab-menu-tonal-disabled-item-icon-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-item-label-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-item-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-open-container-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-open-container-elevation | — | |
--tanrei-comp-fab-menu-tonal-disabled-open-icon-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-open-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-disabled-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-focused-container-color | — | |
--tanrei-comp-fab-menu-tonal-focused-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-focused-icon-color | — | |
--tanrei-comp-fab-menu-tonal-focused-item-container-color | — | |
--tanrei-comp-fab-menu-tonal-focused-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-focused-item-icon-color | — | |
--tanrei-comp-fab-menu-tonal-focused-item-label-color | — | |
--tanrei-comp-fab-menu-tonal-focused-item-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-focused-open-container-color | — | |
--tanrei-comp-fab-menu-tonal-focused-open-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-focused-open-icon-color | — | |
--tanrei-comp-fab-menu-tonal-focused-open-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-focused-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-container-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-container-elevation | — | 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-hovered-icon-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-item-container-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-hovered-item-icon-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-item-label-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-item-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-open-container-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-open-container-elevation | — | 0px 2px 3px 0px rgb(0 0 0 / 0.3), 0px 6px 10px 4px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-hovered-open-icon-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-open-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-hovered-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-fab-menu-tonal-item-container-color | sys.color.secondary-container | |
--tanrei-comp-fab-menu-tonal-item-container-elevation | sys.elevation.level2 | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-item-icon-color | sys.color.on-secondary-container | |
--tanrei-comp-fab-menu-tonal-item-label-color | sys.color.on-secondary-container | |
--tanrei-comp-fab-menu-tonal-item-state-layer-color | comp.fab-menu.tonal.item.label.color | |
--tanrei-comp-fab-menu-tonal-open-container-color | sys.color.primary | |
--tanrei-comp-fab-menu-tonal-open-container-elevation | sys.elevation.level3 | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-open-icon-color | sys.color.on-primary | |
--tanrei-comp-fab-menu-tonal-open-state-layer-color | comp.fab-menu.tonal.open.icon.color | |
--tanrei-comp-fab-menu-tonal-pressed-container-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-pressed-icon-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-item-container-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-item-container-elevation | — | 0px 1px 2px 0px rgb(0 0 0 / 0.3), 0px 2px 6px 2px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-pressed-item-icon-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-item-label-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-item-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-open-container-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-open-container-elevation | — | 0px 1px 3px 0px rgb(0 0 0 / 0.3), 0px 4px 8px 3px rgb(0 0 0 / 0.15) |
--tanrei-comp-fab-menu-tonal-pressed-open-icon-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-open-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-pressed-state-layer-color | — | |
--tanrei-comp-fab-menu-tonal-state-layer-color | comp.fab-menu.tonal.icon.color | |
--tanrei-comp-fab-menu-item-label-typescale | sys.typescale.label | 500 16px/1.5 |
フィクスチャ
基準となる描画パターンです。全プラットフォームが同じ ID でスナップショットを撮り、結果を比較できるようにしています。
variant: filled (5)
filled-closed
variant: filled label: Create items: 3 slots: icon m3-like で表示small-closed
variant: filled size: small label: Create items: 3 slots: icon m3-like で表示large-closed
variant: filled size: large label: Create items: 3 slots: icon m3-like で表示filled-open
variant: filled open: true label: Create items: 3 slots: icon 320 × 320 のウィンドウに単独で描画m3-like で表示filled-disabled
variant: filled label: Create disabled: true items: 3 slots: icon m3-like で表示variant: tonal (2)
tonal-closed
variant: tonal label: Create items: 3 slots: icon m3-like で表示tonal-open
variant: tonal open: true label: Create items: 3 slots: icon 320 × 320 のウィンドウに単独で描画m3-like で表示variant: surface (2)
surface-closed
variant: surface label: Create items: 3 slots: icon m3-like で表示surface-open
variant: surface open: true label: Create items: 3 slots: icon 320 × 320 のウィンドウに単独で描画m3-like で表示生成元 specs/fab-menu.spec.json, tokens/systems/m3-like/comp/fab-menu.tokens.json, design/tokens/m3-like/
画面の主なアクションに、関連するいくつかの形があるときに使います。新しいドキュメント、フォルダー、アップロード、あるいは写真とメモ、といった具合です。トグルが画面にただ 1 つのフローティングアクションをいつでも押せる場所に保ち、押すとそれぞれの形がラベル付きのアクションとして真上に現れます。
アクションが 1 つだけならメインアクションボタンを使います。選択肢が多いときや説明が必要なときは、ボタンから開くメニューかシートを使います。画面全体ではなく特定の項目に対するアクションなら、その項目の横にボタンを置きます。
| バリアント | 強調 | 用途 |
|---|---|---|
| Filled(既定) | 最も強い | 画面の主なアクションを、最も目立つ形で示すとき。 |
| Tonal | 強い | 主役はあくまでコンテンツ、という画面。 |
| Surface | ニュートラル | 地図や写真など、すでに色のあるコンテンツの上に置く FAB メニュー。 |
バリアントはトグルと項目の両方に色を付けます。開いている間、トグルはそのバリアントのもう一方の見た目に切り替わり、円形になって閉じるグリフを表示します。そのため、開いた状態と閉じた状態を見間違えることはありません。サイズが決めるのはトグルの大きさ(40、48、64)だけで、項目の大きさは 1 種類です。
FAB メニューは、メニューと同じく、操作するオーバーレイですが、モーダルではありません。人がその中で操作するので、フォーカスを受け取り、閉じるときに返します。一方で、画面のほかの部分はふさぎません。スクリムも、フォーカスの閉じ込めもありません。
- 開く。 トグルは
toggleで開くよう求めます。項目はトグルの上に、末尾側の端をそろえて、トグルに近いものから順に現れ、フォーカスはいちばん上の最初の項目へ移ります。上矢印で開いたときは、トグルのすぐ上にある最後の項目へ移ります。 - 閉じる。 Escape、プラットフォームの「戻る」、項目とトグルの外側の押下、Tab、フォーカスが外へ出ることで、理由の付いた
closeで閉じるよう求めます。項目を実行すると、まずその項目のクリックが発火し、その後に理由itemの付いたcloseが発火します。開いているときにトグルを押すのは、外側の押下ではなくトグルです。 - フォーカスが戻る。 項目が閉じたとき、フォーカスが項目の中にあれば、トグルへ戻ります。項目で Tab を押すと項目が閉じ、フォーカスはトグルから押したのと同じように次へ進みます。
- 誰が閉じるか。 Web、Compose、React Native では、
toggleを受けてopen(expanded、visible)を設定し、closeを受けて false に戻します。SwiftUI と Flutter では、FAB メニューがバインディングかコントローラーを通じて自分で開閉します。
- トグルには、アクションに共通することを表すラベルを付けます。「作成」などです。開いている間も名前は変わらず、展開状態によって、今はリストを閉じるボタンであることがスクリーンリーダーに伝わります。
- どの項目にも、短いラベルとアイコンを付けます。人が読むのはラベルで、アイコンは見つけやすくするための補助です。
- 項目は 2〜6 個にします。並び順は人が目で追う順にします。最初の項目がいちばん上に描かれ、最後の項目がトグルのすぐ上に来ます。
- スクロールするコンテンツの末尾に余白を取り、トグルが最後の項目をずっと隠したままにならないようにします。
- 項目を無効にしない。使えないアクションはリストから外します。無効な項目という仕組み自体がありません。
- 開いている間にトグルのラベルを「閉じる」に変えたり、アイコンを自分で入れ替えたりしない。どちらもコンポーネントが行います。
- 同じ画面に、同じサイズの FAB メニューとメインアクションボタンを置かない。フローティングアクションは 1 画面に 1 つです。
- 重要度の低いアクション、破壊的なアクション、設定のためのアクションに FAB メニューを使わない。
- ロール
button- ラベル付け
- label names the toggle and the list ("Create"). Each item is named by its label. Icons are decorative. The toggle keeps its name while open; its expanded state says it is open.
- 最小ターゲット
- 見た目のサイズが小さくても、ヒット領域は最小ターゲットサイズを満たします。 minimum 32px · pointer 44px · spacing 8px · touch 48px
キーボード操作
| キー | 動作 |
|---|---|
| Tab / Shift+Tab | Moves focus to the toggle; from an item, closes the menu and moves focus on from the toggle. |
| Enter, Space / Down Arrow on the toggle | Opens the menu (Enter and Space also close it); focus moves to the first item. |
| Up Arrow on the toggle | Opens the menu; focus moves to the last item, the one nearest the toggle. |
| Down Arrow / Up Arrow | Moves focus to the next or previous item, wrapping at the ends. |
| Home / End | Moves focus to the first or last item. |
| Enter / Space on an item | Activates the item and closes the menu. |
| Escape | Closes the menu and returns focus to the toggle. |
備考
- Focus: the FAB menu is an operated overlay that is not modal, like the menu. It moves focus in when it opens and returns it to the toggle when it closes, but does not keep it: Tab from an item closes it.
- Its place in the focus and reading order is where the layout puts the toggle in the element tree, not where it is drawn, as for the main action button.
- Compose has no menu role: the items are buttons in a traversal group named by label, with their position in their collection info.
- Text size does not change the toggle: an icon is not text. The items grow with enlarged text and wrap their labels.
- How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.
生成元 specs/fab-menu.spec.json, tokens/sys/measure.tokens.json
- トグルは、ラベルを名前とするボタンです。プラットフォームにそのプロパティがあれば、メニューを開くことと、メニューが開いているかどうかを伝えます。開いている間も名前は変わりません。
- 開いている間、項目は同じラベルを名前とするメニューで、各項目は自分のラベルを名前とするメニュー項目です。位置と項目の数も伝えます。アイコンは隠します。
- トグルで Enter、Space、下矢印を押すと、メニューを開いて最初の項目へフォーカスを移します。上矢印では開いて最後の項目へ移ります。項目の間は下矢印と上矢印で移動し、端では折り返します。Home と End で両端へ移ります。項目で Enter か Space を押すと、その項目を実行してメニューを閉じます。Escape でメニューを閉じ、トグルへフォーカスを戻します。
- メニューはモーダルではありません。フォーカスを閉じ込めず、画面のほかの部分を操作不能にせず、スクリムも描きません。項目で Tab を押すと閉じます。
- トグルのヒット領域は、どのサイズでも最小ターゲットまで広がります。各項目の高さは最小ターゲット以上で、コンテナ全体がヒット領域です。
- アイコンは文字ではないので、文字サイズを大きくしてもトグルの大きさは変わりません。項目は大きくなり、ラベルを折り返します。
- 動きを減らす設定では、トグルの変化も項目の出入りも瞬時に行います。
- トグルのラベルがない場合、ラベルかアイコンのない項目がある場合、項目が 2 個未満か 7 個以上の場合は、デバッグビルドで報告します。
プラットフォームごとの既知の制約:
- Web:
backは送られません。Web では Escape が閉じる要求です。 - Web:項目の位置は、仕様の Web 向けの注記にある CSS のアンカー配置ではなく、メニューと同じくトグルを基準にスクリプトで決めています。
- 外側を押すと、どのプラットフォームでも項目は閉じます。Web と Flutter では、その押下は下にあるものにも届きます。SwiftUI、Compose、React Native では届きません。
- SwiftUI には、メニューやメニュー項目のロールがなく、トグルがメニューを開くことや、メニューが開いていることを伝える手段もありません。項目はラベルを名前とするコンテナの中のボタンで、それぞれ位置(「2 of 3」、英語のみ)を値に持ちます。
- SwiftUI は項目をポップオーバーで表示し、位置はポップオーバー自身が決めます。項目の背後に独自の吹き出しと矢印を描き、列をトグルの末尾側の端にそろえずにトグルの角を中心に置き、既定のシステムでは列を切り抜きます。トグルからの距離も
list.offsetでは決まりません。このポップオーバーは、Tanrei のオーバーレイ層に置き換える予定です。 - SwiftUI:Tab で項目は閉じてフォーカスはトグルへ戻りますが、同じキー操作でそこから次へは進めません。
- SwiftUI:項目のアクセシビリティ上の枠が描画された影まで含むため、隣り合う項目の枠が数ポイント重なります。
- SwiftUI:
folder.badge.plusのように縦より横に長い SF Symbol は、項目のアイコンの枠からはみ出します。 - SwiftUI:トグルと項目でのキー操作、フォーカスが項目へ移ってトグルへ戻る動きは、規則としてテストしているだけで、実際のキーボードではまだ確かめていません。
- Compose には、メニューのロールも、ボタンがメニューを開くことを伝えるプロパティもありません。項目は、ラベルを名前とする走査グループの中のボタンで、位置はコレクション情報で伝えます。トグルが項目を開くのか閉じるのかは、展開と折りたたみのアクションで伝えます。
- Flutter では、トグルがポップアップを開くことも、メニューの中での位置や項目の数も伝えられません。
- React Native には、ボタンがメニューを開くことを伝えるプロパティがありません。トグルが伝えるのは、メニューが開いているかどうかだけです。
- React Native は項目を
TanreiProviderのオーバーレイ層に描くため、上位にTanreiProviderが必要です。Tab で項目は閉じてフォーカスはトグルへ戻り、次の Tab でそこから先へ進みます。 - React Native:項目の配置、項目が順に現れる動き、フォーカスが移って戻る動き、外側の押下は、実機ではまだ確かめていません。
- トグルにも、すべての項目にもラベルを書きます。アイコンだけでは名前になりません。
- FAB メニューは、要素ツリーの中でアクションにふさわしい位置に置きます。支援技術は、描画された場所ではなくツリーの順にトグルを読み上げます。
- Web、Compose、React Native では、
toggleを受けてopenを設定し、closeを受けて false に戻します。FAB メニューは求めるだけです。 - 閉じたとき、自分でフォーカスを動かさないでください。FAB メニューがトグルへ戻します。
- トグルはナビゲーションバーにかぶらないように置き、上には項目が出る余地を残します。