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

アプリバー

画面の上端で今いる画面の名前を示し、戻る操作と、その画面でよく使うアクションをまとめます。下端の線は画面のヘッダーの縁として常に引かれ、コンテンツが下に潜り込むとスクロール時の見た目になります。

content_copyMarkdown で表示
カテゴリ
ナビゲーション
トークン
comp.app-bar
レベル
基礎

概要

Web
tanrei-app-bar
schedule
SwiftUI
TanreiAppBar
schedule
Jetpack Compose
TanreiAppBar
schedule
Flutter
TanreiAppBar
schedule
React Native
AppBar
schedule

生成元 docs/status/status.json, specs/app-bar.spec.json

Web と React のサンプルは現在の仕様を示しています。現在の仕様では、バーの項目はアイコンだけのボタンです。SwiftUI、Compose、Flutter、React Native は追従待ちで、独立したアイコンボタンのコンポーネントを持つ以前の版を提供しています。追従するまで、これらのサンプルは以前の版を示します。

戻るボタンと 2 つのアクションを持つ小さいアプリバー

arrow_backsearchmore_vert

使い方

アプリバーはレイアウト領域で、オーバーレイではありません。画面の上端に自分の高さを確保し、コンテンツはその下から始まります。バー自体がフォーカスを受け取ることはありません。

アプリバーが持つ項目は 2 種類です。先頭に置く 1 つの navigation(戻る、メニュー、閉じる)と、末尾に置く 3 つまでの action です。どちらも利用者側で用意する、アイコンだけのボタンです。それぞれが自分のラベルと、自分のクリック、または行き先がある場合は自分の href を持ちます。バーはこれらを並べるだけで、見た目を変えることはありません。受け取り方はプラットフォームごとのスロットに従います。Web では slot="navigation" と slot="actions" を付けた子要素、SwiftUI では navigation と actions のビュービルダー、Compose では navigationIcon と actions のラムダ、Flutter では leading と actions:、React Native では navigation と actions に渡すノードです。

タイトルは画面の名前で、画面の見出しでもあります。Web では headline という名前です。HTML のグローバル属性 title を使うと、バー全体にツールチップが出てしまうためです。

スクロール状態。 コンテンツがバーの下に潜り込んでいる間、バーはスクロール時の見た目になります。画面のスクロールするビューをつなげば、バーが自分で scrolled を切り替えます。つなぎ方は、Web では scroll-target(既定はドキュメント)、SwiftUI ではバーを収めた .safeAreaInset の後に付ける .tanreiAppBarScrollSource()、Compose では Modifier.nestedScroll でつなぐ scrollBehavior、Flutter ではページのスクロール通知、React Native では useAppBarScroll() です。つながないときは、利用者側で scrolled を設定します。

システムバー。 バーの色は、上にあるステータスバー、カメラの切り欠き、ウィンドウのキャプションの下まで広がります。中身はそれらより下、左右のインセットの内側に収まります。Web は env(safe-area-inset-*) を読み、Compose は windowInsets(既定はステータスバーと左右の切り欠き)を受け取り、Flutter は MediaQuery のパディングを読み、SwiftUI では .safeAreaInset(edge: .top) でバーを置きます。React Native にはセーフエリアの API がなく、Tanrei もセーフエリアのライブラリには依存しないので、インセットはプロパティからだけ受け取ります。TanreiProvider にウィンドウの insets を渡してください(react-native-safe-area-context を使っているなら、その useSafeAreaInsets() をそのまま渡します)。バー自身の insets プロパティは、指定した辺だけ provider の値を置き換えます。ウィンドウの一番上にないバーには { top: 0 } を渡します。

<!-- タイトルは headline です。HTML の title 属性ではバーの上にツールチップが出てしまいます。 -->
<tanrei-app-bar headline="受信トレイ">
  <tanrei-button variant="text" slot="navigation" label="戻る"><span slot="icon">arrow_back</span></tanrei-button>
  <tanrei-button variant="text" slot="actions" label="検索"><span slot="icon">search</span></tanrei-button>
  <tanrei-button variant="text" slot="actions" label="その他のオプション"><span slot="icon">more_vert</span></tanrei-button>
</tanrei-app-bar>

<!-- #inbox のスクロールに合わせて畳まれる大きいタイトル。heading-level を指定しなければタイトルは h1 です。 -->
<div style="display: flex; flex-direction: column; block-size: 100dvh">
  <tanrei-app-bar size="large" headline="受信トレイ" scroll-target="inbox">
    <tanrei-button variant="text" slot="navigation" label="メニュー"><span slot="icon">menu</span></tanrei-button>
  </tanrei-app-bar>
  <main id="inbox" style="flex: 1; overflow: auto">…</main>
</div>

<!-- ページへ移動するボタンはリンクです。href を指定します。検索は、アイコンのトリガーとしてアクションにできます。 -->
<tanrei-app-bar headline="タブ">
  <tanrei-button variant="text" slot="navigation" href="/components/" label="すべてのコンポーネント"><span slot="icon">arrow_back</span></tanrei-button>
  <tanrei-search slot="actions" bar="icon" presentation="dialog" label="ドキュメントを検索" placeholder="検索"
    clear-label="検索語を消去" back-label="戻る"></tanrei-search>
</tanrei-app-bar>

<script type="module">
  import '@tanrei/web/components/app-bar';
  import '@tanrei/web/components/button';
  import '@tanrei/web/components/search';
  // ボタンは利用者側のものです。バー自身はイベントを持ちません。
  document.querySelector('[label="戻る"]').addEventListener('click', () => history.back());
</script>
import { AppBar, Button, Search } from '@tanrei/react';

<AppBar headline="受信トレイ">
  <Button variant="text" slot="navigation" label="戻る" onClick={goBack}><span slot="icon">arrow_back</span></Button>
  <Button variant="text" slot="actions" label="検索" onClick={search}><span slot="icon">search</span></Button>
  <Button variant="text" slot="actions" label="その他のオプション" onClick={openMore}><span slot="icon">more_vert</span></Button>
</AppBar>

// ページへ移動するボタンはリンクです。href を指定します。検索は、アイコンのトリガーとしてアクションにできます。
<AppBar headline="タブ">
  <Button variant="text" slot="navigation" href="/components/" label="すべてのコンポーネント"><span slot="icon">arrow_back</span></Button>
  <Search slot="actions" bar="icon" presentation="dialog" label="ドキュメントを検索" placeholder="検索" clearLabel="検索語を消去" backLabel="戻る" />
</AppBar>
import Tanrei

// システムのバーを隠した NavigationStack の、上側のセーフエリアのインセットに置きます。
// isScrolled を nil のままにすると、.tanreiAppBarScrollSource() を付けたスクロールビューに従います。
NavigationStack {
    ScrollView { InboxRows() }
        .safeAreaInset(edge: .top, spacing: 0) {
            TanreiAppBar("受信トレイ", size: .large) {
                TanreiIconButton("メニュー", action: openMenu) { Image(systemName: "line.3.horizontal") }
            } actions: {
                TanreiIconButton("検索", action: search) { Image(systemName: "magnifyingglass") }
            }
        }
        // バーを収めた inset の後に付けます。inset の内側に付けたモディファイアーはバーに届きません。
        .tanreiAppBarScrollSource()
        .toolbar(.hidden, for: .navigationBar)
}
import io.github.chnotchy.tanrei.components.TanreiAppBar
import io.github.chnotchy.tanrei.components.TanreiAppBarDefaults
import io.github.chnotchy.tanrei.components.TanreiIconButton
import io.github.chnotchy.tanrei.primitives.TanreiText
import io.github.chnotchy.tanrei.tokens.TanreiAppBarSize

// 小さいバーです。scrollBehavior を渡さなければ平らなままです。
TanreiAppBar(
    title = "受信トレイ",
    navigationIcon = { TanreiIconButton(onClick = onBack, contentDescription = "戻る") { TanreiText("arrow_back") } },
    actions = {
        TanreiIconButton(onClick = onSearch, contentDescription = "検索") { TanreiText("search") }
        TanreiIconButton(onClick = onMore, contentDescription = "その他のオプション") { TanreiText("more_vert") }
    },
)

// リストのスクロールに合わせて畳まれる大きいバーです。スクロールするビューの親で behavior をつなぎます。
// pinnedScrollBehavior() は小さいバー向けで、畳まれず、下にコンテンツがある間だけスクロール状態になります。
val scrollBehavior = TanreiAppBarDefaults.collapseScrollBehavior()
Column(Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
    TanreiAppBar(title = "受信トレイ", size = TanreiAppBarSize.Large, scrollBehavior = scrollBehavior)
    LazyColumn { /* 画面のコンテンツ */ }
}
import 'package:tanrei/tanrei.dart';

// 小さいバーです。Scaffold のアプリバーのスロットか、Column の先頭に置きます。
// scrolledUnder を null のままにすると、AppBar と同じく一番近い ScrollNotificationObserver に従います。
TanreiAppBar(
  leading: TanreiIconButton(onPressed: () => Navigator.of(context).maybePop(), icon: const Icon(AppSymbols.arrowBack), semanticLabel: '戻る'),
  title: '受信トレイ',
  actions: <Widget>[
    TanreiIconButton(onPressed: search, icon: const Icon(AppSymbols.search), semanticLabel: '検索'),
    TanreiIconButton(onPressed: openMore, icon: const Icon(AppSymbols.moreVert), semanticLabel: 'その他のオプション'),
  ],
),

// コンテンツと一緒に畳まれる大きいバーはスライバーです。CustomScrollView の上端に固定されます。
CustomScrollView(
  slivers: <Widget>[
    TanreiSliverAppBar(title: '受信トレイ', size: TanreiAppBarSize.large, leading: menuButton),
    SliverList.list(children: messages),
  ],
),
import { useState } from 'react';
import { ScrollView, View } from 'react-native';
import { AppBar, IconButton, useAppBarScroll } from '@tanrei/react-native';

// インセットは TanreiProvider の insets か、バー自身の insets から受け取ります。
<AppBar
  title="受信トレイ"
  navigation={<IconButton accessibilityLabel="戻る" onPress={goBack}>arrow_back</IconButton>}
  actions={
    <>
      <IconButton accessibilityLabel="検索" onPress={search}>search</IconButton>
      <IconButton accessibilityLabel="その他のオプション" onPress={openMore}>more_vert</IconButton>
    </>
  }
/>

// 畳まれる大きいバー:スクロールビューをつなぎ、バーをその上に重ね、コンテンツにバーの高さぶんの余白を付けます。
const scroll = useAppBarScroll();
const [barHeight, setBarHeight] = useState(0);
<View style={{ flex: 1 }}>
  <ScrollView onScroll={scroll.onScroll} scrollEventThrottle={16} contentContainerStyle={{ paddingTop: barHeight }}>
    {messages}
  </ScrollView>
  <View style={{ position: 'absolute', top: 0, left: 0, right: 0 }} onLayout={(e) => setBarHeight((h) => Math.max(h, e.nativeEvent.layout.height))}>
    <AppBar size="large" title="受信トレイ" scrollState={scroll.state} navigation={menuButton} />
  </View>
</View>

仕様

サブタイトル付きの大きいバーと、中央揃えのタイトル

menusearchaccount_circlearrow_backsearch

リンクであるナビゲーションボタンと、アクションに置いた検索のトリガー

arrow_backmore_vert

構成要素

パーツ 説明
1 container The bar across the top of the screen. Its color reaches under the system inset above it (status bar, camera cutout, window caption).
2 navigation · 任意 The navigation item: one icon-only Button at the start (back, menu or close). The product's icon-only Button, not restyled by the bar.
3 brand · 任意 The product's brand: one link to its first page (or one image), after the navigation item, in a cell as large as an icon button's. It draws one image, the symbol alone, the wordmark alone, the symbol and the wordmark side by side (lockup), or one of the two by the window's width (responsive).
4 context · 任意 The product's context: one control after the brand (a workspace, a project, a design system), the product's own Button, not restyled by the bar.
5 title The screen's name. In the top row for a small bar; under the top row for an expanded large bar, moving into the top row as it collapses.
6 subtitle · 任意 One line under the title: context for it (an account, a status, when it was edited).
7 actions · 任意 The action items: up to three icon-only Buttons at the end. The product's icon-only Buttons, not restyled by the bar.
8 outline The bar's bottom edge. In the default system it is drawn at rest and while content is under the bar; dividerVisibility scroll draws it only once content has scrolled under the bar.

プロパティ

プロパティ 型 既定値 説明
size small | large "small" Small puts the title in the one row the bar has; large gives the title a larger row of its own under the top row, which collapses into the top row as the content scrolls. One product uses small on most screens and large on its top-level ones. A design system may add values.
title string "" The screen's name, and the heading of the screen for assistive technology.
subtitle string "" One line under the title. Empty draws none.
titleAlignment start | center "start" Where the title sits in the top row: after the navigation button, or centered on the bar. An expanded large bar's own title row always starts at the start.
brandHref string "" The address of the product's first page: the brand is a link to it. Empty draws the brand as an image that is not a link.
brandLabel string "" The product's name: the brand's one accessible name, whichever of its images is shown. Required wherever the bar has a brand.
brandLayout lockup | responsive "lockup" How the brand's symbol and wordmark stand when the product gives both (APB-21): lockup sets them side by side, comp.app-bar.brand.lockup.gap apart and aligned as comp.app-bar.brand.lockup.alignment says; responsive shows the wordmark while the window is medium or wider and the symbol while it is compact, never both. One image, a symbol alone or a wordmark alone is shown as it is, at every width, under either value.
scrolled boolean false Whether content lies under the bar. A scrolling view connected to the bar sets it (see platforms); a product without one sets it itself. It selects the bar's scrolled condition, and a large bar is drawn collapsed while it is true.
dividerVisibility auto | always | scroll "auto" When the bar's bottom edge is drawn. always: at rest and while content is under the bar, so that the edge of the screen's header stands still (an app shell's header, a screen whose content starts right under the bar). scroll: only while content lies under the bar, so that the line itself says the content has moved (the scrolled condition's edge alone). auto takes comp.app-bar.outline.visibility, the design system's answer (always in the default system). The bar's color and elevation follow scrolled under either choice.

スロット

スロット 説明
navigation Holds the navigation item: one icon-only Button at the start.
brand The brand as one image: a product's single logo file, drawn comp.app-bar.brand.image.height high. Takes the place of brand-symbol and brand-wordmark where both are given.
brand-symbol The brand's symbol: comp.app-bar.brand.symbol.size high alone, or while the window is compact under brandLayout responsive; comp.app-bar.brand.lockup.symbol.size high (the wordmark's height) beside the wordmark in a lockup.
brand-wordmark The brand's wordmark, drawn comp.app-bar.brand.wordmark.height high: alone at every width, after the symbol in a lockup, or while the window is medium or wider under brandLayout responsive.
context Holds one control of the product's context after the brand: a Button, or a Button that opens a Menu, that says and changes the workspace, the project or the design system the screen is in. The product's own control, not restyled by the bar.
actions Holds the action items, up to three icon-only Buttons at the end, in reading order; more belong in an overflow menu the product provides.

アイテム

このコンポーネントが繰り返すアイテムの種類です。フィクスチャはアイテムを順に並べ、各プラットフォームはそれぞれの流儀で種類ごとに名前を付けます。

navigation

The one icon-only Button at the start of the top row (back, menu or close): a Button of the text variant and the medium size with an icon and no label, unless the product sets otherwise, placed in the navigation slot, which the bar lays out and does not restyle.

プロパティ 型 既定値 説明
label string "" Its accessible name ("Back"), as the Button's label. Required.
スロット 説明
icon Its glyph, as the Button's icon slot. A fixture item's content is this glyph.
イベント 説明
click The button was activated, as the Button's click.
仕様 Web SwiftUI Jetpack Compose Flutter React Native
アイテム tanrei-button TanreiButton TanreiButton TanreiButton Button
label · プロパティ label label contentDescription semanticLabel accessibilityLabel
click · イベント click action onClick onPressed onPress
備考 An icon-only <tanrei-button> child with slot="navigation"; the glyph is its content. An icon-only TanreiButton in the navigation view builder. An icon-only TanreiButton in the navigationIcon lambda. An icon-only TanreiButton given as leading. An icon-only Button element passed as navigation.

action

An icon-only Button at the end of the top row, up to three in reading order: a Button of the text variant and the medium size with an icon and no label, unless the product sets otherwise, placed in the actions slot, which the bar lays out and does not restyle.

プロパティ 型 既定値 説明
label string "" Its accessible name ("Search"), as the Button's label. Required.
スロット 説明
icon Its glyph, as the Button's icon slot. A fixture item's content is this glyph.
イベント 説明
click The button was activated, as the Button's click.
仕様 Web SwiftUI Jetpack Compose Flutter React Native
アイテム tanrei-button TanreiButton TanreiButton TanreiButton Button
label · プロパティ label label contentDescription semanticLabel accessibilityLabel
click · イベント click action onClick onPressed onPress
備考 An icon-only <tanrei-button> child with slot="actions"; the glyph is its content. An icon-only TanreiButton in the actions view builder. An icon-only TanreiButton in the actions lambda. An icon-only TanreiButton in the bar's actions: list. An icon-only Button element among the actions nodes.

状態

振る舞い

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

ID 要件
APB-1 WHILE scrolled is false THE app bar SHALL fill its container with comp.app-bar.flat.container.color, raise it by comp.app-bar.flat.container.elevation and draw its bottom edge comp.app-bar.outline.width thick: in comp.app-bar.scrolled.outline.color WHILE its divider visibility (APB-18) is always, so that the edge stands still whether or not content lies under the bar, and in comp.app-bar.flat.outline.color WHILE it is scroll.
APB-2 WHILE scrolled is true THE app bar SHALL fill its container with comp.app-bar.scrolled.container.color, raise it by comp.app-bar.scrolled.container.elevation and draw its bottom edge comp.app-bar.outline.width thick in comp.app-bar.scrolled.outline.color, under either divider visibility (APB-18).
APB-3 WHEN scrolled changes THE app bar SHALL change its container color, elevation and bottom edge over comp.app-bar.motion.condition-duration with comp.app-bar.motion.condition-easing, and at once where motion is reduced; WHILE its divider visibility (APB-18) is always the bottom edge SHALL NOT change.
APB-4 THE app bar SHALL span the full width of its parent and lay out a top row at least comp.app-bar.container.height.small high: the navigation item comp.app-bar.container.padding-inline from the start edge, the title block, and the action items comp.app-bar.container.padding-inline from the end edge, with comp.app-bar.container.gap between them and comp.app-bar.actions.gap between two actions, each vertically centered in the row; a navigation item SHALL go to the start and action items to the end wherever they stand among the items; WHILE there is no navigation item the title block SHALL start comp.app-bar.title.inset from the start edge, and WHILE there are no action items it SHALL end comp.app-bar.title.inset from the end edge. Right-to-left layouts SHALL mirror the order.
APB-5 THE app bar SHALL draw a title in the top row on one line in comp.app-bar.title.typescale.small and comp.app-bar.title.color, and WHILE subtitle is not empty the subtitle on one line below it in comp.app-bar.subtitle.typescale.small and comp.app-bar.subtitle.color, the two vertically centered as a block; each SHALL be truncated at the end with an ellipsis when it does not fit. THE app bar SHALL set the title's letter-spacing, in every size, by the script of its text, as every title is set (PRS-20): no negative letter-spacing WHERE its language is Chinese, Japanese or Korean, and none WHERE its script joins its letters or stacks marks on them.
APB-6 WHILE titleAlignment is center THE app bar SHALL center the top row's title block on the bar's full width and narrow it equally on both sides, truncating its text, so that it never overlaps the navigation or actions slots, and SHALL center its text; WHILE titleAlignment is start THE app bar SHALL start the title block after the navigation slot (or at comp.app-bar.title.inset) with its text start-aligned.
APB-7 WHILE size is large and the bar is expanded THE app bar SHALL be at least comp.app-bar.container.height.large high, SHALL draw no title in its top row, and SHALL draw below the top row the title in comp.app-bar.title.typescale.large and, WHILE subtitle is not empty, the subtitle in comp.app-bar.subtitle.typescale.large, both starting comp.app-bar.title.inset from the start edge, each on one line truncated at the end, the block ending comp.app-bar.container.padding-block-end above the bar's bottom edge.
APB-8 WHILE size is large and a scrolling view is connected THE app bar SHALL shrink by the distance the content has scrolled from its top, one for one, from its expanded height to comp.app-bar.container.height.small, the expanded title block moving up with the bar's bottom edge and clipped under the top row; the top row's title SHALL be invisible while the expanded title is wholly visible, fully visible once the bar has collapsed, and in between fade in linearly with the part of the expanded title that is covered. WHEN the content scrolls back toward its top THE app bar SHALL expand the same way.
APB-9 WHERE a scrolling view is connected THE app bar SHALL take scrolled from it: true while any content lies under the bar (for a large bar, only once it has fully collapsed) and false when the content is at its top. WHILE size is large and scrolled is true THE app bar SHALL be drawn fully collapsed; WHERE nothing is connected and scrolled is false a large app bar SHALL stay expanded.
APB-10 THE app bar SHALL extend its container under the top system inset (status bar, camera cutout, window caption) and SHALL place its content below that inset and inside the left and right insets, so that its color reaches the screen's top edge while no content sits under a system bar or cutout.
APB-11 THE app bar SHALL NOT restyle its navigation and action items: each SHALL render as the icon-only Button it is, from its own tokens, and keep its own semantics, and the app bar SHALL NOT be focusable itself, so that focus moves from the navigation item to the action items in reading order. A glyph of an item that the system's icon set marks as mirrored (a navigation item's arrow_back) SHALL be mirrored in a right-to-left layout by the item's own icon helper (BTN-19): that is the icon's own behaviour, not a restyling by the app bar.
APB-12 THE app bar SHALL expose title as a heading and subtitle as text read after it, and SHALL expose the title only once WHILE size is large: the expanded title while any of it is visible, the top row's title once the bar has collapsed.
APB-13 WHILE title is empty THE app bar SHALL draw no title block (an expanded large bar then has an empty title row) and SHALL expose no heading; IF the app bar holds more than three action items or more than one navigation item THE app bar SHALL still render them, and in debug builds report that the extra actions belong in an overflow menu or that a bar has one navigation button.
APB-14 WHEN size names a value that the active design system does not have THE app bar SHALL render with the default value's tokens, and in debug builds report the unknown value.
APB-15 WHILE the text size is enlarged THE app bar SHALL grow its rows to hold the title and subtitle lines (its heights are minimums), SHALL keep each on one line truncated at the end, and SHALL NOT scale the navigation and action items it holds.
APB-16 THE app bar 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 app bar is shown THE app bar SHALL draw itself for the new direction at once, without a motion of its own, keeping its size and the scroll state it follows.
APB-17 THE app bar SHALL lay out its title and its subtitle 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 app bar SHALL still place each and align its lines by the layout direction, not by the text's own, and SHALL cut a title or a subtitle that does not fit (APB-5, APB-7) at the end of its own direction, so that a left-to-right label in a right-to-left layout loses its right end and never its beginning.
APB-18 THE app bar SHALL draw its bottom edge (APB-1, APB-2) as its dividerVisibility names: WHILE dividerVisibility is auto, as comp.app-bar.outline.visibility names, which follows sys.behavior.divider-visibility unless a design system sets the app bar apart; WHILE dividerVisibility is always or scroll, as that value names, whatever the tokens say. WHEN dividerVisibility, or the token it follows, changes THE app bar SHALL draw the other form at once, moving no part and keeping the scroll position and focus; WHEN dividerVisibility names a value the app bar does not define THE app bar SHALL treat it as auto and in debug builds report the unknown value. A platform that cannot draw a choice SHALL fall back to the first value, always, and state that in its README.
APB-19 WHERE the product gives the app bar a brand (an image in brand, brand-symbol or brand-wordmark) THE app bar SHALL place it at the start of the top row, after the navigation item at comp.app-bar.actions.gap from it, or comp.app-bar.container.padding-inline from the start edge WHILE no navigation item is drawn, with the title block after it at comp.app-bar.container.gap; the brand SHALL take a cell at least the minimum target high and wide, padded at its start and end by half the minimum target less comp.app-bar.brand.symbol.size, its images centred on the row's centre line, so that a symbol stands where an icon button's icon stands and a wordmark keeps that inset from the cell's edges. Right-to-left layouts SHALL mirror the order.
APB-20 THE app bar SHALL draw the image in brand comp.app-bar.brand.image.height high, the symbol comp.app-bar.brand.symbol.size high and the wordmark comp.app-bar.brand.wordmark.height high, each as wide as its own proportions make it, and SHALL give the brand comp.app-bar.brand.color, which an image drawn in the current color takes and an image of its own colors keeps. WHILE the brand is a lockup THE app bar SHALL draw the symbol and then the wordmark in one row, comp.app-bar.brand.lockup.gap apart, aligned in the row's block axis as comp.app-bar.brand.lockup.alignment names (center, start or end).
APB-21 WHERE the brand holds one image, a symbol alone or a wordmark alone THE app bar SHALL show it at every window width. WHERE it holds both a symbol and a wordmark, WHILE brandLayout is lockup THE app bar SHALL show both as a lockup (APB-20), and WHILE brandLayout is responsive THE app bar SHALL show the wordmark WHILE the window class is medium or expanded and the symbol WHILE it is compact, never both: the window class of the app shell the bar stands in, or, outside one, the window's width against sys.breakpoint.medium. WHEN the window class changes THE app bar SHALL show the other at once, without a motion of its own, the brand's link and its focus unchanged.
APB-22 THE app bar SHALL expose the brand as one link to brandHref named by brandLabel — or, WHILE brandHref is empty, as one image named by brandLabel — between the navigation item and the action items in reading order, and SHALL hide the brand's images from assistive technology, so that the product's name is read once whichever arrangement is shown. THE brand's link SHALL be a stop in the tab order and SHALL draw the focus indicator around its cell, in the shape comp.app-bar.brand.shape.
APB-23 IF brandLayout names a value the app bar does not define THE app bar SHALL treat it as lockup and in debug builds report it; IF brand holds an image together with brand-symbol or brand-wordmark THE app bar SHALL show the image in brand alone and in debug builds report the others; IF the bar has a brand and brandLabel is empty, or a brand slot holds more than one image, THE app bar SHALL still draw what it holds and in debug builds report what is missing or extra.
APB-24 WHILE the brand is a lockup THE app bar SHALL draw the symbol comp.app-bar.brand.lockup.symbol.size high, which is the wordmark's height (comp.app-bar.brand.wordmark.height) unless a design system sets it apart, so that the symbol and the wordmark stand at one height; a symbol shown alone keeps comp.app-bar.brand.symbol.size.
APB-25 WHERE the product puts a control in context THE app bar SHALL place it at the start of the top row after the brand, at comp.app-bar.actions.gap from the brand's cell (from the navigation item WHILE no brand is drawn, and comp.app-bar.container.padding-inline from the start edge WHILE neither is), vertically centred in the row, with the title block after it at comp.app-bar.container.gap; SHALL NOT restyle it, as APB-11 says of the navigation and action items; and SHALL keep it in reading order after the brand and before the title block and the action items. Right-to-left layouts SHALL mirror the order.

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

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

仕様 Web SwiftUI Jetpack Compose Flutter React Native
コンポーネント tanrei-app-bar TanreiAppBar TanreiAppBar TanreiAppBar AppBar
size · プロパティ size size size size size
title · プロパティ headline title title title title
subtitle · プロパティ subtitle subtitle subtitle subtitle subtitle
titleAlignment · プロパティ titleAlignment titleAlignment titleAlignment centerTitle titleAlignment
brandHref · プロパティ brandHref brandHref brandHref brandHref brandHref
brandLabel · プロパティ brandLabel brandLabel brandLabel brandLabel brandLabel
brandLayout · プロパティ brandLayout brandLayout brandLayout brandLayout brandLayout
scrolled · プロパティ scrolled isScrolled scrollBehavior scrolledUnder scrolled
dividerVisibility · プロパティ dividerVisibility dividerVisibility dividerVisibility dividerVisibility dividerVisibility
備考 title is the headline property and attribute, because HTML's global title attribute would show a tooltip over the whole bar. titleAlignment is the title-alignment attribute. The slots are navigation, context and actions. A web-only scroll-target attribute names the id of the scrolling element to connect (default: the document's scrolling element; none connects nothing), from which the bar sets scrolled itself. A web-only heading-level (default 1) sets the title's heading level. Insets are env(safe-area-inset-top|left|right). dividerVisibility is the divider-visibility attribute (auto, always, scroll); auto reads --tanrei-comp-app-bar-outline-visibility from the cascade with a style query, and where the browser has none the bar writes the name it read on its container when it renders. The brand: brand-href, brand-label and brand-layout (lockup | responsive); its images are children with slot="brand", slot="brand-symbol" or slot="brand-wordmark" (an <img> or an inline <svg>), which the bar sizes from its tokens, marks aria-hidden and draws inside its own link (part="brand"). The link is the bar's: a router that follows links by their clicks reads it from the click's composed path. The window class is the enclosing <tanrei-app-shell>'s, else matchMedia against sys.breakpoint.medium. TanreiAppBar(_ title:, subtitle:, size:, titleAlignment:, isScrolled: Bool? = nil) { navigation } actions: { … }, placed with .safeAreaInset(edge: .top) in a NavigationStack whose system bar is hidden; the system bar's heights, title font and edge cannot be set from the tokens. Hiding it must not lose the interactive back swipe: the implementation keeps it working. The connected scroll view is the one marked .tanreiAppBarScrollSource(), applied after the .safeAreaInset that holds the bar (the inset's content sees only the environment where the inset is applied); it connects on iOS 18 and macOS 15 and later. isScrolled: nil follows it, a value overrides it. Not yet: the title's letter-spacing by the script of its text (APB-5, PRS-20). Not built on this platform yet: dividerVisibility (auto, always, scroll; APB-18) — the bar still draws its bottom edge from its condition's outline color, as scroll does. Not built on this platform yet: the brand (brandHref, brandLabel, brandLayout and its slots, APB-19 to APB-24). Not built on this platform yet: the context slot (APB-25). Compose idiom (as TopAppBar): TanreiAppBar(title, navigationIcon = { … }, actions = { … }, subtitle, size, titleAlignment, scrollBehavior, windowInsets). scrollBehavior (TanreiAppBarDefaults.pinnedScrollBehavior() for small, .collapseScrollBehavior() for large) is connected with Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), and its state carries scrolled and the collapse; without one the bar is flat and expanded, and a state created with a given offset draws the scrolled fixtures. windowInsets defaults to the status bars plus the horizontal display cutout. Not yet: the title's letter-spacing by the script of its text (APB-5, PRS-20). Not built on this platform yet: dividerVisibility (auto, always, scroll; APB-18) — the bar still draws its bottom edge from its condition's outline color, as scroll does. Not built on this platform yet: the brand (brandHref, brandLabel, brandLayout and its slots, APB-19 to APB-24). Not built on this platform yet: the context slot (APB-25). Flutter idiom (as AppBar and SliverAppBar.large): TanreiAppBar is a PreferredSizeWidget for Scaffold.appBar, with leading, actions, title (a String), subtitle, size and centerTitle (true for center). scrolledUnder: null follows the ScrollNotifications of the scaffold's body, as AppBar's scrolled-under state does; a bool overrides it. A large bar that collapses is TanreiSliverAppBar inside a CustomScrollView. Insets through SafeArea/MediaQuery.padding. Not yet: the title's letter-spacing by the script of its text (APB-5, PRS-20). Not built on this platform yet: dividerVisibility (auto, always, scroll; APB-18) — the bar still draws its bottom edge from its condition's outline color, as scroll does. Not built on this platform yet: the brand (brandHref, brandLabel, brandLayout and its slots, APB-19 to APB-24). Not built on this platform yet: the context slot (APB-25). navigation and actions are React nodes (an icon-only Button, and a fragment of up to three). The scroll connection is a hook: const scroll = useAppBarScroll() gives scroll.onScroll for a ScrollView or FlatList and scroll.state for the bar's scrollState prop, which sets scrolled and the collapse; without it the bar draws what scrolled says. Insets come from the bar's own insets prop, side by side, falling back to TanreiProvider's insets; the package takes no safe-area dependency, and a product that uses react-native-safe-area-context passes its useSafeAreaInsets() to either. Not yet: the title's letter-spacing by the script of its text (APB-5, PRS-20). Not built on this platform yet: dividerVisibility (auto, always, scroll; APB-18) — the bar still draws its bottom edge from its condition's outline color, as scroll does. Not built on this platform yet: the brand (brandHref, brandLabel, brandLayout and its slots, APB-19 to APB-24). Not built on this platform yet: the context slot (APB-25).

トークン

トークン 参照先 解決値(ライト)
--tanrei-comp-app-bar-actions-gap sys.target.spacing 8px
--tanrei-comp-app-bar-brand-color sys.color.on-surface #1b1b1b
--tanrei-comp-app-bar-brand-image-height comp.app-bar.brand.wordmark.height 40px
--tanrei-comp-app-bar-brand-lockup-alignment — 0
--tanrei-comp-app-bar-brand-lockup-gap sys.space.space100 8px
--tanrei-comp-app-bar-brand-lockup-symbol-size comp.app-bar.brand.wordmark.height 40px
--tanrei-comp-app-bar-brand-shape sys.shape.corner.full 9999px
--tanrei-comp-app-bar-brand-symbol-size sys.icon.medium 24px
--tanrei-comp-app-bar-brand-wordmark-height — 40px
--tanrei-comp-app-bar-container-gap sys.space.space100 8px
--tanrei-comp-app-bar-container-height-large — 120px
--tanrei-comp-app-bar-container-height-small — 64px
--tanrei-comp-app-bar-container-padding-block-end sys.space.space150 12px
--tanrei-comp-app-bar-container-padding-inline sys.space.space50 4px
--tanrei-comp-app-bar-flat-container-color sys.color.surface #ffffff
--tanrei-comp-app-bar-flat-container-elevation sys.elevation.level0
--tanrei-comp-app-bar-flat-outline-color — #000000 · 0%
--tanrei-comp-app-bar-motion-condition-duration sys.motion.role.state.large 250ms
--tanrei-comp-app-bar-motion-condition-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-app-bar-outline-visibility sys.behavior.divider-visibility 0
--tanrei-comp-app-bar-outline-width sys.border.hairline 1px
--tanrei-comp-app-bar-scrolled-container-color sys.color.surface #ffffff
--tanrei-comp-app-bar-scrolled-container-elevation sys.elevation.level0
--tanrei-comp-app-bar-scrolled-outline-color sys.color.outline-variant #000000 · 8%
--tanrei-comp-app-bar-subtitle-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-app-bar-title-color sys.color.on-surface #1b1b1b
--tanrei-comp-app-bar-title-inset sys.space.space200 16px
--tanrei-comp-app-bar-subtitle-typescale-large sys.typescale.body 400 14px/1.5714
--tanrei-comp-app-bar-subtitle-typescale-small sys.typescale.body-small 400 13px/1.5385
--tanrei-comp-app-bar-title-typescale-large sys.typescale.heading-1 500 32px/1.25
--tanrei-comp-app-bar-title-typescale-small sys.typescale.heading-4 500 18px/1.4444

フィクスチャ

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

生成元 specs/app-bar.spec.json, tokens/comp/app-bar.tokens.json, design/tokens/

ガイドライン

使う場面

画面の上端に置き、今どこにいるかを伝え、前の画面に戻ったりメニューを開いたりする手段と、検索、共有、アカウントのように画面全体に関わる少数のアクションをまとめます。1 つの画面に置くアプリバーは 1 つです。

アプリの最上位の行き先の間を移動するには、ナビゲーションバーかドロワーを使います。アプリバーのメニューボタンからモーダルなナビゲーションドロワーを開くこともできます。Tanrei には画面下端のアプリバーはありません。コンパクトな画面の下端にアクションを並べる役割は、ツールバーが担います。

サイズとタイトルの位置の選び方

選択肢 どうなるか 用途
Small(既定) 1 行に、ナビゲーションボタン、タイトル、アクションが並びます。 ほとんどの画面。別の画面からたどり着く画面はすべてこちらです。
Large 上の行の下に、専用の行で大きなタイトルを描きます。コンテンツのスクロールに 1 対 1 で合わせて上の行へ畳まれ、上の行のタイトルがフェードインします。コンテンツが一番上に戻ると、同じように広がります。 プロダクトの最上位の画面。タイトルがその画面の目印にもなる場所です。
titleAlignment が start(既定) タイトルはナビゲーションボタンの後ろに続きます。 ほとんどのバー。
titleAlignment が center タイトルをバー全体の中央に置き、ボタンの下に入り込まないよう左右を同じだけ詰めます。 iOS のような中央揃えのタイトルに合わせるプロダクト。広がった大きいタイトルは常に先頭側から始まります。

デザインシステムはサイズを追加できます。SwiftUI、Compose、Flutter、React Native では、small より高いサイズは large と同じく専用のタイトル行を持ちます。Web では、デザインシステムが追加したサイズはどれも専用のタイトル行を持ちます。m3-like のプリセットには、Material の medium サイズはまだありません。

スクロール

  • 下端の線は動かない。 既定のシステムでは、バーは静止時もコンテンツが下にあるときも、下端に細い線を引きます。バーはアプリシェルのヘッダーと同じく位置の変わらない領域の縁なので、その線がスクロールに応じて現れたり消えたりはしません。
  • スクロールに合わせることもできる。 dividerVisibility を scroll(Web では divider-visibility)にすると、コンテンツがバーの下に潜り込んだときだけ線を引き、線そのものでコンテンツが動いたことを示します。デザインシステムは、すべてのバー、シート、ダイアログについてこの選択を sys.behavior.divider-visibility で、バーだけなら comp.app-bar.outline.visibility で決めます。cupertino と m3-like は、元のデザインに合わせて scroll を選んでいます。
  • 平らな状態とスクロール状態。 コンテンツが一番上にある間、バーは平らです。コンテンツが下に潜り込むとスクロール時の見た目になります。m3-like ではコンテナに色が付き、cupertino ではコンテナが塗られます。切り替えはバーの状態用のモーションで行い、動きを減らす設定では瞬時に変わります。
  • 大きいバーがスクロール状態になるのは、畳み終えてからです。 それまでは、畳まれていく動き自体がコンテンツの移動を伝えます。
  • バーは動きません。 下へスクロールしてもバーは隠れません。それは画面のスクロールの振る舞いで、バーの役割ではありません。

項目

  • ナビゲーションボタンは多くても 1 つにします。別の画面からたどり着いた画面では「戻る」、最上位の画面では「メニュー」、全画面の作業では「閉じる」です。
  • アクションは 3 つまでにします。残りは、Tanrei のメニューを開く「その他のオプション」のアイコンボタンの中にまとめます。4 つ目のアクションや 2 つ目のナビゲーションボタンも描かれますが、デバッグビルドで報告します。
  • ボタンの見た目は、ほかの場所と同じです。バーは並べるだけで、色を変えることはありません。
  • どこかへ移動するボタンはリンクです。href を指定します。親のページへ戻るボタンや、ページを開くヘルプのアクションは、見た目は同じボタンのまま、本物のリンクになります。履歴をたどって戻るボタンは、ボタンのままです。
  • Web では、検索をアイコンのトリガー(bar を icon に設定)として、アクションの 1 つにできます。検索のダイアログまたは全画面のビューを開く、アイコンだけのボタンです。

ブランド

プロダクトは、自分のロゴを最初のページへのリンクとしてバーに置けます。バーにアドレス(brandHref、Web では brand-href)とプロダクト名(brandLabel)を渡すと、バーは渡された画像を囲む 1 つのリンクを描き、そのリンクにプロダクト名を付けます。画像は支援技術から隠すので、名前は 1 回だけ読まれます。バーはブランドをナビゲーションボタンの後ろに、アイコンボタンと同じ大きさのセルで置き、画像に独自の色がない限り comp.app-bar.brand.color を与えます。

  • 画像 1 枚。 1 つのロゴファイルを slot="brand" に入れます。高さは comp.app-bar.brand.image.height です。
  • シンボルだけ。 slot="brand-symbol" に入れます。高さは comp.app-bar.brand.symbol.size で、アイコンボタンのアイコンの位置に立ちます。
  • ワードマークだけ。 slot="brand-wordmark" に入れます。高さは comp.app-bar.brand.wordmark.height です。
  • シンボルとワードマークを一緒に。 両方を渡します。既定(brandLayout が lockup)では横に並び、間隔は comp.app-bar.brand.lockup.gap、揃え方は comp.app-bar.brand.lockup.alignment に従います。このときシンボルはワードマークと同じ高さ(comp.app-bar.brand.lockup.symbol.size)になります。
  • ウィンドウの幅で切り替える。 両方を渡し、brandLayout を responsive(Web では brand-layout)にします。バーは、ウィンドウが medium 以上のあいだはワードマークを、compact のあいだはシンボルを表示し、両方を同時には表示しません。アプリシェルの中では、シェルのウィンドウクラスに従います。

バーは画像ごとに大きさを決め、縦横の比率は保ちます。プロダクト名にタイトルを使わないでください。タイトルは画面の名前です。SwiftUI、Compose、Flutter、React Native は、まだブランドを描きません。

このサイトのヘッダーは、どの幅でも Tanrei のワードマークだけを表示しています。

推奨

  • タイトルには「受信トレイ」「四半期レポート」のように画面の名前を書きます。1 行に収まらなければ省略記号で切られるので、短くします。
  • サブタイトルには、似た画面を見分けるのに役立つ情報を書きます。アカウント、状態、最終更新の時刻などです。
  • 最上位の画面すべてで同じサイズを使い、それ以外の画面にはもう一方のサイズを使います。

非推奨

  • スロットにアイコンボタン以外のものを入れない。テキストフィールド、検索フィールド、タブ、セグメントコントロールは入れません。タブの並びはバーの下に置きます。その下にあるタブビューを、その並びとつなぐことができます。
  • 「作成」のような主要なアクションをバーに置かない。メインアクションボタンを使います。
  • タイトルを 2 行にしたり、リンクにしたりしない。

アクセシビリティ

ロール
none
ラベル付け
The title is the screen's heading (a level 1 heading on the web unless the product sets another level). The subtitle is read after it. The navigation and action items, and the control in context, are named by their own labels. The brand is one link to the product's first page named by brandLabel (the product's name), whichever of its images is shown; its images are hidden from assistive technology.
最小ターゲット
見た目のサイズが小さくても、ヒット領域は最小ターゲットサイズを満たします。 minimum 32px · pointer 44px · spacing 8px · touch 48px

キーボード操作

キー 動作
Tab Moves focus to the navigation item, then to the brand's link, then to the control in context, then through the action items; the bar itself is not a tab stop.

備考

  • The app bar has no role of its own and takes no input: its buttons are the product's icon-only Buttons, which meet the minimum target themselves (BTN-5). The bar is not a landmark, because only the product knows whether it is its page's banner; a product that wants one wraps the bar in its own header element.
  • The title is truncated rather than wrapped even at 200% text size: the bar's rows grow with the text, but a bar of several lines would push the screen's content away. The full title stays available to assistive technology.
  • Where both titles of a large bar are partly visible, only the expanded one is exposed, so the heading is never read twice.

生成元 specs/app-bar.spec.json, tokens/sys/measure.tokens.json

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

  • バー自体はロールを持たず、フォーカスも受け取りません。Tab はナビゲーションボタンからアクションへ、読み上げ順に進みます。各ボタンは自分のロール、名前、最小ターゲットをそのまま保ち、href を持つボタンはリンクとして伝わります。
  • タイトルは画面の見出しとして伝えます。Web では、heading-level を指定しなければレベル 1 の見出しです。サブタイトルはその後に読み上げます。タイトルが空なら何も描かず、見出しも伝えません。
  • 大きいバーは畳まれる途中で 2 つのタイトルを描きますが、伝えるのは 1 つだけです。広がったタイトルが少しでも見えている間はそちらを、畳み終えたら上の行のタイトルを伝えます。見出しが 2 回読まれることはありません。
  • バーはランドマークではありません。ページのバナーにあたるかどうかは、利用者側にしか分からないためです。
  • 文字を大きくすると、最小の高さとして決めてあるバーの行が伸びます。タイトルとサブタイトルは 200% でも 1 行のままで、折り返さずに省略します。バーがコンテンツを押しのけないためです。支援技術には全文が伝わります。アイコンボタンは拡大しません。
  • バーの向きは、使われている場所の向きに従います。右から左(RTL)のレイアウトでは左右が反転し、ナビゲーションボタンが右、アクションが左に並びます。戻る矢印のように、アイコンセットが反転の対象としているグリフは、バーではなく、そのボタン自身が反転します。
  • タイトルとサブタイトルは、周りのテキストから切り離し、それぞれのテキスト自身の方向で配置します。右から左のレイアウトの中にある左から右への名前も、語順を保ち、省略されるのは先頭ではなく、そのテキスト自身の末尾です。
  • アクションが 4 つ以上あるとき、ナビゲーションボタンが 2 つ以上あるとき、デザインシステムにないサイズを指定したときは、デバッグビルドで報告します。

プラットフォームごとの既知の制約:

  • SwiftUI、Compose、Flutter、React Native:dividerVisibility は、これらのプラットフォームの追従を待っています。これらは scroll と同じく、コンテンツがバーの下に潜り込んだときだけ下端の線を引きます。
  • SwiftUI、Compose、Flutter、React Native:右から左のレイアウトの決まり(アイコンセットが対象とするグリフの反転、タイトル自身の方向での配置)と、href を持つボタンは、これらのプラットフォームの追従を待っています。
  • Web:すべての寸法が rem に従うので、ブラウザーの文字サイズを大きくすると、文字と一緒にアイコンボタンも大きくなります。Tanrei の文字の大きさだけを変えたときは大きくなりません。
  • Web:ドキュメント自身のスクロールに従う大きいバーを、ドキュメントの流れの中に position: sticky で置くと、コンテンツがスクロールの分に加えてバーが畳まれた分も動くので、畳まれている間はコンテンツが 2 倍の速さで動きます。畳まれるバーは、スクロールする要素(scroll-target)の上か、固定したヘッダーに置いてください。
  • Web:アイコンボタンはそれぞれ、使っているポインターの最小ターゲット(マウスなら 44)の枠に収まります。そのため 2 つのアクションの中心どうしの間隔は 52 で、48 のタッチターゲットで測る Compose と Flutter の 56 より狭くなります。
  • SwiftUI:バーがスクロールビューに従うのは iOS 18 と macOS 15 以降です。それより前は何もつながらず、バーは isScrolled のとおりに描かれます。
  • SwiftUI:つながった大きいバーに isScrolled: true を渡すと、畳まれた姿で描かれますが、レイアウト上は広がった高さのままなので、コンテンツが先頭にある間はその上に隙間が見えます。
  • SwiftUI:システムのバーを隠している間も、バーは端からのスワイプで戻る操作を保ちます。そのためにナビゲーションコントローラーの pop ジェスチャーの delegate を差し替えており、確かめたのは iOS 26.5 だけです。大きいバーがタイトルを一度に 1 つだけ伝えることは、VoiceOver ではまだ確かめていません。
  • Flutter:大きい TanreiAppBar はコンテンツと一緒に畳まれません。ボックスのウィジェットは、下のスクロールビューに合わせて縮むことができないためです。スクロール通知を無視して広がったままになり、scrolledUnder が true の間だけ畳まれた姿で描かれます。畳まれるバーには TanreiSliverAppBar を使います。
  • Flutter:preferredSize はテーマを読めないため、上限のない高さを返します。Scaffold はバー自身に大きさを決めさせますが、利用者側のレイアウトではこの値を使えません。
  • Compose:畳む動きはスクロールが止まったところで止まるので、大きいバーが半分畳まれたままになることがあります。Material のバーのように、フリングの後で開き切るか閉じ切るかに寄せることはしません。仕様もそれを求めていません。
  • React Native:大きいバーをコンテンツに 1 対 1 で合わせて畳むには、上の例のように、バーをスクロールビューの上に重ね、コンテンツにバーの高さぶんの余白を付けます。畳む動き、インセット、スクリーンリーダーの読み上げは、実機ではまだ確かめていません。
  • すべてのプラットフォーム:cupertino では、アイコンボタンを置くと 44 ポイントの上の行が 48 に伸びます。アイコンボタンのタッチターゲットが 48 で、最小ターゲットが優先されるからです。Web で伸びるのは粗いポインターのときだけで、マウスのターゲットは 44 です。大きいバーの広がった行も、cupertino のコンテナの高さより 1 ポイント高くなります。
  • すべてのプラットフォーム:サブタイトルがあると、バーが畳み終える前に広がったタイトルが隠れます。その間どちらのタイトルを伝えるかは、仕様ではまだ決まっていません。どのプラットフォームも、広がったタイトルが隠れた時点で見出しを上の行へ移します。

利用者側で行うこと

  • どのバーにもタイトルを付けます。タイトルが画面の見出しになります。
  • どのアイコンボタンにも「戻る」「検索」のようなラベルを付けます。
  • Web では、ページにすでにレベル 1 の見出しがあるなら heading-level を設定します。バーがページのバナーにあたるなら <header> で包みます。
  • 画面のスクロールするビューをつなぐか、利用者側で scrolled を設定して、コンテンツが下にあることがバーに表れるようにします。
  • React Native では、ウィンドウのインセットを TanreiProvider に渡します。React Native と Compose では、ウィンドウの一番上にないバーに別のインセットを渡します(insets={{ top: 0 }}、または利用者側の windowInsets)。
  • タイトルは短くします。省略記号で切られても全文は読み上げられますが、目で見る人には先頭しか見えません。