melta-app 0.5.3 → 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 (298) hide show
  1. package/README.md +493 -186
  2. package/docs/architecture.md +99 -0
  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 +28 -7
  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 +4 -1
  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 +10 -5
  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 +27 -1
  94. package/lib/module/theme/define-theme.js.map +1 -1
  95. package/lib/module/theme/index.js +7 -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 +52 -0
  100. package/lib/module/theme/line-height.js.map +1 -0
  101. package/lib/module/theme/native-theme.js +8 -7
  102. package/lib/module/theme/native-theme.js.map +1 -1
  103. package/lib/typescript/src/a11y/tap-target.d.ts +34 -0
  104. package/lib/typescript/src/a11y/tap-target.d.ts.map +1 -0
  105. package/lib/typescript/src/components/ActionSheet.d.ts +8 -1
  106. package/lib/typescript/src/components/ActionSheet.d.ts.map +1 -1
  107. package/lib/typescript/src/components/Alert.d.ts +9 -2
  108. package/lib/typescript/src/components/Alert.d.ts.map +1 -1
  109. package/lib/typescript/src/components/Avatar.d.ts +1 -1
  110. package/lib/typescript/src/components/Avatar.d.ts.map +1 -1
  111. package/lib/typescript/src/components/BottomSheet.d.ts +4 -1
  112. package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -1
  113. package/lib/typescript/src/components/Card.d.ts +2 -2
  114. package/lib/typescript/src/components/Checkbox.d.ts +3 -3
  115. package/lib/typescript/src/components/Checkbox.d.ts.map +1 -1
  116. package/lib/typescript/src/components/Image.d.ts +4 -3
  117. package/lib/typescript/src/components/Image.d.ts.map +1 -1
  118. package/lib/typescript/src/components/Modal.d.ts +9 -3
  119. package/lib/typescript/src/components/Modal.d.ts.map +1 -1
  120. package/lib/typescript/src/components/Progress.d.ts +1 -1
  121. package/lib/typescript/src/components/Progress.d.ts.map +1 -1
  122. package/lib/typescript/src/components/Radio.d.ts +3 -2
  123. package/lib/typescript/src/components/Radio.d.ts.map +1 -1
  124. package/lib/typescript/src/components/Screen.d.ts +28 -5
  125. package/lib/typescript/src/components/Screen.d.ts.map +1 -1
  126. package/lib/typescript/src/components/Skeleton.d.ts +1 -1
  127. package/lib/typescript/src/components/Skeleton.d.ts.map +1 -1
  128. package/lib/typescript/src/components/Surface.d.ts +1 -1
  129. package/lib/typescript/src/components/Surface.d.ts.map +1 -1
  130. package/lib/typescript/src/components/TextField.d.ts +11 -4
  131. package/lib/typescript/src/components/TextField.d.ts.map +1 -1
  132. package/lib/typescript/src/components/Toast.d.ts +10 -3
  133. package/lib/typescript/src/components/Toast.d.ts.map +1 -1
  134. package/lib/typescript/src/components/Toggle.d.ts +3 -2
  135. package/lib/typescript/src/components/Toggle.d.ts.map +1 -1
  136. package/lib/typescript/src/components/action-sheet.styles.d.ts +1 -1
  137. package/lib/typescript/src/components/action-sheet.styles.d.ts.map +1 -1
  138. package/lib/typescript/src/components/alert.styles.d.ts +14 -14
  139. package/lib/typescript/src/components/alert.styles.d.ts.map +1 -1
  140. package/lib/typescript/src/components/avatar.styles.d.ts +36 -1
  141. package/lib/typescript/src/components/avatar.styles.d.ts.map +1 -1
  142. package/lib/typescript/src/components/bottom-sheet.styles.d.ts +1 -1
  143. package/lib/typescript/src/components/bottom-sheet.styles.d.ts.map +1 -1
  144. package/lib/typescript/src/components/card.styles.d.ts +1 -1
  145. package/lib/typescript/src/components/card.styles.d.ts.map +1 -1
  146. package/lib/typescript/src/components/card.validate.d.ts +1 -1
  147. package/lib/typescript/src/components/card.validate.d.ts.map +1 -1
  148. package/lib/typescript/src/components/checkbox.styles.d.ts +6 -3
  149. package/lib/typescript/src/components/checkbox.styles.d.ts.map +1 -1
  150. package/lib/typescript/src/components/empty-state.styles.d.ts +1 -1
  151. package/lib/typescript/src/components/empty-state.styles.d.ts.map +1 -1
  152. package/lib/typescript/src/components/header.styles.d.ts +1 -1
  153. package/lib/typescript/src/components/header.styles.d.ts.map +1 -1
  154. package/lib/typescript/src/components/image.styles.d.ts +1 -1
  155. package/lib/typescript/src/components/image.styles.d.ts.map +1 -1
  156. package/lib/typescript/src/components/index.d.ts +18 -18
  157. package/lib/typescript/src/components/index.d.ts.map +1 -1
  158. package/lib/typescript/src/components/modal.styles.d.ts +13 -2
  159. package/lib/typescript/src/components/modal.styles.d.ts.map +1 -1
  160. package/lib/typescript/src/components/progress.styles.d.ts +1 -1
  161. package/lib/typescript/src/components/progress.styles.d.ts.map +1 -1
  162. package/lib/typescript/src/components/radio.styles.d.ts +10 -2
  163. package/lib/typescript/src/components/radio.styles.d.ts.map +1 -1
  164. package/lib/typescript/src/components/safe-area-registry.d.ts +47 -11
  165. package/lib/typescript/src/components/safe-area-registry.d.ts.map +1 -1
  166. package/lib/typescript/src/components/screen.styles.d.ts +1 -1
  167. package/lib/typescript/src/components/screen.styles.d.ts.map +1 -1
  168. package/lib/typescript/src/components/skeleton.styles.d.ts +1 -1
  169. package/lib/typescript/src/components/skeleton.styles.d.ts.map +1 -1
  170. package/lib/typescript/src/components/status-colors.d.ts +33 -0
  171. package/lib/typescript/src/components/status-colors.d.ts.map +1 -0
  172. package/lib/typescript/src/components/surface.styles.d.ts +1 -1
  173. package/lib/typescript/src/components/surface.styles.d.ts.map +1 -1
  174. package/lib/typescript/src/components/textfield.styles.d.ts +9 -5
  175. package/lib/typescript/src/components/textfield.styles.d.ts.map +1 -1
  176. package/lib/typescript/src/components/toast.styles.d.ts +34 -14
  177. package/lib/typescript/src/components/toast.styles.d.ts.map +1 -1
  178. package/lib/typescript/src/components/toggle.styles.d.ts +17 -1
  179. package/lib/typescript/src/components/toggle.styles.d.ts.map +1 -1
  180. package/lib/typescript/src/icons/Icon.d.ts +12 -6
  181. package/lib/typescript/src/icons/Icon.d.ts.map +1 -1
  182. package/lib/typescript/src/icons/icon.styles.d.ts +14 -2
  183. package/lib/typescript/src/icons/icon.styles.d.ts.map +1 -1
  184. package/lib/typescript/src/icons/index.d.ts +3 -3
  185. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  186. package/lib/typescript/src/index.d.ts +4 -4
  187. package/lib/typescript/src/index.d.ts.map +1 -1
  188. package/lib/typescript/src/primitives/Button.d.ts +8 -4
  189. package/lib/typescript/src/primitives/Button.d.ts.map +1 -1
  190. package/lib/typescript/src/primitives/Metric.d.ts +1 -1
  191. package/lib/typescript/src/primitives/Metric.d.ts.map +1 -1
  192. package/lib/typescript/src/primitives/Row.d.ts +2 -2
  193. package/lib/typescript/src/primitives/Row.d.ts.map +1 -1
  194. package/lib/typescript/src/primitives/Stack.d.ts +2 -2
  195. package/lib/typescript/src/primitives/Stack.d.ts.map +1 -1
  196. package/lib/typescript/src/primitives/Tag.d.ts +2 -2
  197. package/lib/typescript/src/primitives/Tag.d.ts.map +1 -1
  198. package/lib/typescript/src/primitives/Text.d.ts +20 -6
  199. package/lib/typescript/src/primitives/Text.d.ts.map +1 -1
  200. package/lib/typescript/src/primitives/button.styles.d.ts +44 -7
  201. package/lib/typescript/src/primitives/button.styles.d.ts.map +1 -1
  202. package/lib/typescript/src/primitives/index.d.ts +6 -6
  203. package/lib/typescript/src/primitives/index.d.ts.map +1 -1
  204. package/lib/typescript/src/primitives/metric.styles.d.ts +1 -1
  205. package/lib/typescript/src/primitives/metric.styles.d.ts.map +1 -1
  206. package/lib/typescript/src/primitives/row.styles.d.ts +1 -1
  207. package/lib/typescript/src/primitives/row.styles.d.ts.map +1 -1
  208. package/lib/typescript/src/primitives/stack.styles.d.ts +1 -1
  209. package/lib/typescript/src/primitives/stack.styles.d.ts.map +1 -1
  210. package/lib/typescript/src/primitives/tag.styles.d.ts +34 -1
  211. package/lib/typescript/src/primitives/tag.styles.d.ts.map +1 -1
  212. package/lib/typescript/src/primitives/text.styles.d.ts +2 -4
  213. package/lib/typescript/src/primitives/text.styles.d.ts.map +1 -1
  214. package/lib/typescript/src/safe-area/index.d.ts +19 -5
  215. package/lib/typescript/src/safe-area/index.d.ts.map +1 -1
  216. package/lib/typescript/src/theme/ThemeProvider.d.ts +2 -2
  217. package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -1
  218. package/lib/typescript/src/theme/define-theme.d.ts +15 -2
  219. package/lib/typescript/src/theme/define-theme.d.ts.map +1 -1
  220. package/lib/typescript/src/theme/index.d.ts +14 -13
  221. package/lib/typescript/src/theme/index.d.ts.map +1 -1
  222. package/lib/typescript/src/theme/letter-spacing.d.ts +15 -0
  223. package/lib/typescript/src/theme/letter-spacing.d.ts.map +1 -0
  224. package/lib/typescript/src/theme/line-height.d.ts +44 -0
  225. package/lib/typescript/src/theme/line-height.d.ts.map +1 -0
  226. package/lib/typescript/src/theme/native-theme.d.ts +1 -1
  227. package/lib/typescript/src/theme/native-theme.d.ts.map +1 -1
  228. package/lib/typescript/src/theme/types.d.ts +10 -1
  229. package/lib/typescript/src/theme/types.d.ts.map +1 -1
  230. package/llms.txt +2 -1
  231. package/package.json +22 -7
  232. package/src/a11y/tap-target.ts +38 -0
  233. package/src/components/ActionSheet.tsx +25 -15
  234. package/src/components/Alert.tsx +37 -10
  235. package/src/components/Avatar.tsx +12 -4
  236. package/src/components/BottomSheet.tsx +16 -13
  237. package/src/components/Card.tsx +9 -8
  238. package/src/components/Checkbox.tsx +12 -9
  239. package/src/components/EmptyState.tsx +7 -6
  240. package/src/components/Header.tsx +4 -4
  241. package/src/components/Image.tsx +9 -7
  242. package/src/components/Modal.tsx +22 -6
  243. package/src/components/Progress.tsx +5 -4
  244. package/src/components/Radio.tsx +11 -6
  245. package/src/components/Screen.tsx +66 -8
  246. package/src/components/Skeleton.tsx +6 -5
  247. package/src/components/Surface.tsx +5 -4
  248. package/src/components/TextField.tsx +25 -7
  249. package/src/components/Toast.tsx +33 -11
  250. package/src/components/Toggle.tsx +15 -6
  251. package/src/components/action-sheet.styles.ts +1 -1
  252. package/src/components/alert.styles.ts +13 -25
  253. package/src/components/avatar.styles.ts +25 -3
  254. package/src/components/bottom-sheet.styles.ts +1 -1
  255. package/src/components/card.styles.ts +1 -1
  256. package/src/components/card.validate.ts +2 -2
  257. package/src/components/checkbox.styles.ts +6 -3
  258. package/src/components/empty-state.styles.ts +1 -1
  259. package/src/components/header.styles.ts +1 -1
  260. package/src/components/image.styles.ts +1 -1
  261. package/src/components/index.ts +18 -18
  262. package/src/components/modal.styles.ts +11 -2
  263. package/src/components/progress.styles.ts +1 -1
  264. package/src/components/radio.styles.ts +11 -2
  265. package/src/components/safe-area-registry.ts +81 -14
  266. package/src/components/screen.styles.ts +1 -1
  267. package/src/components/skeleton.styles.ts +1 -1
  268. package/src/components/status-colors.ts +59 -0
  269. package/src/components/surface.styles.ts +1 -1
  270. package/src/components/textfield.styles.ts +18 -15
  271. package/src/components/toast.styles.ts +32 -25
  272. package/src/components/toggle.styles.ts +21 -1
  273. package/src/icons/Icon.tsx +13 -8
  274. package/src/icons/icon.styles.ts +38 -3
  275. package/src/icons/index.ts +3 -3
  276. package/src/index.ts +4 -4
  277. package/src/primitives/Button.tsx +28 -14
  278. package/src/primitives/Metric.tsx +5 -4
  279. package/src/primitives/Row.tsx +4 -4
  280. package/src/primitives/Stack.tsx +4 -4
  281. package/src/primitives/Tag.tsx +29 -11
  282. package/src/primitives/Text.tsx +42 -9
  283. package/src/primitives/_internal/focus-ring.tsx +1 -1
  284. package/src/primitives/button.styles.ts +63 -10
  285. package/src/primitives/index.ts +6 -6
  286. package/src/primitives/metric.styles.ts +9 -2
  287. package/src/primitives/row.styles.ts +1 -1
  288. package/src/primitives/stack.styles.ts +1 -1
  289. package/src/primitives/tag.styles.ts +43 -4
  290. package/src/primitives/text.styles.ts +10 -6
  291. package/src/safe-area/index.ts +21 -7
  292. package/src/theme/ThemeProvider.tsx +8 -5
  293. package/src/theme/define-theme.ts +33 -2
  294. package/src/theme/index.ts +18 -16
  295. package/src/theme/letter-spacing.ts +17 -0
  296. package/src/theme/line-height.ts +53 -0
  297. package/src/theme/native-theme.ts +9 -8
  298. package/src/theme/types.ts +10 -1
@@ -13,9 +13,9 @@
13
13
 
14
14
  import type { ReactNode } from "react";
15
15
  import { View, type StyleProp, type ViewStyle } from "react-native";
16
- import { useTheme } from "../theme";
17
- import { CONTRACTS } from "../contracts/contract-types";
18
- import { resolveSurfaceStyle, type SurfaceStyleProps } from "./surface.styles";
16
+ import { useTheme } from "../theme/index.js";
17
+ import { CONTRACTS } from "../contracts/contract-types.js";
18
+ import { resolveSurfaceStyle, type SurfaceStyleProps } from "./surface.styles.js";
19
19
 
20
20
  export interface SurfaceProps extends SurfaceStyleProps {
21
21
  style?: StyleProp<ViewStyle>;
@@ -33,5 +33,6 @@ export function Surface({ bg, radius, elevation, padding, style, testID, childre
33
33
  );
34
34
  }
35
35
 
36
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
36
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
37
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
37
38
  Surface.__contract = CONTRACTS.surface;
@@ -6,7 +6,8 @@
6
6
  * editable=false + accessibilityState.disabled を併せて適用する(recipe description の規約)。
7
7
  * - error 時は errorText を必ずテキストで表示する(FORM_NO_COLOR_ONLY_ERROR = 色だけで伝えない)。
8
8
  * RN には aria-describedby 相当が無いため、(a) errorText を input の accessibilityLabel に合成
9
- * (再フォーカス時に読み上げ)、(b) errorText 側に accessibilityLiveRegion="polite"(Android は
9
+ * (再フォーカス時に読み上げ。合成文は formatErrorAccessibilityLabel で差し替え可能 = i18n フック。
10
+ * 既定は日本語)、(b) errorText 側に accessibilityLiveRegion="polite"(Android は
10
11
  * 出現時に通知。iOS の即時通知が要る画面は submit 時に announceForAccessibility を併用する —
11
12
  * docs/patterns.md 参照)。
12
13
  * - focus は内部 state(onFocus/onBlur)で recipe states.focus の inputStyle 差分
@@ -32,16 +33,16 @@ import {
32
33
  type TextStyle,
33
34
  type ViewStyle,
34
35
  } from "react-native";
35
- import { useTheme } from "../theme";
36
- import { CONTRACTS } from "../contracts/contract-types";
36
+ import { useTheme } from "../theme/index.js";
37
+ import { CONTRACTS } from "../contracts/contract-types.js";
37
38
  import {
38
39
  resolveTextFieldStyle,
39
40
  resolveTextFieldFocusStyle,
40
41
  type TextFieldSize,
41
- } from "./textfield.styles";
42
+ } from "./textfield.styles.js";
42
43
 
43
44
  /**
44
- * Android の EditText は固定 height と組み合わさると既定の縦 padding で文字が
45
+ * Android の EditText は高さ下限(minHeight)と組み合わさると既定の縦 padding で文字が
45
46
  * 上寄り・下端切れになるため、縦 padding を殺して縦センターに揃える。
46
47
  * textAlignVertical は Android 専用、paddingVertical: 0 は iOS の見た目を変えない
47
48
  * (iOS の単一行入力は元々縦センター)。
@@ -65,6 +66,15 @@ export function resolveInputVerticalFix(os: typeof Platform.OS): TextStyle {
65
66
 
66
67
  const INPUT_VERTICAL_FIX = resolveInputVerticalFix(Platform.OS);
67
68
 
69
+ /**
70
+ * error 時の accessibilityLabel 合成の既定(日本語)。
71
+ * props で差し替え可能(formatErrorAccessibilityLabel)。既定値を変えると
72
+ * 既存アプリの VoiceOver 読み上げが変わるので、既定は日本語のまま据え置く。
73
+ */
74
+ function defaultErrorAccessibilityLabel(label: string, errorText: string): string {
75
+ return `${label}。エラー: ${errorText}`;
76
+ }
77
+
68
78
  interface TextFieldProps {
69
79
  /** ラベル(必須。placeholder だけの入力欄を作らせない = FORM_NO_LABEL_OMIT)。 */
70
80
  label: string;
@@ -80,6 +90,12 @@ interface TextFieldProps {
80
90
  helperText?: string;
81
91
  /** variant="error" 時に表示するエラーメッセージ(色だけでなくテキストで伝える)。 */
82
92
  errorText?: string;
93
+ /**
94
+ * error 時に input の accessibilityLabel へ合成する文字列の組み立て(i18n フック)。
95
+ * 既定は日本語の `${label}。エラー: ${errorText}`(既存アプリの読み上げを変えないため据え置き)。
96
+ * 英語アプリなどでは `(label, error) => \`${label}. Error: ${error}\`` を渡す。
97
+ */
98
+ formatErrorAccessibilityLabel?: (label: string, errorText: string) => string;
83
99
  /** キーボード種別(RN TextInput へ透過)。 */
84
100
  keyboardType?: KeyboardTypeOptions;
85
101
  autoCapitalize?: "none" | "sentences" | "words" | "characters";
@@ -104,6 +120,7 @@ export function TextField({
104
120
  size = "medium",
105
121
  helperText,
106
122
  errorText,
123
+ formatErrorAccessibilityLabel = defaultErrorAccessibilityLabel,
107
124
  keyboardType,
108
125
  autoCapitalize,
109
126
  autoCorrect,
@@ -143,7 +160,7 @@ export function TextField({
143
160
  maxLength={maxLength}
144
161
  returnKeyType={returnKeyType}
145
162
  onSubmitEditing={onSubmitEditing}
146
- accessibilityLabel={showError ? `${label}。エラー: ${errorText}` : label}
163
+ accessibilityLabel={showError ? formatErrorAccessibilityLabel(label, errorText) : label}
147
164
  accessibilityState={{ disabled }}
148
165
  onFocus={(e) => {
149
166
  if (!disabled) setFocused(true);
@@ -167,5 +184,6 @@ export function TextField({
167
184
  );
168
185
  }
169
186
 
170
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
187
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
188
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
171
189
  TextField.__contract = CONTRACTS.textfield;
@@ -5,7 +5,9 @@
5
5
  * - presentational component。表示位置・キュー・自動消滅はアプリ層の責務
6
6
  * (entering/visible/dismissing のアニメ state は Phase 外、recipe description)。
7
7
  * - closeButton は契約 anatomy で必須 → onClose を required prop にして型で強制。
8
- * Pressable + テキスト ×(accessibilityLabel="閉じる" 必須)。
8
+ * Pressable + テキスト ×(accessibilityLabel は既定 "閉じる"、closeAccessibilityLabel で差し替え可能)。
9
+ * - action と × は隣接する 2 つの操作要素。当たり判定が重なると押し違いが起きるので、
10
+ * 横 hitSlop を gap の 1/2 に絞る(toast.styles.ts の TOAST_TAP_TARGET)。
9
11
  * - a11y(§2 web→RN mapping): web の role=status + aria-live=polite を
10
12
  * accessibilityLiveRegion="polite"(Android のみ有効)に落とす。
11
13
  * - elevation は theme.elevation.sm(iOS shadow* + Android elevation の複合値)を container に
@@ -17,10 +19,15 @@
17
19
 
18
20
  import { useMemo } from "react";
19
21
  import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
20
- import { Text } from "../primitives/Text";
21
- import { useTheme } from "../theme";
22
- import { CONTRACTS } from "../contracts/contract-types";
23
- import { TOAST_SPEC, resolveToastStyles, type ToastVariant } from "./toast.styles";
22
+ import { Text } from "../primitives/Text.js";
23
+ import { useTheme } from "../theme/index.js";
24
+ import { CONTRACTS } from "../contracts/contract-types.js";
25
+ import {
26
+ TOAST_SPEC,
27
+ TOAST_TAP_TARGET,
28
+ resolveToastStyles,
29
+ type ToastVariant,
30
+ } from "./toast.styles.js";
24
31
 
25
32
  interface ToastProps {
26
33
  /** 通知タイプ(contract variant)。 */
@@ -34,6 +41,11 @@ interface ToastProps {
34
41
  /** あれば message の後にアクションテキスト(onAction とセットで有効)。 */
35
42
  actionLabel?: string;
36
43
  onAction?: () => void;
44
+ /**
45
+ * × ボタンの accessibilityLabel(i18n フック)。既定は日本語 "閉じる"
46
+ * (既存アプリの読み上げを変えないため据え置き)。
47
+ */
48
+ closeAccessibilityLabel?: string;
37
49
  style?: StyleProp<ViewStyle>;
38
50
  testID?: string;
39
51
  }
@@ -45,6 +57,7 @@ export function Toast({
45
57
  onClose,
46
58
  actionLabel,
47
59
  onAction,
60
+ closeAccessibilityLabel = "閉じる",
48
61
  style,
49
62
  testID,
50
63
  }: ToastProps) {
@@ -73,7 +86,9 @@ export function Toast({
73
86
  )}
74
87
  </View>
75
88
  {actionLabel != null && onAction != null && (
76
- <Pressable onPress={onAction} accessibilityRole="button" hitSlop={10}>
89
+ // 横 hitSlop は gap/2(= 6)まで。10 のままだと隣の × と当たり判定が 8pt 重なり、
90
+ // 手前に描かれる × が勝って「元に戻す」が押せない(toast.styles.ts の TOAST_TAP_TARGET 参照)。
91
+ <Pressable onPress={onAction} accessibilityRole="button" hitSlop={TOAST_TAP_TARGET.hitSlop}>
77
92
  <Text
78
93
  variant={TOAST_SPEC.messageFont}
79
94
  weight={TOAST_SPEC.messageWeight}
@@ -83,13 +98,19 @@ export function Toast({
83
98
  </Text>
84
99
  </Pressable>
85
100
  )}
86
- {/* 最小タップターゲット確保: min 24 + hitSlop(Tag removable と同じ手当て)。 */}
101
+ {/* 最小タップターゲット確保: 横 hitSlop を gap/2 に絞るぶん箱を 32pt に広げて 32+6×2 = 44pt。
102
+ 縦は 24 + 10×2 = 44pt(正典パターン)。 */}
87
103
  <Pressable
88
104
  onPress={onClose}
89
105
  accessibilityRole="button"
90
- accessibilityLabel="閉じる"
91
- hitSlop={10}
92
- style={{ minWidth: 24, minHeight: 24, alignItems: "center", justifyContent: "center" }}
106
+ accessibilityLabel={closeAccessibilityLabel}
107
+ hitSlop={TOAST_TAP_TARGET.hitSlop}
108
+ style={{
109
+ minWidth: TOAST_TAP_TARGET.closeMinWidth,
110
+ minHeight: TOAST_TAP_TARGET.closeMinHeight,
111
+ alignItems: "center",
112
+ justifyContent: "center",
113
+ }}
93
114
  >
94
115
  <Text variant="xs" style={{ color: styles.messageStyle.color }}>
95
116
  ×
@@ -99,5 +120,6 @@ export function Toast({
99
120
  );
100
121
  }
101
122
 
102
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
123
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
124
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
103
125
  Toast.__contract = CONTRACTS.toast;
@@ -8,22 +8,24 @@
8
8
  * flex-start / flex-end を切り替える(アニメは Phase 外)。
9
9
  * - disabled: recipe states.disabled の opacity + accessibilityState.disabled + onPress 無効。
10
10
  * - a11y: accessibilityRole="switch" + accessibilityState.checked。label があれば右に表示し、
11
- * Pressable 全体がタップ領域 + accessibilityLabel になる。
11
+ * Pressable 全体がタップ領域 + accessibilityLabel になる。実効タップ標的 44pt は
12
+ * 縦 hitSlop で確保する(TOGGLE_VERTICAL_HIT_SLOP、A11Y_MIN_TAP_TARGET_44)。
12
13
  * - 色・寸法の決定は pure resolver(toggle.styles.ts)に分離。
13
14
  * recipes/app/toggle.recipe.json との機械照合は scripts/lib/toggle-conformance.test.ts が行う。
14
15
  */
15
16
 
16
17
  import { useMemo } from "react";
17
18
  import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
18
- import { Text } from "../primitives/Text";
19
- import { useTheme } from "../theme";
20
- import { CONTRACTS } from "../contracts/contract-types";
19
+ import { Text } from "../primitives/Text.js";
20
+ import { useTheme } from "../theme/index.js";
21
+ import { CONTRACTS } from "../contracts/contract-types.js";
21
22
  import {
22
23
  resolveToggleStyle,
23
24
  TOGGLE_DISABLED_OPACITY,
25
+ TOGGLE_VERTICAL_HIT_SLOP,
24
26
  type ToggleSize,
25
27
  type ToggleVariant,
26
- } from "./toggle.styles";
28
+ } from "./toggle.styles.js";
27
29
 
28
30
  interface ToggleProps {
29
31
  /** 現在値(true=on / false=off。variant はここから暗黙決定)。 */
@@ -61,6 +63,12 @@ export function Toggle({
61
63
  accessibilityRole="switch"
62
64
  accessibilityState={{ checked: value, disabled }}
63
65
  accessibilityLabel={label}
66
+ // track(24/28pt)も label 付きの行(36pt)も 44pt に届かないので縦 hitSlop で補う。
67
+ // 値の根拠は toggle.styles.ts の TOGGLE_VERTICAL_HIT_SLOP。
68
+ hitSlop={{
69
+ top: TOGGLE_VERTICAL_HIT_SLOP[size],
70
+ bottom: TOGGLE_VERTICAL_HIT_SLOP[size],
71
+ }}
64
72
  testID={testID}
65
73
  style={[
66
74
  {
@@ -90,5 +98,6 @@ export function Toggle({
90
98
  );
91
99
  }
92
100
 
93
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
101
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
102
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
94
103
  Toggle.__contract = CONTRACTS.toggle;
@@ -13,7 +13,7 @@
13
13
  * - sheet の下余白(SafeArea)は実装(ActionSheet.tsx)が SafeAreaView で加算する。
14
14
  */
15
15
 
16
- import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
16
+ import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
17
17
 
18
18
  /** overlay の黒 50%(token 外 literal。modal と同値)。 */
19
19
  // eslint-disable-next-line melta/no-raw-color -- overlay の黒 50% は契約上 token 外の literal(recipe description / web の bg-black/50 と同値)
@@ -7,8 +7,7 @@
7
7
  *
8
8
  * - RN では border を持たず bg subtle + text 色で表現する(status token に border 用の中間色が
9
9
  * 無いため。COLOR_ONLY_FORBIDDEN は title/message のテキストと icon slot で担保、recipe description)。
10
- * - dark mode は status.*.subtleDark / textDark 側を解決する。info variant のみ primary 固定
11
- * (dark token が無い既知の割り切り、web と同じ)。
10
+ * - status 色(mode ごとの subtle / text、info の primary 固定)の解決は status-colors.ts が SSOT。
12
11
  */
13
12
 
14
13
  import type {
@@ -19,14 +18,23 @@ import type {
19
18
  RadiusKey,
20
19
  SpacingKey,
21
20
  ThemeMode,
22
- } from "../theme";
21
+ } from "../theme/index.js";
22
+ import { resolveStatusVariantColors, type StatusVariant } from "./status-colors.js";
23
+ import { CANONICAL_TAP_TARGET } from "../a11y/tap-target.js";
23
24
 
24
- export type AlertVariant = "info" | "success" | "warning" | "error";
25
+ /** alert.contract の variant 語彙(status 共通語彙と 1:1)。 */
26
+ export type AlertVariant = StatusVariant;
25
27
 
26
28
  /**
27
29
  * 構成キー(token キー)。component は Text にこのキーをそのまま渡し、resolver は同じキーから
28
30
  * style 値を解決する(component と resolver が別々のキーを持って drift する事故を防ぐ SSOT)。
29
31
  */
32
+ /**
33
+ * × の当たり判定(正典パターン: 視覚 24pt + hitSlop 10 = 実効 44pt)。
34
+ * Alert の × は右端に単独で立つ(Toast と違い隣接する操作要素が無い)ので横も 10 のままでよい。
35
+ */
36
+ export const ALERT_CLOSE_TAP_TARGET = CANONICAL_TAP_TARGET;
37
+
30
38
  export const ALERT_SPEC = {
31
39
  padding: "4",
32
40
  gap: "3",
@@ -66,33 +74,13 @@ export interface AlertStyles {
66
74
  };
67
75
  }
68
76
 
69
- /**
70
- * variant → bg / text 色の解決。
71
- * - info: primary.50 / primary.800 固定(mode 非依存)。
72
- * - success / warning / error: status token(error は status キー上は danger)。
73
- * light は subtleLight / textLight、dark は subtleDark / textDark(recipe description の写像)。
74
- */
75
- export function resolveAlertColors(
76
- theme: NativeTheme,
77
- mode: ThemeMode,
78
- variant: AlertVariant,
79
- ): { bg: string; text: string } {
80
- if (variant === "info") {
81
- return { bg: theme.color.primary["50"], text: theme.color.primary["800"] };
82
- }
83
- const status = theme.color.status[variant === "error" ? "danger" : variant];
84
- return mode === "dark"
85
- ? { bg: status.subtleDark, text: status.textDark }
86
- : { bg: status.subtleLight, text: status.textLight };
87
- }
88
-
89
77
  /** variant → 全 slot の style 解決(alert.recipe styleRefs の 1:1 写像)。 */
90
78
  export function resolveAlertStyles(
91
79
  theme: NativeTheme,
92
80
  mode: ThemeMode,
93
81
  variant: AlertVariant,
94
82
  ): AlertStyles {
95
- const { bg, text } = resolveAlertColors(theme, mode, variant);
83
+ const { bg, text } = resolveStatusVariantColors(theme, mode, variant);
96
84
  return {
97
85
  containerStyle: {
98
86
  backgroundColor: bg,
@@ -6,13 +6,13 @@
6
6
  * 機械照合は scripts/lib/avatar-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
9
+ import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
10
10
 
11
11
  export type AvatarSize = "small" | "medium" | "large";
12
12
  export type AvatarStatus = "online" | "away" | "offline";
13
13
 
14
14
  /** サイズ軸(contract sizes の height と 1:1。recipe sizes の width/height/dotSize/fontSize に対応)。 */
15
- const SIZE_TABLE = {
15
+ export const AVATAR_SIZE_SPEC = {
16
16
  small: { box: 32, dot: 8, fontSize: "xs" },
17
17
  medium: { box: 40, dot: 10, fontSize: "sm" },
18
18
  large: { box: 48, dot: 12, fontSize: "base" },
@@ -21,6 +21,28 @@ const SIZE_TABLE = {
21
21
  /** group の重なり(-spacing.2 相当。負値 token が無いため recipe と同じ literal)。 */
22
22
  export const AVATAR_GROUP_OVERLAP = -8;
23
23
 
24
+ /**
25
+ * initials の文字サイズ倍率の上限(size 別)。
26
+ *
27
+ * Avatar の器は **width = height の円**(recipe sizes の box)で、fontScale で伸びるのは
28
+ * 中身の文字だけ。器は伸びないので、上限を掛けないと OS の文字サイズ拡大で文字が円から
29
+ * はみ出す(円が楕円に見える / 文字が切れる)。
30
+ *
31
+ * 導出は「box ÷ その size が使う fontSize token の lineHeight」を**小数第1位で切り捨て**(安全側):
32
+ * small 32 ÷ 19(xs の lineHeight) = 1.68… → 1.6
33
+ * medium 40 ÷ 26(sm の lineHeight) = 1.53… → 1.5
34
+ * large 48 ÷ 36(base の lineHeight) = 1.33… → 1.3
35
+ *
36
+ * lineHeight(fontSize ではなく)を分母に採るのは、RN が確保する行ボックスの高さが
37
+ * 溢れの実体だから。theme を差し替えても壊れないよう、この導出そのものは
38
+ * avatar-conformance.test.ts が実 theme の値で再検算する。
39
+ */
40
+ export const AVATAR_INITIALS_MAX_FONT_SCALE: Record<AvatarSize, number> = {
41
+ small: 1.6,
42
+ medium: 1.5,
43
+ large: 1.3,
44
+ };
45
+
24
46
  /**
25
47
  * group variant(avatar.recipe の group.style / group.overlapStyle の 1:1 写像)。
26
48
  * container は横並び、2枚目以降の子に overlap(負 margin)を適用する。
@@ -61,7 +83,7 @@ export function resolveAvatarStyle(
61
83
  variant: "image" | "initials",
62
84
  size: AvatarSize,
63
85
  ): AvatarStyleResult {
64
- const s = SIZE_TABLE[size];
86
+ const s = AVATAR_SIZE_SPEC[size];
65
87
  return {
66
88
  container: {
67
89
  width: s.box,
@@ -13,7 +13,7 @@
13
13
  * - sheet の下余白(SafeArea)は実装(BottomSheet.tsx)が SafeAreaView で加算する。
14
14
  */
15
15
 
16
- import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
16
+ import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
17
17
 
18
18
  /** overlay の黒 50%(token 外 literal。modal と同値)。 */
19
19
  // eslint-disable-next-line melta/no-raw-color -- overlay の黒 50% は契約上 token 外の literal(recipe description / web の bg-black/50 と同値)
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { ViewStyle } from "react-native";
10
- import type { NativeTheme, ThemeMode, ElevationKey } from "../theme";
10
+ import type { NativeTheme, ThemeMode, ElevationKey } from "../theme/index.js";
11
11
 
12
12
  export type CardVariant = "basic" | "media" | "action" | "link";
13
13
 
@@ -10,8 +10,8 @@
10
10
  */
11
11
 
12
12
  import { isValidElement } from "react";
13
- import type { CardVariant } from "./card.styles";
14
- import { CARD_INTERACTIVE } from "./card.styles";
13
+ import type { CardVariant } from "./card.styles.js";
14
+ import { CARD_INTERACTIVE } from "./card.styles.js";
15
15
 
16
16
  export interface CardPropsForValidation {
17
17
  variant: CardVariant;
@@ -6,18 +6,21 @@
6
6
  * 機械照合は scripts/lib/checkbox-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { NativeTheme, ThemeMode } from "../theme";
9
+ import type { NativeTheme, ThemeMode } from "../theme/index.js";
10
10
 
11
11
  /**
12
12
  * recipe 由来の literal 値テーブル(token 化されていない数値。conformance が recipe と照合する)。
13
13
  * - box は 20px(web の 16px より大きいのはタッチ前提、recipe description)
14
- * - hitSlop は (44 - box 20) / 2 = 12。行全体 Pressable に足して実タッチ領域 44pt を確保
14
+ * - 実効タップ標的 44pt は行の minHeight で確保(A11Y_MIN_TAP_TARGET_44。規約 10-(b):
15
+ * 背景を持たない行は hitSlop でなく寸法で満たす — 旧実装の四方 hitSlop 12 は行全体
16
+ * Pressable に効くため、縦積みの隣接 Checkbox と当たり判定が最大 24pt 重なっていた
17
+ * (Codex レビュー指摘)。Radio の option 行と同じ手当てに統一)
15
18
  */
16
19
  export const CHECKBOX_SPEC = {
17
20
  boxSize: 20,
18
21
  boxBorderWidth: 2,
19
22
  disabledOpacity: 0.5,
20
- hitSlop: 12,
23
+ rowMinHeight: 44,
21
24
  } as const;
22
25
 
23
26
  /** 見た目(色)に効く状態入力。disabled は opacity のみで色を変えないため含めない。 */
@@ -15,7 +15,7 @@ import type {
15
15
  SemanticColors,
16
16
  SpacingKey,
17
17
  ThemeMode,
18
- } from "../theme";
18
+ } from "../theme/index.js";
19
19
 
20
20
  /**
21
21
  * default variant の構成キー(token キー)。
@@ -6,7 +6,7 @@
6
6
  * 機械照合は scripts/lib/header-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { NativeTheme, ThemeMode } from "../theme";
9
+ import type { NativeTheme, ThemeMode } from "../theme/index.js";
10
10
 
11
11
  export interface HeaderStyle {
12
12
  containerStyle: {
@@ -11,7 +11,7 @@
11
11
  * (token キー限定)指定時のみ borderRadius + overflow:hidden を付ける。
12
12
  */
13
13
 
14
- import type { NativeTheme, RadiusKey } from "../theme";
14
+ import type { NativeTheme, RadiusKey } from "../theme/index.js";
15
15
 
16
16
  /**
17
17
  * ImageStyle / ViewStyle 双方に有効な共通サブセット。
@@ -3,21 +3,21 @@
3
3
  * Surface はライブラリ公開(P1)で正式 export(契約 surface.contract.json + recipe を持つ implemented)。
4
4
  */
5
5
 
6
- export { Surface } from "./Surface";
7
- export { Image } from "./Image";
8
- export { Card } from "./Card";
9
- export { Skeleton } from "./Skeleton";
10
- export { EmptyState } from "./EmptyState";
11
- export { Screen } from "./Screen";
12
- export { Header } from "./Header";
13
- export { Avatar } from "./Avatar";
14
- export { TextField } from "./TextField";
15
- export { Toggle } from "./Toggle";
16
- export { Checkbox } from "./Checkbox";
17
- export { Radio } from "./Radio";
18
- export { Alert } from "./Alert";
19
- export { Toast } from "./Toast";
20
- export { Progress } from "./Progress";
21
- export { Modal } from "./Modal";
22
- export { ActionSheet } from "./ActionSheet";
23
- export { BottomSheet } from "./BottomSheet";
6
+ export { Surface } from "./Surface.js";
7
+ export { Image } from "./Image.js";
8
+ export { Card } from "./Card.js";
9
+ export { Skeleton } from "./Skeleton.js";
10
+ export { EmptyState } from "./EmptyState.js";
11
+ export { Screen } from "./Screen.js";
12
+ export { Header } from "./Header.js";
13
+ export { Avatar } from "./Avatar.js";
14
+ export { TextField } from "./TextField.js";
15
+ export { Toggle } from "./Toggle.js";
16
+ export { Checkbox } from "./Checkbox.js";
17
+ export { Radio } from "./Radio.js";
18
+ export { Alert } from "./Alert.js";
19
+ export { Toast } from "./Toast.js";
20
+ export { Progress } from "./Progress.js";
21
+ export { Modal } from "./Modal.js";
22
+ export { ActionSheet } from "./ActionSheet.js";
23
+ export { BottomSheet } from "./BottomSheet.js";
@@ -13,8 +13,17 @@
13
13
  * - sizes はタブレット以上で効く maxWidth(モバイルは width 100% - overlay padding が先に効く)。
14
14
  */
15
15
 
16
- import type { ElevationStyle, FontWeightValue, NativeTheme, ThemeMode } from "../theme";
17
- import type { SizeOf, VariantOf } from "../contracts/contract-types";
16
+ import type { ElevationStyle, FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
17
+ import type { SizeOf, VariantOf } from "../contracts/contract-types.js";
18
+ import { CANONICAL_TAP_TARGET } from "../a11y/tap-target.js";
19
+
20
+ /**
21
+ * title 行の × の当たり判定(正典パターン: 視覚 24pt + hitSlop 10 = 実効 44pt)。
22
+ *
23
+ * 以前は hitSlop=spacing.2(8)だけで、× のグリフ幅(fontSize.xl の "×" ≈ 13pt)+ 8×2 =
24
+ * 実効 29pt しか無かった(Toast / Alert の × は正典パターン済みだったので Modal だけ取り残されていた)。
25
+ */
26
+ export const MODAL_CLOSE_TAP_TARGET = CANONICAL_TAP_TARGET;
18
27
 
19
28
  export type ModalVariant = VariantOf<"modal">;
20
29
  export type ModalSize = SizeOf<"modal">;
@@ -12,7 +12,7 @@
12
12
  * fill は primary.500 / status.success.base(mode 非依存)。
13
13
  */
14
14
 
15
- import type { NativeTheme, RadiusKey, ThemeMode } from "../theme";
15
+ import type { NativeTheme, RadiusKey, ThemeMode } from "../theme/index.js";
16
16
 
17
17
  export type ProgressVariant = "primary" | "success" | "indeterminate";
18
18
 
@@ -11,17 +11,26 @@
11
11
  * 実装ではこの共通部を variant に関わらず同値で返す(conformance が variant 間一致を照合)。
12
12
  */
13
13
 
14
- import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
14
+ import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
15
+ import { MIN_TAP_TARGET } from "../a11y/tap-target.js";
15
16
 
16
17
  export type RadioVariant = "vertical" | "horizontal" | "card-style";
17
18
 
18
- /** recipe 由来の literal 値テーブル(token 化されていない数値。conformance が recipe と照合する)。 */
19
+ /**
20
+ * recipe 由来の literal 値テーブル(token 化されていない数値。conformance が recipe と照合する)。
21
+ *
22
+ * `optionMinHeight` だけは recipe 外の a11y 手当て(A11Y_MIN_TAP_TARGET_44)。
23
+ * option 行の視覚高さは label(base)の lineHeight 36pt しかなく 44pt に届かないが、
24
+ * option 行は**背景を持たない**(card-style を除く。card は padding 16×2 で既に 68pt)ので
25
+ * hitSlop ではなく minHeight で下げ止める — 見た目は変わらず、hitSlop の重なりも原理的に起きない。
26
+ */
19
27
  export const RADIO_SPEC = {
20
28
  circleSize: 20,
21
29
  circleBorderWidth: 2,
22
30
  dotSize: 10,
23
31
  cardBorderWidth: 1,
24
32
  disabledOpacity: 0.5,
33
+ optionMinHeight: MIN_TAP_TARGET,
25
34
  } as const;
26
35
 
27
36
  export interface RadioGroupResolved {