メインコンテンツへスキップ
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
はじめにWebReactSwiftUIJetpack ComposeFlutterReact Nativeプリセット自分のデザインシステムを作るAI エージェントと Tanrei を使うブランドデザイントークンデザインシステムとカスタマイズTheme Studioカラータイポグラフィアイコン形状エレベーションモーションインタラクション状態振る舞いレイアウトアクセシビリティ言葉カラートークンタイプスケールトークン形状トークンエレベーショントークンモーショントークン状態トークンスペースと寸法のトークンコンポーネントボタンメインアクションボタンセグメントボタンボタングループメニュー付きボタンツールバーコピーボタンチェックボックスラジオボタンスイッチチップメニュースライダースライダーフィールドカレンダーDateTimePickerフィールドグループラジオグループチェックボックスグループセレクトセレクトフィールドメニューバーファイルアップロードファイルピッカー 上級者向けドロップゾーンテキスト入力欄 上級者向けテキストフィールドチップフィールドフィールドコンボボックス 上級者向けコンボボックスフィールド数値入力欄 上級者向け数値フィールドカラー入力欄 上級者向けカラーフィールドワンタイムコード入力欄 上級者向けワンタイムコードフィールド埋め込みテキスト入力欄 上級者向けチャットコンポーザーカード区切り線モーダルレイヤー 上級者向けダイアログリストリスト行ボトムシートサイドシートカルーセルコードブロックテーブルポップオーバーステップアコーディオン説明リスト図アプリシェルページヘッダーツリービュータイムライン画像プレビューハンドルサイズ変更できる枠スクロールエリアバッジプログレスインジケーターメーターナンバーティッカーローディングインジケータースナックバーツールチップキーボードショートカットタグインラインコードコールアウトアバター空の状態タググループスケルトンファイル項目色見本マーキーテキストタブタブビューアプリバーナビゲーションバーナビゲーションドロワー検索リンクページネーション前後のリンクステッパーパンくずリスト目次リンクカードFAB メニューステータスTanrei の作り方コントリビューションライセンスと帰属
ホームコンポーネントメインアクションボタン

メインアクションボタン

画面でいちばん大切なアクションを、コンテンツの上に浮かぶ持ち上がったコンテナで実行します。名前を書き添えた形か、アイコンだけに縮めた形で表示します。

content_copyMarkdown で表示
カテゴリ
アクション
トークン
comp.main-action-button
レベル
基礎

概要

Web
tanrei-main-action-button
schedule
SwiftUI
TanreiMainActionButton
radio_button_unchecked
Jetpack Compose
TanreiMainActionButton
radio_button_unchecked
Flutter
TanreiMainActionButton
radio_button_unchecked
React Native
MainActionButton
radio_button_unchecked

生成元 docs/status/status.json, specs/main-action-button.spec.json

伸ばした形と縮めた形

editedit

メインアクションボタンは、その画面が何のためにあるかを表す、ただ 1 つのアクションです。メールボックスの「作成」、カレンダーの「新しい予定」、リストの「追加」などです。コンテンツの上に浮かぶ持ち上がったコンテナに載るので、下のコンテンツがスクロールしても、いつでも押せる場所にあります。

1 つのボタンが 2 つの形を持ちます。伸ばした形(extended、既定)は、アイコンとラベルを表示します。縮めた形(collapsed)はアイコンだけを表示します。伸ばした形と同じ高さの正方形で、既定のシステムでは円になります。2 つの形の間を動かすのはプロダクトです。たとえば、コンテンツをスクロールしている間は縮め、先頭に戻ったらまた伸ばします。トグルではなくアクションです。縮めても変わるのは形だけで意味は変わらず、ラベルはどちらの形でもアクセシブルネームのままです。

Web では <tanrei-main-action-button>、React では MainActionButton です。SwiftUI、Compose、Flutter、React Native はまだ実装していません。

使い方

ラベルは子要素ではなくプロパティです。伸ばした形では表示され、どちらの形でもアクセシブルネームになるので、いつでも必要だからです。アイコンはアイコンセットのグリフです。ボタンは自分では位置を決めません。置くのはレイアウトの役目で、ふつうは画面の末尾側の下の角に、ナビゲーションバーと重ならないように置きます。

<tanrei-main-action-button class="primary-action" label="作成">
  <span slot="icon">edit</span>
</tanrei-main-action-button>

<!-- 縮めた形。属性は extended プロパティの逆です。 -->
<tanrei-main-action-button label="作成" collapsed><span slot="icon">edit</span></tanrei-main-action-button>

<!-- アクションの進む先を指すグリフは、ラベルの後ろに置きます。 -->
<tanrei-main-action-button label="次へ" icon-position="trailing"><span slot="icon">arrow_forward</span></tanrei-main-action-button>

<style>
  /* 要素は自分では位置を決めません。ページが末尾側の下の角に置きます。 */
  tanrei-main-action-button.primary-action {
    position: fixed;
    inset-inline-end: var(--tanrei-sys-space-space400);
    inset-block-end: var(--tanrei-sys-space-space400);
  }
</style>

<script type="module">
  import '@tanrei/web/components/main-action-button';
  const compose = document.querySelector('tanrei-main-action-button.primary-action');
  compose.addEventListener('click', newMessage);
  // extended は既定が true のプロパティです。ページをスクロールしている間など、外から縮めます。
  addEventListener('scroll', () => (compose.extended = scrollY === 0), { passive: true });
</script>
import { MainActionButton } from '@tanrei/react';

<MainActionButton className="primary-action" label="作成" extended={atTop} onClick={newMessage}>
  <span slot="icon">edit</span>
</MainActionButton>

<MainActionButton variant="tonal" label="次へ" iconPosition="trailing" onClick={next}>
  <span slot="icon">arrow_forward</span>
</MainActionButton>

FAB と拡張 FAB の移り先

メインアクションボタンは、フローティングアクションボタンと拡張 FAB を 1 つにまとめたものです。プロダクトが入れ替えて使っていた 2 つのコンポーネントが、1 つのコンポーネントの 2 つの形になりました。

以前 現在
フローティングアクションボタン(fab) 縮めたメインアクションボタン:extended を false にする
拡張 FAB(extended-fab) 伸ばしたメインアクションボタン(既定)
拡張 FAB の expanded extended
comp.fab.*、comp.extended-fab.* comp.main-action-button.*

Material 3 の名前との対応

  • Material 3 の FAB、small FAB、large FAB は、medium、small、large のサイズで縮めたメインアクションボタンです。extended FAB は伸ばした形です。
  • Compose の expanded(ExtendedFloatingActionButton)と Flutter の isExtended(FloatingActionButton.extended)は extended にあたります。
  • Material 3 の primary、secondary、tertiary、surface の FAB の色は、filled、tonal、surface のバリアントと、プリセットが加える値に対応します。m3-like プリセットは、Material の角丸の四角と 56 / 96 の高さを、このコンポーネントのトークンで描きます。
  • Material の用語では、メインアクションボタンは FAB に当たります。Material の FAB メニューは FAB メニューで、m3-like レイヤーのコンポーネントです。そのレイヤーを継ぐシステムが持っています。

仕様

バリアント

editeditedit

サイズ(伸ばした形と縮めた形)

editediteditediteditedit

末尾のアイコン、アイコンなし、無効状態

arrow_forwardeditedit

構成要素

構成要素

Web パッケージが描くフィクスチャ filled-extended の上に描いています。
パーツ 説明
1 container The raised shape that holds the icon and the label, casts the shadow and carries hover, focus and press. Extended it is as wide as its content; collapsed it is a square of its height, a circle under full corners.
2 icon · 任意 The glyph that names the action, at the leading or the trailing side of the label; drawn in the label color. Required to collapse.
3 label · 任意 Text that names the action. Shown while extended; hidden, but still the accessible name, while collapsed. Required to extend.
4 focus-ring Gap-then-ring indicator shown for keyboard focus.

寸法

寸法

長さ トークン 値(px)
container padding-inline-start-with-icon tanrei.comp.main-action-button.container.padding-inline-start-with-icon.medium 16
container gap tanrei.comp.main-action-button.container.gap.medium 12
container padding-inline-end tanrei.comp.main-action-button.container.padding-inline-end.medium 20
container height tanrei.comp.main-action-button.container.height.medium 56
icon size tanrei.comp.main-action-button.icon.size.medium 24
Web パッケージが描くフィクスチャ filled-extended の上に描いています。

プロパティ

プロパティ 型 既定値 説明
variant filled | tonal | surface "filled" Emphasis. Filled is the screen's main action at full strength; tonal is quieter; surface keeps the container neutral where a colored one would compete with the content beneath (a map, a photo). A design system may add values.
size small | medium | large "medium" Visual height (40 / 56 / 96), which is also the side of the collapsed form, so one button keeps its height while it collapses and extends. Small sits beside a medium one for a related action; large marks the action on a wide or empty screen. The hit area always meets the minimum target. A design system may add values.
extended boolean true Shows the label beside the icon. False collapses the button to its icon (for example while the content scrolls); the label stays the accessible name. It changes the button's form, never its meaning: this is not a toggle. A button without an icon stays extended, and one without a label never extends (MAB-15, MAB-16).
fullWidth boolean false Makes the button take the whole width its container gives it, its icon and label centred: the one action of a screen held across its foot on a narrow screen (MAB-24). A full-width button is always drawn extended, so extended is ignored while it is true.
iconPosition leading | trailing "leading" The side of the label the icon is on, in the reading direction. Trailing is for a glyph that points where the action goes, such as the arrow of Next. Collapsed, the icon is centered whichever side it is on.
disabled boolean false Prevents interaction and removes the button from the focus order. Hiding it when its action is unavailable is usually better than disabling it.
label string "" The visible text while extended, and the accessible name in either form. Required: an icon alone is not a name.

スロット

スロット 説明
icon The icon (a glyph of the system's icon set).

イベント

イベント 説明
click The button was activated by pointer, touch, keyboard or assistive technology.

状態

振る舞い

EARS 記法による要件です。各プラットフォームの実装は、この ID に対応するテストで検証されます。要件の原文は仕様ファイルのものをそのまま掲載しています。

ID 要件
MAB-1 WHEN the user activates the main action button by pointer, touch, Enter or Space THE main action button SHALL emit click exactly once.
MAB-2 WHILE disabled is true THE main action button SHALL NOT emit click, SHALL NOT be focusable and SHALL expose the disabled state to assistive technology.
MAB-3 WHILE hovered, keyboard-focused or pressed THE main action button SHALL render its container, label, icon, elevation and opacity from the component tokens of the highest-precedence active state, with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state.
MAB-4 WHEN the main action button receives keyboard focus THE main action button SHALL show the focus ring without animation; focus gained by pointer or touch SHALL NOT show it.
MAB-5 WHERE the visual size is below the minimum target THE main action button 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; a platform that cannot tell the input type SHALL use sys.target.touch.
MAB-6 WHILE disabled is true THE main action button SHALL render its container, label, icon, elevation and opacity from the disabled component tokens of its variant.
MAB-7 WHEN variant or size names a value that the active design system does not have THE main action button SHALL render with the default value's tokens, and in debug builds report the unknown value.
MAB-8 WHEN the main action button is pressed by pointer, touch, Enter or Space THE main action button 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.
MAB-9 WHILE pressed THE main action button 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; label, 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.main-action-button.motion.feedback-duration with comp.main-action-button.motion.state-easing, fades over the state duration of its size (comp.main-action-button.motion.state-duration.small, .medium or .large) with comp.main-action-button.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced.
MAB-10 THE main action button 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.
MAB-11 WHILE it is extended THE main action button SHALL draw a container at least the height of its size (comp.main-action-button.container.height.medium for medium, and so on) with the corners of its size (comp.main-action-button.container.shape.medium, and so on), holding its label and, when it has one, its icon on the side iconPosition names (leading: before the label in the reading direction; trailing: after it), the two separated by comp.main-action-button.container.gap.medium (and so on); it SHALL pad the icon's side by comp.main-action-button.container.padding-inline-start-with-icon.medium for a leading icon or comp.main-action-button.container.padding-inline-end-with-icon.medium for a trailing one, a start without an icon by comp.main-action-button.container.padding-inline-start.medium and an end without an icon by comp.main-action-button.container.padding-inline-end.medium, and the top and bottom by at least comp.main-action-button.container.padding-block.medium (and so on for each size), and SHALL be as wide as that content.
MAB-12 THE main action button SHALL set its label in the text style of its size (comp.main-action-button.label.typescale.medium, and so on) and the label color of its variant and state (comp.main-action-button.filled.label.color for filled at rest, and so on), SHALL NOT truncate it (it stays on one line while it fits, wraps when the layout gives it less width, and the container grows in both axes with it), and SHALL draw the icon, in either form, in the same color at the icon size of its size (comp.main-action-button.icon.size.medium, and so on) 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.
MAB-13 WHILE it is collapsed THE main action button SHALL hide its label and draw a square container whose side is the height of its size (comp.main-action-button.container.height.medium, and so on) with the corners of its size (comp.main-action-button.container.shape.medium, and so on; a circle where they are full, as in the default system) and its icon centered in it whichever side iconPosition names, and SHALL keep label as its accessible name.
MAB-14 WHEN extended changes THE main action button SHALL change its width between the two forms over comp.main-action-button.motion.resize-duration with comp.main-action-button.motion.resize-easing, keeping its height and corners, fading the label out as it narrows and in as it widens, and moving the icon between its side of the label and the center, and SHALL change at once where motion is reduced; which edge stays in place is decided by the layout that holds it, as for any change of its width.
MAB-15 IF the main action button has no icon THEN THE main action button SHALL draw its extended form whatever extended says (there is no empty collapsed form), and in debug builds report it while extended is false.
MAB-16 WHILE label is not empty THE main action button SHALL expose it as its accessible name, in either form, over a name the product gives it on the element (aria-label or aria-labelledby). IF label is empty THEN THE main action button SHALL draw its collapsed form whatever extended says, SHALL expose the name the product gives it on the element — aria-label, or aria-labelledby naming an element — as its accessible name, and WHILE the product gives it none SHALL in debug builds report that it has no accessible name; this holds over MAB-15 when it has no icon either.
MAB-17 THE main action button SHALL cast the elevation of its variant and state (comp.main-action-button.filled.container.elevation for filled at rest, and so on) outside its container without clipping it, in either form and while it changes between them, and WHEN the elevation or the colors change between states SHALL animate the change over the state duration of its size (comp.main-action-button.motion.state-duration.small, .medium or .large) with comp.main-action-button.motion.state-easing, changing them at once where motion is reduced.
MAB-18 THE main action button SHALL NOT position itself: it SHALL lay out at its own size, or across the width of its container while fullWidth is true (MAB-24), where the layout that holds it places it (normally the inline-end bottom corner of the screen, clear of any navigation bar), and SHALL NOT draw a scrim or move content.
MAB-19 WHEN the layout direction is right-to-left THE main action button SHALL mirror the order of its icon and label (a leading icon on the right, a trailing one on the left) and apply its paddings by logical start and end, SHALL NOT mirror the glyph itself (the icon helper mirrors a glyph the icon set marks, MAB-22), and SHALL grow a ripple from the point of contact as in left-to-right.
MAB-20 THE main action button SHALL expose itself to assistive technology as a button named by label with no pressed, selected, checked or expanded state, and WHEN extended changes SHALL NOT change its role, its name or its focus, and SHALL NOT announce the change.
MAB-21 THE main action button 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 main action button is shown THE main action button SHALL draw itself for the new direction at once, without a motion of its own, keeping what it shows and its state.
MAB-22 WHERE the layout direction is right-to-left THE main action button SHALL draw the glyph of its icon slot 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 MAB-19 gives. THE main action button 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.
MAB-23 THE main action button SHALL lay out its label 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 main action button SHALL still place the label and align its lines by the layout direction, not by the text's own.
MAB-24 WHILE fullWidth is true THE main action button SHALL take the whole inline size its container gives it and SHALL draw its extended form whatever extended says — the height, corners, paddings, elevation and states of MAB-11 and MAB-17 — with its icon and label centred in that width as one row, in the order MAB-19 gives them; WHERE that width is narrower than the label THE main action button SHALL wrap the label, its lines centred (MAB-12). WHEN extended changes while fullWidth is true THE main action button SHALL NOT change its form, and in debug builds SHALL report that a full-width button does not collapse. MAB-16 holds over this rule: a button with no label draws its collapsed form, at its own size, and is reported.
MAB-25 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 main action button 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 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 main action button is invalid — except those the main action button owns (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 main action button SHALL take it off that element. WHEN the product sets an aria- attribute the main action button owns THE main action button SHALL report it in debug builds.

プラットフォーム別の名前対応

パリティは API が同一であることを意味しません。同じ契約を、各プラットフォームの流儀で表現します。

仕様 Web SwiftUI Jetpack Compose Flutter React Native
コンポーネント tanrei-main-action-button TanreiMainActionButton TanreiMainActionButton TanreiMainActionButton MainActionButton
variant · プロパティ variant variant variant variant variant
size · プロパティ size size size size size
extended · プロパティ extended isExtended expanded isExtended extended
fullWidth · プロパティ fullWidth fullWidth fullWidth fullWidth fullWidth
iconPosition · プロパティ iconPosition iconPosition iconPosition iconPosition iconPosition
disabled · プロパティ disabled disabled enabled onPressed disabled
label · プロパティ label title text label label
click · イベント click action onClick onPressed onPress
備考 The icon is the icon slot (a glyph of the icon font). extended is a boolean property that defaults to true; its attribute is the inverse collapsed, because an HTML boolean attribute cannot default to true. iconPosition is the icon-position attribute. The host has no aria-expanded or aria-pressed (MAB-20). The element is inline; the page positions it (for example with position: fixed or a sticky container). fullWidth is the full-width attribute: the host is then a block-level flex box as wide as its container, and the native <button> fills it (MAB-24). The initializer takes the title first, as Button(_:action:) does, and an optional icon name. disabled follows .disabled(_:). The view is placed by the caller, typically in an .overlay(alignment: .bottomTrailing). Compose idiom (as Material 3's extended FAB composable): expanded collapses it; enabled is the inverse flag; text is a String so it is also the content description. Placed through Scaffold's floatingActionButton slot or by the caller. Flutter idiom (as Material 3's extended FAB): isExtended collapses it and a null onPressed disables it. Placed through Scaffold.floatingActionButton or by the caller; it adds no Hero. The icon is the icon prop (a glyph name). The caller positions it (for example absolutely inside the screen's root view).

トークン

トークン 参照先 解決値(ライト)
--tanrei-comp-main-action-button-container-gap-large sys.space.space200 16px
--tanrei-comp-main-action-button-container-gap-medium sys.space.space150 12px
--tanrei-comp-main-action-button-container-gap-small sys.space.space100 8px
--tanrei-comp-main-action-button-container-height-large — 96px
--tanrei-comp-main-action-button-container-height-medium — 56px
--tanrei-comp-main-action-button-container-height-small — 40px
--tanrei-comp-main-action-button-container-padding-block-large — 6px
--tanrei-comp-main-action-button-container-padding-block-medium — 6px
--tanrei-comp-main-action-button-container-padding-block-small — 6px
--tanrei-comp-main-action-button-container-padding-inline-end-with-icon-large sys.space.space400 32px
--tanrei-comp-main-action-button-container-padding-inline-end-with-icon-medium sys.space.space250 20px
--tanrei-comp-main-action-button-container-padding-inline-end-with-icon-small sys.space.space150 12px
--tanrei-comp-main-action-button-container-padding-inline-end-large sys.space.space400 32px
--tanrei-comp-main-action-button-container-padding-inline-end-medium sys.space.space250 20px
--tanrei-comp-main-action-button-container-padding-inline-end-small sys.space.space200 16px
--tanrei-comp-main-action-button-container-padding-inline-start-with-icon-large sys.space.space300 24px
--tanrei-comp-main-action-button-container-padding-inline-start-with-icon-medium sys.space.space200 16px
--tanrei-comp-main-action-button-container-padding-inline-start-with-icon-small sys.space.space150 12px
--tanrei-comp-main-action-button-container-padding-inline-start-large sys.space.space400 32px
--tanrei-comp-main-action-button-container-padding-inline-start-medium sys.space.space250 20px
--tanrei-comp-main-action-button-container-padding-inline-start-small sys.space.space200 16px
--tanrei-comp-main-action-button-container-shape-large sys.shape.corner.full 9999px
--tanrei-comp-main-action-button-container-shape-medium sys.shape.corner.full 9999px
--tanrei-comp-main-action-button-container-shape-small sys.shape.corner.full 9999px
--tanrei-comp-main-action-button-disabled-opacity — 1
--tanrei-comp-main-action-button-filled-container-color sys.color.primary #1b1b1b
--tanrei-comp-main-action-button-filled-container-elevation sys.elevation.level3 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-filled-disabled-container-color — #1b1b1b · 12%
--tanrei-comp-main-action-button-filled-disabled-container-elevation —
--tanrei-comp-main-action-button-filled-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-main-action-button-filled-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-main-action-button-filled-focused-container-color — #323232
--tanrei-comp-main-action-button-filled-focused-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-filled-focused-label-color — #ffffff
--tanrei-comp-main-action-button-filled-focused-state-layer-color — #ffffff
--tanrei-comp-main-action-button-filled-hovered-container-color — #2d2d2d
--tanrei-comp-main-action-button-filled-hovered-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 8px 16px -4px rgb(0 0 0 / 0.04), 0px 24px 32px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-filled-hovered-label-color — #ffffff
--tanrei-comp-main-action-button-filled-hovered-state-layer-color — #ffffff
--tanrei-comp-main-action-button-filled-label-color sys.color.on-primary #ffffff
--tanrei-comp-main-action-button-filled-pressed-container-color — #323232
--tanrei-comp-main-action-button-filled-pressed-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-filled-pressed-label-color — #ffffff
--tanrei-comp-main-action-button-filled-pressed-state-layer-color — #ffffff
--tanrei-comp-main-action-button-filled-state-layer-color comp.main-action-button.filled.label.color #ffffff
--tanrei-comp-main-action-button-focused-opacity — 1
--tanrei-comp-main-action-button-hovered-opacity — 1
--tanrei-comp-main-action-button-icon-size-large sys.icon.large 32px
--tanrei-comp-main-action-button-icon-size-medium sys.icon.medium 24px
--tanrei-comp-main-action-button-icon-size-small sys.icon.medium 24px
--tanrei-comp-main-action-button-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-main-action-button-motion-resize-duration sys.motion.role.resize.large 400ms
--tanrei-comp-main-action-button-motion-resize-easing sys.motion.easing.spatial cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-main-action-button-motion-state-duration-large sys.motion.role.state.large 250ms
--tanrei-comp-main-action-button-motion-state-duration-medium sys.motion.role.state.default 250ms
--tanrei-comp-main-action-button-motion-state-duration-small sys.motion.role.state.compact 200ms
--tanrei-comp-main-action-button-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-main-action-button-pressed-opacity — 1
--tanrei-comp-main-action-button-surface-container-color sys.color.surface-container-lowest #ffffff
--tanrei-comp-main-action-button-surface-container-elevation sys.elevation.level3 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-surface-disabled-container-color — #1b1b1b · 12%
--tanrei-comp-main-action-button-surface-disabled-container-elevation —
--tanrei-comp-main-action-button-surface-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-main-action-button-surface-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-main-action-button-surface-focused-container-color — #e8e8e8
--tanrei-comp-main-action-button-surface-focused-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-surface-focused-label-color — #1b1b1b
--tanrei-comp-main-action-button-surface-focused-state-layer-color — #1b1b1b
--tanrei-comp-main-action-button-surface-hovered-container-color — #ededed
--tanrei-comp-main-action-button-surface-hovered-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 8px 16px -4px rgb(0 0 0 / 0.04), 0px 24px 32px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-surface-hovered-label-color — #1b1b1b
--tanrei-comp-main-action-button-surface-hovered-state-layer-color — #1b1b1b
--tanrei-comp-main-action-button-surface-label-color sys.color.primary #1b1b1b
--tanrei-comp-main-action-button-surface-pressed-container-color — #e8e8e8
--tanrei-comp-main-action-button-surface-pressed-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-surface-pressed-label-color — #1b1b1b
--tanrei-comp-main-action-button-surface-pressed-state-layer-color — #1b1b1b
--tanrei-comp-main-action-button-surface-state-layer-color comp.main-action-button.surface.label.color #1b1b1b
--tanrei-comp-main-action-button-tonal-container-color sys.color.secondary-container #eeeeee
--tanrei-comp-main-action-button-tonal-container-elevation sys.elevation.level3 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-tonal-disabled-container-color — #1b1b1b · 12%
--tanrei-comp-main-action-button-tonal-disabled-container-elevation —
--tanrei-comp-main-action-button-tonal-disabled-label-color — #1b1b1b · 38%
--tanrei-comp-main-action-button-tonal-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-main-action-button-tonal-focused-container-color — #dadada
--tanrei-comp-main-action-button-tonal-focused-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-tonal-focused-label-color — #262626
--tanrei-comp-main-action-button-tonal-focused-state-layer-color — #262626
--tanrei-comp-main-action-button-tonal-hovered-container-color — #dedede
--tanrei-comp-main-action-button-tonal-hovered-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 8px 16px -4px rgb(0 0 0 / 0.04), 0px 24px 32px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-tonal-hovered-label-color — #262626
--tanrei-comp-main-action-button-tonal-hovered-state-layer-color — #262626
--tanrei-comp-main-action-button-tonal-label-color sys.color.on-secondary-container #262626
--tanrei-comp-main-action-button-tonal-pressed-container-color — #dadada
--tanrei-comp-main-action-button-tonal-pressed-container-elevation — 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 1px 0px rgb(0 0 0 / 0.04), 0px 4px 8px -4px rgb(0 0 0 / 0.04), 0px 16px 24px -8px rgb(0 0 0 / 0.06)
--tanrei-comp-main-action-button-tonal-pressed-label-color — #262626
--tanrei-comp-main-action-button-tonal-pressed-state-layer-color — #262626
--tanrei-comp-main-action-button-tonal-state-layer-color comp.main-action-button.tonal.label.color #262626
--tanrei-comp-main-action-button-label-typescale-large sys.typescale.heading-5 500 16px/1.5
--tanrei-comp-main-action-button-label-typescale-medium sys.typescale.label 500 14px/1.4286
--tanrei-comp-main-action-button-label-typescale-small sys.typescale.label 500 14px/1.4286

フィクスチャ

基準となる描画パターンです。全プラットフォームが同じ ID でスナップショットを撮り、結果を比較できるようにしています。

生成元 specs/main-action-button.spec.json, tokens/comp/main-action-button.tokens.json, design/tokens/

ガイドライン

使う場面

その画面に来た人の多くが行うアクションで、どこまでスクロールしてもすぐ押せるようにしておきたいものに使います。メッセージを書く、項目を追加する、録音を始める、などです。1 つの画面に置くのは多くても 1 つです。必要のない画面も多くあります。設定のページや読むための画面には、上に浮かべるほどのアクションはありません。

  • フォーム、ダイアログ、コンテンツの中の特定の項目に属するアクションには、そのコンテンツのある場所にボタンを置きます。
  • いまの画面や選択に対するアクションがいくつかあるなら、ツールバーを使います。
  • 主なアクションに関連するいくつかの形(新しいドキュメント、フォルダー、アップロード)があるときは、メインアクションボタンから、それらを並べたボトムシートを開きます。FAB メニューは、それがある m3-like レイヤーのシステムでその役目を果たします。既定のシステムはこれを持ちません。
  • メインアクションボタンはアクションを実行するもので、移動先は持ちません。別の場所へ移動するだけのコントロールはリンクです。href を付けたボタンを使います。

伸ばすか、縮めるか

最初は伸ばした形にします。ボタンが何をするかを伝えるのは書かれた名前で、アイコンだけで通じるのは、人がそれを覚えた後です。

ラベルの得より損が大きいときに縮めます。コンテンツをスクロールしていてボタンが内容を隠してしまうときや、幅の狭いウィンドウなどです。リストの先頭に戻ったときなど、余裕ができたらまた伸ばします。形を変えるのは利用者側で、extended を設定します。ボタンを押しても形は変わりません。

  • 形が変わる間も、高さと角はそのままです。どの端を固定するかはレイアウトが決めます。末尾側に固定してあれば、その端に向かって縮みます。
  • アイコンのないボタンは、縮める先がないので伸ばした形のままです。ラベルのないボタンは縮めた形で描かれ、名前がないことをデバッグビルドが報告します。
  • 動きを減らす設定では、形は瞬時に切り替わります。

バリアントの選び方

バリアント 強調 用途
Filled(既定) 最も強い 画面の主なアクションを、最も目立つ形で示すとき。
Tonal 強い 主役はあくまでコンテンツ、という画面の主なアクション。
Surface ニュートラル 地図や写真など、すでに色のあるコンテンツの上に置く主なアクション。色の付いたコンテナでは内容と競合してしまうときに使います。

デザインシステムは独自のバリアントを加えられます。

サイズの選び方

サイズ 既定のシステムでの高さ 用途
Small 40 medium の隣に置く、関連する 2 つ目のアクション。または密度の高いレイアウト。
Medium(既定) 56 ほとんどの画面の主なアクション。
Large 96 広い画面や、ほとんど何もない画面のアクション。medium では埋もれてしまうときに使います。

高さは縮めた形の 1 辺でもあるので、1 つのボタンは縮めても伸ばしても同じ高さを保ちます。

配置

ボタンはスクリムを描かず、コンテンツも動かしません。コンテンツの上に、ふつうは末尾側の下の角に置き、ナビゲーションバー、ドックのツールバー、システムのインセットと重ならないようにします。スクロールするコンテンツの末尾に余白を取り、ボタンが最後の項目をずっと隠したままにならないようにします。影はコンテナの外側に落ちます。ボタンを入れる要素がはみ出しを切り取るなら、影の分の余裕を取ってください。

全幅

full-width を指定すると、ボタンはコンテナと同じ幅になり、アイコンとラベルは中央に置かれます。幅の狭い画面で、「購入手続きへ」や「送信」のような画面で 1 つだけのアクションを画面の下端に渡して置くときに使います。全幅のボタンは常に伸びた形です。コンテンツのスクロール中も縮まず、extended を false にするとデバッグビルドで報告されます。高さ、角、影はそのサイズのままで、収まらないラベルは折り返します。自分で位置を決めないのは変わらないので、画面の下端に置くコンテナの中に入れてください。

全幅のメインアクションボタン

shopping_cart

推奨

  • ラベルには、アクションを表す動詞、または動詞と名詞の組み合わせを使います。作成、新しい予定、写真を追加 など。
  • 人がそのアクションとすでに結び付けているグリフを選びます。書くことには edit、作ることには add などです。
  • 「次へ」の矢印のように、アクションの進む先を指すグリフには、iconPosition を trailing にします。
  • メインアクションボタンのあるどの画面でも、同じ位置に置きます。
  • その画面でアクションが使えないときは、ボタンを隠します。無効なボタンをコンテンツの上に浮かべておくより、たいていはそのほうが親切です。

非推奨

  • 同じサイズのメインアクションボタンを 1 つの画面に 2 つ置かない。2 つのアクションが同じくらい重要に思えるなら、どちらも主なアクションではありません。コンテンツの中のボタンにします。
  • 「削除」「設定」「戻る」のような、重要度の低いアクション、破壊的なアクション、移動するだけのアクションに使わない。
  • トグルとして使わない。状態を示すためにラベルやアイコンを変えることもしません。
  • 初めて見る画面で、アイコンの意味を伝えるものがほかにないのに縮めない。
  • 押すかどうかを判断するのに必要なコンテンツを、ボタンで隠さない。

文言

ラベルは簡潔に、句読点なしで書き、1〜2 語を目安にします。英語ではセンテンスケース(先頭のみ大文字)にします。ラベルは途中で切れません。収まる間は 1 行のままで、レイアウトから与えられる幅が足りなくなると折り返します。

アクセシビリティ

ロール
button
ラベル付け
label is the accessible name, whether it is shown or collapsed away. The icon is decorative to assistive technology. A tooltip may show the same text to sighted pointer users while it is collapsed, but it never replaces label.
最小ターゲット
見た目のサイズが小さくても、ヒット領域は最小ターゲットサイズを満たします。 minimum 32px · pointer 44px · spacing 8px · touch 48px

キーボード操作

キー 動作
Tab Moves focus to the main action button.
Enter / Space Activates it.

備考

  • It is an action, never a toggle. Collapsing and extending change what is drawn, not what is announced: the role stays button, the name stays label, and there is no expanded state to report (MAB-20).
  • Its place in the focus and reading order is where the layout puts it in the element tree, not where it is drawn. Put it where its action belongs: after the content it acts on, or first when it is the screen's reason to exist.
  • Extended, text must remain readable at 200% text size: the container grows, the label never truncates. Collapsed, the icon is not text: where sizes are points or dp (SwiftUI, Compose, Flutter, React Native), text size leaves the square and the icon as they are; on the web every Tanrei size follows the root font size (rem), so the square and its icon grow together and stay square and whole. Either way the label is still announced.
  • A trailing icon that points (an arrow) keeps its glyph in right-to-left layouts; the product chooses the glyph that points the right way for the direction.
  • 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/main-action-button.spec.json, tokens/sys/measure.tokens.json

コンポーネントが担うこと

  • どちらの形でも、label を名前とするボタンです。アイコンは支援技術から隠されます。
  • 縮めたり伸ばしたりして変わるのは描かれるものだけで、読み上げられる内容は変わりません。ロール、名前、フォーカスはそのままで、何も読み上げず、押された状態や展開状態もありません。
  • Enter と Space で実行します。無効のときは、何も発火せず、フォーカス順から外れ、無効として伝わります。
  • small を含むどのサイズでも、ヒット領域は最小ターゲットを満たします。
  • フォーカスインジケーターは、キーボードフォーカスのときだけ、アニメーションなしで表示します。
  • 伸ばした形では、文字サイズに合わせてコンテナが大きくなり、ラベルは途中で切れません。縮めた形のアイコンは文字ではありません。Web では Tanrei のサイズはすべてルートのフォントサイズに従うので、正方形とアイコンは一緒に大きくなり、正方形のままです。
  • 右から左(RTL)のレイアウトでは、アイコンとラベルの位置が入れ替わり、パディングもそれに従います。グリフ自体を反転するのは、アイコンセットが向きを示すグリフとして印を付けているときだけです。
  • デバッグビルドは、ラベルのないボタンと、アイコンがないのに縮めたボタンを報告します。

利用者側で行うこと

  • 縮めた形でしか使わない場合でも、必ずプロダクトの言語で label を付けます。
  • 要素ツリーの中で、そのアクションが属する場所に置きます。対象のコンテンツの後ろか、画面の存在理由そのものなら先頭です。支援技術と Tab キーは、描かれる位置ではなくツリーの順にたどり着きます。
  • 縮めているときは、ポインターを使う人のために、同じ文言のツールチップを検討します。ツールチップは label の代わりにはなりません。
  • キーボードフォーカスのあるコンテンツやコントロールを、ボタンが隠さないようにします。コンテンツの末尾に余白を取るか、ボタンをよけます。
  • 意味をバリアントの色だけで伝えないでください。ラベルの言葉で示します。