Chip
Material chips
Assist and suggestion actions plus selectable filter and input chips, including flat, elevated, and Expressive shape treatments.
Assist and suggestion
Filter selection
Input chips
Constrained and large text
Disabled states
Last action: none. Selected filters: Grammar.import { useState } from 'react'
import {
Chip,
Icon,
Surface,
Text,
} from '@language-lit/material3-expressive'
export function ChipExample() {
const [selectedTopics, setSelectedTopics] = useState<readonly string[]>(['Grammar'])
const [personSelected, setPersonSelected] = useState(true)
const [lastAction, setLastAction] = useState('none')
const toggleTopic = (topic: string, selected: boolean) => {
setSelectedTopics((current) =>
selected ? [...current, topic] : current.filter((entry) => entry !== topic),
)
}
return (
<Surface
as="section"
aria-labelledby="chip-example-title"
color="surface-container-low"
shape="extra-large"
className="chip-example"
>
<Text as="h2" id="chip-example-title" variant="titleLarge" emphasis="emphasized">
Material chips
</Text>
<Text as="p" variant="bodyMedium">
Assist and suggestion actions plus selectable filter and input chips,
including flat, elevated, and Expressive shape treatments.
</Text>
<Text as="h3" variant="titleMedium" emphasis="emphasized">
Assist and suggestion
</Text>
<div className="chip-example__row">
<Chip
kind="assist"
leadingIcon={<Icon source="add" />}
onClick={() => setLastAction('Add to calendar')}
>
Add to calendar
</Chip>
<Chip
kind="assist"
variant="elevated"
leadingIcon={<Icon source="favorite" />}
trailingIcon={<Icon source="arrow_forward" />}
onClick={() => setLastAction('Translate')}
>
Translate
</Chip>
<Chip
kind="suggestion"
leadingIcon={<Icon source="info" />}
onClick={() => setLastAction('Try a dialogue')}
>
Try a dialogue
</Chip>
<Chip
kind="suggestion"
variant="elevated"
onClick={() => setLastAction('Review vocabulary')}
>
Review vocabulary
</Chip>
</div>
<Text as="h3" variant="titleMedium" emphasis="emphasized">
Filter selection
</Text>
<div className="chip-example__row">
{['Grammar', 'Listening', 'Reading'].map((topic, index) => {
const selected = selectedTopics.includes(topic)
return (
<Chip
key={topic}
kind="filter"
variant={index === 1 ? 'elevated' : 'flat'}
shape={index === 2 ? 'expressive' : 'standard'}
selected={selected}
onSelectedChange={(nextSelected) => toggleTopic(topic, nextSelected)}
leadingIcon={selected ? <Icon source="check" /> : null}
trailingIcon={topic === 'Reading' ? <Icon source="description" /> : null}
>
{topic}
</Chip>
)
})}
<Chip kind="filter" defaultSelected shape="expressive">
Uncontrolled
</Chip>
</div>
<Text as="h3" variant="titleMedium" emphasis="emphasized">
Input chips
</Text>
<div className="chip-example__row">
<Chip
kind="input"
selected={personSelected}
onSelectedChange={setPersonSelected}
avatar={<span className="chip-example__avatar">A</span>}
leadingIcon={<Icon source="person" />}
trailingIcon={<Icon source="close" />}
>
Aiko
</Chip>
<Chip
kind="input"
shape="expressive"
defaultSelected
leadingIcon={<Icon source="bookmark" />}
trailingIcon={<Icon source="close" />}
>
Japanese
</Chip>
</div>
<Text as="h3" variant="titleMedium" emphasis="emphasized">
Constrained and large text
</Text>
<div className="chip-example__row">
<Chip
kind="filter"
className="chip-example__constrained"
trailingIcon={<Icon source="close" />}
>
A deliberately long filter label that must leave its trailing icon visible
</Chip>
<span className="chip-example__large-type">
<Chip kind="suggestion" leadingIcon={<Icon source="info" />}>
Large label
</Chip>
</span>
</div>
<Text as="h3" variant="titleMedium" emphasis="emphasized">
Disabled states
</Text>
<div className="chip-example__row">
<Chip kind="assist" disabled leadingIcon={<Icon source="add" />}>
Assist
</Chip>
<Chip kind="assist" variant="elevated" disabled>
Elevated assist
</Chip>
<Chip kind="filter" disabled defaultSelected>
Selected filter
</Chip>
<Chip kind="filter" variant="elevated" disabled>
Elevated filter
</Chip>
<Chip kind="input" disabled avatar={<span className="chip-example__avatar">M</span>}>
Input
</Chip>
<Chip kind="suggestion" variant="elevated" disabled>
Suggestion
</Chip>
</div>
<Text as="span" role="status" variant="bodySmall" aria-live="polite">
Last action: {lastAction}. Selected filters: {selectedTopics.join(', ') || 'none'}.
</Text>
</Surface>
)
}Chipは、コンパクトなMaterialのアクションおよび選択コントロールです。用途に応じたAPIで、assist、filter、input、suggestionを表現し、ソースで無効なpropsの組み合わせを型で防ぎます。
import { Chip, Icon } from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'
<Chip
kind="assist"
variant="elevated"
leadingIcon={<Icon source="translate" />}
>
Translate
</Chip>
<Chip
kind="filter"
shape="expressive"
selected={grammar}
onSelectedChange={setGrammar}
leadingIcon={grammar ? <Icon source="check" /> : null}
>
Grammar
</Chip>
<Chip
kind="input"
avatar={<span>A</span>}
trailingIcon={<Icon source="close" />}
>
Aiko
</Chip>仕様
kindは必須で、assist、filter、input、suggestionから選択します。- assistとsuggestionのChipは単発のアクションです。通常のネイティブbutton propsを受け取りますが、選択状態のpropsはありません。
- filterとinputのChipは選択できます。制御する場合は
selected/onSelectedChange、非制御の場合はdefaultSelectedを使います。 variantの既定値はflatです。assist、filter、suggestionではelevatedも指定できます。ファーストパーティのソースにはelevatedなinput Chipはありません。shape="expressive"を指定すると、filterとinputで仕様の状態別シェイプ変化が有効になります。既定のstandardでは小さい角を保ちます。leadingIconはすべての用途で使えます。trailingIconはassist、filter、inputで使えます。suggestionでソースが公開するアイコンは先頭側のみです。avatarはinput専用です。両方を指定するとleadingIconの代わりに使われます。
すべてのバリアントはネイティブの<button type="button">を描画します。フォーム送信にはtype="submit"を明示します。ネイティブ属性、フォーム所有者、name/value、イベントハンドラー、class/style、転送されたref、無効状態、draggableを保持します。選択可能なChipでは、利用側のonClickを先に実行し、preventDefault()によって状態変更を取り消せます。
構造と形状
ルート要素は、表示コンテナーの高さ32pxを囲むテーマの最小48px操作対象を確保します。表示行は常に先頭スロット、ラベル、末尾スロットの3つの子要素を持ちます。スロットがない場合は幅を0にしますが、その前後の間隔は残します。これは、子要素が2つの行として扱うのではなく、固定されたソースに合わせた動作です。
standardの間隔は8pxです。Expressiveなfilter/inputでは、存在するスロットの隣の間隔が4pxになります。末尾アイコンだけの場合もこの規則を適用し、ラベルと末尾アイコンの間は4pxです。inputの端の余白は通常4pxで、先頭アイコンの前は8px、avatarの前は4px、末尾アイコンの後は8pxです。
アイコンは18×18pxです。inputのavatarは24×24pxの完全な角丸領域に収めてクリップします。選択可能なChipの最大幅はソースと同じ1000pxです。ラベルは折り返し可能で、大きな文字では表示コンテナーを広げます。長いラベルに押しつぶされて末尾スロットが消えることはありません。
バリアント、状態、モーション
flatのassist/suggestionと、選択されていないfilter/inputはアウトライン付きです。選択されたfilter/inputにはsecondary-containerを使い、アウトラインを取り除きます。elevatedなassist/suggestionと未選択のfilterにはsurface-container-lowを使います。filterのエレベーションは、flatでは通常時がLevel 0、hover時がLevel 1、elevatedでは通常時がLevel 1、hover時がLevel 2です。elevatedなassistとsuggestionはLevel 1/2、flatはLevel 0です。ドラッグ可能なChipはドラッグ中にLevel 4を使います。無効状態のエレベーションはLevel 0です。
hover、フォーカス、押下時にはMaterialのステートレイヤーを描画します。キーボードフォーカスにはトークンに基づくsecondaryリングを追加します。standardのChipは小さいシェイプを保ちます。Expressiveのfilter/inputは未選択時にmedium、選択時にfull、押下時にsmallを使います。
選択可能なChipのスロット追加・削除は、ソースのAnimatedVisibilityに合わせて、退出アニメーションの完了まで削除中の要素を保持します。standardのスロットは、表示時にslow-effectsとfast-spatial、退出時にfast-effectsとdefault-effectsを組み合わせます。Expressiveのオーバーロードでは、default-effectsの不透明度とfast-spatialのサイズを使います。モーションを減らす設定では、シェイプ、エレベーション、色、スロットの変化をすべて即時に適用します。
アクセシビリティ
ネイティブボタンが名前付け、ポインター、Enter、Space、フォーカス、無効状態、フォームの動作を提供します。filterとinputはaria-pressedで選択状態を公開し、assistとsuggestionは不要なトグル状態を公開しません。これは、ソースの選択可能なRole.CheckboxをWebネイティブに置き換えたものです。
スロットのラッパーは装飾用としてaria-hiddenになっており、アイコンの名前がボタン名に混ざることはありません。表示ラベル、aria-label、aria-labelledbyのいずれかを指定してください。Chipの内側にリンク、コントロール、その他のインタラクティブ要素を入れないでください。
論理方向の余白はRTLで反転しますが、DOM順は変わりません。強制カラー表示では明示的な境界線、選択状態とフォーカスにHighlight、選択済みコンテンツにHighlightText、無効状態にGrayTextを使います。半透明のステートレイヤーと作者指定のshadowは取り除きます。
トークンとソースの境界
Chipは検索可能な--m3e-comp-chip-*変数を次の用途に登録します。
- 表示高さ32px、最小操作対象、最大幅、スロットのサイズとシェイプ。
- standard/compactの間隔とinput専用の論理端余白。
- standardおよびExpressiveの未選択、選択、押下時のシェイプ。
- 各用途の有効、選択、無効、アウトライン、elevated時の色。
- 通常、hover、フォーカス、押下、ドラッグ、無効状態のshadow。
- フォーカスリングの形状と色。
登録ではAndroidXのリビジョン225f50d42bf0adeb2abf4b6109befb5ab6ce4efcを固定しています。Chip.ktが読み取らない生成ロールは、実在しないコンポーネント状態として登録せず、未使用として記録します。ソースが別ファミリーのelevated suggestion無効状態を参照する点も明記しています。そのアイコンとコンテナーの不透明度は、現行値が同じassist-chipロールを使用し、flat suggestionはsuggestionアイコンのロールを保ちます。
Composeのインスタンス単位の色、エレベーション、borderオブジェクトはReact propsではありません。カスタマイズにはMaterial3Providerでスコープされたコンポーネントトークンを使います。非推奨のAndroid互換オーバーロードと、Compose専用のModifier、InteractionSource、Arrangement、計測ポリシー処理は対象外ですが、そこで外部から観察できる出力は実装しています。対応関係の全体はChipの準拠記録と実行可能なソース台帳テストに記録されています。