@wordpress/ui 0.8.0 → 0.9.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 (357) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +106 -0
  3. package/build/badge/badge.cjs +3 -3
  4. package/build/badge/badge.cjs.map +2 -2
  5. package/build/button/button.cjs +7 -7
  6. package/build/button/button.cjs.map +2 -2
  7. package/build/card/content.cjs +54 -0
  8. package/build/card/content.cjs.map +7 -0
  9. package/build/card/full-bleed.cjs +57 -0
  10. package/build/card/full-bleed.cjs.map +7 -0
  11. package/build/card/header.cjs +54 -0
  12. package/build/card/header.cjs.map +7 -0
  13. package/build/card/index.cjs +43 -0
  14. package/build/card/index.cjs.map +7 -0
  15. package/build/card/root.cjs +73 -0
  16. package/build/card/root.cjs.map +7 -0
  17. package/build/card/title.cjs +55 -0
  18. package/build/card/title.cjs.map +7 -0
  19. package/build/card/types.cjs +19 -0
  20. package/build/card/types.cjs.map +7 -0
  21. package/build/collapsible/index.cjs +37 -0
  22. package/build/collapsible/index.cjs.map +7 -0
  23. package/build/collapsible/panel.cjs +38 -0
  24. package/build/collapsible/panel.cjs.map +7 -0
  25. package/build/collapsible/root.cjs +38 -0
  26. package/build/collapsible/root.cjs.map +7 -0
  27. package/build/collapsible/trigger.cjs +38 -0
  28. package/build/collapsible/trigger.cjs.map +7 -0
  29. package/build/collapsible/types.cjs +19 -0
  30. package/build/collapsible/types.cjs.map +7 -0
  31. package/build/collapsible-card/content.cjs +77 -0
  32. package/build/collapsible-card/content.cjs.map +7 -0
  33. package/build/collapsible-card/header.cjs +102 -0
  34. package/build/collapsible-card/header.cjs.map +7 -0
  35. package/build/collapsible-card/index.cjs +37 -0
  36. package/build/collapsible-card/index.cjs.map +7 -0
  37. package/build/collapsible-card/root.cjs +56 -0
  38. package/build/collapsible-card/root.cjs.map +7 -0
  39. package/build/collapsible-card/types.cjs +19 -0
  40. package/build/collapsible-card/types.cjs.map +7 -0
  41. package/build/dialog/footer.cjs +3 -3
  42. package/build/dialog/footer.cjs.map +2 -2
  43. package/build/dialog/header.cjs +3 -3
  44. package/build/dialog/header.cjs.map +2 -2
  45. package/build/dialog/popup.cjs +3 -3
  46. package/build/dialog/popup.cjs.map +2 -2
  47. package/build/dialog/title.cjs +3 -3
  48. package/build/dialog/title.cjs.map +2 -2
  49. package/build/dialog/types.cjs.map +1 -1
  50. package/build/form/primitives/field/label.cjs +6 -1
  51. package/build/form/primitives/field/label.cjs.map +2 -2
  52. package/build/form/primitives/field/types.cjs.map +1 -1
  53. package/build/form/primitives/fieldset/legend.cjs +5 -1
  54. package/build/form/primitives/fieldset/legend.cjs.map +2 -2
  55. package/build/form/primitives/fieldset/types.cjs.map +1 -1
  56. package/build/form/primitives/input/input.cjs +4 -4
  57. package/build/form/primitives/input/input.cjs.map +2 -2
  58. package/build/form/primitives/input-layout/slot.cjs +3 -2
  59. package/build/form/primitives/input-layout/slot.cjs.map +2 -2
  60. package/build/form/primitives/select/item.cjs +3 -3
  61. package/build/form/primitives/select/item.cjs.map +2 -2
  62. package/build/form/primitives/select/popup.cjs +3 -3
  63. package/build/form/primitives/select/popup.cjs.map +2 -2
  64. package/build/form/primitives/select/trigger.cjs +7 -7
  65. package/build/form/primitives/select/trigger.cjs.map +2 -2
  66. package/build/index.cjs +13 -0
  67. package/build/index.cjs.map +2 -2
  68. package/build/link/index.cjs +31 -0
  69. package/build/link/index.cjs.map +7 -0
  70. package/build/link/link.cjs +125 -0
  71. package/build/link/link.cjs.map +7 -0
  72. package/build/link/types.cjs +19 -0
  73. package/build/link/types.cjs.map +7 -0
  74. package/build/notice/action-button.cjs +3 -3
  75. package/build/notice/action-button.cjs.map +2 -2
  76. package/build/notice/action-link.cjs +17 -18
  77. package/build/notice/action-link.cjs.map +2 -2
  78. package/build/notice/actions.cjs +3 -3
  79. package/build/notice/actions.cjs.map +2 -2
  80. package/build/notice/close-icon.cjs +3 -3
  81. package/build/notice/close-icon.cjs.map +2 -2
  82. package/build/notice/description.cjs +26 -15
  83. package/build/notice/description.cjs.map +3 -3
  84. package/build/notice/root.cjs +3 -3
  85. package/build/notice/root.cjs.map +2 -2
  86. package/build/notice/title.cjs +26 -12
  87. package/build/notice/title.cjs.map +3 -3
  88. package/build/notice/types.cjs.map +1 -1
  89. package/build/tabs/list.cjs +3 -3
  90. package/build/tabs/list.cjs.map +2 -2
  91. package/build/tabs/panel.cjs +3 -3
  92. package/build/tabs/panel.cjs.map +2 -2
  93. package/build/tabs/tab.cjs +3 -3
  94. package/build/tabs/tab.cjs.map +2 -2
  95. package/build/text/index.cjs +31 -0
  96. package/build/text/index.cjs.map +7 -0
  97. package/build/text/text.cjs +65 -0
  98. package/build/text/text.cjs.map +7 -0
  99. package/build/text/types.cjs +19 -0
  100. package/build/text/types.cjs.map +7 -0
  101. package/build/tooltip/popup.cjs +1 -1
  102. package/build/tooltip/popup.cjs.map +1 -1
  103. package/build/visually-hidden/visually-hidden.cjs +3 -3
  104. package/build/visually-hidden/visually-hidden.cjs.map +2 -2
  105. package/build-module/badge/badge.mjs +3 -3
  106. package/build-module/badge/badge.mjs.map +2 -2
  107. package/build-module/button/button.mjs +7 -7
  108. package/build-module/button/button.mjs.map +2 -2
  109. package/build-module/card/content.mjs +29 -0
  110. package/build-module/card/content.mjs.map +7 -0
  111. package/build-module/card/full-bleed.mjs +32 -0
  112. package/build-module/card/full-bleed.mjs.map +7 -0
  113. package/build-module/card/header.mjs +29 -0
  114. package/build-module/card/header.mjs.map +7 -0
  115. package/build-module/card/index.mjs +14 -0
  116. package/build-module/card/index.mjs.map +7 -0
  117. package/build-module/card/root.mjs +38 -0
  118. package/build-module/card/root.mjs.map +7 -0
  119. package/build-module/card/title.mjs +30 -0
  120. package/build-module/card/title.mjs.map +7 -0
  121. package/build-module/card/types.mjs +1 -0
  122. package/build-module/card/types.mjs.map +7 -0
  123. package/build-module/collapsible/index.mjs +10 -0
  124. package/build-module/collapsible/index.mjs.map +7 -0
  125. package/build-module/collapsible/panel.mjs +13 -0
  126. package/build-module/collapsible/panel.mjs.map +7 -0
  127. package/build-module/collapsible/root.mjs +13 -0
  128. package/build-module/collapsible/root.mjs.map +7 -0
  129. package/build-module/collapsible/trigger.mjs +13 -0
  130. package/build-module/collapsible/trigger.mjs.map +7 -0
  131. package/build-module/collapsible/types.mjs +1 -0
  132. package/build-module/collapsible/types.mjs.map +7 -0
  133. package/build-module/collapsible-card/content.mjs +42 -0
  134. package/build-module/collapsible-card/content.mjs.map +7 -0
  135. package/build-module/collapsible-card/header.mjs +67 -0
  136. package/build-module/collapsible-card/header.mjs.map +7 -0
  137. package/build-module/collapsible-card/index.mjs +10 -0
  138. package/build-module/collapsible-card/index.mjs.map +7 -0
  139. package/build-module/collapsible-card/root.mjs +21 -0
  140. package/build-module/collapsible-card/root.mjs.map +7 -0
  141. package/build-module/collapsible-card/types.mjs +1 -0
  142. package/build-module/collapsible-card/types.mjs.map +7 -0
  143. package/build-module/dialog/footer.mjs +3 -3
  144. package/build-module/dialog/footer.mjs.map +2 -2
  145. package/build-module/dialog/header.mjs +3 -3
  146. package/build-module/dialog/header.mjs.map +2 -2
  147. package/build-module/dialog/popup.mjs +3 -3
  148. package/build-module/dialog/popup.mjs.map +2 -2
  149. package/build-module/dialog/title.mjs +3 -3
  150. package/build-module/dialog/title.mjs.map +2 -2
  151. package/build-module/form/primitives/field/label.mjs +6 -1
  152. package/build-module/form/primitives/field/label.mjs.map +2 -2
  153. package/build-module/form/primitives/fieldset/legend.mjs +5 -1
  154. package/build-module/form/primitives/fieldset/legend.mjs.map +2 -2
  155. package/build-module/form/primitives/input/input.mjs +4 -4
  156. package/build-module/form/primitives/input/input.mjs.map +2 -2
  157. package/build-module/form/primitives/input-layout/slot.mjs +3 -2
  158. package/build-module/form/primitives/input-layout/slot.mjs.map +2 -2
  159. package/build-module/form/primitives/select/item.mjs +3 -3
  160. package/build-module/form/primitives/select/item.mjs.map +2 -2
  161. package/build-module/form/primitives/select/popup.mjs +3 -3
  162. package/build-module/form/primitives/select/popup.mjs.map +2 -2
  163. package/build-module/form/primitives/select/trigger.mjs +7 -7
  164. package/build-module/form/primitives/select/trigger.mjs.map +2 -2
  165. package/build-module/index.mjs +8 -0
  166. package/build-module/index.mjs.map +2 -2
  167. package/build-module/link/index.mjs +6 -0
  168. package/build-module/link/index.mjs.map +7 -0
  169. package/build-module/link/link.mjs +90 -0
  170. package/build-module/link/link.mjs.map +7 -0
  171. package/build-module/link/types.mjs +1 -0
  172. package/build-module/link/types.mjs.map +7 -0
  173. package/build-module/notice/action-button.mjs +3 -3
  174. package/build-module/notice/action-button.mjs.map +2 -2
  175. package/build-module/notice/action-link.mjs +17 -18
  176. package/build-module/notice/action-link.mjs.map +2 -2
  177. package/build-module/notice/actions.mjs +3 -3
  178. package/build-module/notice/actions.mjs.map +2 -2
  179. package/build-module/notice/close-icon.mjs +3 -3
  180. package/build-module/notice/close-icon.mjs.map +2 -2
  181. package/build-module/notice/description.mjs +16 -15
  182. package/build-module/notice/description.mjs.map +2 -2
  183. package/build-module/notice/root.mjs +3 -3
  184. package/build-module/notice/root.mjs.map +2 -2
  185. package/build-module/notice/title.mjs +16 -12
  186. package/build-module/notice/title.mjs.map +2 -2
  187. package/build-module/tabs/list.mjs +3 -3
  188. package/build-module/tabs/list.mjs.map +2 -2
  189. package/build-module/tabs/panel.mjs +3 -3
  190. package/build-module/tabs/panel.mjs.map +2 -2
  191. package/build-module/tabs/tab.mjs +3 -3
  192. package/build-module/tabs/tab.mjs.map +2 -2
  193. package/build-module/text/index.mjs +6 -0
  194. package/build-module/text/index.mjs.map +7 -0
  195. package/build-module/text/text.mjs +30 -0
  196. package/build-module/text/text.mjs.map +7 -0
  197. package/build-module/text/types.mjs +1 -0
  198. package/build-module/text/types.mjs.map +7 -0
  199. package/build-module/tooltip/popup.mjs +1 -1
  200. package/build-module/tooltip/popup.mjs.map +1 -1
  201. package/build-module/visually-hidden/visually-hidden.mjs +3 -3
  202. package/build-module/visually-hidden/visually-hidden.mjs.map +2 -2
  203. package/build-types/card/content.d.ts +6 -0
  204. package/build-types/card/content.d.ts.map +1 -0
  205. package/build-types/card/full-bleed.d.ts +9 -0
  206. package/build-types/card/full-bleed.d.ts.map +1 -0
  207. package/build-types/card/header.d.ts +7 -0
  208. package/build-types/card/header.d.ts.map +1 -0
  209. package/build-types/card/index.d.ts +7 -0
  210. package/build-types/card/index.d.ts.map +1 -0
  211. package/build-types/card/root.d.ts +23 -0
  212. package/build-types/card/root.d.ts.map +1 -0
  213. package/build-types/card/stories/index.story.d.ts +22 -0
  214. package/build-types/card/stories/index.story.d.ts.map +1 -0
  215. package/build-types/card/test/index.test.d.ts +2 -0
  216. package/build-types/card/test/index.test.d.ts.map +1 -0
  217. package/build-types/card/title.d.ts +7 -0
  218. package/build-types/card/title.d.ts.map +1 -0
  219. package/build-types/card/types.d.ts +34 -0
  220. package/build-types/card/types.d.ts.map +1 -0
  221. package/build-types/collapsible/index.d.ts +5 -0
  222. package/build-types/collapsible/index.d.ts.map +1 -0
  223. package/build-types/collapsible/panel.d.ts +16 -0
  224. package/build-types/collapsible/panel.d.ts.map +1 -0
  225. package/build-types/collapsible/root.d.ts +15 -0
  226. package/build-types/collapsible/root.d.ts.map +1 -0
  227. package/build-types/collapsible/stories/index.story.d.ts +18 -0
  228. package/build-types/collapsible/stories/index.story.d.ts.map +1 -0
  229. package/build-types/collapsible/test/index.test.d.ts +2 -0
  230. package/build-types/collapsible/test/index.test.d.ts.map +1 -0
  231. package/build-types/collapsible/trigger.d.ts +15 -0
  232. package/build-types/collapsible/trigger.d.ts.map +1 -0
  233. package/build-types/collapsible/types.d.ts +22 -0
  234. package/build-types/collapsible/types.d.ts.map +1 -0
  235. package/build-types/collapsible-card/content.d.ts +7 -0
  236. package/build-types/collapsible-card/content.d.ts.map +1 -0
  237. package/build-types/collapsible-card/header.d.ts +12 -0
  238. package/build-types/collapsible-card/header.d.ts.map +1 -0
  239. package/build-types/collapsible-card/index.d.ts +5 -0
  240. package/build-types/collapsible-card/index.d.ts.map +1 -0
  241. package/build-types/collapsible-card/root.d.ts +24 -0
  242. package/build-types/collapsible-card/root.d.ts.map +1 -0
  243. package/build-types/collapsible-card/stories/index.story.d.ts +28 -0
  244. package/build-types/collapsible-card/stories/index.story.d.ts.map +1 -0
  245. package/build-types/collapsible-card/test/index.test.d.ts +2 -0
  246. package/build-types/collapsible-card/test/index.test.d.ts.map +1 -0
  247. package/build-types/collapsible-card/types.d.ts +52 -0
  248. package/build-types/collapsible-card/types.d.ts.map +1 -0
  249. package/build-types/dialog/types.d.ts +3 -3
  250. package/build-types/form/primitives/field/label.d.ts +1 -0
  251. package/build-types/form/primitives/field/label.d.ts.map +1 -1
  252. package/build-types/form/primitives/field/stories/index.story.d.ts +5 -0
  253. package/build-types/form/primitives/field/stories/index.story.d.ts.map +1 -1
  254. package/build-types/form/primitives/field/types.d.ts +7 -0
  255. package/build-types/form/primitives/field/types.d.ts.map +1 -1
  256. package/build-types/form/primitives/fieldset/legend.d.ts +1 -0
  257. package/build-types/form/primitives/fieldset/legend.d.ts.map +1 -1
  258. package/build-types/form/primitives/fieldset/stories/index.story.d.ts +5 -0
  259. package/build-types/form/primitives/fieldset/stories/index.story.d.ts.map +1 -1
  260. package/build-types/form/primitives/fieldset/types.d.ts +7 -0
  261. package/build-types/form/primitives/fieldset/types.d.ts.map +1 -1
  262. package/build-types/form/primitives/input-layout/slot.d.ts.map +1 -1
  263. package/build-types/index.d.ts +5 -0
  264. package/build-types/index.d.ts.map +1 -1
  265. package/build-types/link/index.d.ts +2 -0
  266. package/build-types/link/index.d.ts.map +1 -0
  267. package/build-types/link/link.d.ts +7 -0
  268. package/build-types/link/link.d.ts.map +1 -0
  269. package/build-types/link/stories/index.story.d.ts +18 -0
  270. package/build-types/link/stories/index.story.d.ts.map +1 -0
  271. package/build-types/link/test/index.test.d.ts +2 -0
  272. package/build-types/link/test/index.test.d.ts.map +1 -0
  273. package/build-types/link/types.d.ts +33 -0
  274. package/build-types/link/types.d.ts.map +1 -0
  275. package/build-types/notice/action-link.d.ts +0 -2
  276. package/build-types/notice/action-link.d.ts.map +1 -1
  277. package/build-types/notice/description.d.ts +1 -1
  278. package/build-types/notice/description.d.ts.map +1 -1
  279. package/build-types/notice/title.d.ts.map +1 -1
  280. package/build-types/notice/types.d.ts +3 -2
  281. package/build-types/notice/types.d.ts.map +1 -1
  282. package/build-types/text/index.d.ts +2 -0
  283. package/build-types/text/index.d.ts.map +1 -0
  284. package/build-types/text/stories/index.story.d.ts +9 -0
  285. package/build-types/text/stories/index.story.d.ts.map +1 -0
  286. package/build-types/text/test/index.test.d.ts +2 -0
  287. package/build-types/text/test/index.test.d.ts.map +1 -0
  288. package/build-types/text/text.d.ts +7 -0
  289. package/build-types/text/text.d.ts.map +1 -0
  290. package/build-types/text/types.d.ts +15 -0
  291. package/build-types/text/types.d.ts.map +1 -0
  292. package/package.json +14 -14
  293. package/src/badge/style.module.css +5 -2
  294. package/src/button/style.module.css +2 -0
  295. package/src/card/content.tsx +20 -0
  296. package/src/card/full-bleed.tsx +26 -0
  297. package/src/card/header.tsx +21 -0
  298. package/src/card/index.ts +7 -0
  299. package/src/card/root.tsx +42 -0
  300. package/src/card/stories/index.story.tsx +128 -0
  301. package/src/card/style.module.css +48 -0
  302. package/src/card/test/index.test.tsx +96 -0
  303. package/src/card/title.tsx +22 -0
  304. package/src/card/types.ts +38 -0
  305. package/src/collapsible/index.ts +5 -0
  306. package/src/collapsible/panel.tsx +16 -0
  307. package/src/collapsible/root.tsx +15 -0
  308. package/src/collapsible/stories/index.story.tsx +108 -0
  309. package/src/collapsible/test/index.test.tsx +228 -0
  310. package/src/collapsible/trigger.tsx +15 -0
  311. package/src/collapsible/types.ts +24 -0
  312. package/src/collapsible-card/content.tsx +33 -0
  313. package/src/collapsible-card/header.tsx +53 -0
  314. package/src/collapsible-card/index.ts +5 -0
  315. package/src/collapsible-card/root.tsx +37 -0
  316. package/src/collapsible-card/stories/index.story.tsx +207 -0
  317. package/src/collapsible-card/style.module.css +78 -0
  318. package/src/collapsible-card/test/index.test.tsx +181 -0
  319. package/src/collapsible-card/types.ts +54 -0
  320. package/src/dialog/style.module.css +5 -5
  321. package/src/dialog/types.ts +3 -3
  322. package/src/form/primitives/field/label.tsx +9 -1
  323. package/src/form/primitives/field/stories/index.story.tsx +17 -0
  324. package/src/form/primitives/field/test/index.test.tsx +13 -0
  325. package/src/form/primitives/field/types.ts +7 -0
  326. package/src/form/primitives/fieldset/legend.tsx +8 -1
  327. package/src/form/primitives/fieldset/stories/index.story.tsx +20 -0
  328. package/src/form/primitives/fieldset/test/index.test.tsx +14 -0
  329. package/src/form/primitives/fieldset/types.ts +7 -0
  330. package/src/form/primitives/input-layout/slot.tsx +6 -2
  331. package/src/index.ts +5 -0
  332. package/src/link/index.ts +1 -0
  333. package/src/link/link.tsx +73 -0
  334. package/src/link/stories/index.story.tsx +92 -0
  335. package/src/link/style.module.css +68 -0
  336. package/src/link/test/index.test.tsx +93 -0
  337. package/src/link/types.ts +36 -0
  338. package/src/notice/action-link.tsx +12 -18
  339. package/src/notice/description.tsx +12 -14
  340. package/src/notice/style.module.css +9 -22
  341. package/src/notice/test/index.test.tsx +2 -2
  342. package/src/notice/title.tsx +11 -10
  343. package/src/notice/types.ts +3 -2
  344. package/src/tabs/style.module.css +1 -1
  345. package/src/text/index.ts +1 -0
  346. package/src/text/stories/index.story.tsx +68 -0
  347. package/src/text/style.module.css +67 -0
  348. package/src/text/test/index.test.tsx +46 -0
  349. package/src/text/text.tsx +25 -0
  350. package/src/text/types.ts +25 -0
  351. package/src/tooltip/popup.tsx +1 -1
  352. package/src/utils/css/focus.module.css +4 -2
  353. package/src/utils/css/item-popup.module.css +1 -0
  354. package/src/utils/css/select-trigger.module.css +1 -0
  355. package/src/visually-hidden/style.module.css +1 -0
  356. package/AGENTS.md +0 -9
  357. package/CLAUDE.md +0 -1
@@ -0,0 +1,68 @@
1
+ @layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
2
+
3
+ @layer wp-ui-components {
4
+ .link {
5
+ text-underline-offset: 0.2em;
6
+ text-decoration-thickness: 0.5px;
7
+ }
8
+
9
+ /* Brand tone */
10
+ .is-brand,
11
+ .is-brand:visited {
12
+ color: var(--wpds-color-fg-interactive-brand);
13
+ }
14
+
15
+ .is-brand:hover,
16
+ .is-brand:active {
17
+ color: var(--wpds-color-fg-interactive-brand-active);
18
+ }
19
+
20
+ /* Neutral tone */
21
+ .is-neutral,
22
+ .is-neutral:visited {
23
+ color: var(--wpds-color-fg-interactive-neutral);
24
+ text-decoration-color: var(--wpds-color-stroke-interactive-neutral);
25
+ }
26
+
27
+ .is-neutral:hover,
28
+ .is-neutral:active {
29
+ color: var(--wpds-color-fg-interactive-neutral-active);
30
+ }
31
+
32
+ /* Unstyled variant */
33
+ .is-unstyled {
34
+ color: inherit;
35
+ text-decoration: none;
36
+ }
37
+
38
+ /* Link icon pattern — arrow rendered via ::after pseudo-element to avoid
39
+ * Twemoji replacement and text-selection issues. Root <a> gets
40
+ * text-decoration:none; the link-contents span re-applies it.
41
+ * text-underline-offset is inherited and propagates from .link
42
+ * automatically. text-decoration-thickness and text-decoration-color
43
+ * are NOT inherited, so they are explicitly redeclared on
44
+ * .link-contents (the latter via `inherit` to carry the neutral
45
+ * tone's custom color token through). */
46
+ .has-link-icon {
47
+ text-decoration: none;
48
+ }
49
+
50
+ .link-contents {
51
+ text-decoration: underline;
52
+ text-decoration-color: inherit;
53
+ text-decoration-thickness: 0.5px;
54
+ }
55
+
56
+ .link-icon {
57
+ margin-inline-start: var(--wpds-dimension-padding-xs);
58
+ font-weight: var(--wpds-font-weight-regular);
59
+ }
60
+
61
+ .link-icon::after {
62
+ content: "\2197";
63
+ }
64
+
65
+ .link-icon:dir(rtl)::after {
66
+ content: "\2196";
67
+ }
68
+ }
@@ -0,0 +1,93 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { createRef } from '@wordpress/element';
4
+ import { Link } from '../index';
5
+
6
+ describe( 'Link', () => {
7
+ it( 'forwards ref', () => {
8
+ const ref = createRef< HTMLAnchorElement >();
9
+
10
+ render(
11
+ <Link ref={ ref } href="/">
12
+ Home
13
+ </Link>
14
+ );
15
+
16
+ expect( ref.current ).toBeInstanceOf( HTMLAnchorElement );
17
+ } );
18
+
19
+ describe( 'openInNewTab', () => {
20
+ it( 'sets target="_blank" when true', () => {
21
+ render(
22
+ <Link href="https://example.com" openInNewTab>
23
+ External
24
+ </Link>
25
+ );
26
+
27
+ expect( screen.getByRole( 'link' ) ).toHaveAttribute(
28
+ 'target',
29
+ '_blank'
30
+ );
31
+ } );
32
+
33
+ it( 'does not set target="_blank" when false', () => {
34
+ render( <Link href="https://example.com">External</Link> );
35
+
36
+ expect( screen.getByRole( 'link' ) ).not.toHaveAttribute(
37
+ 'target'
38
+ );
39
+ } );
40
+
41
+ it( 'renders an accessible arrow indicator', () => {
42
+ render(
43
+ <Link href="https://example.com" openInNewTab>
44
+ External
45
+ </Link>
46
+ );
47
+
48
+ expect(
49
+ screen.getByLabelText( '(opens in a new tab)' )
50
+ ).toBeInTheDocument();
51
+ } );
52
+
53
+ it( 'prevents default for internal anchor links', async () => {
54
+ const user = userEvent.setup();
55
+ const onClick = jest.fn();
56
+
57
+ render(
58
+ <Link href="#section" openInNewTab onClick={ onClick }>
59
+ Jump
60
+ </Link>
61
+ );
62
+
63
+ await user.click( screen.getByRole( 'link' ) );
64
+
65
+ expect( onClick ).toHaveBeenCalledTimes( 1 );
66
+ expect( onClick.mock.calls[ 0 ][ 0 ].defaultPrevented ).toBe(
67
+ true
68
+ );
69
+ } );
70
+
71
+ it( 'does not prevent default for external links', async () => {
72
+ const user = userEvent.setup();
73
+ const onClick = jest.fn();
74
+
75
+ render(
76
+ <Link
77
+ href="https://example.com"
78
+ openInNewTab
79
+ onClick={ onClick }
80
+ >
81
+ External
82
+ </Link>
83
+ );
84
+
85
+ await user.click( screen.getByRole( 'link' ) );
86
+
87
+ expect( onClick ).toHaveBeenCalledTimes( 1 );
88
+ expect( onClick.mock.calls[ 0 ][ 0 ].defaultPrevented ).toBe(
89
+ false
90
+ );
91
+ } );
92
+ } );
93
+ } );
@@ -0,0 +1,36 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type ComponentProps } from '../utils/types';
3
+
4
+ export interface LinkProps extends Omit< ComponentProps< 'a' >, 'target' > {
5
+ /**
6
+ * The visual treatment of the link.
7
+ *
8
+ * - `default`: Applies tone-based color and underline styles.
9
+ * - `unstyled`: Strips all visual styles so consumers can bring their own.
10
+ *
11
+ * @default "default"
12
+ */
13
+ variant?: 'default' | 'unstyled';
14
+
15
+ /**
16
+ * The tone of the link. Tone describes a semantic color intent.
17
+ * Only applies when `variant` is `default`.
18
+ *
19
+ * @default "brand"
20
+ */
21
+ tone?: 'brand' | 'neutral';
22
+
23
+ /**
24
+ * Whether to open the link in a new browser tab.
25
+ * When true, sets `target="_blank"`, appends a visual arrow indicator,
26
+ * and prevents navigation for internal anchors (`#`-prefixed hrefs).
27
+ *
28
+ * @default false
29
+ */
30
+ openInNewTab?: boolean;
31
+
32
+ /**
33
+ * The content to be rendered inside the component.
34
+ */
35
+ children?: ReactNode;
36
+ }
@@ -1,28 +1,22 @@
1
- import { forwardRef } from '@wordpress/element';
2
- import { useRender, mergeProps } from '@base-ui/react';
3
1
  import clsx from 'clsx';
2
+ import { forwardRef } from '@wordpress/element';
3
+ import { Link } from '../link';
4
4
  import type { ActionLinkProps } from './types';
5
5
  import styles from './style.module.css';
6
6
 
7
7
  /**
8
8
  * An action link for use within Notice.Actions.
9
- *
10
- * TODO: This should use a shared Link component.
11
9
  */
12
10
  export const ActionLink = forwardRef< HTMLAnchorElement, ActionLinkProps >(
13
- function NoticeActionLink( { className, render, ...props }, ref ) {
14
- const element = useRender( {
15
- defaultTagName: 'a',
16
- render,
17
- ref,
18
- props: mergeProps< 'a' >(
19
- {
20
- className: clsx( styles[ 'action-link' ], className ),
21
- },
22
- props
23
- ),
24
- } );
25
-
26
- return element;
11
+ function NoticeActionLink( { className, ...props }, ref ) {
12
+ return (
13
+ <Link
14
+ ref={ ref }
15
+ className={ clsx( styles[ 'action-link' ], className ) }
16
+ { ...props }
17
+ tone="neutral"
18
+ variant="default"
19
+ />
20
+ );
27
21
  }
28
22
  );
@@ -1,23 +1,21 @@
1
1
  import { forwardRef } from '@wordpress/element';
2
- import { useRender, mergeProps } from '@base-ui/react';
2
+ import clsx from 'clsx';
3
3
  import type { DescriptionProps } from './types';
4
+ import { Text } from '../text';
4
5
  import styles from './style.module.css';
5
6
 
6
7
  /**
7
8
  * The description text for a notice.
8
9
  */
9
- export const Description = forwardRef< HTMLDivElement, DescriptionProps >(
10
- function NoticeDescription( { render, ...props }, ref ) {
11
- const element = useRender( {
12
- defaultTagName: 'div',
13
- render,
14
- ref,
15
- props: mergeProps< 'div' >(
16
- { className: styles.description },
17
- props
18
- ),
19
- } );
20
-
21
- return element;
10
+ export const Description = forwardRef< HTMLSpanElement, DescriptionProps >(
11
+ function NoticeDescription( { className, ...props }, ref ) {
12
+ return (
13
+ <Text
14
+ ref={ ref }
15
+ variant="body-md"
16
+ className={ clsx( styles.description, className ) }
17
+ { ...props }
18
+ />
19
+ );
22
20
  }
23
21
  );
@@ -10,6 +10,7 @@
10
10
  --wp-ui-notice-text-color: var(--wpds-color-fg-content-neutral);
11
11
  --wp-ui-notice-decorative-icon-color: var(--wpds-color-fg-content-neutral);
12
12
 
13
+ container-type: inline-size;
13
14
  display: grid;
14
15
  align-items: start;
15
16
  grid-template-columns: auto 1fr auto;
@@ -31,10 +32,6 @@
31
32
 
32
33
  padding-block: var(--text-vertical-padding);
33
34
 
34
- font-family: var(--wpds-font-family-heading);
35
- font-size: var(--wpds-font-size-md);
36
- font-weight: var(--wpds-font-weight-medium);
37
- line-height: var(--wpds-font-line-height-sm);
38
35
  color: var(--wp-ui-notice-text-color);
39
36
  }
40
37
 
@@ -43,10 +40,6 @@
43
40
 
44
41
  padding-block: var(--text-vertical-padding);
45
42
 
46
- font-family: var(--wpds-font-family-body);
47
- font-size: var(--wpds-font-size-md);
48
- font-weight: var(--wpds-font-weight-regular);
49
- line-height: var(--wpds-font-line-height-sm);
50
43
  text-wrap: pretty;
51
44
  color: var(--wp-ui-notice-text-color);
52
45
  }
@@ -76,20 +69,6 @@
76
69
  font-weight: var(--wpds-font-weight-regular);
77
70
  line-height: var(--wpds-font-line-height-sm);
78
71
  margin-block: auto;
79
- text-underline-offset: 0.2em;
80
- text-decoration-thickness: 0.5px;
81
- color: var(--wpds-color-fg-interactive-neutral);
82
- text-decoration-color: var(--wpds-color-stroke-interactive-neutral);
83
-
84
- &:visited {
85
- color: var(--wpds-color-fg-interactive-neutral);
86
- text-decoration-color: var(--wpds-color-stroke-interactive-neutral);
87
- }
88
-
89
- &:hover,
90
- &:active {
91
- color: var(--wpds-color-fg-interactive-neutral-active);
92
- }
93
72
 
94
73
  /* Add more horizontal space when following another action link/button */
95
74
  &:not(:first-child) {
@@ -135,6 +114,14 @@
135
114
  --wp-ui-notice-text-color: var(--wpds-color-fg-content-error);
136
115
  --wp-ui-notice-decorative-icon-color: var(--wpds-color-fg-content-error-weak);
137
116
  }
117
+
118
+ /* Matches --wpds-dimension-surface-width-sm (container queries don't support custom properties). */
119
+ @container (max-width: 320px) {
120
+ .notice:has(.title) .description,
121
+ .notice:has(.title) .actions {
122
+ grid-column: 1 / 3;
123
+ }
124
+ }
138
125
  }
139
126
 
140
127
  @layer wp-ui-compositions {
@@ -8,7 +8,7 @@ describe( 'Notice', () => {
8
8
  it( 'forwards ref', () => {
9
9
  const rootRef = createRef< HTMLDivElement >();
10
10
  const titleRef = createRef< HTMLSpanElement >();
11
- const descriptionRef = createRef< HTMLDivElement >();
11
+ const descriptionRef = createRef< HTMLSpanElement >();
12
12
  const actionsRef = createRef< HTMLDivElement >();
13
13
  const actionButtonRef = createRef< HTMLButtonElement >();
14
14
  const actionLinkRef = createRef< HTMLAnchorElement >();
@@ -36,7 +36,7 @@ describe( 'Notice', () => {
36
36
  );
37
37
  expect( rootRef.current ).toBeInstanceOf( HTMLDivElement );
38
38
  expect( titleRef.current ).toBeInstanceOf( HTMLSpanElement );
39
- expect( descriptionRef.current ).toBeInstanceOf( HTMLDivElement );
39
+ expect( descriptionRef.current ).toBeInstanceOf( HTMLSpanElement );
40
40
  expect( actionsRef.current ).toBeInstanceOf( HTMLDivElement );
41
41
  expect( actionButtonRef.current ).toBeInstanceOf(
42
42
  HTMLButtonElement
@@ -1,20 +1,21 @@
1
1
  import { forwardRef } from '@wordpress/element';
2
- import { useRender, mergeProps } from '@base-ui/react';
2
+ import clsx from 'clsx';
3
3
  import type { TitleProps } from './types';
4
+ import { Text } from '../text';
4
5
  import styles from './style.module.css';
5
6
 
6
7
  /**
7
8
  * A short heading that communicates the main message of the notice.
8
9
  */
9
10
  export const Title = forwardRef< HTMLSpanElement, TitleProps >(
10
- function NoticeTitle( { render, ...props }, ref ) {
11
- const element = useRender( {
12
- defaultTagName: 'span',
13
- render,
14
- ref,
15
- props: mergeProps< 'span' >( { className: styles.title }, props ),
16
- } );
17
-
18
- return element;
11
+ function NoticeTitle( { className, ...props }, ref ) {
12
+ return (
13
+ <Text
14
+ ref={ ref }
15
+ variant="heading-md"
16
+ className={ clsx( styles.title, className ) }
17
+ { ...props }
18
+ />
19
+ );
19
20
  }
20
21
  );
@@ -3,6 +3,7 @@ import type { ComponentProps } from '../utils/types';
3
3
  import type { IconProps } from '../icon/types';
4
4
  import type { ButtonProps } from '../button/types';
5
5
  import type { IconButtonProps } from '../icon-button/types';
6
+ import type { LinkProps } from '../link/types';
6
7
 
7
8
  export type NoticeIntent = 'warning' | 'success' | 'error' | 'info' | 'neutral';
8
9
 
@@ -47,7 +48,7 @@ export interface TitleProps extends ComponentProps< 'span' > {
47
48
  children?: ReactNode;
48
49
  }
49
50
 
50
- export interface DescriptionProps extends ComponentProps< 'div' > {
51
+ export interface DescriptionProps extends ComponentProps< 'span' > {
51
52
  /**
52
53
  * The description text of the notice.
53
54
  */
@@ -92,7 +93,7 @@ export interface ActionButtonProps
92
93
  children?: ReactNode;
93
94
  }
94
95
 
95
- export interface ActionLinkProps extends ComponentProps< 'a' > {
96
+ export interface ActionLinkProps extends Omit< LinkProps, 'variant' | 'tone' > {
96
97
  /**
97
98
  * The content to be rendered inside the component.
98
99
  */
@@ -119,7 +119,7 @@
119
119
  align-items: center;
120
120
 
121
121
  /* Appearance */
122
- cursor: pointer;
122
+ cursor: var(--wpds-cursor-control);
123
123
 
124
124
  /* Typography */
125
125
  font-family: var(--wpds-font-family-body);
@@ -0,0 +1 @@
1
+ export { Text } from './text';
@@ -0,0 +1,68 @@
1
+ /* eslint-disable jsx-a11y/heading-has-content */
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { Text } from '../index';
4
+ import { Stack } from '../../stack';
5
+
6
+ const meta: Meta< typeof Text > = {
7
+ title: 'Design System/Components/Text',
8
+ component: Text,
9
+ };
10
+ export default meta;
11
+
12
+ type Story = StoryObj< typeof Text >;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ variant: 'body-md',
17
+ children: 'The quick brown fox jumps over the lazy dog.',
18
+ },
19
+ };
20
+
21
+ export const AllVariants: Story = {
22
+ render: () => (
23
+ <Stack
24
+ direction="column"
25
+ gap="lg"
26
+ style={ { color: 'var(--wpds-color-fg-content-neutral)' } }
27
+ >
28
+ { (
29
+ [
30
+ 'heading-2xl',
31
+ 'heading-xl',
32
+ 'heading-lg',
33
+ 'heading-md',
34
+ 'heading-sm',
35
+ 'body-xl',
36
+ 'body-lg',
37
+ 'body-md',
38
+ 'body-sm',
39
+ ] as const
40
+ ).map( ( variant ) => (
41
+ <Stack key={ variant } direction="column" gap="xs">
42
+ <Text variant="heading-sm">{ variant }</Text>
43
+ <Text variant={ variant }>
44
+ The quick brown fox jumps over the lazy dog.
45
+ </Text>
46
+ </Stack>
47
+ ) ) }
48
+ </Stack>
49
+ ),
50
+ };
51
+
52
+ export const WithRenderProp: Story = {
53
+ render: () => (
54
+ <Stack direction="column" gap="md">
55
+ <Text variant="heading-2xl" render={ <h1 /> }>
56
+ Page Title
57
+ </Text>
58
+ <Text variant="heading-xl" render={ <h2 /> }>
59
+ Section Heading
60
+ </Text>
61
+ <Text variant="body-md" render={ <p /> }>
62
+ A paragraph of body text rendered as a semantic paragraph
63
+ element.
64
+ </Text>
65
+ </Stack>
66
+ ),
67
+ };
68
+ /* eslint-enable jsx-a11y/heading-has-content */
@@ -0,0 +1,67 @@
1
+ @layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
2
+
3
+ @layer wp-ui-components {
4
+ .heading-2xl {
5
+ font-family: var(--wpds-font-family-heading);
6
+ font-size: var(--wpds-font-size-2xl);
7
+ line-height: var(--wpds-font-line-height-2xl);
8
+ font-weight: var(--wpds-font-weight-medium);
9
+ }
10
+
11
+ .heading-xl {
12
+ font-family: var(--wpds-font-family-heading);
13
+ font-size: var(--wpds-font-size-xl);
14
+ line-height: var(--wpds-font-line-height-md);
15
+ font-weight: var(--wpds-font-weight-medium);
16
+ }
17
+
18
+ .heading-lg {
19
+ font-family: var(--wpds-font-family-heading);
20
+ font-size: var(--wpds-font-size-lg);
21
+ line-height: var(--wpds-font-line-height-sm);
22
+ font-weight: var(--wpds-font-weight-medium);
23
+ }
24
+
25
+ .heading-md {
26
+ font-family: var(--wpds-font-family-heading);
27
+ font-size: var(--wpds-font-size-md);
28
+ line-height: var(--wpds-font-line-height-sm);
29
+ font-weight: var(--wpds-font-weight-medium);
30
+ }
31
+
32
+ .heading-sm {
33
+ font-family: var(--wpds-font-family-heading);
34
+ font-size: var(--wpds-font-size-xs);
35
+ line-height: var(--wpds-font-line-height-xs);
36
+ font-weight: var(--wpds-font-weight-medium);
37
+ text-transform: uppercase;
38
+ }
39
+
40
+ .body-xl {
41
+ font-family: var(--wpds-font-family-body);
42
+ font-size: var(--wpds-font-size-xl);
43
+ line-height: var(--wpds-font-line-height-xl);
44
+ font-weight: var(--wpds-font-weight-regular);
45
+ }
46
+
47
+ .body-lg {
48
+ font-family: var(--wpds-font-family-body);
49
+ font-size: var(--wpds-font-size-lg);
50
+ line-height: var(--wpds-font-line-height-md);
51
+ font-weight: var(--wpds-font-weight-regular);
52
+ }
53
+
54
+ .body-md {
55
+ font-family: var(--wpds-font-family-body);
56
+ font-size: var(--wpds-font-size-md);
57
+ line-height: var(--wpds-font-line-height-sm);
58
+ font-weight: var(--wpds-font-weight-regular);
59
+ }
60
+
61
+ .body-sm {
62
+ font-family: var(--wpds-font-family-body);
63
+ font-size: var(--wpds-font-size-sm);
64
+ line-height: var(--wpds-font-line-height-xs);
65
+ font-weight: var(--wpds-font-weight-regular);
66
+ }
67
+ }
@@ -0,0 +1,46 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { createRef } from '@wordpress/element';
3
+ import { Text } from '../index';
4
+
5
+ describe( 'Text', () => {
6
+ it( 'forwards ref', () => {
7
+ const ref = createRef< HTMLSpanElement >();
8
+
9
+ render( <Text ref={ ref }>Content</Text> );
10
+
11
+ expect( ref.current ).toBeInstanceOf( HTMLSpanElement );
12
+ } );
13
+
14
+ it( 'renders children', () => {
15
+ render( <Text>Hello world</Text> );
16
+
17
+ expect( screen.getByText( 'Hello world' ) ).toBeVisible();
18
+ } );
19
+
20
+ it( 'forwards props to the rendered element', () => {
21
+ render( <Text data-testid="text">Content</Text> );
22
+
23
+ expect( screen.getByTestId( 'text' ) ).toBeInTheDocument();
24
+ } );
25
+
26
+ it( 'forwards the className to the rendered element', () => {
27
+ render(
28
+ <Text data-testid="text" className="custom-class">
29
+ Content
30
+ </Text>
31
+ );
32
+
33
+ expect( screen.getByTestId( 'text' ) ).toHaveClass( 'custom-class' );
34
+ } );
35
+
36
+ it( 'supports the render prop', () => {
37
+ render(
38
+ // eslint-disable-next-line jsx-a11y/heading-has-content
39
+ <Text render={ <h2 /> }>Section title</Text>
40
+ );
41
+
42
+ expect(
43
+ screen.getByRole( 'heading', { level: 2, name: 'Section title' } )
44
+ ).toBeVisible();
45
+ } );
46
+ } );
@@ -0,0 +1,25 @@
1
+ import { useRender, mergeProps } from '@base-ui/react';
2
+ import clsx from 'clsx';
3
+ import { forwardRef } from '@wordpress/element';
4
+ import { type TextProps } from './types';
5
+ import styles from './style.module.css';
6
+
7
+ /**
8
+ * A text component for rendering content with predefined typographic variants.
9
+ * Built on design tokens for consistent typography across the UI.
10
+ */
11
+ export const Text = forwardRef< HTMLSpanElement, TextProps >( function Text(
12
+ { variant = 'body-md', render, className, ...props },
13
+ ref
14
+ ) {
15
+ const element = useRender( {
16
+ render,
17
+ defaultTagName: 'span',
18
+ ref,
19
+ props: mergeProps< 'span' >( props, {
20
+ className: clsx( styles[ variant ], className ),
21
+ } ),
22
+ } );
23
+
24
+ return element;
25
+ } );
@@ -0,0 +1,25 @@
1
+ import { type ComponentProps } from '../utils/types';
2
+
3
+ export interface TextProps extends ComponentProps< 'span' > {
4
+ /**
5
+ * The typographic variant to apply, controlling font family, size,
6
+ * line height, and weight.
7
+ *
8
+ * @default "body-md"
9
+ */
10
+ variant?:
11
+ | 'heading-2xl'
12
+ | 'heading-xl'
13
+ | 'heading-lg'
14
+ | 'heading-md'
15
+ | 'heading-sm'
16
+ | 'body-xl'
17
+ | 'body-lg'
18
+ | 'body-md'
19
+ | 'body-sm';
20
+
21
+ /**
22
+ * The content to be rendered inside the component.
23
+ */
24
+ children?: React.ReactNode;
25
+ }
@@ -16,7 +16,7 @@ const ThemeProvider: typeof ThemeProviderType =
16
16
  const Popup = forwardRef< HTMLDivElement, PopupProps >( function TooltipPopup(
17
17
  {
18
18
  align = 'center',
19
- side = 'bottom',
19
+ side = 'top',
20
20
  sideOffset = 4,
21
21
  children,
22
22
  className,