@wordpress/ui 0.10.0 → 0.11.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 (454) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/CONTRIBUTING.md +149 -0
  3. package/README.md +34 -6
  4. package/build/alert-dialog/context.cjs +6 -1
  5. package/build/alert-dialog/context.cjs.map +2 -2
  6. package/build/alert-dialog/popup.cjs +105 -33
  7. package/build/alert-dialog/popup.cjs.map +4 -4
  8. package/build/alert-dialog/root.cjs +106 -6
  9. package/build/alert-dialog/root.cjs.map +2 -2
  10. package/build/alert-dialog/trigger.cjs +4 -14
  11. package/build/alert-dialog/trigger.cjs.map +3 -3
  12. package/build/alert-dialog/types.cjs.map +1 -1
  13. package/build/badge/badge.cjs +14 -14
  14. package/build/badge/badge.cjs.map +2 -2
  15. package/build/button/button.cjs +16 -6
  16. package/build/button/button.cjs.map +3 -3
  17. package/build/card/content.cjs +4 -4
  18. package/build/card/content.cjs.map +2 -2
  19. package/build/card/full-bleed.cjs +4 -4
  20. package/build/card/full-bleed.cjs.map +2 -2
  21. package/build/card/header.cjs +4 -4
  22. package/build/card/header.cjs.map +2 -2
  23. package/build/card/root.cjs +4 -4
  24. package/build/card/root.cjs.map +2 -2
  25. package/build/card/title.cjs +5 -25
  26. package/build/card/title.cjs.map +4 -4
  27. package/build/collapsible-card/content.cjs +3 -3
  28. package/build/collapsible-card/content.cjs.map +1 -1
  29. package/build/collapsible-card/header.cjs +6 -6
  30. package/build/collapsible-card/header.cjs.map +2 -2
  31. package/build/dialog/footer.cjs +3 -3
  32. package/build/dialog/footer.cjs.map +2 -2
  33. package/build/dialog/header.cjs +3 -3
  34. package/build/dialog/header.cjs.map +2 -2
  35. package/build/dialog/popup.cjs +5 -4
  36. package/build/dialog/popup.cjs.map +2 -2
  37. package/build/dialog/title.cjs +10 -19
  38. package/build/dialog/title.cjs.map +3 -3
  39. package/build/dialog/types.cjs.map +1 -1
  40. package/build/empty-state/actions.cjs +3 -3
  41. package/build/empty-state/actions.cjs.map +2 -2
  42. package/build/empty-state/description.cjs +8 -5
  43. package/build/empty-state/description.cjs.map +2 -2
  44. package/build/empty-state/icon.cjs +3 -3
  45. package/build/empty-state/icon.cjs.map +2 -2
  46. package/build/empty-state/root.cjs +3 -3
  47. package/build/empty-state/root.cjs.map +2 -2
  48. package/build/empty-state/title.cjs +8 -5
  49. package/build/empty-state/title.cjs.map +2 -2
  50. package/build/empty-state/visual.cjs +3 -3
  51. package/build/empty-state/visual.cjs.map +2 -2
  52. package/build/form/primitives/field/description.cjs +17 -4
  53. package/build/form/primitives/field/description.cjs.map +3 -3
  54. package/build/form/primitives/field/details.cjs +4 -4
  55. package/build/form/primitives/field/details.cjs.map +2 -2
  56. package/build/form/primitives/field/label.cjs +8 -8
  57. package/build/form/primitives/field/label.cjs.map +2 -2
  58. package/build/form/primitives/field/root.cjs +2 -2
  59. package/build/form/primitives/field/root.cjs.map +2 -2
  60. package/build/form/primitives/fieldset/description.cjs +20 -4
  61. package/build/form/primitives/fieldset/description.cjs.map +3 -3
  62. package/build/form/primitives/fieldset/details.cjs +3 -3
  63. package/build/form/primitives/fieldset/details.cjs.map +2 -2
  64. package/build/form/primitives/fieldset/legend.cjs +8 -7
  65. package/build/form/primitives/fieldset/legend.cjs.map +2 -2
  66. package/build/form/primitives/input/input.cjs +23 -7
  67. package/build/form/primitives/input/input.cjs.map +3 -3
  68. package/build/form/primitives/input-layout/input-layout.cjs +13 -3
  69. package/build/form/primitives/input-layout/input-layout.cjs.map +3 -3
  70. package/build/form/primitives/input-layout/slot.cjs +3 -3
  71. package/build/form/primitives/input-layout/slot.cjs.map +2 -2
  72. package/build/form/primitives/select/item.cjs +3 -3
  73. package/build/form/primitives/select/item.cjs.map +2 -2
  74. package/build/form/primitives/select/popup.cjs +5 -5
  75. package/build/form/primitives/select/popup.cjs.map +2 -2
  76. package/build/form/primitives/select/trigger.cjs +6 -6
  77. package/build/form/primitives/select/trigger.cjs.map +2 -2
  78. package/build/form/primitives/select/types.cjs.map +1 -1
  79. package/build/form/primitives/textarea/textarea.cjs +20 -1
  80. package/build/form/primitives/textarea/textarea.cjs.map +3 -3
  81. package/build/index.cjs +3 -0
  82. package/build/index.cjs.map +2 -2
  83. package/build/link/link.cjs +16 -6
  84. package/build/link/link.cjs.map +3 -3
  85. package/build/notice/action-button.cjs +3 -3
  86. package/build/notice/action-button.cjs.map +2 -2
  87. package/build/notice/action-link.cjs +3 -3
  88. package/build/notice/action-link.cjs.map +2 -2
  89. package/build/notice/actions.cjs +3 -3
  90. package/build/notice/actions.cjs.map +2 -2
  91. package/build/notice/close-icon.cjs +3 -3
  92. package/build/notice/close-icon.cjs.map +2 -2
  93. package/build/notice/description.cjs +3 -3
  94. package/build/notice/description.cjs.map +2 -2
  95. package/build/notice/root.cjs +3 -3
  96. package/build/notice/root.cjs.map +2 -2
  97. package/build/notice/title.cjs +3 -3
  98. package/build/notice/title.cjs.map +2 -2
  99. package/build/popover/arrow.cjs +94 -0
  100. package/build/popover/arrow.cjs.map +7 -0
  101. package/build/popover/close.cjs +45 -0
  102. package/build/popover/close.cjs.map +7 -0
  103. package/build/popover/context.cjs +76 -0
  104. package/build/popover/context.cjs.map +7 -0
  105. package/build/popover/description.cjs +70 -0
  106. package/build/popover/description.cjs.map +7 -0
  107. package/build/popover/index.cjs +49 -0
  108. package/build/popover/index.cjs.map +7 -0
  109. package/build/popover/popup.cjs +138 -0
  110. package/build/popover/popup.cjs.map +7 -0
  111. package/build/popover/root.cjs +35 -0
  112. package/build/popover/root.cjs.map +7 -0
  113. package/build/popover/title.cjs +56 -0
  114. package/build/popover/title.cjs.map +7 -0
  115. package/build/popover/trigger.cjs +38 -0
  116. package/build/popover/trigger.cjs.map +7 -0
  117. package/build/popover/types.cjs +19 -0
  118. package/build/popover/types.cjs.map +7 -0
  119. package/build/tabs/list.cjs +3 -4
  120. package/build/tabs/list.cjs.map +2 -2
  121. package/build/tabs/panel.cjs +3 -3
  122. package/build/tabs/panel.cjs.map +2 -2
  123. package/build/tabs/tab.cjs +3 -3
  124. package/build/tabs/tab.cjs.map +2 -2
  125. package/build/text/text.cjs +20 -5
  126. package/build/text/text.cjs.map +3 -3
  127. package/build/tooltip/popup.cjs +5 -4
  128. package/build/tooltip/popup.cjs.map +2 -2
  129. package/build/tooltip/root.cjs.map +2 -2
  130. package/build/tooltip/types.cjs.map +1 -1
  131. package/build/utils/types.cjs.map +1 -1
  132. package/build/utils/use-deprioritized-initial-focus.cjs.map +2 -2
  133. package/build/visually-hidden/visually-hidden.cjs.map +2 -2
  134. package/build-module/alert-dialog/context.mjs +6 -1
  135. package/build-module/alert-dialog/context.mjs.map +2 -2
  136. package/build-module/alert-dialog/popup.mjs +107 -33
  137. package/build-module/alert-dialog/popup.mjs.map +4 -4
  138. package/build-module/alert-dialog/root.mjs +113 -7
  139. package/build-module/alert-dialog/root.mjs.map +2 -2
  140. package/build-module/alert-dialog/trigger.mjs +4 -4
  141. package/build-module/alert-dialog/trigger.mjs.map +3 -3
  142. package/build-module/badge/badge.mjs +14 -14
  143. package/build-module/badge/badge.mjs.map +2 -2
  144. package/build-module/button/button.mjs +16 -6
  145. package/build-module/button/button.mjs.map +3 -3
  146. package/build-module/card/content.mjs +4 -4
  147. package/build-module/card/content.mjs.map +2 -2
  148. package/build-module/card/full-bleed.mjs +4 -4
  149. package/build-module/card/full-bleed.mjs.map +2 -2
  150. package/build-module/card/header.mjs +4 -4
  151. package/build-module/card/header.mjs.map +2 -2
  152. package/build-module/card/root.mjs +4 -4
  153. package/build-module/card/root.mjs.map +2 -2
  154. package/build-module/card/title.mjs +5 -15
  155. package/build-module/card/title.mjs.map +3 -3
  156. package/build-module/collapsible-card/content.mjs +3 -3
  157. package/build-module/collapsible-card/content.mjs.map +1 -1
  158. package/build-module/collapsible-card/header.mjs +6 -6
  159. package/build-module/collapsible-card/header.mjs.map +2 -2
  160. package/build-module/dialog/footer.mjs +3 -3
  161. package/build-module/dialog/footer.mjs.map +2 -2
  162. package/build-module/dialog/header.mjs +3 -3
  163. package/build-module/dialog/header.mjs.map +2 -2
  164. package/build-module/dialog/popup.mjs +5 -4
  165. package/build-module/dialog/popup.mjs.map +2 -2
  166. package/build-module/dialog/title.mjs +10 -9
  167. package/build-module/dialog/title.mjs.map +2 -2
  168. package/build-module/empty-state/actions.mjs +3 -3
  169. package/build-module/empty-state/actions.mjs.map +2 -2
  170. package/build-module/empty-state/description.mjs +8 -5
  171. package/build-module/empty-state/description.mjs.map +2 -2
  172. package/build-module/empty-state/icon.mjs +3 -3
  173. package/build-module/empty-state/icon.mjs.map +2 -2
  174. package/build-module/empty-state/root.mjs +3 -3
  175. package/build-module/empty-state/root.mjs.map +2 -2
  176. package/build-module/empty-state/title.mjs +8 -5
  177. package/build-module/empty-state/title.mjs.map +2 -2
  178. package/build-module/empty-state/visual.mjs +3 -3
  179. package/build-module/empty-state/visual.mjs.map +2 -2
  180. package/build-module/form/primitives/field/description.mjs +17 -4
  181. package/build-module/form/primitives/field/description.mjs.map +3 -3
  182. package/build-module/form/primitives/field/details.mjs +4 -4
  183. package/build-module/form/primitives/field/details.mjs.map +2 -2
  184. package/build-module/form/primitives/field/label.mjs +8 -8
  185. package/build-module/form/primitives/field/label.mjs.map +2 -2
  186. package/build-module/form/primitives/field/root.mjs +2 -2
  187. package/build-module/form/primitives/field/root.mjs.map +2 -2
  188. package/build-module/form/primitives/fieldset/description.mjs +20 -4
  189. package/build-module/form/primitives/fieldset/description.mjs.map +3 -3
  190. package/build-module/form/primitives/fieldset/details.mjs +3 -3
  191. package/build-module/form/primitives/fieldset/details.mjs.map +2 -2
  192. package/build-module/form/primitives/fieldset/legend.mjs +8 -7
  193. package/build-module/form/primitives/fieldset/legend.mjs.map +2 -2
  194. package/build-module/form/primitives/input/input.mjs +23 -7
  195. package/build-module/form/primitives/input/input.mjs.map +3 -3
  196. package/build-module/form/primitives/input-layout/input-layout.mjs +13 -3
  197. package/build-module/form/primitives/input-layout/input-layout.mjs.map +3 -3
  198. package/build-module/form/primitives/input-layout/slot.mjs +3 -3
  199. package/build-module/form/primitives/input-layout/slot.mjs.map +2 -2
  200. package/build-module/form/primitives/select/item.mjs +3 -3
  201. package/build-module/form/primitives/select/item.mjs.map +2 -2
  202. package/build-module/form/primitives/select/popup.mjs +5 -5
  203. package/build-module/form/primitives/select/popup.mjs.map +2 -2
  204. package/build-module/form/primitives/select/trigger.mjs +6 -6
  205. package/build-module/form/primitives/select/trigger.mjs.map +2 -2
  206. package/build-module/form/primitives/textarea/textarea.mjs +20 -1
  207. package/build-module/form/primitives/textarea/textarea.mjs.map +3 -3
  208. package/build-module/index.mjs +2 -0
  209. package/build-module/index.mjs.map +2 -2
  210. package/build-module/link/link.mjs +16 -6
  211. package/build-module/link/link.mjs.map +3 -3
  212. package/build-module/notice/action-button.mjs +3 -3
  213. package/build-module/notice/action-button.mjs.map +2 -2
  214. package/build-module/notice/action-link.mjs +3 -3
  215. package/build-module/notice/action-link.mjs.map +2 -2
  216. package/build-module/notice/actions.mjs +3 -3
  217. package/build-module/notice/actions.mjs.map +2 -2
  218. package/build-module/notice/close-icon.mjs +3 -3
  219. package/build-module/notice/close-icon.mjs.map +2 -2
  220. package/build-module/notice/description.mjs +3 -3
  221. package/build-module/notice/description.mjs.map +2 -2
  222. package/build-module/notice/root.mjs +3 -3
  223. package/build-module/notice/root.mjs.map +2 -2
  224. package/build-module/notice/title.mjs +3 -3
  225. package/build-module/notice/title.mjs.map +2 -2
  226. package/build-module/popover/arrow.mjs +59 -0
  227. package/build-module/popover/arrow.mjs.map +7 -0
  228. package/build-module/popover/close.mjs +20 -0
  229. package/build-module/popover/close.mjs.map +7 -0
  230. package/build-module/popover/context.mjs +57 -0
  231. package/build-module/popover/context.mjs.map +7 -0
  232. package/build-module/popover/description.mjs +35 -0
  233. package/build-module/popover/description.mjs.map +7 -0
  234. package/build-module/popover/index.mjs +18 -0
  235. package/build-module/popover/index.mjs.map +7 -0
  236. package/build-module/popover/popup.mjs +105 -0
  237. package/build-module/popover/popup.mjs.map +7 -0
  238. package/build-module/popover/root.mjs +10 -0
  239. package/build-module/popover/root.mjs.map +7 -0
  240. package/build-module/popover/title.mjs +31 -0
  241. package/build-module/popover/title.mjs.map +7 -0
  242. package/build-module/popover/trigger.mjs +13 -0
  243. package/build-module/popover/trigger.mjs.map +7 -0
  244. package/build-module/popover/types.mjs +1 -0
  245. package/build-module/popover/types.mjs.map +7 -0
  246. package/build-module/tabs/list.mjs +3 -4
  247. package/build-module/tabs/list.mjs.map +2 -2
  248. package/build-module/tabs/panel.mjs +3 -3
  249. package/build-module/tabs/panel.mjs.map +2 -2
  250. package/build-module/tabs/tab.mjs +3 -3
  251. package/build-module/tabs/tab.mjs.map +2 -2
  252. package/build-module/text/text.mjs +20 -5
  253. package/build-module/text/text.mjs.map +3 -3
  254. package/build-module/tooltip/popup.mjs +5 -4
  255. package/build-module/tooltip/popup.mjs.map +2 -2
  256. package/build-module/tooltip/root.mjs.map +2 -2
  257. package/build-module/utils/use-deprioritized-initial-focus.mjs.map +2 -2
  258. package/build-module/visually-hidden/visually-hidden.mjs.map +2 -2
  259. package/build-types/alert-dialog/context.d.ts +6 -3
  260. package/build-types/alert-dialog/context.d.ts.map +1 -1
  261. package/build-types/alert-dialog/popup.d.ts.map +1 -1
  262. package/build-types/alert-dialog/root.d.ts +2 -8
  263. package/build-types/alert-dialog/root.d.ts.map +1 -1
  264. package/build-types/alert-dialog/stories/index.story.d.ts +18 -6
  265. package/build-types/alert-dialog/stories/index.story.d.ts.map +1 -1
  266. package/build-types/alert-dialog/trigger.d.ts +2 -1
  267. package/build-types/alert-dialog/trigger.d.ts.map +1 -1
  268. package/build-types/alert-dialog/types.d.ts +61 -26
  269. package/build-types/alert-dialog/types.d.ts.map +1 -1
  270. package/build-types/badge/badge.d.ts.map +1 -1
  271. package/build-types/button/button.d.ts.map +1 -1
  272. package/build-types/card/stories/index.story.d.ts.map +1 -1
  273. package/build-types/card/title.d.ts.map +1 -1
  274. package/build-types/collapsible/panel.d.ts +2 -1
  275. package/build-types/collapsible/panel.d.ts.map +1 -1
  276. package/build-types/collapsible/root.d.ts +2 -1
  277. package/build-types/collapsible/root.d.ts.map +1 -1
  278. package/build-types/collapsible/trigger.d.ts +2 -1
  279. package/build-types/collapsible/trigger.d.ts.map +1 -1
  280. package/build-types/dialog/popup.d.ts.map +1 -1
  281. package/build-types/dialog/stories/index.story.d.ts +8 -0
  282. package/build-types/dialog/stories/index.story.d.ts.map +1 -1
  283. package/build-types/dialog/title.d.ts +12 -2
  284. package/build-types/dialog/title.d.ts.map +1 -1
  285. package/build-types/dialog/types.d.ts +8 -1
  286. package/build-types/dialog/types.d.ts.map +1 -1
  287. package/build-types/empty-state/description.d.ts.map +1 -1
  288. package/build-types/empty-state/title.d.ts.map +1 -1
  289. package/build-types/form/primitives/field/description.d.ts +2 -1
  290. package/build-types/form/primitives/field/description.d.ts.map +1 -1
  291. package/build-types/form/primitives/field/details.d.ts +2 -1
  292. package/build-types/form/primitives/field/details.d.ts.map +1 -1
  293. package/build-types/form/primitives/field/label.d.ts +2 -1
  294. package/build-types/form/primitives/field/label.d.ts.map +1 -1
  295. package/build-types/form/primitives/fieldset/description.d.ts +2 -1
  296. package/build-types/form/primitives/fieldset/description.d.ts.map +1 -1
  297. package/build-types/form/primitives/fieldset/details.d.ts +2 -1
  298. package/build-types/form/primitives/fieldset/details.d.ts.map +1 -1
  299. package/build-types/form/primitives/fieldset/legend.d.ts +2 -1
  300. package/build-types/form/primitives/fieldset/legend.d.ts.map +1 -1
  301. package/build-types/form/primitives/fieldset/root.d.ts +2 -1
  302. package/build-types/form/primitives/fieldset/root.d.ts.map +1 -1
  303. package/build-types/form/primitives/input/input.d.ts.map +1 -1
  304. package/build-types/form/primitives/input-layout/input-layout.d.ts.map +1 -1
  305. package/build-types/form/primitives/select/item.d.ts +6 -2
  306. package/build-types/form/primitives/select/item.d.ts.map +1 -1
  307. package/build-types/form/primitives/select/popup.d.ts +11 -1
  308. package/build-types/form/primitives/select/popup.d.ts.map +1 -1
  309. package/build-types/form/primitives/select/trigger.d.ts +12 -2
  310. package/build-types/form/primitives/select/trigger.d.ts.map +1 -1
  311. package/build-types/form/primitives/select/types.d.ts +13 -3
  312. package/build-types/form/primitives/select/types.d.ts.map +1 -1
  313. package/build-types/form/primitives/textarea/textarea.d.ts.map +1 -1
  314. package/build-types/form/stories/shared.d.ts.map +1 -1
  315. package/build-types/index.d.ts +1 -0
  316. package/build-types/index.d.ts.map +1 -1
  317. package/build-types/link/link.d.ts.map +1 -1
  318. package/build-types/popover/arrow.d.ts +10 -0
  319. package/build-types/popover/arrow.d.ts.map +1 -0
  320. package/build-types/popover/close.d.ts +11 -0
  321. package/build-types/popover/close.d.ts.map +1 -0
  322. package/build-types/popover/context.d.ts +22 -0
  323. package/build-types/popover/context.d.ts.map +1 -0
  324. package/build-types/popover/description.d.ts +10 -0
  325. package/build-types/popover/description.d.ts.map +1 -0
  326. package/build-types/popover/index.d.ts +9 -0
  327. package/build-types/popover/index.d.ts.map +1 -0
  328. package/build-types/popover/popup.d.ts +11 -0
  329. package/build-types/popover/popup.d.ts.map +1 -0
  330. package/build-types/popover/root.d.ts +37 -0
  331. package/build-types/popover/root.d.ts.map +1 -0
  332. package/build-types/popover/stories/index.story.d.ts +211 -0
  333. package/build-types/popover/stories/index.story.d.ts.map +1 -0
  334. package/build-types/popover/stories/utils.d.ts +25 -0
  335. package/build-types/popover/stories/utils.d.ts.map +1 -0
  336. package/build-types/popover/test/index.test.d.ts +2 -0
  337. package/build-types/popover/test/index.test.d.ts.map +1 -0
  338. package/build-types/popover/title.d.ts +20 -0
  339. package/build-types/popover/title.d.ts.map +1 -0
  340. package/build-types/popover/trigger.d.ts +10 -0
  341. package/build-types/popover/trigger.d.ts.map +1 -0
  342. package/build-types/popover/types.d.ts +83 -0
  343. package/build-types/popover/types.d.ts.map +1 -0
  344. package/build-types/tabs/list.d.ts +2 -1
  345. package/build-types/tabs/list.d.ts.map +1 -1
  346. package/build-types/tabs/panel.d.ts +2 -1
  347. package/build-types/tabs/panel.d.ts.map +1 -1
  348. package/build-types/tabs/root.d.ts +2 -1
  349. package/build-types/tabs/root.d.ts.map +1 -1
  350. package/build-types/tabs/tab.d.ts +2 -1
  351. package/build-types/tabs/tab.d.ts.map +1 -1
  352. package/build-types/text/stories/index.story.d.ts +4 -0
  353. package/build-types/text/stories/index.story.d.ts.map +1 -1
  354. package/build-types/text/text.d.ts.map +1 -1
  355. package/build-types/tooltip/popup.d.ts.map +1 -1
  356. package/build-types/tooltip/root.d.ts +9 -8
  357. package/build-types/tooltip/root.d.ts.map +1 -1
  358. package/build-types/tooltip/stories/usage-guidelines.story.d.ts +21 -0
  359. package/build-types/tooltip/stories/usage-guidelines.story.d.ts.map +1 -0
  360. package/build-types/tooltip/types.d.ts +4 -0
  361. package/build-types/tooltip/types.d.ts.map +1 -1
  362. package/build-types/utils/types.d.ts +6 -2
  363. package/build-types/utils/types.d.ts.map +1 -1
  364. package/build-types/utils/use-deprioritized-initial-focus.d.ts +6 -5
  365. package/build-types/utils/use-deprioritized-initial-focus.d.ts.map +1 -1
  366. package/build-types/visually-hidden/stories/index.story.d.ts +7 -0
  367. package/build-types/visually-hidden/stories/index.story.d.ts.map +1 -1
  368. package/build-types/visually-hidden/visually-hidden.d.ts +34 -0
  369. package/build-types/visually-hidden/visually-hidden.d.ts.map +1 -1
  370. package/package.json +12 -12
  371. package/src/alert-dialog/context.tsx +12 -4
  372. package/src/alert-dialog/popup.tsx +91 -33
  373. package/src/alert-dialog/root.tsx +191 -13
  374. package/src/alert-dialog/stories/index.story.tsx +116 -65
  375. package/src/alert-dialog/style.module.css +11 -0
  376. package/src/alert-dialog/test/index.test.tsx +1319 -347
  377. package/src/alert-dialog/trigger.tsx +2 -2
  378. package/src/alert-dialog/types.ts +64 -28
  379. package/src/badge/badge.tsx +11 -14
  380. package/src/badge/style.module.css +0 -4
  381. package/src/button/button.tsx +2 -0
  382. package/src/button/style.module.css +7 -3
  383. package/src/card/stories/index.story.tsx +4 -5
  384. package/src/card/style.module.css +1 -5
  385. package/src/card/test/index.test.tsx +17 -1
  386. package/src/card/title.tsx +6 -5
  387. package/src/collapsible-card/stories/index.story.tsx +5 -5
  388. package/src/dialog/popup.tsx +2 -1
  389. package/src/dialog/stories/index.story.tsx +33 -0
  390. package/src/dialog/style.module.css +13 -9
  391. package/src/dialog/test/index.test.tsx +63 -4
  392. package/src/dialog/title.tsx +21 -9
  393. package/src/dialog/types.ts +9 -1
  394. package/src/empty-state/description.tsx +6 -2
  395. package/src/empty-state/style.module.css +1 -1
  396. package/src/empty-state/test/description.test.tsx +13 -0
  397. package/src/empty-state/test/title.test.tsx +13 -0
  398. package/src/empty-state/title.tsx +9 -3
  399. package/src/form/primitives/field/description.tsx +6 -1
  400. package/src/form/primitives/field/details.tsx +4 -2
  401. package/src/form/primitives/field/label.tsx +9 -5
  402. package/src/form/primitives/field/root.tsx +2 -2
  403. package/src/form/primitives/field/test/index.test.tsx +11 -0
  404. package/src/form/primitives/fieldset/description.tsx +9 -1
  405. package/src/form/primitives/fieldset/legend.tsx +9 -4
  406. package/src/form/primitives/fieldset/test/index.test.tsx +22 -0
  407. package/src/form/primitives/input/input.tsx +6 -1
  408. package/src/form/primitives/input/style.module.css +4 -0
  409. package/src/form/primitives/input-layout/input-layout.tsx +2 -0
  410. package/src/form/primitives/input-layout/style.module.css +3 -3
  411. package/src/form/primitives/select/popup.tsx +5 -2
  412. package/src/form/primitives/select/test/index.test.tsx +60 -1
  413. package/src/form/primitives/select/types.ts +14 -4
  414. package/src/form/primitives/textarea/textarea.tsx +10 -1
  415. package/src/form/stories/shared.tsx +4 -2
  416. package/src/index.ts +1 -0
  417. package/src/link/link.tsx +2 -0
  418. package/src/link/style.module.css +11 -1
  419. package/src/notice/style.module.css +5 -5
  420. package/src/popover/arrow.tsx +49 -0
  421. package/src/popover/close.tsx +24 -0
  422. package/src/popover/context.tsx +100 -0
  423. package/src/popover/description.tsx +29 -0
  424. package/src/popover/index.ts +9 -0
  425. package/src/popover/popup.tsx +106 -0
  426. package/src/popover/root.tsx +41 -0
  427. package/src/popover/stories/index.story.tsx +1315 -0
  428. package/src/popover/stories/utils.tsx +91 -0
  429. package/src/popover/style.module.css +64 -0
  430. package/src/popover/test/index.test.tsx +727 -0
  431. package/src/popover/title.tsx +47 -0
  432. package/src/popover/trigger.tsx +17 -0
  433. package/src/popover/types.ts +113 -0
  434. package/src/tabs/list.tsx +0 -1
  435. package/src/tabs/style.module.css +2 -2
  436. package/src/text/stories/index.story.tsx +4 -2
  437. package/src/text/style.module.css +62 -36
  438. package/src/text/test/index.test.tsx +1 -4
  439. package/src/text/text.tsx +8 -1
  440. package/src/tooltip/popup.tsx +2 -1
  441. package/src/tooltip/root.tsx +9 -8
  442. package/src/tooltip/stories/usage-guidelines.mdx +91 -0
  443. package/src/tooltip/stories/usage-guidelines.story.tsx +119 -0
  444. package/src/tooltip/style.module.css +2 -2
  445. package/src/tooltip/test/index.test.tsx +61 -0
  446. package/src/tooltip/types.ts +5 -0
  447. package/src/utils/css/field.module.css +12 -9
  448. package/src/utils/css/focus.module.css +7 -5
  449. package/src/utils/css/global-css-defense.module.css +117 -0
  450. package/src/utils/css/item-popup.module.css +2 -2
  451. package/src/utils/types.ts +7 -2
  452. package/src/utils/use-deprioritized-initial-focus.ts +5 -4
  453. package/src/visually-hidden/stories/index.story.tsx +25 -0
  454. package/src/visually-hidden/visually-hidden.tsx +34 -0
@@ -1,6 +1,6 @@
1
+ import { AlertDialog as _AlertDialog } from '@base-ui/react/alert-dialog';
1
2
  import { forwardRef } from '@wordpress/element';
2
3
 
3
- import * as Dialog from '../dialog';
4
4
  import type { TriggerProps } from './types';
5
5
 
6
6
  /**
@@ -8,7 +8,7 @@ import type { TriggerProps } from './types';
8
8
  */
9
9
  const Trigger = forwardRef< HTMLButtonElement, TriggerProps >(
10
10
  function AlertDialogTrigger( props, ref ) {
11
- return <Dialog.Trigger ref={ ref } { ...props } />;
11
+ return <_AlertDialog.Trigger ref={ ref } { ...props } />;
12
12
  }
13
13
  );
14
14
 
@@ -1,7 +1,18 @@
1
1
  import type { AlertDialog as _AlertDialog } from '@base-ui/react/alert-dialog';
2
2
  import type { ReactNode } from 'react';
3
3
 
4
- import type { TriggerProps as DialogTriggerProps } from '../dialog/types';
4
+ import type { ComponentProps } from '../utils/types';
5
+
6
+ /**
7
+ * The return type of `onConfirm`. Return `void` (or nothing) to auto-close
8
+ * the dialog after the confirm handler completes. Return `{ close: false }`
9
+ * to keep the dialog open (e.g. for validation errors).
10
+ *
11
+ * Return `{ error: '...' }` to display a built-in error message below the
12
+ * action buttons. When `error` is provided, the dialog stays open
13
+ * regardless of the `close` value.
14
+ */
15
+ export type ConfirmResult = void | { close?: boolean; error?: string };
5
16
 
6
17
  export interface RootProps
7
18
  extends Pick<
@@ -14,6 +25,48 @@ export interface RootProps
14
25
  */
15
26
  children: ReactNode;
16
27
 
28
+ /**
29
+ * Callback fired when the user confirms the action.
30
+ *
31
+ * - Synchronous handlers: the dialog closes immediately after the
32
+ * handler returns.
33
+ * - Async handlers: the dialog enters a "pending" state (buttons
34
+ * disabled, spinner shown on the confirm button) until the promise
35
+ * settles.
36
+ *
37
+ * Return `{ close: false }` to keep the dialog open after the handler
38
+ * completes (e.g. for server-side validation). Return `void` or
39
+ * `{ close: true }` to close the dialog (the default).
40
+ *
41
+ * Return `{ error: '...' }` to show a built-in error message below
42
+ * the action buttons. The dialog stays open regardless of the `close`
43
+ * value. The error is announced to screen readers and is automatically
44
+ * cleared on the next confirm attempt or when the dialog reopens.
45
+ *
46
+ * If the promise rejects (or the handler throws) without returning an
47
+ * `error`, the dialog stays open and returns to idle without showing
48
+ * a visible error message. The error is logged to the console.
49
+ * To show a user-facing message on failure, catch the error and
50
+ * return `{ close: false, error: '...' }`.
51
+ */
52
+ onConfirm?: () => ConfirmResult | Promise< ConfirmResult >;
53
+ }
54
+
55
+ export interface TriggerProps extends ComponentProps< 'button' > {
56
+ /**
57
+ * The content to be rendered inside the component.
58
+ */
59
+ children?: ReactNode;
60
+ }
61
+
62
+ export interface PopupProps
63
+ extends ComponentProps< 'div' >,
64
+ Pick< _AlertDialog.Popup.Props, 'initialFocus' | 'finalFocus' > {
65
+ /**
66
+ * A parent element to render the portal into.
67
+ */
68
+ container?: _AlertDialog.Portal.Props[ 'container' ];
69
+
17
70
  /**
18
71
  * The semantic intent of the dialog, which determines its styling.
19
72
  *
@@ -28,26 +81,27 @@ export interface RootProps
28
81
  * @default 'default'
29
82
  */
30
83
  intent?: 'default' | 'irreversible';
31
- }
32
-
33
- export type TriggerProps = DialogTriggerProps;
34
84
 
35
- export interface PopupProps {
36
85
  /**
37
86
  * The title displayed in the dialog header. This serves as both the
38
- * visible heading and the accessible label for the dialog.
87
+ * visible heading and the accessible label (`aria-labelledby`) for the
88
+ * dialog. Must be a plain string to ensure a predictable accessible name.
39
89
  */
40
90
  title: string;
41
91
 
42
92
  /**
43
- * The message content displayed in the dialog body.
93
+ * An optional description displayed below the title. Rendered using
94
+ * Base UI's `AlertDialog.Description` for proper `aria-describedby`
95
+ * association with the dialog. Must be a plain string to ensure a
96
+ * predictable accessible description.
44
97
  */
45
- children: ReactNode;
98
+ description?: string;
46
99
 
47
100
  /**
48
- * Callback fired when the user confirms the action.
101
+ * Optional body content displayed between the description and the
102
+ * action buttons. Use for supplementary details or form fields.
49
103
  */
50
- onConfirm: () => void;
104
+ children?: ReactNode;
51
105
 
52
106
  /**
53
107
  * Custom text for the confirm button.
@@ -62,22 +116,4 @@ export interface PopupProps {
62
116
  * @default 'Cancel'
63
117
  */
64
118
  cancelButtonText?: string;
65
-
66
- /**
67
- * Whether the confirm action is in a loading state (e.g. an async
68
- * operation is in progress). When `true`, the confirm button shows a
69
- * spinner and the cancel button is disabled.
70
- *
71
- * **Important:** Passing this prop — even as `false` — opts into
72
- * manual-close mode: the confirm button will no longer auto-close the
73
- * dialog. The consumer is responsible for setting `open={false}` when
74
- * the operation completes. Omit the prop entirely for the default
75
- * auto-close-on-confirm behavior.
76
- *
77
- * To implement an async confirm flow, use controlled mode
78
- * (`open` / `onOpenChange`) and manage the loading state externally:
79
- * prevent closing in `onOpenChange` while loading, and set
80
- * `open={false}` once the operation completes.
81
- */
82
- loading?: boolean;
83
119
  }
@@ -1,29 +1,26 @@
1
- import { useRender, mergeProps } from '@base-ui/react';
2
1
  import clsx from 'clsx';
3
2
  import { forwardRef } from '@wordpress/element';
4
3
  import { type BadgeProps } from './types';
5
4
  import styles from './style.module.css';
5
+ import { Text } from '../text';
6
6
 
7
7
  /**
8
8
  * A badge component for displaying labels with semantic intent.
9
9
  */
10
10
  export const Badge = forwardRef< HTMLSpanElement, BadgeProps >( function Badge(
11
- { children, intent = 'none', render, className, ...props },
11
+ { intent = 'none', className, ...props },
12
12
  ref
13
13
  ) {
14
- const element = useRender( {
15
- render,
16
- defaultTagName: 'span',
17
- ref,
18
- props: mergeProps< 'span' >( props, {
19
- className: clsx(
14
+ return (
15
+ <Text
16
+ ref={ ref }
17
+ className={ clsx(
20
18
  styles.badge,
21
19
  styles[ `is-${ intent }-intent` ],
22
20
  className
23
- ),
24
- children,
25
- } ),
26
- } );
27
-
28
- return element;
21
+ ) }
22
+ { ...props }
23
+ variant="body-sm"
24
+ />
25
+ );
29
26
  } );
@@ -5,10 +5,6 @@
5
5
  padding-inline: var(--wpds-dimension-padding-sm);
6
6
  padding-block: var(--wpds-dimension-padding-xs);
7
7
  border-radius: var(--wpds-border-radius-lg);
8
- font-family: var(--wpds-font-family-body);
9
- font-size: var(--wpds-font-size-sm);
10
- font-weight: var(--wpds-font-weight-regular);
11
- line-height: var(--wpds-font-line-height-xs);
12
8
  }
13
9
 
14
10
  .is-high-intent {
@@ -7,6 +7,7 @@ import { type ButtonProps } from './types';
7
7
  import styles from './style.module.css';
8
8
  import resetStyles from '../utils/css/resets.module.css';
9
9
  import focusStyles from '../utils/css/focus.module.css';
10
+ import defenseStyles from '../utils/css/global-css-defense.module.css';
10
11
 
11
12
  export const Button = forwardRef< HTMLButtonElement, ButtonProps >(
12
13
  function Button(
@@ -25,6 +26,7 @@ export const Button = forwardRef< HTMLButtonElement, ButtonProps >(
25
26
  ref
26
27
  ) {
27
28
  const mergedClassName = clsx(
29
+ defenseStyles.button,
28
30
  resetStyles[ 'box-sizing' ],
29
31
  focusStyles[ 'outset-ring--focus-except-active' ],
30
32
  variant !== 'unstyled' && styles.button,
@@ -20,7 +20,7 @@
20
20
  --wp-ui-button-padding-inline: var(--wpds-dimension-padding-md);
21
21
  --wp-ui-button-height: 40px;
22
22
  --wp-ui-button-aspect-ratio: auto; /* Useful for overrides such as icon buttons */
23
- --wp-ui-button-font-size: var(--wpds-font-size-md);
23
+ --wp-ui-button-font-size: var(--wpds-typography-font-size-md);
24
24
  --wp-ui-button-min-width: calc(4ch + 2 * var(--wp-ui-button-padding-inline));
25
25
 
26
26
  /* by default, borders have the same color as the background */
@@ -28,6 +28,10 @@
28
28
  --wp-ui-button-border-color-active: var(--wp-ui-button-background-color-active);
29
29
  --wp-ui-button-border-color-disabled: var(--wp-ui-button-background-color-disabled);
30
30
 
31
+ --_gcd-button-font-family: var(--wpds-typography-font-family-body);
32
+ --_gcd-button-font-size: var(--wp-ui-button-font-size);
33
+ --_gcd-button-font-weight: var(--wp-ui-button-font-weight);
34
+
31
35
  /* Styles */
32
36
  position: relative;
33
37
  display: inline-flex;
@@ -44,10 +48,10 @@
44
48
  border-radius: var(--wpds-border-radius-sm);
45
49
  background-color: var(--wp-ui-button-background-color);
46
50
  background-clip: padding-box;
47
- font-family: var(--wpds-font-family-body);
51
+ font-family: var(--wpds-typography-font-family-body);
48
52
  font-size: var(--wp-ui-button-font-size);
49
53
  font-weight: var(--wp-ui-button-font-weight);
50
- line-height: var(--wpds-font-line-height-sm);
54
+ line-height: var(--wpds-typography-line-height-sm);
51
55
  text-decoration: none;
52
56
  color: var(--wp-ui-button-foreground-color);
53
57
  cursor: var(--wpds-cursor-control);
@@ -10,10 +10,10 @@ function Text( { children }: { children: React.ReactNode } ) {
10
10
  <p
11
11
  style={ {
12
12
  margin: 0,
13
- fontFamily: 'var(--wpds-font-family-body)',
14
- fontSize: 'var(--wpds-font-size-md)',
15
- fontWeight: 'var(--wpds-font-weight-regular)',
16
- lineHeight: 'var(--wpds-font-line-height-sm)',
13
+ fontFamily: 'var(--wpds-typography-font-family-body)',
14
+ fontSize: 'var(--wpds-typography-font-size-md)',
15
+ fontWeight: 'var(--wpds-typography-font-weight-regular)',
16
+ lineHeight: 'var(--wpds-typography-line-height-sm)',
17
17
  textWrap: 'pretty',
18
18
  color: 'var(--wpds-color-fg-content-neutral-weak)',
19
19
  } }
@@ -116,7 +116,6 @@ export const CustomSemantics: Story = {
116
116
  children: (
117
117
  <>
118
118
  <Card.Header>
119
- { /* eslint-disable-next-line jsx-a11y/heading-has-content -- content provided via render prop */ }
120
119
  <Card.Title render={ <h2 /> }>Section heading</Card.Title>
121
120
  </Card.Header>
122
121
  <Card.Content>
@@ -12,6 +12,7 @@
12
12
  border-radius: var(--wpds-border-radius-lg);
13
13
  overflow: clip;
14
14
  background-color: var(--wpds-color-bg-surface-neutral-strong);
15
+ color: var(--wpds-color-fg-content-neutral);
15
16
  }
16
17
 
17
18
  /* Padding is applied to the individual header/content elements to enable
@@ -37,9 +38,4 @@
37
38
  margin-inline: calc(-1 * var(--wp-ui-card-padding));
38
39
  width: calc(100% + 2 * var(--wp-ui-card-padding));
39
40
  }
40
-
41
- .title {
42
- margin: 0;
43
- color: var(--wpds-color-fg-content-neutral);
44
- }
45
41
  }
@@ -82,7 +82,6 @@ describe( 'Card', () => {
82
82
  render(
83
83
  <Card.Root>
84
84
  <Card.Header>
85
- { /* eslint-disable-next-line jsx-a11y/heading-has-content -- content provided via render prop */ }
86
85
  <Card.Title render={ <h2 /> }>Heading</Card.Title>
87
86
  </Card.Header>
88
87
  </Card.Root>
@@ -92,5 +91,22 @@ describe( 'Card', () => {
92
91
  screen.getByRole( 'heading', { level: 2, name: 'Heading' } )
93
92
  ).toBeVisible();
94
93
  } );
94
+
95
+ it( 'forwards ref to custom Title render element', () => {
96
+ const titleRef = createRef< HTMLHeadingElement >();
97
+
98
+ render(
99
+ <Card.Root>
100
+ <Card.Header>
101
+ <Card.Title ref={ titleRef } render={ <h3 /> }>
102
+ Heading
103
+ </Card.Title>
104
+ </Card.Header>
105
+ </Card.Root>
106
+ );
107
+
108
+ expect( titleRef.current ).toBeInstanceOf( HTMLHeadingElement );
109
+ expect( titleRef.current?.tagName ).toBe( 'H3' );
110
+ } );
95
111
  } );
96
112
  } );
@@ -1,20 +1,21 @@
1
1
  import { forwardRef } from '@wordpress/element';
2
- import clsx from 'clsx';
3
2
  import { Text } from '../text';
4
- import styles from './style.module.css';
5
3
  import type { TitleProps } from './types';
6
4
 
5
+ const DEFAULT_TAG = <div />;
6
+
7
7
  /**
8
8
  * The title for a card. Renders as a `<div>` by default — use the `render`
9
9
  * prop to swap in a semantic heading element when appropriate.
10
10
  */
11
11
  export const Title = forwardRef< HTMLDivElement, TitleProps >(
12
- function CardTitle( { className, render, children, ...props }, ref ) {
12
+ function CardTitle( { render = DEFAULT_TAG, children, ...props }, ref ) {
13
13
  return (
14
14
  <Text
15
+ ref={ ref }
15
16
  variant="heading-lg"
16
- render={ render ?? <div ref={ ref } { ...props } /> }
17
- className={ clsx( styles.title, className ) }
17
+ render={ render }
18
+ { ...props }
18
19
  >
19
20
  { children }
20
21
  </Text>
@@ -12,10 +12,10 @@ function Text( { children }: { children: React.ReactNode } ) {
12
12
  <p
13
13
  style={ {
14
14
  margin: 0,
15
- fontFamily: 'var(--wpds-font-family-body)',
16
- fontSize: 'var(--wpds-font-size-md)',
17
- fontWeight: 'var(--wpds-font-weight-regular)',
18
- lineHeight: 'var(--wpds-font-line-height-sm)',
15
+ fontFamily: 'var(--wpds-typography-font-family-body)',
16
+ fontSize: 'var(--wpds-typography-font-size-md)',
17
+ fontWeight: 'var(--wpds-typography-font-weight-regular)',
18
+ lineHeight: 'var(--wpds-typography-line-height-sm)',
19
19
  textWrap: 'pretty',
20
20
  color: 'var(--wpds-color-fg-content-neutral-weak)',
21
21
  } }
@@ -183,7 +183,7 @@ export const WithHeaderDescription: Story = {
183
183
  <CollapsibleCard.HeaderDescription>
184
184
  <span
185
185
  style={ {
186
- fontSize: 'var(--wpds-font-size-sm)',
186
+ fontSize: 'var(--wpds-typography-font-size-sm)',
187
187
  color: 'var(--wpds-color-fg-content-neutral-weak)',
188
188
  } }
189
189
  >
@@ -24,6 +24,7 @@ const CLOSE_ICON_ATTR = 'data-wp-ui-dialog-close-icon';
24
24
  const Popup = forwardRef< HTMLDivElement, PopupProps >( function DialogPopup(
25
25
  {
26
26
  className,
27
+ container,
27
28
  size = 'medium',
28
29
  initialFocus,
29
30
  finalFocus,
@@ -39,7 +40,7 @@ const Popup = forwardRef< HTMLDivElement, PopupProps >( function DialogPopup(
39
40
  const mergedRef = useMergeRefs( [ ref, popupRef ] );
40
41
 
41
42
  return (
42
- <_Dialog.Portal>
43
+ <_Dialog.Portal container={ container }>
43
44
  <_Dialog.Backdrop className={ styles.backdrop } />
44
45
  <ThemeProvider>
45
46
  <_Dialog.Popup
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useId, useState } from '@wordpress/element';
3
3
  import type { ComponentProps } from 'react';
4
+ import { VisuallyHidden } from '../../visually-hidden';
4
5
  import * as Dialog from '../index';
5
6
 
6
7
  const meta: Meta< typeof Dialog.Root > = {
@@ -161,3 +162,35 @@ export const WithCustomZIndex: Story = {
161
162
  ..._Default,
162
163
  name: 'With Custom z-index',
163
164
  };
165
+
166
+ /**
167
+ * A dialog with a visually hidden title. The title is still present in the
168
+ * DOM for `aria-labelledby`, but is not visible to sighted users.
169
+ *
170
+ * Use `<VisuallyHidden render={ <Dialog.Title /> }>` so that `Dialog.Title`
171
+ * keeps its `<h2>` element while being visually hidden.
172
+ */
173
+ export const WithVisuallyHiddenTitle: Story = {
174
+ args: {
175
+ children: (
176
+ <>
177
+ <Dialog.Trigger>Open Dialog</Dialog.Trigger>
178
+ <Dialog.Popup>
179
+ <Dialog.Header>
180
+ <VisuallyHidden render={ <Dialog.Title /> }>
181
+ Accessible dialog heading
182
+ </VisuallyHidden>
183
+ <Dialog.CloseIcon />
184
+ </Dialog.Header>
185
+ <p>
186
+ This dialog has a visually hidden title. Inspect the DOM
187
+ or use a screen reader to verify the heading is present.
188
+ </p>
189
+ <Dialog.Footer>
190
+ <Dialog.Action>Got it</Dialog.Action>
191
+ </Dialog.Footer>
192
+ </Dialog.Popup>
193
+ </>
194
+ ),
195
+ },
196
+ };
@@ -37,9 +37,9 @@
37
37
  border-radius: var(--wpds-border-radius-lg);
38
38
  box-shadow: var(--wpds-elevation-lg);
39
39
  overflow: auto;
40
- font-family: var(--wpds-font-family-body);
41
- font-size: var(--wpds-font-size-md);
42
- line-height: var(--wpds-font-line-height-md);
40
+ font-family: var(--wpds-typography-font-family-body);
41
+ font-size: var(--wpds-typography-font-size-md);
42
+ line-height: var(--wpds-typography-line-height-md);
43
43
  color: var(--wpds-color-fg-content-neutral);
44
44
 
45
45
  &[data-starting-style],
@@ -92,17 +92,21 @@
92
92
 
93
93
  .header {
94
94
  display: flex;
95
- justify-content: space-between;
96
95
  align-items: center;
96
+ gap: var(--wpds-dimension-gap-sm);
97
97
  margin-bottom: var(--wpds-dimension-gap-lg);
98
98
  }
99
99
 
100
100
  .title {
101
- margin: 0;
102
- font-size: var(--wpds-font-size-xl);
103
- font-weight: var(--wpds-font-weight-medium);
104
- line-height: var(--wpds-font-line-height-xl);
105
- color: var(--wpds-color-fg-content-neutral);
101
+ margin-inline-end: auto;
102
+
103
+ /* Workaround: the GCD shorthand `margin` overrides the longhand above.
104
+ * Keep both so the layout survives GCD removal. */
105
+ --_gcd-heading-margin: 0 auto 0 0;
106
+
107
+ &:dir(rtl) {
108
+ --_gcd-heading-margin: 0 0 0 auto;
109
+ }
106
110
  }
107
111
 
108
112
  .footer {
@@ -203,10 +203,8 @@ describe( 'Dialog', () => {
203
203
  <Dialog.Trigger>Open Dialog</Dialog.Trigger>
204
204
  <Dialog.Popup>
205
205
  <Dialog.Header>
206
- { /* @ts-expect-error this is just for test purposes */ }
207
- <Dialog.Title>
208
- { /* Empty title */ }
209
- </Dialog.Title>
206
+ { /* Empty title */ }
207
+ <Dialog.Title />
210
208
  </Dialog.Header>
211
209
  <p>Content with empty title</p>
212
210
  <Dialog.Footer>
@@ -423,4 +421,65 @@ describe( 'Dialog', () => {
423
421
  expect( customFocus ).toHaveBeenCalled();
424
422
  } );
425
423
  } );
424
+
425
+ describe( 'container', () => {
426
+ it( 'should render inside the container when provided', async () => {
427
+ const user = userEvent.setup();
428
+ const containerRef = createRef< HTMLDivElement >();
429
+
430
+ render(
431
+ <div data-testid="wrapper">
432
+ <Dialog.Root>
433
+ <Dialog.Trigger>Open</Dialog.Trigger>
434
+ <div
435
+ ref={ containerRef }
436
+ data-testid="custom-container"
437
+ />
438
+ <Dialog.Popup container={ containerRef }>
439
+ <Dialog.Header>
440
+ <Dialog.Title>Title</Dialog.Title>
441
+ </Dialog.Header>
442
+ Dialog content
443
+ </Dialog.Popup>
444
+ </Dialog.Root>
445
+ </div>
446
+ );
447
+
448
+ await user.click( screen.getByRole( 'button', { name: 'Open' } ) );
449
+
450
+ const content = await screen.findByText( 'Dialog content' );
451
+ expect( content ).toBeVisible();
452
+
453
+ expect( screen.getByTestId( 'custom-container' ) ).toContainElement(
454
+ content
455
+ );
456
+ } );
457
+
458
+ it( 'should render with a portal by default', async () => {
459
+ const user = userEvent.setup();
460
+
461
+ render(
462
+ <div data-testid="wrapper">
463
+ <Dialog.Root>
464
+ <Dialog.Trigger>Open</Dialog.Trigger>
465
+ <Dialog.Popup>
466
+ <Dialog.Header>
467
+ <Dialog.Title>Title</Dialog.Title>
468
+ </Dialog.Header>
469
+ Portal content
470
+ </Dialog.Popup>
471
+ </Dialog.Root>
472
+ </div>
473
+ );
474
+
475
+ await user.click( screen.getByRole( 'button', { name: 'Open' } ) );
476
+
477
+ const content = await screen.findByText( 'Portal content' );
478
+ expect( content ).toBeVisible();
479
+
480
+ expect( screen.getByTestId( 'wrapper' ) ).not.toContainElement(
481
+ content
482
+ );
483
+ } );
484
+ } );
426
485
  } );
@@ -1,7 +1,7 @@
1
1
  import { Dialog as _Dialog } from '@base-ui/react/dialog';
2
- import clsx from 'clsx';
3
2
  import { useMergeRefs } from '@wordpress/compose';
4
3
  import { forwardRef, useLayoutEffect, useRef } from '@wordpress/element';
4
+ import { Text } from '../text';
5
5
  import { useDialogValidationContext } from './context';
6
6
  import styles from './style.module.css';
7
7
  import type { TitleProps } from './types';
@@ -11,11 +11,21 @@ import type { TitleProps } from './types';
11
11
  * and serves as both the visible heading and the accessible label for
12
12
  * the dialog.
13
13
  *
14
- * Base UI's Dialog.Title renders an `<h2>` by default. Use the `render` prop
15
- * to customize the element if needed.
14
+ * **Required** every dialog must include a `Dialog.Title`, even if
15
+ * visually hidden. The rendered element is linked to the popup via
16
+ * `aria-labelledby`. Renders an `<h2>` by default.
17
+ *
18
+ * To visually hide the title while keeping it accessible, wrap it with
19
+ * `VisuallyHidden` using the `render` prop:
20
+ *
21
+ * ```jsx
22
+ * <VisuallyHidden render={ <Dialog.Title /> }>
23
+ * Accessible title text
24
+ * </VisuallyHidden>
25
+ * ```
16
26
  */
17
27
  const Title = forwardRef< HTMLHeadingElement, TitleProps >(
18
- function DialogTitle( { className, render, ...props }, forwardedRef ) {
28
+ function DialogTitle( { children, ...props }, forwardedRef ) {
19
29
  const validationContext = useDialogValidationContext();
20
30
  const internalRef = useRef< HTMLHeadingElement >( null );
21
31
  const mergedRef = useMergeRefs( [ internalRef, forwardedRef ] );
@@ -26,12 +36,14 @@ const Title = forwardRef< HTMLHeadingElement, TitleProps >(
26
36
  }, [ validationContext ] );
27
37
 
28
38
  return (
29
- <_Dialog.Title
39
+ <Text
30
40
  ref={ mergedRef }
31
- className={ clsx( styles.title, className ) }
32
- render={ render }
33
- { ...props }
34
- />
41
+ variant="heading-xl"
42
+ render={ <_Dialog.Title { ...props } /> }
43
+ className={ styles.title }
44
+ >
45
+ { children }
46
+ </Text>
35
47
  );
36
48
  }
37
49
  );
@@ -34,6 +34,11 @@ export interface PopupProps
34
34
  */
35
35
  children?: ReactNode;
36
36
 
37
+ /**
38
+ * A parent element to render the portal into.
39
+ */
40
+ container?: _Dialog.Portal.Props[ 'container' ];
41
+
37
42
  /**
38
43
  * Renders the dialog at a preset width (excluding additional padding from
39
44
  * the viewport edges).
@@ -74,8 +79,11 @@ export interface TitleProps extends ComponentProps< 'h2' > {
74
79
  /**
75
80
  * The title content to be rendered. This serves as both the visible
76
81
  * heading and the accessible label for the dialog.
82
+ *
83
+ * When `Dialog.Title` is passed as a render element (e.g. to
84
+ * `VisuallyHidden`), children can be provided by the wrapper instead.
77
85
  */
78
- children: ReactNode;
86
+ children?: ReactNode;
79
87
  }
80
88
 
81
89
  export interface CloseIconProps
@@ -4,6 +4,8 @@ import { Text } from '../text';
4
4
  import type { EmptyStateDescriptionProps } from './types';
5
5
  import styles from './style.module.css';
6
6
 
7
+ const DEFAULT_TAG = <p />;
8
+
7
9
  /**
8
10
  * The description text for an empty state, providing additional context and
9
11
  * guidance on what the user should do next.
@@ -12,14 +14,16 @@ export const Description = forwardRef<
12
14
  HTMLParagraphElement,
13
15
  EmptyStateDescriptionProps
14
16
  >( function EmptyStateDescription(
15
- { render, className, children, ...props },
17
+ { render = DEFAULT_TAG, className, children, ...props },
16
18
  ref
17
19
  ) {
18
20
  return (
19
21
  <Text
22
+ ref={ ref }
20
23
  variant="body-md"
21
- render={ render ?? <p ref={ ref } { ...props } /> }
24
+ render={ render }
22
25
  className={ clsx( styles.description, className ) }
26
+ { ...props }
23
27
  >
24
28
  { children }
25
29
  </Text>
@@ -8,7 +8,7 @@
8
8
  text-align: center;
9
9
  max-width: var(--wpds-dimension-surface-width-sm);
10
10
  gap: var(--wpds-dimension-gap-xs);
11
- font-family: var(--wpds-font-family-body);
11
+ font-family: var(--wpds-typography-font-family-body);
12
12
  color: var(--wpds-color-fg-content-neutral);
13
13
  text-wrap: balance;
14
14
  }