melta-app 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +220 -12
  2. package/docs/architecture.md +11 -4
  3. package/eslint-rules/melta.d.mts +44 -0
  4. package/lib/module/a11y/tap-target.js +41 -0
  5. package/lib/module/a11y/tap-target.js.map +1 -0
  6. package/lib/module/components/ActionSheet.js +20 -6
  7. package/lib/module/components/ActionSheet.js.map +1 -1
  8. package/lib/module/components/Alert.js +11 -7
  9. package/lib/module/components/Alert.js.map +1 -1
  10. package/lib/module/components/Avatar.js +7 -2
  11. package/lib/module/components/Avatar.js.map +1 -1
  12. package/lib/module/components/BottomSheet.js +11 -3
  13. package/lib/module/components/BottomSheet.js.map +1 -1
  14. package/lib/module/components/Card.js +4 -3
  15. package/lib/module/components/Card.js.map +1 -1
  16. package/lib/module/components/Checkbox.js +8 -5
  17. package/lib/module/components/Checkbox.js.map +1 -1
  18. package/lib/module/components/EmptyState.js +2 -1
  19. package/lib/module/components/EmptyState.js.map +1 -1
  20. package/lib/module/components/Header.js.map +1 -1
  21. package/lib/module/components/Image.js +5 -3
  22. package/lib/module/components/Image.js.map +1 -1
  23. package/lib/module/components/Modal.js +12 -4
  24. package/lib/module/components/Modal.js.map +1 -1
  25. package/lib/module/components/Progress.js +2 -1
  26. package/lib/module/components/Progress.js.map +1 -1
  27. package/lib/module/components/Radio.js +11 -3
  28. package/lib/module/components/Radio.js.map +1 -1
  29. package/lib/module/components/Screen.js +28 -3
  30. package/lib/module/components/Screen.js.map +1 -1
  31. package/lib/module/components/Skeleton.js +2 -1
  32. package/lib/module/components/Skeleton.js.map +1 -1
  33. package/lib/module/components/Surface.js +2 -1
  34. package/lib/module/components/Surface.js.map +1 -1
  35. package/lib/module/components/TextField.js +16 -4
  36. package/lib/module/components/TextField.js.map +1 -1
  37. package/lib/module/components/Toast.js +17 -9
  38. package/lib/module/components/Toast.js.map +1 -1
  39. package/lib/module/components/Toggle.js +13 -4
  40. package/lib/module/components/Toggle.js.map +1 -1
  41. package/lib/module/components/alert.styles.js +12 -26
  42. package/lib/module/components/alert.styles.js.map +1 -1
  43. package/lib/module/components/avatar.styles.js +24 -2
  44. package/lib/module/components/avatar.styles.js.map +1 -1
  45. package/lib/module/components/card.validate.js.map +1 -1
  46. package/lib/module/components/checkbox.styles.js +5 -2
  47. package/lib/module/components/checkbox.styles.js.map +1 -1
  48. package/lib/module/components/index.js.map +1 -1
  49. package/lib/module/components/modal.styles.js +9 -0
  50. package/lib/module/components/modal.styles.js.map +1 -1
  51. package/lib/module/components/radio.styles.js +11 -2
  52. package/lib/module/components/radio.styles.js.map +1 -1
  53. package/lib/module/components/safe-area-registry.js +64 -13
  54. package/lib/module/components/safe-area-registry.js.map +1 -1
  55. package/lib/module/components/status-colors.js +53 -0
  56. package/lib/module/components/status-colors.js.map +1 -0
  57. package/lib/module/components/textfield.styles.js +15 -8
  58. package/lib/module/components/textfield.styles.js.map +1 -1
  59. package/lib/module/components/toast.styles.js +36 -25
  60. package/lib/module/components/toast.styles.js.map +1 -1
  61. package/lib/module/components/toggle.styles.js +20 -0
  62. package/lib/module/components/toggle.styles.js.map +1 -1
  63. package/lib/module/icons/Icon.js +4 -1
  64. package/lib/module/icons/Icon.js.map +1 -1
  65. package/lib/module/icons/icon.styles.js +32 -1
  66. package/lib/module/icons/icon.styles.js.map +1 -1
  67. package/lib/module/icons/index.js.map +1 -1
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/primitives/Button.js +23 -11
  70. package/lib/module/primitives/Button.js.map +1 -1
  71. package/lib/module/primitives/Metric.js +2 -1
  72. package/lib/module/primitives/Metric.js.map +1 -1
  73. package/lib/module/primitives/Row.js.map +1 -1
  74. package/lib/module/primitives/Stack.js.map +1 -1
  75. package/lib/module/primitives/Tag.js +15 -7
  76. package/lib/module/primitives/Tag.js.map +1 -1
  77. package/lib/module/primitives/Text.js +13 -5
  78. package/lib/module/primitives/Text.js.map +1 -1
  79. package/lib/module/primitives/_internal/focus-ring.js.map +1 -1
  80. package/lib/module/primitives/button.styles.js +58 -8
  81. package/lib/module/primitives/button.styles.js.map +1 -1
  82. package/lib/module/primitives/index.js.map +1 -1
  83. package/lib/module/primitives/metric.styles.js +2 -2
  84. package/lib/module/primitives/metric.styles.js.map +1 -1
  85. package/lib/module/primitives/tag.styles.js +42 -3
  86. package/lib/module/primitives/tag.styles.js.map +1 -1
  87. package/lib/module/primitives/text.styles.js +6 -7
  88. package/lib/module/primitives/text.styles.js.map +1 -1
  89. package/lib/module/safe-area/index.js +11 -5
  90. package/lib/module/safe-area/index.js.map +1 -1
  91. package/lib/module/theme/ThemeProvider.js +5 -2
  92. package/lib/module/theme/ThemeProvider.js.map +1 -1
  93. package/lib/module/theme/define-theme.js +14 -1
  94. package/lib/module/theme/define-theme.js.map +1 -1
  95. package/lib/module/theme/index.js +6 -8
  96. package/lib/module/theme/index.js.map +1 -1
  97. package/lib/module/theme/letter-spacing.js +20 -0
  98. package/lib/module/theme/letter-spacing.js.map +1 -0
  99. package/lib/module/theme/line-height.js +9 -0
  100. package/lib/module/theme/line-height.js.map +1 -1
  101. package/lib/module/theme/native-theme.js +1 -1
  102. package/lib/typescript/src/a11y/tap-target.d.ts +34 -0
  103. package/lib/typescript/src/a11y/tap-target.d.ts.map +1 -0
  104. package/lib/typescript/src/components/ActionSheet.d.ts +8 -1
  105. package/lib/typescript/src/components/ActionSheet.d.ts.map +1 -1
  106. package/lib/typescript/src/components/Alert.d.ts +9 -2
  107. package/lib/typescript/src/components/Alert.d.ts.map +1 -1
  108. package/lib/typescript/src/components/Avatar.d.ts +1 -1
  109. package/lib/typescript/src/components/Avatar.d.ts.map +1 -1
  110. package/lib/typescript/src/components/BottomSheet.d.ts +4 -1
  111. package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -1
  112. package/lib/typescript/src/components/Card.d.ts +2 -2
  113. package/lib/typescript/src/components/Checkbox.d.ts +3 -3
  114. package/lib/typescript/src/components/Checkbox.d.ts.map +1 -1
  115. package/lib/typescript/src/components/Image.d.ts +4 -3
  116. package/lib/typescript/src/components/Image.d.ts.map +1 -1
  117. package/lib/typescript/src/components/Modal.d.ts +9 -3
  118. package/lib/typescript/src/components/Modal.d.ts.map +1 -1
  119. package/lib/typescript/src/components/Progress.d.ts +1 -1
  120. package/lib/typescript/src/components/Progress.d.ts.map +1 -1
  121. package/lib/typescript/src/components/Radio.d.ts +3 -2
  122. package/lib/typescript/src/components/Radio.d.ts.map +1 -1
  123. package/lib/typescript/src/components/Screen.d.ts +28 -5
  124. package/lib/typescript/src/components/Screen.d.ts.map +1 -1
  125. package/lib/typescript/src/components/Skeleton.d.ts +1 -1
  126. package/lib/typescript/src/components/Skeleton.d.ts.map +1 -1
  127. package/lib/typescript/src/components/Surface.d.ts +1 -1
  128. package/lib/typescript/src/components/Surface.d.ts.map +1 -1
  129. package/lib/typescript/src/components/TextField.d.ts +11 -4
  130. package/lib/typescript/src/components/TextField.d.ts.map +1 -1
  131. package/lib/typescript/src/components/Toast.d.ts +10 -3
  132. package/lib/typescript/src/components/Toast.d.ts.map +1 -1
  133. package/lib/typescript/src/components/Toggle.d.ts +3 -2
  134. package/lib/typescript/src/components/Toggle.d.ts.map +1 -1
  135. package/lib/typescript/src/components/action-sheet.styles.d.ts +1 -1
  136. package/lib/typescript/src/components/action-sheet.styles.d.ts.map +1 -1
  137. package/lib/typescript/src/components/alert.styles.d.ts +14 -14
  138. package/lib/typescript/src/components/alert.styles.d.ts.map +1 -1
  139. package/lib/typescript/src/components/avatar.styles.d.ts +36 -1
  140. package/lib/typescript/src/components/avatar.styles.d.ts.map +1 -1
  141. package/lib/typescript/src/components/bottom-sheet.styles.d.ts +1 -1
  142. package/lib/typescript/src/components/bottom-sheet.styles.d.ts.map +1 -1
  143. package/lib/typescript/src/components/card.styles.d.ts +1 -1
  144. package/lib/typescript/src/components/card.styles.d.ts.map +1 -1
  145. package/lib/typescript/src/components/card.validate.d.ts +1 -1
  146. package/lib/typescript/src/components/card.validate.d.ts.map +1 -1
  147. package/lib/typescript/src/components/checkbox.styles.d.ts +6 -3
  148. package/lib/typescript/src/components/checkbox.styles.d.ts.map +1 -1
  149. package/lib/typescript/src/components/empty-state.styles.d.ts +1 -1
  150. package/lib/typescript/src/components/empty-state.styles.d.ts.map +1 -1
  151. package/lib/typescript/src/components/header.styles.d.ts +1 -1
  152. package/lib/typescript/src/components/header.styles.d.ts.map +1 -1
  153. package/lib/typescript/src/components/image.styles.d.ts +1 -1
  154. package/lib/typescript/src/components/image.styles.d.ts.map +1 -1
  155. package/lib/typescript/src/components/index.d.ts +18 -18
  156. package/lib/typescript/src/components/index.d.ts.map +1 -1
  157. package/lib/typescript/src/components/modal.styles.d.ts +13 -2
  158. package/lib/typescript/src/components/modal.styles.d.ts.map +1 -1
  159. package/lib/typescript/src/components/progress.styles.d.ts +1 -1
  160. package/lib/typescript/src/components/progress.styles.d.ts.map +1 -1
  161. package/lib/typescript/src/components/radio.styles.d.ts +10 -2
  162. package/lib/typescript/src/components/radio.styles.d.ts.map +1 -1
  163. package/lib/typescript/src/components/safe-area-registry.d.ts +47 -11
  164. package/lib/typescript/src/components/safe-area-registry.d.ts.map +1 -1
  165. package/lib/typescript/src/components/screen.styles.d.ts +1 -1
  166. package/lib/typescript/src/components/screen.styles.d.ts.map +1 -1
  167. package/lib/typescript/src/components/skeleton.styles.d.ts +1 -1
  168. package/lib/typescript/src/components/skeleton.styles.d.ts.map +1 -1
  169. package/lib/typescript/src/components/status-colors.d.ts +33 -0
  170. package/lib/typescript/src/components/status-colors.d.ts.map +1 -0
  171. package/lib/typescript/src/components/surface.styles.d.ts +1 -1
  172. package/lib/typescript/src/components/surface.styles.d.ts.map +1 -1
  173. package/lib/typescript/src/components/textfield.styles.d.ts +9 -5
  174. package/lib/typescript/src/components/textfield.styles.d.ts.map +1 -1
  175. package/lib/typescript/src/components/toast.styles.d.ts +34 -14
  176. package/lib/typescript/src/components/toast.styles.d.ts.map +1 -1
  177. package/lib/typescript/src/components/toggle.styles.d.ts +17 -1
  178. package/lib/typescript/src/components/toggle.styles.d.ts.map +1 -1
  179. package/lib/typescript/src/icons/Icon.d.ts +12 -6
  180. package/lib/typescript/src/icons/Icon.d.ts.map +1 -1
  181. package/lib/typescript/src/icons/icon.styles.d.ts +14 -2
  182. package/lib/typescript/src/icons/icon.styles.d.ts.map +1 -1
  183. package/lib/typescript/src/icons/index.d.ts +3 -3
  184. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  185. package/lib/typescript/src/index.d.ts +4 -4
  186. package/lib/typescript/src/index.d.ts.map +1 -1
  187. package/lib/typescript/src/primitives/Button.d.ts +8 -4
  188. package/lib/typescript/src/primitives/Button.d.ts.map +1 -1
  189. package/lib/typescript/src/primitives/Metric.d.ts +1 -1
  190. package/lib/typescript/src/primitives/Metric.d.ts.map +1 -1
  191. package/lib/typescript/src/primitives/Row.d.ts +2 -2
  192. package/lib/typescript/src/primitives/Row.d.ts.map +1 -1
  193. package/lib/typescript/src/primitives/Stack.d.ts +2 -2
  194. package/lib/typescript/src/primitives/Stack.d.ts.map +1 -1
  195. package/lib/typescript/src/primitives/Tag.d.ts +2 -2
  196. package/lib/typescript/src/primitives/Tag.d.ts.map +1 -1
  197. package/lib/typescript/src/primitives/Text.d.ts +20 -6
  198. package/lib/typescript/src/primitives/Text.d.ts.map +1 -1
  199. package/lib/typescript/src/primitives/button.styles.d.ts +44 -7
  200. package/lib/typescript/src/primitives/button.styles.d.ts.map +1 -1
  201. package/lib/typescript/src/primitives/index.d.ts +6 -6
  202. package/lib/typescript/src/primitives/index.d.ts.map +1 -1
  203. package/lib/typescript/src/primitives/metric.styles.d.ts +1 -1
  204. package/lib/typescript/src/primitives/metric.styles.d.ts.map +1 -1
  205. package/lib/typescript/src/primitives/row.styles.d.ts +1 -1
  206. package/lib/typescript/src/primitives/row.styles.d.ts.map +1 -1
  207. package/lib/typescript/src/primitives/stack.styles.d.ts +1 -1
  208. package/lib/typescript/src/primitives/stack.styles.d.ts.map +1 -1
  209. package/lib/typescript/src/primitives/tag.styles.d.ts +34 -1
  210. package/lib/typescript/src/primitives/tag.styles.d.ts.map +1 -1
  211. package/lib/typescript/src/primitives/text.styles.d.ts +2 -4
  212. package/lib/typescript/src/primitives/text.styles.d.ts.map +1 -1
  213. package/lib/typescript/src/safe-area/index.d.ts +19 -5
  214. package/lib/typescript/src/safe-area/index.d.ts.map +1 -1
  215. package/lib/typescript/src/theme/ThemeProvider.d.ts +2 -2
  216. package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -1
  217. package/lib/typescript/src/theme/define-theme.d.ts +15 -2
  218. package/lib/typescript/src/theme/define-theme.d.ts.map +1 -1
  219. package/lib/typescript/src/theme/index.d.ts +14 -14
  220. package/lib/typescript/src/theme/index.d.ts.map +1 -1
  221. package/lib/typescript/src/theme/letter-spacing.d.ts +15 -0
  222. package/lib/typescript/src/theme/letter-spacing.d.ts.map +1 -0
  223. package/lib/typescript/src/theme/line-height.d.ts +10 -0
  224. package/lib/typescript/src/theme/line-height.d.ts.map +1 -1
  225. package/lib/typescript/src/theme/native-theme.d.ts +1 -1
  226. package/lib/typescript/src/theme/native-theme.d.ts.map +1 -1
  227. package/lib/typescript/src/theme/types.d.ts +1 -1
  228. package/package.json +22 -7
  229. package/src/a11y/tap-target.ts +38 -0
  230. package/src/components/ActionSheet.tsx +25 -15
  231. package/src/components/Alert.tsx +37 -10
  232. package/src/components/Avatar.tsx +12 -4
  233. package/src/components/BottomSheet.tsx +16 -13
  234. package/src/components/Card.tsx +9 -8
  235. package/src/components/Checkbox.tsx +12 -9
  236. package/src/components/EmptyState.tsx +7 -6
  237. package/src/components/Header.tsx +4 -4
  238. package/src/components/Image.tsx +9 -7
  239. package/src/components/Modal.tsx +22 -6
  240. package/src/components/Progress.tsx +5 -4
  241. package/src/components/Radio.tsx +11 -6
  242. package/src/components/Screen.tsx +66 -8
  243. package/src/components/Skeleton.tsx +6 -5
  244. package/src/components/Surface.tsx +5 -4
  245. package/src/components/TextField.tsx +25 -7
  246. package/src/components/Toast.tsx +33 -11
  247. package/src/components/Toggle.tsx +15 -6
  248. package/src/components/action-sheet.styles.ts +1 -1
  249. package/src/components/alert.styles.ts +13 -25
  250. package/src/components/avatar.styles.ts +25 -3
  251. package/src/components/bottom-sheet.styles.ts +1 -1
  252. package/src/components/card.styles.ts +1 -1
  253. package/src/components/card.validate.ts +2 -2
  254. package/src/components/checkbox.styles.ts +6 -3
  255. package/src/components/empty-state.styles.ts +1 -1
  256. package/src/components/header.styles.ts +1 -1
  257. package/src/components/image.styles.ts +1 -1
  258. package/src/components/index.ts +18 -18
  259. package/src/components/modal.styles.ts +11 -2
  260. package/src/components/progress.styles.ts +1 -1
  261. package/src/components/radio.styles.ts +11 -2
  262. package/src/components/safe-area-registry.ts +81 -14
  263. package/src/components/screen.styles.ts +1 -1
  264. package/src/components/skeleton.styles.ts +1 -1
  265. package/src/components/status-colors.ts +59 -0
  266. package/src/components/surface.styles.ts +1 -1
  267. package/src/components/textfield.styles.ts +18 -15
  268. package/src/components/toast.styles.ts +32 -25
  269. package/src/components/toggle.styles.ts +21 -1
  270. package/src/icons/Icon.tsx +13 -8
  271. package/src/icons/icon.styles.ts +38 -3
  272. package/src/icons/index.ts +3 -3
  273. package/src/index.ts +4 -4
  274. package/src/primitives/Button.tsx +28 -14
  275. package/src/primitives/Metric.tsx +5 -4
  276. package/src/primitives/Row.tsx +4 -4
  277. package/src/primitives/Stack.tsx +4 -4
  278. package/src/primitives/Tag.tsx +29 -11
  279. package/src/primitives/Text.tsx +28 -11
  280. package/src/primitives/_internal/focus-ring.tsx +1 -1
  281. package/src/primitives/button.styles.ts +63 -10
  282. package/src/primitives/index.ts +6 -6
  283. package/src/primitives/metric.styles.ts +3 -3
  284. package/src/primitives/row.styles.ts +1 -1
  285. package/src/primitives/stack.styles.ts +1 -1
  286. package/src/primitives/tag.styles.ts +43 -4
  287. package/src/primitives/text.styles.ts +7 -12
  288. package/src/safe-area/index.ts +21 -7
  289. package/src/theme/ThemeProvider.tsx +8 -5
  290. package/src/theme/define-theme.ts +15 -2
  291. package/src/theme/index.ts +14 -17
  292. package/src/theme/letter-spacing.ts +17 -0
  293. package/src/theme/line-height.ts +9 -0
  294. package/src/theme/native-theme.ts +2 -2
  295. package/src/theme/types.ts +1 -1
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # melta for APP(melta-app)
2
2
 
3
+ [![Check](https://github.com/tsubotax/melta-app/actions/workflows/check.yml/badge.svg)](https://github.com/tsubotax/melta-app/actions/workflows/check.yml)
4
+ [![npm](https://img.shields.io/npm/v/melta-app)](https://www.npmjs.com/package/melta-app)
5
+
3
6
  **web と同じデザイン契約を React Native に降ろす UI kit。使う側のコードにも lint 強制層を npm で配る。**
4
7
  アプリ本体ではなく `npm install melta-app` で使うライブラリ。
5
8
 
@@ -21,7 +24,7 @@
21
24
 
22
25
  - **ドキュメントは契約からの生成物** — 下の[コンポーネント表](#コンポーネント)・[llms.txt](https://app.melta.tsubotax.com/llms.txt)・showcase の統計は `melta-contracts` から生成し、手書きの表はこのリポに存在しない。腐りは drift 検査が落とす([scripts/check-drift.ts](./scripts/check-drift.ts) / [CI](./.github/workflows/check.yml))
23
26
  - **消費者プロジェクトでの実導入検証(2026-08-04)** — 別リポジトリの自プロジェクト(非公開 RN アプリ)に npm 経由で導入し、AI が違反コードを書いた直後に検出 → 修正フィードバック → 自己修正、のループを実測で確認。導入時に見つかった hook の欠陥(実行失敗時に無言で素通りする)は同日中に本体へ還元し、故障系を含む E2E 14 ケースで固定([scripts/lib/hook-lint.test.ts](./scripts/lib/hook-lint.test.ts) / [CHANGELOG 0.5.3](./CHANGELOG.md#053--2026-08-04))
24
- - **「npm install すれば動く」の機械証明** — pack → tarball 実体検査 → fixture へ install → 本体 / icons / safe-area の import + typecheck → lint plugin を実 import してルール 4 本と推奨 severity を照合、までを公開ゲートにしている([scripts/check-installability.sh](./scripts/check-installability.sh)。`npm run release` の必須ステップ)
27
+ - **「npm install すれば動く」の機械証明** — pack → tarball 実体検査 → [attw](https://github.com/arethetypeswrong/arethetypeswrong.github.io) で exports の型解決を resolution mode ごとに検査 → fixture へ install → 本体 / icons / safe-area / eslint-plugin の import + typecheck を **`moduleResolution` 3 種(bundler / node16 / nodenext)すべてで**実行 → lint plugin を実 import してルール 4 本と推奨 severity を照合、までを公開ゲートにしている([scripts/check-installability.sh](./scripts/check-installability.sh)。`npm run release` の必須ステップ)
25
28
 
26
29
  ## Quickstart
27
30
 
@@ -29,7 +32,17 @@
29
32
  npm install melta-app
30
33
  ```
31
34
 
32
- 必須の peerDependencies は `react` / `react-native` の 2 つ(runtime 依存ゼロ)。機能別の optional peer が 2 つ — `react-native-svg`(`melta-app/icons` を使う場合のみ)と `react-native-safe-area-context`(`melta-app/safe-area` を使う場合のみ)。検証済み構成は Expo 56 / RN 0.85 / React 19.2。
35
+ 必須の peerDependencies は `react` / `react-native` の 2 つ(runtime 依存ゼロ)。機能別の optional peer が 2 つ — `react-native-svg`(`melta-app/icons` を使う場合のみ)と `react-native-safe-area-context`(`melta-app/safe-area` を使う場合のみ)。配布物は **ESM のみ**(`require()` では読めない。検証済みは Expo 56 / Metro 他バンドラは ESM 対応が前提)。型は `moduleResolution` が `bundler` / `node16` / `nodenext` のいずれでも解決できることを CI で検査している(`skipLibCheck: true` の消費者でも型が欠落しない)。eslint plugin / hook などの Node ツール面は Node.js 22 で検証している(`engines` 宣言)。
36
+
37
+ **React Native バージョン対応**(下限 0.71 は `gap` / `role` 使用のため。型・Jest で検証済みなのは Expo 56 / RN 0.85 / React 19.2 の組のみ):
38
+
39
+ | RN | melta-app | optional peer の目安 |
40
+ |---|---|---|
41
+ | 0.85(検証済み) | ✅ | safe-area-context 5.x / svg 15.13+ |
42
+ | 0.74〜0.84 | 型上は互換(未検証) | safe-area-context 5.x / svg 15.8+ |
43
+ | 0.73 | 型上は互換(未検証) | safe-area-context 4.x / svg 15.8+ |
44
+ | 0.71〜0.72 | 型上は互換(未検証) | safe-area-context 4.x / svg 15.0〜15.7 |
45
+ | 〜0.70 | ❌(`gap` レイアウトが崩れる) | — |
33
46
 
34
47
  > ⚠️ **0.x 系につき破壊的変更は minor で入る**(CHANGELOG 方針)。`^` 範囲でなく**バージョン固定か `~` 範囲**を推奨。
35
48
 
@@ -52,6 +65,57 @@ export default function App() {
52
65
 
53
66
  テーマは `ThemeProvider` が OS の light / dark に自動追従(`forcedMode` で固定も可)。トークンは `useTheme()` / `nativeTheme` から取れる。自分のブランドで塗り替えるなら[テーマを注入する](#テーマを注入するブランドトークン)。本体エントリ以外は subpath 3 つ([Icon](#iconmelta-appicons) / [SafeArea 差替](#safearea-の差し替えmelta-appsafe-area) / [lint plugin](#利用側コードの-lint-強制層melta-appeslint-plugin))。
54
67
 
68
+ ### bare React Native(Expo を使わない場合)
69
+
70
+ melta-app 本体は JS のみで、ネイティブモジュールを含まない。ただし**optional peer の 2 つ
71
+ (`react-native-safe-area-context` / `react-native-svg`)は本物のネイティブモジュール**なので、
72
+ bare RN では npm install だけでは動かない。iOS は Pods の再インストールと、両 OS ともネイティブの
73
+ 再ビルドが要る(JS の reload では反映されない):
74
+
75
+ ```bash
76
+ # bare RN — 使う subpath に対応する peer だけ入れる
77
+ npm install react-native-svg react-native-safe-area-context
78
+ npx pod-install # iOS(= cd ios && pod install)
79
+ # その後 Xcode / Gradle でネイティブを**再ビルド**する(Metro の reload では足りない)
80
+ ```
81
+
82
+ Expo(managed / prebuild)なら従来どおり `npx expo install` が RN 版に合う版を選び、
83
+ config plugin と prebuild が iOS/Android 側を面倒見る:
84
+
85
+ ```bash
86
+ npx expo install react-native-svg react-native-safe-area-context
87
+ ```
88
+
89
+ どちらの peer も使わない(`melta-app` 本体エントリだけ import する)なら、
90
+ melta-app の導入にネイティブ作業は発生しない。
91
+
92
+ > 検証済みの構成は **Expo 56 / RN 0.85 / React 19.2** の組だけ([制約と正直な範囲](#制約と正直な範囲))。
93
+ > bare RN 構成は型・依存関係の上では成立するが、実機での検証は行っていない。
94
+
95
+ ### dev 検証と `__DEV__`
96
+
97
+ melta の dev 専用チェックは `__DEV__`(Metro のグローバル)が真のときだけ動く:
98
+
99
+ - `defineTheme()` の `validateTheme` — 壊れた theme をその場で throw
100
+ - 解決済み theme の `deepFreeze` — 後からの書き換えを凍結して検出
101
+ - `ThemeProvider` の mode 違反レポート / `Card` の不正 prop 組み合わせの `console.error`
102
+
103
+ **Metro 以外のバンドラ(Vite / webpack での react-native-web など)は `__DEV__` を定義しない。**
104
+ その環境では `process.env.NODE_ENV === "development"` にフォールバックし、
105
+ どちらも判定できなければ dev 検証は**自動で無効**になる(本番に dev コストを漏らさない側に倒す設計)。
106
+ つまり「警告が一度も出ない = 違反が無い」ではない。有効にしたい場合はどちらかを与える:
107
+
108
+ ```js
109
+ // Vite
110
+ export default { define: { __DEV__: JSON.stringify(true) } };
111
+ // webpack
112
+ new webpack.DefinePlugin({ __DEV__: JSON.stringify(true) });
113
+ // もしくは NODE_ENV=development でビルド / 起動する
114
+ ```
115
+
116
+ production ビルドでは `__DEV__` を偽(または未定義)のままにする — dev 検証は本番で不要な
117
+ コストであり、Metro は偽に畳み込まれた分岐ごとバンドルから落とす。
118
+
55
119
  ## 利用側コードの lint 強制層(`melta-app/eslint-plugin`)
56
120
 
57
121
  melta 契約からの逸脱を機械検知する eslint カスタムルール 4 本を同梱している。
@@ -240,6 +304,110 @@ export default [
240
304
 
241
305
  0.4.x からの移行は [CHANGELOG](./CHANGELOG.md#050--2026-07-29) を参照。
242
306
 
307
+ ### 画面骨格(`Screen`)
308
+
309
+ `Screen` は safe-area + `bg-page` + content padding + ScrollView を 1 つにまとめた画面の器。
310
+
311
+ | prop | 型 | default | 意味 |
312
+ |---|---|---|---|
313
+ | `variant` | `"scroll"` \| `"fixed"` | `"scroll"` | content を ScrollView に載せるか、`flex: 1` の View に載せるか |
314
+ | `padding` | spacing token キー \| `"none"` | `"4"` | content の padding |
315
+ | `edges` | `readonly ("top" \| "right" \| "bottom" \| "left")[]` | [`enableSafeAreaContext`](#safearea-の差し替えmelta-appsafe-area) の既定 | safe-area を適用する辺 |
316
+ | `header` | `ReactNode` | — | scroll の外(safe-area 直下)に固定されるヘッダー slot |
317
+ | `scrollViewProps` | `Omit<ScrollViewProps, "children">` | — | 内部 ScrollView への passthrough(`variant="scroll"` のみ) |
318
+ | `scrollViewRef` | `Ref<ScrollView>` | — | 内部 ScrollView の ref(`scrollTo` / `scrollToEnd` 用) |
319
+ | `style` | `StyleProp<ViewStyle>` | — | safe-area の器に重ねる style |
320
+ | `testID` | `string` | — | — |
321
+
322
+ ```tsx
323
+ const listRef = useRef<ScrollView>(null);
324
+
325
+ <Screen
326
+ header={<Header title="フィード" />}
327
+ scrollViewProps={{ onScroll: handleScroll, keyboardShouldPersistTaps: "handled" }}
328
+ scrollViewRef={listRef}
329
+ >
330
+ <Text>本文</Text>
331
+ </Screen>
332
+ ```
333
+
334
+ 契約(passthrough は「素通し」ではない):
335
+
336
+ - **`contentInsetAdjustmentBehavior` は渡さないこと。** safe-area は Screen が持つので二重 inset になる
337
+ - `contentContainerStyle` は DS の padding と**配列合成**される(渡した側が後勝ち)。padding を**消す**目的では使えない(消したいときは `padding="none"`)
338
+ - `scrollEventThrottle` は Screen 側の既定が `16`(iOS の既定 0 では `onScroll` が 1 ドラッグ 1 発しか来ずスクロール連動ヘッダが動かない)。消費者の指定で上書きできる
339
+ - `variant="fixed"` に `scrollViewProps` / `scrollViewRef` を渡すと dev ビルドで警告する(内部 ScrollView が無いので黙って捨てられる)
340
+
341
+ タブバーと併用する画面は、bottom をタブバー側に任せる:
342
+
343
+ ```tsx
344
+ // タブ配下の画面だけ edge を絞る(アプリ全体を top-only にしない。下記 SafeArea 節を参照)
345
+ <Screen edges={["top"]}>…</Screen>
346
+ ```
347
+
348
+ ## アクセシビリティの既定
349
+
350
+ 3 点(タップ標的 / OS の文字サイズ拡大 / 読み上げラベル)は**利用側で何もしなくても既定で満たす**ように組んである。
351
+
352
+ ### タップ標的 44pt
353
+
354
+ すべての操作要素は**視覚寸法を変えないまま**実効タップ標的 44pt を満たす。
355
+ 契約側の正本は melta-contracts の `A11Y_MIN_TAP_TARGET_44`、実装との照合は
356
+ [scripts/lib/tap-target-conformance.test.ts](./scripts/lib/tap-target-conformance.test.ts)(CI で毎回検査)。
357
+
358
+ | 要素 | 視覚寸法 | 手当て | 実効 |
359
+ |---|---|---|---|
360
+ | `Button` small / medium / large(labeled) | minHeight 32 / 40 / 48 | 縦 hitSlop 6 / 2 / 0 | 44 / 44 / 48 |
361
+ | `Button`(iconOnly) | 32 / 40 / 48 の正方形 | 四方 hitSlop 6 / 2 / 0 | 同上(縦横とも) |
362
+ | `Tag variant="filter-chip"` | 高さ 34(padding 駆動) | 縦 hitSlop 5 | 44 |
363
+ | `Tag variant="removable"` の × / `Toast` の × / `Alert` の × / `Modal` の × | 24 の箱 | hitSlop 10 | 44 |
364
+ | `Radio` の option 行 | 行の高さ 36 | `minHeight: 44` | 44 |
365
+ | `Toggle` medium / large | track 24 / 28 | 縦 hitSlop 10 / 8 | 44 |
366
+ | `Checkbox` の行 | box 20(行は label 込み) | `minHeight: 44` | 44 |
367
+
368
+ - **背景を持つ要素は hitSlop、持たない要素は `minHeight`**。背景がある要素を minHeight で伸ばすと見た目が変わるため
369
+ - **横方向の hitSlop は隣接する操作要素との gap の 1/2 まで**。超えると当たり判定が重なって押し違いが起きる
370
+ (`Toast` の action と × はこれで実際に 8pt 重なっていた。0.7.0 で修正)
371
+ - ⚠️ **iconOnly の `Button` だけは横にも hitSlop が付く**(幅 32/40 では 44pt に届かないため)。
372
+ iconOnly を横に並べるときは **gap を hitSlop の 2 倍以上**(small: 12 / medium: 4)取ること。
373
+ gap 0 で密着させると当たり判定が重なる
374
+
375
+ ### OS の文字サイズ拡大(fontScale)
376
+
377
+ - `Button` / `TextField` の高さは **`minHeight`**(`height` 固定ではない)。文字が拡大しても縦にクリップしない
378
+ - `Text` は `allowFontScaling` / `maxFontSizeMultiplier` を RN `Text` へ透過する。**既定は未指定 = RN 既定**(拡大に追随)。
379
+ 固定寸法の図版ラベルなど、拡大するとレイアウトが壊れる箇所だけ opt-in で絞る
380
+
381
+ ```tsx
382
+ <Text maxFontSizeMultiplier={1.3}>グラフの軸ラベル</Text>
383
+ ```
384
+
385
+ - `Avatar` の initials は器(円)が伸びないため、size 別に上限を掛けている(small 1.6 / medium 1.5 / large 1.3)。
386
+ 拡大は許しつつ円からの溢れだけを止める設定で、利用側の指定は不要
387
+
388
+ ### 読み上げラベルの差し替え(i18n)
389
+
390
+ 内蔵の日本語ラベルは props で差し替えられる。**既定値は日本語のまま**(変えると既存アプリの VoiceOver 読み上げが変わるため)。
391
+
392
+ | コンポーネント | prop | 既定 |
393
+ |---|---|---|
394
+ | `Toast` / `Alert` / `Modal` | `closeAccessibilityLabel` | `"閉じる"` |
395
+ | `TextField` | `formatErrorAccessibilityLabel: (label, errorText) => string` | `` `${label}。エラー: ${errorText}` `` |
396
+ | `ActionSheet` | `cancelLabel` | `"キャンセル"` |
397
+ | `Skeleton` | `accessibilityLabel` | `"読み込み中"` |
398
+
399
+ ```tsx
400
+ <Modal title="Delete route" closeAccessibilityLabel="Close" onClose={close} visible>…</Modal>
401
+
402
+ <TextField
403
+ label="Email"
404
+ value={email}
405
+ variant="error"
406
+ errorText="Invalid format"
407
+ formatErrorAccessibilityLabel={(label, error) => `${label}. Error: ${error}`}
408
+ />
409
+ ```
410
+
243
411
  ## テーマを注入する(ブランドトークン)
244
412
 
245
413
  `theme` を渡すと自分のブランドトークンで塗り替わる。未指定なら melta 既定(`nativeTheme`)。
@@ -307,13 +475,19 @@ export const theme = defineTheme({
307
475
  typography: {
308
476
  fontFamily: {}, // 未指定 = OS デフォルト(RN の fontFamily は string 一本。embed は expo-font 側の責務)
309
477
  fontSize: {
310
- xxs: { fontSize: 10, lineHeight: 14 }, xs: { fontSize: 12, lineHeight: 18 },
478
+ // lineHeight fontSize × minLineHeightRatio(既定 1.45)以上にする。未満を書いても
479
+ // 実行時に下限へクランプされ、宣言値と描画値がズレるだけ(機序は src/theme/line-height.ts)
480
+ xxs: { fontSize: 10, lineHeight: 15 }, xs: { fontSize: 12, lineHeight: 18 },
311
481
  sm: { fontSize: 14, lineHeight: 21 }, base: { fontSize: 16, lineHeight: 26 },
312
- lg: { fontSize: 18, lineHeight: 27 }, xl: { fontSize: 20, lineHeight: 28 },
313
- "2xl": { fontSize: 24, lineHeight: 31 }, "3xl": { fontSize: 30, lineHeight: 36 },
482
+ lg: { fontSize: 18, lineHeight: 27 }, xl: { fontSize: 20, lineHeight: 29 },
483
+ "2xl": { fontSize: 24, lineHeight: 35 }, "3xl": { fontSize: 30, lineHeight: 44 },
314
484
  },
315
485
  fontWeight: { normal: "400", medium: "500", semibold: "600", bold: "700" },
316
486
  letterSpacingRatio: { heading: -0.02, body: 0 }, // em 比率(px ではない)
487
+ // フォントを同梱するなら、そのフォントが要求する最小行間比を宣言する(例: LINE Seed JP = 1.61。
488
+ // 下回る lineHeight は RN Android で字形が欠けるため実行時に下限へクランプされる)。
489
+ // 未宣言は日本語安全側の 1.45(Android system の Noto Sans CJK JP 実測):
490
+ // minLineHeightRatio: 1.61,
317
491
  },
318
492
  spacing: { "1": 4, "2": 8, "3": 12, "4": 16, "5": 20, "6": 24, "8": 32, "10": 40, "12": 48, "14": 56, "16": 64 },
319
493
  radius: { sm: 4, md: 8, lg: 16, full: 9999 },
@@ -369,13 +543,22 @@ Icon は唯一 `react-native-svg`(optional peerDependency)に依存するた
369
543
  npx expo install react-native-svg
370
544
  ```
371
545
 
546
+ > bare RN(Expo なし)は `npm install` + `npx pod-install` + ネイティブ再ビルド。
547
+ > [bare React Native](#bare-react-nativeexpo-を使わない場合) を参照。
548
+
372
549
  ```tsx
373
550
  import { Icon } from "melta-app/icons";
374
551
 
375
552
  <Icon name="like-on" accessibilityLabel="いいね" /> // 意味を持つ icon は label 必須
376
553
  <Icon name="close" size="sm" color="text-muted" /> // 省略時は装飾扱い(a11y ツリーから除外)
554
+ <Icon name="check" color="status-success" /> // status 色(Alert / Toast と同じ status token)
377
555
  ```
378
556
 
557
+ `color` は semantic token キーに加えて `"status-success"` / `"status-warning"` / `"status-error"` を受ける
558
+ (`theme.color.status.*.base` を引く。生 hex は受けない)。
559
+ `"status-info"` は**無い** — status token に info の実体が無いため(web 側と同じ割り切り)。
560
+ info 相当は `color="text-accent"` で表す。
561
+
379
562
  グリフは Charcoal Icons(pixiv、Apache-2.0)の curated サブセット + Charcoal に無いグリフの
380
563
  Material Symbols Rounded(Google、Apache-2.0)補完(`assets/icons/*.svg` →
381
564
  `npm run generate:icons` で `src/icons/glyphs.ts` に codegen、commit 済みを配布)。
@@ -383,29 +566,47 @@ Material Symbols Rounded(Google、Apache-2.0)補完(`assets/icons/*.svg`
383
566
 
384
567
  ### SafeArea の差し替え(`melta-app/safe-area`)
385
568
 
386
- Screen の SafeArea は default で RN core の SafeAreaView(deprecated / iOS のみの最小対応、
387
- 依存ゼロ維持)。`react-native-safe-area-context` を使うアプリは subpath から一度有効化すると
388
- Screen context 版に切り替わり、RN 0.85+ deprecation 警告も出なくなる:
569
+ safe-area は default で RN core の SafeAreaView(deprecated / iOS のみの最小対応、依存ゼロ維持。
570
+ **Android では完全な no-op**)。`react-native-safe-area-context` を使うアプリは subpath から
571
+ 一度有効化すると `Screen` / `ActionSheet` / `BottomSheet` context 版に切り替わり、
572
+ Android でも inset が入り、RN 0.85+ の deprecation 警告も出なくなる:
389
573
 
390
574
  ```bash
391
575
  npx expo install react-native-safe-area-context
392
576
  ```
393
577
 
578
+ > bare RN(Expo なし)は `npm install` + `npx pod-install` + ネイティブ再ビルド。
579
+ > [bare React Native](#bare-react-nativeexpo-を使わない場合) を参照。
580
+
394
581
  ```tsx
395
- // アプリの entry(Screen の初回 render より前)で一度だけ
582
+ // アプリの entry(初回 render より前)で一度だけ
396
583
  import { enableSafeAreaContext } from "melta-app/safe-area";
397
584
  enableSafeAreaContext();
398
585
 
399
- // ボトムタブバー等が bottom inset を自前処理するアプリは edge を絞る
400
- // (全 edge のまま使うとタブ内 Screen の bottom が二重余白になる)
401
- enableSafeAreaContext({ edges: ["top"] });
586
+ // edges は「Screen の既定値」。省略した Screen にだけ効く
587
+ enableSafeAreaContext({ edges: ["top", "left", "right"] });
402
588
  ```
403
589
 
590
+ 適用する辺の決まり方は 3 段(後のものが勝つ):
591
+
592
+ | 対象 | 適用される edge |
593
+ |---|---|
594
+ | `Screen`(`edges` 省略) | `enableSafeAreaContext({ edges })` の既定(未指定なら全 edge) |
595
+ | `Screen edges={[...]}` | その指定(画面ごとに変えられる) |
596
+ | `ActionSheet` / `BottomSheet` | 常に bottom + 左右(画面下端に出るため top は不要) |
597
+
598
+ > ボトムタブバーがある画面は、アプリ全体を `edges: ["top"]` にするのではなく
599
+ > **タブ配下の画面だけ [`<Screen edges={["top"]}>`](#画面骨格screen)** で絞る。シート系は
600
+ > bottom inset を自前で持つので、グローバル top-only はシートの下余白まで削ってしまう
601
+ > (0.5.x までのアプリ全体 1 個のグローバル指定はこの理由で推奨から外した)。
602
+
404
603
  ⚠️ 前提と契約:
405
604
 
406
605
  - 祖先に `SafeAreaProvider` が**必須**(無いと `useSafeAreaInsets` が **throw する**。
407
606
  React Navigation / Expo Router を使っていれば設置済みのことが多い)。初回 render から
408
607
  正しい inset を使うには Provider に `initialMetrics` を渡す
608
+ - **adapter 未登録(RN core フォールバック)では `edges` は無視される。** core の SafeAreaView は
609
+ 「安全域と交差する辺すべて」に padding を入れる仕様で辺を選べないため(Android は no-op)
409
610
  - adapter は `useSafeAreaInsets()` を render 中に同期参照して View padding に加算する方式
410
611
  (native SafeAreaView の初回フレーム inset 未適用によるフラッシュを避けるため)
411
612
  - safe-area と合成する padding は**数値のみ**サポート(`"5%"` 等の非数値は基底として扱えず、
@@ -434,10 +635,17 @@ enableSafeAreaContext({ edges: ["top"] });
434
635
  ## 成熟度・メンテナンス
435
636
 
436
637
  - **0.x**。API は安定に向かっているが、破壊的変更は minor で入る([CHANGELOG](./CHANGELOG.md) が変更の正本。npm に公開した版だけを記録する)
638
+ - **バグ・要望は [GitHub Issues](https://github.com/tsubotax/melta-app/issues)**、コントリビュートの手引きは [CONTRIBUTING.md](./CONTRIBUTING.md)、脆弱性は [SECURITY.md](./SECURITY.md) の非公開経路へ
437
639
  - **個人メンテナンスのプロジェクト**(tsubotax)。SLA・サポート窓口は無い。issue / PR は歓迎するが応答は best effort
438
640
  - **現在の adopter は 1 つ**(非公開プロジェクトのモバイルアプリ)。dogfood はそこで回している。D2I は当初想定していた最初の adopter だが、mobile 側はまだ melta-app を導入していない(D2I の web 側が使っているのは melta-ui)
439
641
  - React Native Directory 登録は [PR #2606](https://github.com/react-native-community/directory/pull/2606) がレビュー待ち
440
642
 
643
+ ### 安定性の読み方
644
+
645
+ - **何も書いていない公開 API は安定**として扱ってよい。変更は semver に従う(0.x なので破壊的変更は minor bump で入り、移行手順を CHANGELOG に書く)
646
+ - **`@experimental` 注記が付いたものは予告なく変わる**(挙動・シグネチャ・メッセージとも)。現在は theme の解決規則ヘルパ(`validateTheme` / `resolveMode` / `supportedModes` / `deriveColorScheme` / `declaredModes`)が該当し、JSDoc と型定義に注記が出る
647
+ - **公開面の増減は機械強制**。export の増減は snapshot テスト([scripts/lib/export-surface.test.ts](./scripts/lib/export-surface.test.ts))が、契約の破壊的変更は melta-ui 側の `npm run design:compat` が semver bump を要求する。「気づいたら消えていた」は CI で止まる
648
+
441
649
  ## セキュリティ・データ境界
442
650
 
443
651
  lint も theme 生成もすべて**ローカル処理**で完結する。コード・トークン・生成物を外部へ送信しない。
@@ -27,10 +27,17 @@ melta-app/
27
27
  generate-native-theme.ts melta-contracts/tokens.json → src/theme/native-theme.ts
28
28
  generate-contract-types.ts 契約 JSON → src/contracts/contract-types.ts
29
29
  check-drift.ts README / docs / catalog / allowlist の drift 検査(--write で heal)
30
- check-installability.sh pack → install → import → typecheck の公開ゲート
30
+ check-installability.sh pack → attw → install → import → typecheck の公開ゲート
31
31
  lib/ token 正規化 + conformance / consumer テスト
32
32
  ```
33
33
 
34
+ ## style resolver
35
+
36
+ 見た目の値の解決は component(`.tsx`)ではなく隣の `<component>.styles.ts`(react-native を値 import しない
37
+ pure module)に置く。node の `tsx --test` から直接実行できることが conformance テストの前提になっている。
38
+ 書き方の規約(`<COMPONENT>_SPEC` / slot 単位の戻り値 / 命名 / 共有 module)は [AGENTS.md](../AGENTS.md) が正。
39
+ resolver は3世代が混在しているので、**現行世代を規範とする**(初代の `button.styles.ts` を写さない)。
40
+
34
41
  ## トークン正規化(web → RN)
35
42
 
36
43
  `scripts/lib/normalize-tokens.ts` が変換する。壊れやすい箇所はテスト必須。
@@ -41,7 +48,7 @@ melta-app/
41
48
  | spacing / radius / fontSize | rem + px | px の数値を採用(RN は unitless) |
42
49
  | shadow | CSS box-shadow | iOS shadow* + Android elevation に分解(1 ViewStyle に同居) |
43
50
  | fontFamily | 配列 | 単一文字列(先頭採用、未ロード時 OS デフォルト fallback) |
44
- | lineHeight | 比率("2.0") | `fontSize × ratio` を px 算出 |
51
+ | lineHeight | 比率("2.0") | `fontSize × ratio` を px 算出(四捨五入)し、安全下限 `ceil(fontSize × minLineHeightRatio)`(既定 1.45)でクランプ(RN Android の字形欠け対策。根拠は `src/theme/line-height.ts`) |
45
52
  | letterSpacing | em | **em ratio の数値で保持**(fontSize 相対のため token 単体で px 化不可。適用側が `fontSize × ratio` で解決) |
46
53
  | motion duration | "150ms" | 150(数値) |
47
54
  | motion easing | cubic-bezier | `[a,b,c,d]` tuple(`Easing.bezier(...)` に展開) |
@@ -67,7 +74,7 @@ npm run ios # または android / web
67
74
  - ✅ ライブラリ化(root=ライブラリ / example=カタログアプリ、peerDeps react + react-native、runtime deps ゼロ)
68
75
  - ✅ `melta-contracts` を npm 依存として購読(recipes/app の styleRefs 同梱)
69
76
  - ✅ conformance: 契約源 ↔ 生成メタ ↔ `__contract` 宣言の照合 + consumer テスト(契約 subset / token 実在 / contractVersion 同期)+ styleRefs conformance(全実装コンポーネント展開済み)+ RN mount smoke(light/dark × 全公開コンポーネント)
70
- - ✅ ハーネス: design lint(CI `--max-warnings 0` + PostToolUse hook)/ drift 検査(README・docs・catalog・allowlist 突合)/ installability ゲート(pack → tarball 実体検査 → fixture へ install → 本体 / icons / safe-area の import + typecheck → eslint plugin を実 import してルール 4 本の実在を照合 → exports の解決先確認。`npm run release` チェーンの必須ステップ)
77
+ - ✅ ハーネス: design lint(CI `--max-warnings 0` + PostToolUse hook)/ drift 検査(README・docs・catalog・allowlist 突合)/ installability ゲート(pack → tarball 実体検査 → attw で exports の型解決を resolution mode ごとに検査 → fixture へ install → 本体 / icons / safe-area / eslint-plugin の import + typecheck を moduleResolution 3 種(bundler / node16 / nodenext)で実行 → eslint plugin を実 import してルール 4 本の実在を照合 → exports の解決先確認。`npm run release` チェーンの必須ステップ)
71
78
  - ✅ layout 6 個(Stack / Row / Screen / Header / Icon / Avatar)— dogfood 不足 1〜4 を解消、ProjectFeedScreen は公開 primitive だけで構成
72
79
  - Screen の SafeArea は adapter registry 化済み: default は RN core SafeAreaView(依存ゼロ維持)、`melta-app/safe-area` の `enableSafeAreaContext()` で react-native-safe-area-context に差し替え可(optional peer)
73
80
  - ✅ form / feedback 10 個(TextField / Toggle / Checkbox / Radio / Alert / Toast / Progress / Modal / ActionSheet / BottomSheet)— checkbox / radio は Pressable + 描画(svg 非依存)、ActionSheet / BottomSheet は select / dropdown の adapted 変換先の受け皿
@@ -89,4 +96,4 @@ push / PR で以下を順に回す。CI には兄弟ディレクトリ(melta-u
89
96
  4. `npm test`(consumer + conformance)
90
97
  5. `npm run check:drift`(README / docs / catalog / allowlist)
91
98
  6. `npm run test:rn`(RN mount smoke)
92
- 7. `npm run check:installability`(pack → install → import → typecheck
99
+ 7. `npm run check:installability`(pack → attw → install → import → typecheck ×3 解決)
@@ -0,0 +1,44 @@
1
+ /**
2
+ * melta design lint プラグインの型宣言(melta.mjs の手書き型)。
3
+ *
4
+ * `melta.mjs` は実装の正本。この宣言は npm 利用者(moduleResolution: node16/nodenext)が
5
+ * `melta-app/eslint-plugin` を型付きで import できるようにするためだけに存在する。
6
+ * 実装を変えたらこちらも手で追随させる(ルール名は check-installability が機械照合している)。
7
+ *
8
+ * ESLint 本体の型(`eslint` / `@types/eslint`)には**意図的に依存しない**。
9
+ * eslint は melta-app の dependencies ではないため、import すると
10
+ * 「eslint 未 install の利用者で型が壊れる」経路を作ってしまう。
11
+ * flat config に差し込むのに必要な最小限を構造的に宣言する。
12
+ */
13
+
14
+ /** ESLint のルール実装(AST visitor を返す `create` と表示用 `meta`)。 */
15
+ export interface MeltaRule {
16
+ meta: {
17
+ type: "problem" | "suggestion" | "layout";
18
+ docs: { description: string };
19
+ };
20
+ // AST ノード型は eslint の型に依存しないため unknown 止まり(消費者は呼び出さない)。
21
+ create(context: unknown): Record<string, (...args: never[]) => void>;
22
+ }
23
+
24
+ /** このプラグインが提供するルール ID(`melta/` prefix なし)。 */
25
+ export type MeltaRuleName =
26
+ | "no-raw-color"
27
+ | "no-raw-radius"
28
+ | "no-raw-spacing"
29
+ | "no-raw-fontsize";
30
+
31
+ /** flat config の 1 要素として展開できる推奨設定。 */
32
+ export interface MeltaFlatConfig {
33
+ name: string;
34
+ plugins: { melta: MeltaPlugin };
35
+ /** severity の正本は melta.mjs の RECOMMENDED_RULES(ドキュメントではない)。 */
36
+ rules: Record<string, "error" | "warn" | "off">;
37
+ }
38
+
39
+ export interface MeltaPlugin {
40
+ rules: Record<MeltaRuleName, MeltaRule>;
41
+ configs: { recommended: MeltaFlatConfig };
42
+ }
43
+
44
+ export declare const meltaPlugin: MeltaPlugin;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * tap-target — 実効タップ標的 44pt(横断方針)の数値 SSOT。
5
+ *
6
+ * 正本は melta-contracts の `A11Y_MIN_TAP_TARGET_44`(rules.json):
7
+ * 「すべての操作要素は実効タップ標的 44pt を下回らない。視覚寸法は契約どおり据え置き、
8
+ * 当たり判定だけを広げる。app は視覚 24pt + hitSlop 10 の正典パターンか minHeight で下限を保証する
9
+ * (height 固定は fontScale でクリップするので使わない)」。
10
+ *
11
+ * ここは「44」という数と正典パターンの寸法を**一箇所**に置くための pure module。
12
+ * 各コンポーネントの hitSlop 値そのものは **literal で各 `*.styles.ts` に置く**(ここから
13
+ * 自動導出しない)。導出にすると視覚寸法が変わったとき hitSlop も一緒に動いてしまい、
14
+ * conformance テスト(scripts/lib/tap-target-conformance.test.ts)が構造的に fail-open になるため。
15
+ *
16
+ * react-native を import しない純粋モジュール(`*.styles.ts` と同じ規約)。
17
+ */
18
+
19
+ /** 実効タップ標的の下限(pt)。WCAG 2.2 SC 2.5.5(AAA)水準を melta の横断方針として採る。 */
20
+ export const MIN_TAP_TARGET = 44;
21
+
22
+ /**
23
+ * 正典パターン: 視覚 24pt の箱 + hitSlop 10 → 実効 44pt。
24
+ * 背景を持たない小さな操作要素(Tag removable の × / Toast の × / Alert の × / Modal の ×)はこれ。
25
+ */
26
+ export const CANONICAL_TAP_TARGET = {
27
+ minWidth: 24,
28
+ minHeight: 24,
29
+ hitSlop: 10
30
+ };
31
+
32
+ /**
33
+ * 視覚寸法 `visual` を実効 44pt に届かせるのに必要な**片側** hitSlop(切り上げ = 安全側)。
34
+ *
35
+ * ⚠️ 実装の hitSlop 値をこの関数で計算しないこと(上のコメント参照)。
36
+ * 使うのは (a) 値を決めるときの検算、(b) conformance テストの期待値算出。
37
+ */
38
+ export function requiredHitSlop(visual) {
39
+ return Math.max(0, Math.ceil((MIN_TAP_TARGET - visual) / 2));
40
+ }
41
+ //# sourceMappingURL=tap-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MIN_TAP_TARGET","CANONICAL_TAP_TARGET","minWidth","minHeight","hitSlop","requiredHitSlop","visual","Math","max","ceil"],"sourceRoot":"../../../src","sources":["a11y/tap-target.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO,MAAMA,cAAc,GAAG,EAAE;;AAEhC;AACA;AACA;AACA;AACA,OAAO,MAAMC,oBAAoB,GAAG;EAClCC,QAAQ,EAAE,EAAE;EACZC,SAAS,EAAE,EAAE;EACbC,OAAO,EAAE;AACX,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,eAAeA,CAACC,MAAc,EAAU;EACtD,OAAOC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAED,IAAI,CAACE,IAAI,CAAC,CAACT,cAAc,GAAGM,MAAM,IAAI,CAAC,CAAC,CAAC;AAC9D","ignoreList":[]}
@@ -11,17 +11,28 @@
11
11
  * 色だけでなくラベル文言でも破壊的操作と分かる文言にするのは呼び出し側の責務(contract a11y)。
12
12
  * - 先頭 action の borderTop は title が無い場合は消す(title がある時だけ区切りとして意味を持つ)。
13
13
  * - action の onPress 後は onClose も呼ぶ(選択 = シートを閉じる)。
14
- * - sheet 群は SafeAreaView で包み下余白(ホームインジケータ)を確保(recipe description 参照)。
14
+ * - 下余白(ホームインジケータ)は SafeArea で確保する(recipe description 参照)。位置は
15
+ * **最下部の面である cancel ブロックの内側**(BottomSheet と同じ「内側」方針に統一)。
16
+ * 0.5.x までは sheet 群の外側に置いていたため、iOS では inset 帯に overlay の黒が見えていた。
17
+ * 内側に移したことで inset 帯まで bg-surface で塗られる代わり、cancel ブロックは
18
+ * inset のぶん背が高くなる(ラベルは上寄りに見える)=意図した見た目の変更。
19
+ * edge は bottom + 左右のみ(top は overlay 側で、シートは上端に接しない)。
20
+ * SafeArea 実装は registry 経由(safe-area-registry.ts)— "melta-app/safe-area" を
21
+ * 有効化したアプリでは Android でも inset が入る(RN core の SafeAreaView は Android で no-op)。
15
22
  * - sheet 側は onStartShouldSetResponder で touch を claim し、overlay tap でだけ閉じる。
16
23
  * - style の決定は pure resolver(action-sheet.styles.ts)に分離 — recipe との機械照合対象。
17
24
  */
18
25
 
19
26
  import { useMemo } from "react";
20
- import { Modal as RNModal, Pressable, SafeAreaView, Text as RNText, View } from "react-native";
27
+ import { Modal as RNModal, Pressable, Text as RNText, View } from "react-native";
21
28
  import { useTheme } from "../theme/index.js";
22
29
  import { CONTRACTS } from "../contracts/contract-types.js";
23
30
  import { resolveActionSheetStyle } from "./action-sheet.styles.js";
31
+ import { resolveSafeAreaView } from "./safe-area-registry.js";
32
+
33
+ /** シートは画面下端に密着するので top inset は不要。 */
24
34
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
35
+ const SHEET_EDGES = ["bottom", "left", "right"];
25
36
  export function ActionSheet({
26
37
  visible,
27
38
  onClose,
@@ -35,6 +46,7 @@ export function ActionSheet({
35
46
  mode
36
47
  } = useTheme();
37
48
  const styles = useMemo(() => resolveActionSheetStyle(theme, mode), [theme, mode]);
49
+ const SafeArea = resolveSafeAreaView(SHEET_EDGES);
38
50
  return /*#__PURE__*/_jsx(RNModal, {
39
51
  visible: visible,
40
52
  transparent: true,
@@ -45,7 +57,7 @@ export function ActionSheet({
45
57
  style: styles.overlayStyle,
46
58
  onPress: onClose,
47
59
  accessible: false,
48
- children: /*#__PURE__*/_jsxs(SafeAreaView, {
60
+ children: /*#__PURE__*/_jsxs(View, {
49
61
  accessibilityViewIsModal: true,
50
62
  onStartShouldSetResponder: () => true,
51
63
  testID: testID,
@@ -74,9 +86,11 @@ export function ActionSheet({
74
86
  accessibilityRole: "button",
75
87
  onPress: onClose,
76
88
  style: styles.cancelStyle,
77
- children: /*#__PURE__*/_jsx(RNText, {
78
- style: styles.cancelTextStyle,
79
- children: cancelLabel
89
+ children: /*#__PURE__*/_jsx(SafeArea, {
90
+ children: /*#__PURE__*/_jsx(RNText, {
91
+ style: styles.cancelTextStyle,
92
+ children: cancelLabel
93
+ })
80
94
  })
81
95
  })]
82
96
  })
@@ -1 +1 @@
1
- {"version":3,"names":["useMemo","Modal","RNModal","Pressable","SafeAreaView","Text","RNText","View","useTheme","CONTRACTS","resolveActionSheetStyle","jsx","_jsx","jsxs","_jsxs","ActionSheet","visible","onClose","title","actions","cancelLabel","testID","theme","mode","styles","transparent","animationType","onRequestClose","statusBarTranslucent","children","style","overlayStyle","onPress","accessible","accessibilityViewIsModal","onStartShouldSetResponder","sheetStyle","titleStyle","map","action","index","accessibilityRole","actionStyle","borderTopWidth","destructive","destructiveTextStyle","actionTextStyle","label","cancelStyle","cancelTextStyle","__contract","actionSheet"],"sourceRoot":"../../../src","sources":["components/ActionSheet.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,OAAO,QAAQ,OAAO;AAC/B,SACEC,KAAK,IAAIC,OAAO,EAChBC,SAAS,EACTC,YAAY,EACZC,IAAI,IAAIC,MAAM,EACdC,IAAI,QACC,cAAc;AACrB,SAASC,QAAQ,QAAQ,mBAAU;AACnC,SAASC,SAAS,QAAQ,gCAA6B;AACvD,SAASC,uBAAuB,QAA+B,0BAAuB;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAsBvF,OAAO,SAASC,WAAWA,CAAC;EAC1BC,OAAO;EACPC,OAAO;EACPC,KAAK;EACLC,OAAO;EACPC,WAAW,GAAG,OAAO;EACrBC;AACgB,CAAC,EAAE;EACnB,MAAM;IAAEC,KAAK;IAAEC;EAAK,CAAC,GAAGf,QAAQ,CAAC,CAAC;EAElC,MAAMgB,MAAM,GAAGxB,OAAO,CACpB,MAAMU,uBAAuB,CAACY,KAAK,EAAEC,IAAI,CAAC,EAC1C,CAACD,KAAK,EAAEC,IAAI,CACd,CAAC;EAED,oBACEX,IAAA,CAACV,OAAO;IACNc,OAAO,EAAEA,OAAQ;IACjBS,WAAW;IACXC,aAAa,EAAC,OAAO;IACrBC,cAAc,EAAEV,OAAQ;IACxBW,oBAAoB;IAAAC,QAAA,eAEpBjB,IAAA,CAACT,SAAS;MAAC2B,KAAK,EAAEN,MAAM,CAACO,YAAa;MAACC,OAAO,EAAEf,OAAQ;MAACgB,UAAU,EAAE,KAAM;MAAAJ,QAAA,eAEzEf,KAAA,CAACV,YAAY;QACX8B,wBAAwB;QACxBC,yBAAyB,EAAEA,CAAA,KAAM,IAAK;QACtCd,MAAM,EAAEA,MAAO;QAAAQ,QAAA,gBAEff,KAAA,CAACP,IAAI;UAACuB,KAAK,EAAEN,MAAM,CAACY,UAAW;UAAAP,QAAA,GAC5BX,KAAK,IAAI,IAAI,iBAAIN,IAAA,CAACN,MAAM;YAACwB,KAAK,EAAEN,MAAM,CAACa,UAAW;YAAAR,QAAA,EAAEX;UAAK,CAAS,CAAC,EACnEC,OAAO,CAACmB,GAAG,CAAC,CAACC,MAAM,EAAEC,KAAK,kBACzB5B,IAAA,CAACT,SAAS;YAERsC,iBAAiB,EAAC,QAAQ;YAC1BT,OAAO,EAAEA,CAAA,KAAM;cACbO,MAAM,CAACP,OAAO,CAAC,CAAC;cAChBf,OAAO,CAAC,CAAC;YACX,CAAE;YACFa,KAAK,EAAE,CACLN,MAAM,CAACkB,WAAW;YAClB;YACAF,KAAK,KAAK,CAAC,IAAItB,KAAK,IAAI,IAAI,GAAG;cAAEyB,cAAc,EAAE;YAAE,CAAC,GAAG,IAAI,CAC3D;YAAAd,QAAA,eAEFjB,IAAA,CAACN,MAAM;cACLwB,KAAK,EAAES,MAAM,CAACK,WAAW,GAAGpB,MAAM,CAACqB,oBAAoB,GAAGrB,MAAM,CAACsB,eAAgB;cAAAjB,QAAA,EAEhFU,MAAM,CAACQ;YAAK,CACP;UAAC,GAhBJ,GAAGP,KAAK,IAAID,MAAM,CAACQ,KAAK,EAiBpB,CACZ,CAAC;QAAA,CACE,CAAC,eACPnC,IAAA,CAACT,SAAS;UAACsC,iBAAiB,EAAC,QAAQ;UAACT,OAAO,EAAEf,OAAQ;UAACa,KAAK,EAAEN,MAAM,CAACwB,WAAY;UAAAnB,QAAA,eAChFjB,IAAA,CAACN,MAAM;YAACwB,KAAK,EAAEN,MAAM,CAACyB,eAAgB;YAAApB,QAAA,EAAET;UAAW,CAAS;QAAC,CACpD,CAAC;MAAA,CACA;IAAC,CACN;EAAC,CACL,CAAC;AAEd;AAEAL,WAAW,CAACmC,UAAU,GAAGzC,SAAS,CAAC0C,WAAW","ignoreList":[]}
1
+ {"version":3,"names":["useMemo","Modal","RNModal","Pressable","Text","RNText","View","useTheme","CONTRACTS","resolveActionSheetStyle","resolveSafeAreaView","jsx","_jsx","jsxs","_jsxs","SHEET_EDGES","ActionSheet","visible","onClose","title","actions","cancelLabel","testID","theme","mode","styles","SafeArea","transparent","animationType","onRequestClose","statusBarTranslucent","children","style","overlayStyle","onPress","accessible","accessibilityViewIsModal","onStartShouldSetResponder","sheetStyle","titleStyle","map","action","index","accessibilityRole","actionStyle","borderTopWidth","destructive","destructiveTextStyle","actionTextStyle","label","cancelStyle","cancelTextStyle","__contract","actionSheet"],"sourceRoot":"../../../src","sources":["components/ActionSheet.tsx"],"mappings":"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","ignoreList":[]}
@@ -7,6 +7,8 @@
7
7
  * - RN では border を持たず bg subtle + text 色で表現(status token に border 用の中間色が無い、
8
8
  * recipe description)。COLOR_ONLY_FORBIDDEN は title/message のテキストと icon slot で担保する設計。
9
9
  * - icon は ReactNode slot(EmptyState 前例。Icon を差すなら melta-app/icons を利用者が opt-in)。
10
+ * - × の accessibilityLabel は既定 "閉じる"(closeAccessibilityLabel で差し替え可能)。
11
+ * 当たり判定は正典パターン(24 + hitSlop 10 = 44pt)。
10
12
  * - a11y(§2 web→RN mapping): web の role=alert(error/warning) / status(info/success) を、
11
13
  * error/warning → accessibilityRole="alert"、info/success → accessibilityLiveRegion="polite"
12
14
  * (Android のみ有効。RN に status role が無いための写像)に落とす。
@@ -20,7 +22,7 @@ import { Pressable, View } from "react-native";
20
22
  import { Text } from "../primitives/Text.js";
21
23
  import { useTheme } from "../theme/index.js";
22
24
  import { CONTRACTS } from "../contracts/contract-types.js";
23
- import { ALERT_SPEC, resolveAlertStyles } from "./alert.styles.js";
25
+ import { ALERT_CLOSE_TAP_TARGET, ALERT_SPEC, resolveAlertStyles } from "./alert.styles.js";
24
26
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
25
27
  export function Alert({
26
28
  variant,
@@ -28,6 +30,7 @@ export function Alert({
28
30
  message,
29
31
  icon,
30
32
  onClose,
33
+ closeAccessibilityLabel = "閉じる",
31
34
  style,
32
35
  testID
33
36
  }) {
@@ -65,15 +68,15 @@ export function Alert({
65
68
  })]
66
69
  }), onClose != null &&
67
70
  /*#__PURE__*/
68
- // 最小タップターゲット確保: min 24 + hitSlop(Tag removable と同じ手当て)。
71
+ // 最小タップターゲット確保: 正典パターン 24 + hitSlop 10 = 実効 44pt(Tag removable と同型)。
69
72
  _jsx(Pressable, {
70
73
  onPress: onClose,
71
74
  accessibilityRole: "button",
72
- accessibilityLabel: "\u9589\u3058\u308B",
73
- hitSlop: 10,
75
+ accessibilityLabel: closeAccessibilityLabel,
76
+ hitSlop: ALERT_CLOSE_TAP_TARGET.hitSlop,
74
77
  style: {
75
- minWidth: 24,
76
- minHeight: 24,
78
+ minWidth: ALERT_CLOSE_TAP_TARGET.minWidth,
79
+ minHeight: ALERT_CLOSE_TAP_TARGET.minHeight,
77
80
  alignItems: "center",
78
81
  justifyContent: "center"
79
82
  },
@@ -88,6 +91,7 @@ export function Alert({
88
91
  });
89
92
  }
90
93
 
91
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
94
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
95
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
92
96
  Alert.__contract = CONTRACTS.alert;
93
97
  //# sourceMappingURL=Alert.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useMemo","Pressable","View","Text","useTheme","CONTRACTS","ALERT_SPEC","resolveAlertStyles","jsx","_jsx","jsxs","_jsxs","Alert","variant","title","message","icon","onClose","style","testID","theme","mode","styles","urgent","accessibilityRole","undefined","accessibilityLiveRegion","containerStyle","children","flex","gap","spacing","titleFont","weight","titleWeight","color","titleStyle","messageFont","messageStyle","onPress","accessibilityLabel","hitSlop","minWidth","minHeight","alignItems","justifyContent","__contract","alert"],"sourceRoot":"../../../src","sources":["components/Alert.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,OAAO,QAAwB,OAAO;AAC/C,SAASC,SAAS,EAAEC,IAAI,QAAwC,cAAc;AAC9E,SAASC,IAAI,QAAQ,uBAAoB;AACzC,SAASC,QAAQ,QAAQ,mBAAU;AACnC,SAASC,SAAS,QAAQ,gCAA6B;AACvD,SAASC,UAAU,EAAEC,kBAAkB,QAA2B,mBAAgB;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAiBnF,OAAO,SAASC,KAAKA,CAAC;EAAEC,OAAO;EAAEC,KAAK;EAAEC,OAAO;EAAEC,IAAI;EAAEC,OAAO;EAAEC,KAAK;EAAEC;AAAmB,CAAC,EAAE;EAC3F,MAAM;IAAEC,KAAK;IAAEC;EAAK,CAAC,GAAGjB,QAAQ,CAAC,CAAC;EAClC,MAAMkB,MAAM,GAAGtB,OAAO,CAAC,MAAMO,kBAAkB,CAACa,KAAK,EAAEC,IAAI,EAAER,OAAO,CAAC,EAAE,CAACO,KAAK,EAAEC,IAAI,EAAER,OAAO,CAAC,CAAC;EAC9F,MAAMU,MAAM,GAAGV,OAAO,KAAK,OAAO,IAAIA,OAAO,KAAK,SAAS;EAE3D,oBACEF,KAAA,CAACT,IAAI;IACHsB,iBAAiB,EAAED,MAAM,GAAG,OAAO,GAAGE,SAAU;IAChDC,uBAAuB,EAAEH,MAAM,GAAGE,SAAS,GAAG,QAAS;IACvDN,MAAM,EAAEA,MAAO;IACfD,KAAK,EAAE,CAACI,MAAM,CAACK,cAAc,EAAET,KAAK,CAAE;IAAAU,QAAA,GAErCZ,IAAI,IAAI,IAAI,iBAAIP,IAAA,CAACP,IAAI;MAAA0B,QAAA,EAAEZ;IAAI,CAAO,CAAC,eACpCL,KAAA,CAACT,IAAI;MAACgB,KAAK,EAAE;QAAEW,IAAI,EAAE,CAAC;QAAEC,GAAG,EAAEV,KAAK,CAACW,OAAO,CAAC,GAAG;MAAE,CAAE;MAAAH,QAAA,GAC/Cd,KAAK,IAAI,IAAI,iBACZL,IAAA,CAACN,IAAI;QACHU,OAAO,EAAEP,UAAU,CAAC0B,SAAU;QAC9BC,MAAM,EAAE3B,UAAU,CAAC4B,WAAY;QAC/BhB,KAAK,EAAE;UAAEiB,KAAK,EAAEb,MAAM,CAACc,UAAU,CAACD;QAAM,CAAE;QAAAP,QAAA,EAEzCd;MAAK,CACF,CACP,eACDL,IAAA,CAACN,IAAI;QAACU,OAAO,EAAEP,UAAU,CAAC+B,WAAY;QAACnB,KAAK,EAAE;UAAEiB,KAAK,EAAEb,MAAM,CAACgB,YAAY,CAACH;QAAM,CAAE;QAAAP,QAAA,EAChFb;MAAO,CACJ,CAAC;IAAA,CACH,CAAC,EACNE,OAAO,IAAI,IAAI;IAAA;IACd;IACAR,IAAA,CAACR,SAAS;MACRsC,OAAO,EAAEtB,OAAQ;MACjBO,iBAAiB,EAAC,QAAQ;MAC1BgB,kBAAkB,EAAC,oBAAK;MACxBC,OAAO,EAAE,EAAG;MACZvB,KAAK,EAAE;QAAEwB,QAAQ,EAAE,EAAE;QAAEC,SAAS,EAAE,EAAE;QAAEC,UAAU,EAAE,QAAQ;QAAEC,cAAc,EAAE;MAAS,CAAE;MAAAjB,QAAA,eAEvFnB,IAAA,CAACN,IAAI;QAACU,OAAO,EAAC,IAAI;QAACK,KAAK,EAAE;UAAEiB,KAAK,EAAEb,MAAM,CAACgB,YAAY,CAACH;QAAM,CAAE;QAAAP,QAAA,EAAC;MAEhE,CAAM;IAAC,CACE,CACZ;EAAA,CACG,CAAC;AAEX;;AAEA;AACAhB,KAAK,CAACkC,UAAU,GAAGzC,SAAS,CAAC0C,KAAK","ignoreList":[]}
1
+ {"version":3,"names":["useMemo","Pressable","View","Text","useTheme","CONTRACTS","ALERT_CLOSE_TAP_TARGET","ALERT_SPEC","resolveAlertStyles","jsx","_jsx","jsxs","_jsxs","Alert","variant","title","message","icon","onClose","closeAccessibilityLabel","style","testID","theme","mode","styles","urgent","accessibilityRole","undefined","accessibilityLiveRegion","containerStyle","children","flex","gap","spacing","titleFont","weight","titleWeight","color","titleStyle","messageFont","messageStyle","onPress","accessibilityLabel","hitSlop","minWidth","minHeight","alignItems","justifyContent","__contract","alert"],"sourceRoot":"../../../src","sources":["components/Alert.tsx"],"mappings":"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","ignoreList":[]}