Share
Embed
AI に Inclusive UI を書かせよう — Design Rules Skill で Compose UI を作り直す
DroidKaigi 2026 9/3(Day3) 11:20 ~ 12:00 Narwhalのセッション
https://2026.droidkaigi.jp/timetable/1233663/
テオリア・テクノロジーズ
September 03, 2026
Transcript
86 slides
-
AI に Inclusive UI を書かせよう — Design Rules Skill で
Compose UI を作り直す bami / テオリア‧テクノロジーズ DroidKaigi 2026 -
⾃⼰紹介 bami テオリア‧テクノロジーズ — アプリエンジニア 合同会社 CoBrews — 代表社員 (クラフトビール販売)
X: @rs_bami ‧ GitHub: @bami-oxaliser 3 -
Before / 今 After / 聞き終わったら AI に、 ルールを渡さずに UI
を 書かせている AI に、 ルールを渡して、やさしい UI を 書かせている 8 -
アジェンダ A. 問題の所在 — 「それっぽい」は、「やさしい」ではない B. 経緯と背景 — 『認知症の⼈にもやさしいデザインの⼿引き』と輪読会 C.
Skill 設計 — AI に読ませるルールの作り⽅ D. Compose 実装 — 実コードと録画デモ E. Skill を育てるループ — ⼿動‧⾃動‧CI で確かめて、ルールを強くする F. まとめ 9 -
ただし"それっぽい"は"保証"されていない ① 未検証 — "綺麗に⾒える"は"読める"の保証ではない。 読める下限を WCAG が 4.5:1 と定めているが、AI
は計算して確かめない ② 運任せ — 同じ依頼でもプロンプト‧モデルの引きで揺れる WCAG 2.2 コントラスト最低限(1.4.3)∕ 実測でも semantics を保つ⽣成と落とす⽣成があった 11 -
なぜこうなるのか AI が最適化しているのは 「⾒た⽬で良さそうに動く UI」 (⾃由に書かせたときは標準部品‧状態保持まで⾃然に書く) ただし⽬に⾒えない層は、指⽰しなければ抜ける ①コントラスト⽐の計算 ②⾒出しを意味づけ(TalkBack ⽤)
③⼤画⾯で作り直す 「⾒た⽬ OK」≠「基準を満たすと確認済み」 12 -
つながっていない2つの世界 AIコーディングの普及 ⇔ 認知特性のある ユーザーの増加 書ける AI と、伝わるルールが、まだ別々に存在している 出典=認知症‧MCI 有病率の将来推計(厚⽣労働省研究班∕九州⼤学
⼆宮利治教授ら‧2024年公表)。 認知症‧MCI とも今後増加が⾒込まれる 14 -
「うちのアプリには関係ない」と思いますか? 認知症という取りこぼされがちな起点から設計する = Inclusive Design その原理が Universal Design 限定的な⼊⼝の⼯夫が、全ユーザーに広がる Inclusive
Design(⽅法) → Inclusive UI(成果物) ⽀える原理 = Universal Design Inclusive Design Principles(7原則‧⽇本語訳あり)∕ Universal Design=Ronald Mace「⼀つの設計で全員に」の原理(7原則) 20 -
⼿引きの基本:2つの考え⽅の上に、5つの視点 ① 記憶に頼らず⾏動できる空間づくり — その場の⼿がかりで分かる ② 安⼼して⾃分で選べる居場所づくり — 迷わず、間違えても戻れる ⼿引きの5視点
アプリでの配慮 A ⾊‧明度 コントラスト B サイン ⽂字併記‧ことば C 明るさ テーマ∕ダークモード追従 D 親しみ‧安⼼ 現在地‧戻れる安⼼ E 安全な屋外 危険な操作からさりげなく守る(破壊的操作の確認) 『認知症の⼈にもやさしいデザインの⼿引き』第3章「デザインの基本的な考え⽅」「5つの視点」(P.6〜) 21 -
認知症から始めた⼯夫が、みんなに届く UI になる ⼿引きが物理空間で実証した効果 (トイレ認識率 13%→100%∕記号の理解度 ⼀般92点‧認知症35点) ⾒えにくい‧伝わりにくい場⾯は、 加齢‧疲労‧⽚⼿での操作‧屋外の光が重なって誰にでも起こり得る 認知症の当事者
→ Inclusive Design(⽅法) → Inclusive UI(成果物) → 全員に届く 25 -
公式ガイドも Skill も、あるにはある 公式ガイド(Material Design 3‧Android Accessibility)はある =AI も Android
Knowledge Base 経由で引ける 公式 Skill もある(android/skills) =ただしアクセシビリティを主題にした Skill は無い Google ⽅針「docs にあることは Skill 化しない」 =我々が⾜す知識は docs に無い Material Design 3∕Android Accessibility ガイド(developer.android.com)∕android/skills(2026-08 v1.0.7 実確認) ∕Google philosophy「Built for deprecation」2026-08 27 -
公開されている AI 向けリソースを調べた(2026年8⽉) リソース Compose 固有 認知負荷 実機検証 Web 向け
a11y lint (eslint-plugin-jsx-a11y 等) ✕ — ✕ material-3-skill ◎ ✕ ✕ aldefy / Meet-Miyani ◦ ✕ ✕ inclusive-design-skills ✕ ◎ audit 我々 ◎ ◎ ◎ 出典 eslint-plugin-jsx-a11y(Web 向け a11y lint): https://github.com/jsx-eslint/eslint-plugin-jsx-a11y material-3-skill(Ivan Morgillo): https://github.com/hamen/material-3-skill compose-skill(aldefy): https://github.com/aldefy/compose-skill compose-skill(Meet-Miyani): https://github.com/Meet-Miyani/compose-skill inclusive-design-skills(Owl-Listener): https://github.com/Owl-Listener/inclusive-design-skills WCAG=公式の国際アクセシビリティ基準(個⼈ OSS とは権威レベルが異なる) 28 -
どれも「書いた時点のチェック」で⽌まっている 既存の多くは「書く時に基準と突き合わせる」 静的な準拠チェック(=lint 型) 無いのは 実機(on-device)で動かして確かめる検証 material-3-skill ⾃⾝が README で
「ズレるかもしれない("may drift")」と明記 "may drift"=material-3-skill README("best-effort distillation … may drift as Google updates the spec") 29 -
いまの Skill も、DESIGN.md も、捨てない 役割 内容 担い⼿ ⾜す 認知負荷×Compose(コントラスト∕テーマ∕ナビ ∕⽂⾔∕エラー∕ピクト∕48dp)
我々 借りる Material Design の⾒た⽬(トークン‧配⾊‧コン ポーネント) material-3-skill 乗る Compose の⼟台(XML移⾏‧ナビ‧テスト環境) android/skills 委譲 ブランドテーマなどの意匠 DESIGN.md 順位:inclusive の [must](=必ず守る⼀線) > Figma‧DESIGN.md 忠実 > 余計な装飾を⾜さない 31 -
Skill の全体像 inclusive-compose-skill/ ── 自分で書く ── ├── .rulesync/ │ ├──
rules/ │ └── skills/inclusive-compose/ ├── rulesync.jsonc ├── verification.md ├── .github/workflows/verify.yml └── README.md・LICENSE ← Skill の単一ソース(ここを直す) overview + 14カテゴリ SKILL.md = いつ使うか 生成の設定 検証手順 CI(ドリフト検知+connectedCheck) (通常のリポファイル) ── npx rulesync@16 generate(生成物・触らない・delete:true)── ├── CLAUDE.md Claude Code の入口 ├── .claude/ rules / skills ├── .cursor/ rules / skills └── .github/ copilot-instructions / skills 別リポ(借りる)── material-3-skill … Material Design 3 の見た目 32 -
must と nice-to-have で階層化 # 情報の量(手引き:記憶に頼らせない) ## must - 1画面1タスクを基本にする。関係ない情報を同時に出さない
- 選択肢は折り返して全文を見せる(切らない) ## nice - 段階的開示(必要になったら追加情報を出す) 35 -
実物:ルール本体 # 情報の量(手引き:記憶に頼らせない) ## must / ## nice ## Compose
Text(choiceLabel) // maxLines / Ellipsis で切らない=全文が折り返る var expanded by rememberSaveable { mutableStateOf(false) } if (expanded) Text(detail) // 必要になってから追加情報を出す ## 検証 フォントスケール 200% で全文が見切れないか確認 36 -
深掘り①コントラスト|Compose ではこう書く [must] 文字・重要 UI と背景は 4.5:1 以上(大きな文字・太字は 3:1) [must]
色は ColorScheme に集約 (画面ごとにハードコードしない・アプリルート=画面間で一貫) [must] 主役は 4.5:1 を検証して固定 (標準の配色ペア〔primary/onPrimary 等〕はトーン差50+で 4.5:1 を狙う 設計・公式の数値保証は無い/カスタム色・非対ロール・outline は保証外) [nice] Theme Builder の contrast level(medium / high)を使う 38 -
深掘り②⽂⾔とエラー|Compose ではこう書く [must] ボタンは動詞(「送信する」) [must] エラーは原因+解決策をセット [must] 「入力が不正です」 →「メールの形式が違います。@ が入っているか確認してください」
// Compose: TextField の isError と supportingText で解決策を提示 // エラー文言は semantics にも載せて読み上げにも届ける 40 -
深掘り③|⼿引き由来とUI慣習を分けて書く [must] アイコンには文字を併記する ← 手引き由来 [must] 色・文字・アイコンの三重で状態を伝える ← 手引き由来 [nice]
赤=危険 / 緑=完了 / 青=情報 ← 一般的なUI慣習・補助 // Compose: Icon(contentDescription) + 隣に Text ラベル // 状態は色以外(テキスト・形)でも必ず示す 42 -
ルールが良くても、description が曖昧だと呼ばれない ルールに frontmatter(いつ使うか)を付けたもの=Skill description の⼀覧だけが常駐‧本⽂は使う時だけ読み込まれる 悪い例「Inclusive UI のルール集」→ 効く例「Compose
で UI を書く‧直す時に使 う」 name: inclusive-compose description: "… Compose の UI を書く・直すときは必ず参照する。" targets: ["*"] user-invocable: true claudecode: { paths: ["**/*.kt", "**/*.kts"] } 44 -
1ソースから、rulesync が3ツールに配る ルール本体 + いつ使うかの設定 .rulesync/ ↓ skill層 ← 正はここだけ
npx rulesync@16 generate SKILL.md(標準) rules層 各ツール固有 ↓ Claude Code / Cursor / GitHub Copilot 正は .rulesync の1か所‧rulesync が skill層+rules層を3ツール分⽣成=1か所直せば全部追従 (⾃分で書かず OSS) 受け取る側は SKILL.md の skill層+各ツール固有の rules層の2層 3形式は"差別化"でなく「多くの⼈が使えるように」の配布形態 rulesync(dyoshikawa): https://github.com/dyoshikawa/rulesync 45 -
検証⼿順も Skill に組み込む ルールだけでなく「書いた後どう確かめるか」も持つ 満たしたかを AI に判定させず、 実⾏できる成果物(テスト+チェックリスト)を出させる 書く →
確かめる → ルールを強くする を回す前提で設計 47 -
ソースと⽣成物がズレたら、CI が落とす npx --yes rulesync@16 generate --check # 再生成して差分が出れば CI
が落ちる 1か所直せば全部に伝わるという仕組みを、CI が守る 49 -
では、実際に書かせてみる Skill を渡した AI は、どんな Compose を書くか → 実機の Before
/ After で⾒てみる 50 -
このデモの実⾏条件 コードは AI(claude-opus-4-8)が書いた (⼿編集は静⽌画撮影⽤の引数のみ‧挙動不変) ⽣成なので出⼒は毎回同⼀でない =今⽇は代表の1回分 (⾒えない層の差は何度⽣成しても同傾向)‧事前録画 録画端末=Android 17(API 37)
会場で⾳が出せない→TalkBack は字幕(⾳声出⼒の表⽰)で⾒せる 今⽇扱わないもの:Autofill‧Gemini Nano の alt-text‧ダークモード 52 -
デモ:同じデザインを渡し、ルールの有無だけ変える sasaeru の実 Figma 2画⾯(今⽇の記録∕レポート)を AI に渡す naive=ルールを渡さない素の⽣成 ∕ Skill=作ったルールを渡した⽣成
デザイン⼊⼒は同じで、変えるのはルールだけ ⽣成コードは⼿元で gradle コンパイルを通して確認 53 -
naive のコード:選択が semantics に出ない Box(Modifier.clip(shape).background(bg).border(...) .selectableCardClick(onClick)) { Text(text, sc-camel-font-size =
20.sp) } // sc-camel-selectable-card-click = clickable{...} = 生の clickable // role も選択状態も semantics に出ない 58 -
skill のコード:selectable+role+group+48dp Row(Modifier .selectable(selected, role = Role.RadioButton, sc-camel-on-click = onClick)
.heightIn(min = 48.dp) // タップ48dp ) { Text(text); if (selected) Icon(CheckCircle, sc-camel-content-description = null) } // 親 Column に Modifier.selectableGroup() // 選択チェックは"色以外"の手がかり 59 -
⾒えない差:実機 TalkBack の読み上げ naive ラベルだけ skill 状態‧役割‧位置‧まとまりも Android 17∕TalkBack 17.0.0
実測。会場で⾳が出せないため「⾳声出⼒の表⽰」の字幕で提⽰ 61 -
【知識点】Predictive Back は naive でも効いている naive も Skill も戻るを独⾃に処理していない=両⽅とも効いている 効かなくなるのは、戻るをアプリ側で受け取ったとき
(BackHandler で progress を扱わない場合も) 既定有効=Android 16(targetSdk 36)で何もしなくても効く 62 -
実描画:fontScale 2.0 で残る課題は、⽂字ではなく配置 naive(fontScale 2.0) skill(fontScale 2.0) 拡⼤追従は原則(最⼩16sp‧折返し‧固定 ⾼さ禁⽌)を守れば Compose
が担保 = Skill の差別化点ではない 両⽅に共通の窮屈さ: 帯が本⽂に重なる ‧ナビラベルが切れる 200% は等倍のデザインにもコードにも写 らない → Skill の検証ルール =fontScale 2.0 のスクショテストで描画 して確認 64 -
skill のコード:WindowSizeClass で組み替える val expanded = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)) .widthSizeClass ==
WindowWidthSizeClass.Expanded if (expanded) Row else { … } // ≥840dp:左右2ペイン・行長を保つ Column { … } // 1列。順序は同じ skill は画⾯幅を WindowSizeClass で⾒て、広いとき(Expanded‧≥840dp)だけ 1列→2ペインに組み替える(引き伸ばさず⾏⻑を保つ‧順序は変えない) naive にこの分岐は無い=そのまま横に伸びる 67 -
naive:Snackbar(親切だが⾃動で消える) snackbarHostState.showSnackbar( message = "レポートを送るには当事者様のお名前の登録が必要です", sc-camel-action-label = "登録する", duration =
SnackbarDuration.Short, // 自動で消える ) // 文言は親切。だが semantics error 無し・自動消滅 69 -
skill:寄り添う⽂⾔+出た瞬間に読み上げ、消さない if (showNameRequiredError) Text( text = "お名前が未設定のため、レポートを送れません。" + "上の「当事者様のお名前」からお名前を登録してください。", color
= MaterialTheme.colorScheme.error, modifier = Modifier.semantics { sc-camel-live-region = LiveRegionMode.Polite; error(msg) // 出た瞬間に読み上げ }, ) // 自動では消さず、名前登録まで残す ⽂⾔を「原因+次の⼀⼿」に書き換える 表⽰と読み上げを同じ⽂字列にし、error+liveRegion で TalkBack へ伝える 70 -
⼿動検証は3⼿段 TalkBack Font Scale 200% 読み上げの内容 ⽂字の⾒切れ Accessibility Scanner コントラスト‧タップ領域
‧ラベル ツールは守備範囲が違う Scanner は読み上げの中⾝までは⾒ない=naive でも「提案なし」 そこは TalkBack で聞く 75 -
⾃動検証:semantics をテストでアサート @Test fun 選択肢は選択状態を読み上げる() { rule.onNodeWithText("自分で電話をかけた").assertIsSelected() } @Test fun
タッチターゲットは48dpが確保される() { rule.onNodeWithText("自分で電話をかけた").assertHeightIsAtLeast(48.dp) } @Test fun エラーにerrorセマンティクスが付く() { rule.onNode(hasText("お名前が未設定", substring = true)) .assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.Error)) } 76 -
⾃動検証②:コントラスト‧タップ‧ラベルをまとめてチェック // 依存: androidx.compose.ui:ui-test-junit4-accessibility @Before fun enableA11yChecks() { composeTestRule.enableAccessibilityChecks() }
// 以降のアクション実行時に ラベル欠落・低コントラスト・48dp未満 を自動 fail // 任意の時点で: composeTestRule.onRoot().tryPerformAccessibilityChecks() 77 -
CI に組み込む+ドリフト検知 # 単一ソース(rules/) → 生成 → 差分が出たら落とす - run:
npx --yes rulesync@16 generate --check # ドリフト検知=差分で exit 1 - run: ./gradlew connectedCheck # accessibility テストを実機/エミュで ルールとコード、両⽅の退⾏を CI が⾒張る 78 -
育てた実例:原則だけでは過剰装飾する 原則だけ版 skill 原則だけを渡すと、AI は「⽬⽴たせる」 を過剰に適⽤してデザインに無い装飾を ⾜す skill は同じ「いま何問⽬か」を 読み上げに⾜して⾒た⽬は元のまま
検証で⾒つけ、 「デザインに無い装飾を ⾜さない」 の⼀⾏を後から明⽂化 79 -
3つの持ち帰り ① Skill 設計の型 輪読会で「なぜこのルールか」を⼈の側でそろえる → 単⼀ソース → 層設計 →
検証を Skill 本 ⽂に ② Compose 固有の Inclusive UI 実装パターン semantics / 48dp / ⽂字スケール / 戻る安⼼‧具体コードで ③ 書いて終わりにしない、育てるループ ⼿動 → ⾃動 → CI → ルール強化 82 -
GitHub で公開しています https://github.com/theoria-tech/inclusive-compose-skill rules層=単⼀ソース14ファイル(認知症デザインの⼿引きをもとに) → ⽣成 → skill層=Claude / Cursor
/ Copilot の3形式 ⼿動+⾃動+CI の verification 同梱 3形式は"差別化"でなく配布形態 83 -
ありがとうございました 資料はこの QR から ∕ 質疑は Ask the Speaker で
※QRコードはデンソーウェーブの登録商標です。