@wordpress/ui 0.10.0 → 0.11.1-next.v.202604091042.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 (245) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/CONTRIBUTING.md +25 -0
  3. package/README.md +22 -2
  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/button/button.cjs +16 -6
  14. package/build/button/button.cjs.map +3 -3
  15. package/build/card/content.cjs +3 -3
  16. package/build/card/content.cjs.map +1 -1
  17. package/build/card/full-bleed.cjs +3 -3
  18. package/build/card/full-bleed.cjs.map +1 -1
  19. package/build/card/header.cjs +3 -3
  20. package/build/card/header.cjs.map +1 -1
  21. package/build/card/root.cjs +3 -3
  22. package/build/card/root.cjs.map +1 -1
  23. package/build/card/title.cjs +3 -3
  24. package/build/card/title.cjs.map +1 -1
  25. package/build/collapsible-card/header.cjs +3 -3
  26. package/build/collapsible-card/header.cjs.map +2 -2
  27. package/build/empty-state/title.cjs.map +2 -2
  28. package/build/form/primitives/field/description.cjs +17 -4
  29. package/build/form/primitives/field/description.cjs.map +3 -3
  30. package/build/form/primitives/field/details.cjs +3 -3
  31. package/build/form/primitives/field/details.cjs.map +2 -2
  32. package/build/form/primitives/field/label.cjs +3 -3
  33. package/build/form/primitives/field/label.cjs.map +2 -2
  34. package/build/form/primitives/fieldset/description.cjs +20 -4
  35. package/build/form/primitives/fieldset/description.cjs.map +3 -3
  36. package/build/form/primitives/fieldset/details.cjs +3 -3
  37. package/build/form/primitives/fieldset/details.cjs.map +2 -2
  38. package/build/form/primitives/fieldset/legend.cjs +3 -3
  39. package/build/form/primitives/fieldset/legend.cjs.map +2 -2
  40. package/build/form/primitives/input/input.cjs +23 -7
  41. package/build/form/primitives/input/input.cjs.map +3 -3
  42. package/build/form/primitives/input-layout/input-layout.cjs +10 -0
  43. package/build/form/primitives/input-layout/input-layout.cjs.map +3 -3
  44. package/build/form/primitives/select/trigger.cjs +3 -3
  45. package/build/form/primitives/select/trigger.cjs.map +2 -2
  46. package/build/form/primitives/textarea/textarea.cjs +20 -1
  47. package/build/form/primitives/textarea/textarea.cjs.map +3 -3
  48. package/build/index.cjs +3 -0
  49. package/build/index.cjs.map +2 -2
  50. package/build/link/link.cjs +16 -6
  51. package/build/link/link.cjs.map +3 -3
  52. package/build/popover/arrow.cjs +94 -0
  53. package/build/popover/arrow.cjs.map +7 -0
  54. package/build/popover/close.cjs +45 -0
  55. package/build/popover/close.cjs.map +7 -0
  56. package/build/popover/context.cjs +76 -0
  57. package/build/popover/context.cjs.map +7 -0
  58. package/build/popover/description.cjs +70 -0
  59. package/build/popover/description.cjs.map +7 -0
  60. package/build/popover/index.cjs +49 -0
  61. package/build/popover/index.cjs.map +7 -0
  62. package/build/popover/popup.cjs +138 -0
  63. package/build/popover/popup.cjs.map +7 -0
  64. package/build/popover/root.cjs +35 -0
  65. package/build/popover/root.cjs.map +7 -0
  66. package/build/popover/title.cjs +56 -0
  67. package/build/popover/title.cjs.map +7 -0
  68. package/build/popover/trigger.cjs +38 -0
  69. package/build/popover/trigger.cjs.map +7 -0
  70. package/build/popover/types.cjs +19 -0
  71. package/build/popover/types.cjs.map +7 -0
  72. package/build/text/text.cjs +20 -5
  73. package/build/text/text.cjs.map +3 -3
  74. package/build/utils/use-deprioritized-initial-focus.cjs.map +2 -2
  75. package/build-module/alert-dialog/context.mjs +6 -1
  76. package/build-module/alert-dialog/context.mjs.map +2 -2
  77. package/build-module/alert-dialog/popup.mjs +107 -33
  78. package/build-module/alert-dialog/popup.mjs.map +4 -4
  79. package/build-module/alert-dialog/root.mjs +113 -7
  80. package/build-module/alert-dialog/root.mjs.map +2 -2
  81. package/build-module/alert-dialog/trigger.mjs +4 -4
  82. package/build-module/alert-dialog/trigger.mjs.map +3 -3
  83. package/build-module/button/button.mjs +16 -6
  84. package/build-module/button/button.mjs.map +3 -3
  85. package/build-module/card/content.mjs +3 -3
  86. package/build-module/card/content.mjs.map +1 -1
  87. package/build-module/card/full-bleed.mjs +3 -3
  88. package/build-module/card/full-bleed.mjs.map +1 -1
  89. package/build-module/card/header.mjs +3 -3
  90. package/build-module/card/header.mjs.map +1 -1
  91. package/build-module/card/root.mjs +3 -3
  92. package/build-module/card/root.mjs.map +1 -1
  93. package/build-module/card/title.mjs +3 -3
  94. package/build-module/card/title.mjs.map +1 -1
  95. package/build-module/collapsible-card/header.mjs +3 -3
  96. package/build-module/collapsible-card/header.mjs.map +2 -2
  97. package/build-module/empty-state/title.mjs.map +2 -2
  98. package/build-module/form/primitives/field/description.mjs +17 -4
  99. package/build-module/form/primitives/field/description.mjs.map +3 -3
  100. package/build-module/form/primitives/field/details.mjs +3 -3
  101. package/build-module/form/primitives/field/details.mjs.map +2 -2
  102. package/build-module/form/primitives/field/label.mjs +3 -3
  103. package/build-module/form/primitives/field/label.mjs.map +2 -2
  104. package/build-module/form/primitives/fieldset/description.mjs +20 -4
  105. package/build-module/form/primitives/fieldset/description.mjs.map +3 -3
  106. package/build-module/form/primitives/fieldset/details.mjs +3 -3
  107. package/build-module/form/primitives/fieldset/details.mjs.map +2 -2
  108. package/build-module/form/primitives/fieldset/legend.mjs +3 -3
  109. package/build-module/form/primitives/fieldset/legend.mjs.map +2 -2
  110. package/build-module/form/primitives/input/input.mjs +23 -7
  111. package/build-module/form/primitives/input/input.mjs.map +3 -3
  112. package/build-module/form/primitives/input-layout/input-layout.mjs +10 -0
  113. package/build-module/form/primitives/input-layout/input-layout.mjs.map +3 -3
  114. package/build-module/form/primitives/select/trigger.mjs +3 -3
  115. package/build-module/form/primitives/select/trigger.mjs.map +2 -2
  116. package/build-module/form/primitives/textarea/textarea.mjs +20 -1
  117. package/build-module/form/primitives/textarea/textarea.mjs.map +3 -3
  118. package/build-module/index.mjs +2 -0
  119. package/build-module/index.mjs.map +2 -2
  120. package/build-module/link/link.mjs +16 -6
  121. package/build-module/link/link.mjs.map +3 -3
  122. package/build-module/popover/arrow.mjs +59 -0
  123. package/build-module/popover/arrow.mjs.map +7 -0
  124. package/build-module/popover/close.mjs +20 -0
  125. package/build-module/popover/close.mjs.map +7 -0
  126. package/build-module/popover/context.mjs +57 -0
  127. package/build-module/popover/context.mjs.map +7 -0
  128. package/build-module/popover/description.mjs +35 -0
  129. package/build-module/popover/description.mjs.map +7 -0
  130. package/build-module/popover/index.mjs +18 -0
  131. package/build-module/popover/index.mjs.map +7 -0
  132. package/build-module/popover/popup.mjs +105 -0
  133. package/build-module/popover/popup.mjs.map +7 -0
  134. package/build-module/popover/root.mjs +10 -0
  135. package/build-module/popover/root.mjs.map +7 -0
  136. package/build-module/popover/title.mjs +31 -0
  137. package/build-module/popover/title.mjs.map +7 -0
  138. package/build-module/popover/trigger.mjs +13 -0
  139. package/build-module/popover/trigger.mjs.map +7 -0
  140. package/build-module/popover/types.mjs +1 -0
  141. package/build-module/popover/types.mjs.map +7 -0
  142. package/build-module/text/text.mjs +20 -5
  143. package/build-module/text/text.mjs.map +3 -3
  144. package/build-module/utils/use-deprioritized-initial-focus.mjs.map +2 -2
  145. package/build-types/alert-dialog/context.d.ts +6 -3
  146. package/build-types/alert-dialog/context.d.ts.map +1 -1
  147. package/build-types/alert-dialog/popup.d.ts.map +1 -1
  148. package/build-types/alert-dialog/root.d.ts +2 -8
  149. package/build-types/alert-dialog/root.d.ts.map +1 -1
  150. package/build-types/alert-dialog/stories/index.story.d.ts +18 -6
  151. package/build-types/alert-dialog/stories/index.story.d.ts.map +1 -1
  152. package/build-types/alert-dialog/trigger.d.ts +2 -1
  153. package/build-types/alert-dialog/trigger.d.ts.map +1 -1
  154. package/build-types/alert-dialog/types.d.ts +57 -26
  155. package/build-types/alert-dialog/types.d.ts.map +1 -1
  156. package/build-types/button/button.d.ts.map +1 -1
  157. package/build-types/card/stories/index.story.d.ts.map +1 -1
  158. package/build-types/empty-state/title.d.ts.map +1 -1
  159. package/build-types/form/primitives/field/description.d.ts.map +1 -1
  160. package/build-types/form/primitives/fieldset/description.d.ts.map +1 -1
  161. package/build-types/form/primitives/input/input.d.ts.map +1 -1
  162. package/build-types/form/primitives/input-layout/input-layout.d.ts.map +1 -1
  163. package/build-types/form/primitives/textarea/textarea.d.ts.map +1 -1
  164. package/build-types/form/stories/shared.d.ts.map +1 -1
  165. package/build-types/index.d.ts +1 -0
  166. package/build-types/index.d.ts.map +1 -1
  167. package/build-types/link/link.d.ts.map +1 -1
  168. package/build-types/popover/arrow.d.ts +10 -0
  169. package/build-types/popover/arrow.d.ts.map +1 -0
  170. package/build-types/popover/close.d.ts +11 -0
  171. package/build-types/popover/close.d.ts.map +1 -0
  172. package/build-types/popover/context.d.ts +22 -0
  173. package/build-types/popover/context.d.ts.map +1 -0
  174. package/build-types/popover/description.d.ts +10 -0
  175. package/build-types/popover/description.d.ts.map +1 -0
  176. package/build-types/popover/index.d.ts +9 -0
  177. package/build-types/popover/index.d.ts.map +1 -0
  178. package/build-types/popover/popup.d.ts +11 -0
  179. package/build-types/popover/popup.d.ts.map +1 -0
  180. package/build-types/popover/root.d.ts +37 -0
  181. package/build-types/popover/root.d.ts.map +1 -0
  182. package/build-types/popover/stories/index.story.d.ts +211 -0
  183. package/build-types/popover/stories/index.story.d.ts.map +1 -0
  184. package/build-types/popover/stories/utils.d.ts +25 -0
  185. package/build-types/popover/stories/utils.d.ts.map +1 -0
  186. package/build-types/popover/test/index.test.d.ts +2 -0
  187. package/build-types/popover/test/index.test.d.ts.map +1 -0
  188. package/build-types/popover/title.d.ts +20 -0
  189. package/build-types/popover/title.d.ts.map +1 -0
  190. package/build-types/popover/trigger.d.ts +10 -0
  191. package/build-types/popover/trigger.d.ts.map +1 -0
  192. package/build-types/popover/types.d.ts +83 -0
  193. package/build-types/popover/types.d.ts.map +1 -0
  194. package/build-types/text/stories/index.story.d.ts +4 -0
  195. package/build-types/text/stories/index.story.d.ts.map +1 -1
  196. package/build-types/text/text.d.ts.map +1 -1
  197. package/build-types/utils/use-deprioritized-initial-focus.d.ts +6 -5
  198. package/build-types/utils/use-deprioritized-initial-focus.d.ts.map +1 -1
  199. package/package.json +11 -11
  200. package/src/alert-dialog/context.tsx +12 -4
  201. package/src/alert-dialog/popup.tsx +91 -33
  202. package/src/alert-dialog/root.tsx +191 -13
  203. package/src/alert-dialog/stories/index.story.tsx +116 -65
  204. package/src/alert-dialog/style.module.css +11 -0
  205. package/src/alert-dialog/test/index.test.tsx +1265 -347
  206. package/src/alert-dialog/trigger.tsx +2 -2
  207. package/src/alert-dialog/types.ts +59 -28
  208. package/src/button/button.tsx +2 -0
  209. package/src/button/style.module.css +4 -0
  210. package/src/card/stories/index.story.tsx +0 -1
  211. package/src/card/style.module.css +1 -1
  212. package/src/card/test/index.test.tsx +0 -1
  213. package/src/empty-state/title.tsx +0 -1
  214. package/src/form/primitives/field/description.tsx +6 -1
  215. package/src/form/primitives/fieldset/description.tsx +9 -1
  216. package/src/form/primitives/input/input.tsx +6 -1
  217. package/src/form/primitives/input/style.module.css +4 -0
  218. package/src/form/primitives/input-layout/input-layout.tsx +2 -0
  219. package/src/form/primitives/textarea/textarea.tsx +10 -1
  220. package/src/form/stories/shared.tsx +4 -2
  221. package/src/index.ts +1 -0
  222. package/src/link/link.tsx +2 -0
  223. package/src/link/style.module.css +10 -0
  224. package/src/popover/arrow.tsx +49 -0
  225. package/src/popover/close.tsx +24 -0
  226. package/src/popover/context.tsx +100 -0
  227. package/src/popover/description.tsx +34 -0
  228. package/src/popover/index.ts +9 -0
  229. package/src/popover/popup.tsx +106 -0
  230. package/src/popover/root.tsx +41 -0
  231. package/src/popover/stories/index.story.tsx +1315 -0
  232. package/src/popover/stories/utils.tsx +91 -0
  233. package/src/popover/style.module.css +64 -0
  234. package/src/popover/test/index.test.tsx +727 -0
  235. package/src/popover/title.tsx +50 -0
  236. package/src/popover/trigger.tsx +17 -0
  237. package/src/popover/types.ts +113 -0
  238. package/src/text/stories/index.story.tsx +4 -2
  239. package/src/text/style.module.css +26 -0
  240. package/src/text/test/index.test.tsx +1 -4
  241. package/src/text/text.tsx +8 -1
  242. package/src/utils/css/field.module.css +4 -1
  243. package/src/utils/css/focus.module.css +7 -5
  244. package/src/utils/css/global-css-defense.module.css +117 -0
  245. package/src/utils/use-deprioritized-initial-focus.ts +5 -4
@@ -0,0 +1,50 @@
1
+ import { Popover as _Popover } from '@base-ui/react/popover';
2
+ import { useMergeRefs } from '@wordpress/compose';
3
+ import { forwardRef, useLayoutEffect, useRef } from '@wordpress/element';
4
+ import { Text } from '../text';
5
+ import { usePopoverValidationContext } from './context';
6
+ import type { TitleProps } from './types';
7
+
8
+ /**
9
+ * Renders a heading that labels the popover for accessibility.
10
+ *
11
+ * **Required** — every popover must include a `Popover.Title`, even if
12
+ * visually hidden. The rendered element is linked to the popup via
13
+ * `aria-labelledby`. Uses the `heading-xl` text variant, matching Dialog.
14
+ *
15
+ * To visually hide the title while keeping it accessible, wrap it with
16
+ * `VisuallyHidden` using the `render` prop:
17
+ *
18
+ * ```jsx
19
+ * <VisuallyHidden render={ <Popover.Title /> }>
20
+ * Accessible title text
21
+ * </VisuallyHidden>
22
+ * ```
23
+ */
24
+ const Title = forwardRef< HTMLHeadingElement, TitleProps >(
25
+ function PopoverTitle(
26
+ { className, children, render, ...props },
27
+ forwardedRef
28
+ ) {
29
+ const validationContext = usePopoverValidationContext();
30
+ const internalRef = useRef< HTMLHeadingElement >( null );
31
+ const mergedRef = useMergeRefs( [ internalRef, forwardedRef ] );
32
+
33
+ useLayoutEffect( () => {
34
+ validationContext?.registerTitle( internalRef.current );
35
+ }, [ validationContext ] );
36
+
37
+ return (
38
+ <Text
39
+ ref={ mergedRef }
40
+ variant="heading-xl"
41
+ render={ <_Popover.Title render={ render } { ...props } /> }
42
+ className={ className }
43
+ >
44
+ { children }
45
+ </Text>
46
+ );
47
+ }
48
+ );
49
+
50
+ export { Title };
@@ -0,0 +1,17 @@
1
+ import { Popover as _Popover } from '@base-ui/react/popover';
2
+ import { forwardRef } from '@wordpress/element';
3
+ import type { TriggerProps } from './types';
4
+
5
+ /**
6
+ * Renders a button that toggles the popover popup when clicked.
7
+ *
8
+ * Renders as a `<button>` by default. Also supports hover-triggered
9
+ * popovers via the `openOnHover`, `delay`, and `closeDelay` props.
10
+ */
11
+ const Trigger = forwardRef< HTMLButtonElement, TriggerProps >(
12
+ function PopoverTrigger( props, ref ) {
13
+ return <_Popover.Trigger ref={ ref } { ...props } />;
14
+ }
15
+ );
16
+
17
+ export { Trigger };
@@ -0,0 +1,113 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Popover as _Popover } from '@base-ui/react/popover';
3
+ import type { ComponentProps } from '../utils/types';
4
+
5
+ export interface RootProps
6
+ extends Pick<
7
+ _Popover.Root.Props,
8
+ 'open' | 'onOpenChange' | 'defaultOpen' | 'modal'
9
+ > {
10
+ /**
11
+ * The popover sub-components (`Popover.Trigger`, `Popover.Popup`, etc.).
12
+ */
13
+ children?: ReactNode;
14
+ }
15
+
16
+ export interface TriggerProps
17
+ extends ComponentProps< 'button' >,
18
+ Pick< _Popover.Trigger.Props, 'openOnHover' | 'delay' | 'closeDelay' > {
19
+ /**
20
+ * The content to be rendered inside the component.
21
+ */
22
+ children?: ReactNode;
23
+ }
24
+
25
+ /**
26
+ * `Popover.Popup` maps to two Base UI elements internally: the
27
+ * **Positioner** (outer, handles fixed positioning and z-index) and the
28
+ * **Popup** (inner, holds content and visual styles).
29
+ *
30
+ * `style` and `className` are forwarded to the **Positioner** so that
31
+ * z-index overrides (`--wp-ui-popover-z-index`) and Base UI CSS variables
32
+ * (`--available-height`, `--available-width`) work correctly. All other
33
+ * HTML attributes are forwarded to the inner **Popup** element.
34
+ */
35
+ export interface PopupProps
36
+ extends ComponentProps< 'div' >,
37
+ Pick<
38
+ _Popover.Positioner.Props,
39
+ | 'align'
40
+ | 'alignOffset'
41
+ | 'anchor'
42
+ | 'arrowPadding'
43
+ | 'collisionAvoidance'
44
+ | 'collisionBoundary'
45
+ | 'collisionPadding'
46
+ | 'side'
47
+ | 'sideOffset'
48
+ | 'sticky'
49
+ >,
50
+ Pick< _Popover.Popup.Props, 'initialFocus' | 'finalFocus' > {
51
+ /**
52
+ * Whether to render a backdrop overlay behind the popover.
53
+ *
54
+ * Typically used with `modal` to signal that interaction with the rest
55
+ * of the page is blocked. The backdrop is a semi-transparent dark overlay.
56
+ *
57
+ * @default false
58
+ */
59
+ backdrop?: boolean;
60
+
61
+ /**
62
+ * The content to be rendered inside the component.
63
+ */
64
+ children?: ReactNode;
65
+
66
+ /**
67
+ * A parent element to render the portal into.
68
+ *
69
+ * Useful for cross-document rendering, such as rendering a popover
70
+ * in a parent document when the trigger is inside an iframe.
71
+ */
72
+ container?: _Popover.Portal.Props[ 'container' ];
73
+
74
+ /**
75
+ * The visual style variant of the popup.
76
+ *
77
+ * - `'default'` — standard surface styling with background, padding,
78
+ * border radius, and shadow.
79
+ * - `'unstyled'` — no visual treatment; useful as a blank positioning
80
+ * container for fully custom content.
81
+ *
82
+ * @default 'default'
83
+ */
84
+ variant?: 'default' | 'unstyled';
85
+ }
86
+
87
+ export interface ArrowProps extends ComponentProps< 'div' > {
88
+ /**
89
+ * Custom arrow visuals to render inside the positioned container.
90
+ */
91
+ children?: ReactNode;
92
+ }
93
+
94
+ export interface TitleProps extends ComponentProps< 'h2' > {
95
+ /**
96
+ * The title content to be rendered.
97
+ */
98
+ children?: ReactNode;
99
+ }
100
+
101
+ export interface DescriptionProps extends ComponentProps< 'p' > {
102
+ /**
103
+ * The description content to be rendered.
104
+ */
105
+ children?: ReactNode;
106
+ }
107
+
108
+ export interface CloseProps extends ComponentProps< 'button' > {
109
+ /**
110
+ * The content to be rendered inside the component.
111
+ */
112
+ children?: ReactNode;
113
+ }
@@ -1,4 +1,3 @@
1
- /* eslint-disable jsx-a11y/heading-has-content */
2
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
3
2
  import { Text } from '../index';
4
3
  import { Stack } from '../../stack';
@@ -18,6 +17,10 @@ export const Default: Story = {
18
17
  },
19
18
  };
20
19
 
20
+ /**
21
+ * Important: Setting the `variant` prop to a `heading` variant will not automatically render a heading element.
22
+ * Use the `render` prop to render a heading element with the appropriate level.
23
+ */
21
24
  export const AllVariants: Story = {
22
25
  render: () => (
23
26
  <Stack
@@ -65,4 +68,3 @@ export const WithRenderProp: Story = {
65
68
  </Stack>
66
69
  ),
67
70
  };
68
- /* eslint-enable jsx-a11y/heading-has-content */
@@ -1,7 +1,13 @@
1
1
  @layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;
2
2
 
3
3
  @layer wp-ui-components {
4
+ .text {
5
+ margin: 0;
6
+ }
7
+
4
8
  .heading-2xl {
9
+ --_gcd-heading-font-size: var(--wpds-font-size-2xl);
10
+
5
11
  font-family: var(--wpds-font-family-heading);
6
12
  font-size: var(--wpds-font-size-2xl);
7
13
  line-height: var(--wpds-font-line-height-2xl);
@@ -9,6 +15,8 @@
9
15
  }
10
16
 
11
17
  .heading-xl {
18
+ --_gcd-heading-font-size: var(--wpds-font-size-xl);
19
+
12
20
  font-family: var(--wpds-font-family-heading);
13
21
  font-size: var(--wpds-font-size-xl);
14
22
  line-height: var(--wpds-font-line-height-md);
@@ -16,6 +24,8 @@
16
24
  }
17
25
 
18
26
  .heading-lg {
27
+ --_gcd-heading-font-size: var(--wpds-font-size-lg);
28
+
19
29
  font-family: var(--wpds-font-family-heading);
20
30
  font-size: var(--wpds-font-size-lg);
21
31
  line-height: var(--wpds-font-line-height-sm);
@@ -23,6 +33,8 @@
23
33
  }
24
34
 
25
35
  .heading-md {
36
+ --_gcd-heading-font-size: var(--wpds-font-size-md);
37
+
26
38
  font-family: var(--wpds-font-family-heading);
27
39
  font-size: var(--wpds-font-size-md);
28
40
  line-height: var(--wpds-font-line-height-sm);
@@ -30,6 +42,8 @@
30
42
  }
31
43
 
32
44
  .heading-sm {
45
+ --_gcd-heading-font-size: var(--wpds-font-size-xs);
46
+
33
47
  font-family: var(--wpds-font-family-heading);
34
48
  font-size: var(--wpds-font-size-xs);
35
49
  line-height: var(--wpds-font-line-height-xs);
@@ -38,6 +52,9 @@
38
52
  }
39
53
 
40
54
  .body-xl {
55
+ --_gcd-p-font-size: var(--wpds-font-size-xl);
56
+ --_gcd-p-line-height: var(--wpds-font-line-height-xl);
57
+
41
58
  font-family: var(--wpds-font-family-body);
42
59
  font-size: var(--wpds-font-size-xl);
43
60
  line-height: var(--wpds-font-line-height-xl);
@@ -45,6 +62,9 @@
45
62
  }
46
63
 
47
64
  .body-lg {
65
+ --_gcd-p-font-size: var(--wpds-font-size-lg);
66
+ --_gcd-p-line-height: var(--wpds-font-line-height-md);
67
+
48
68
  font-family: var(--wpds-font-family-body);
49
69
  font-size: var(--wpds-font-size-lg);
50
70
  line-height: var(--wpds-font-line-height-md);
@@ -52,6 +72,9 @@
52
72
  }
53
73
 
54
74
  .body-md {
75
+ --_gcd-p-font-size: var(--wpds-font-size-md);
76
+ --_gcd-p-line-height: var(--wpds-font-line-height-sm);
77
+
55
78
  font-family: var(--wpds-font-family-body);
56
79
  font-size: var(--wpds-font-size-md);
57
80
  line-height: var(--wpds-font-line-height-sm);
@@ -59,6 +82,9 @@
59
82
  }
60
83
 
61
84
  .body-sm {
85
+ --_gcd-p-font-size: var(--wpds-font-size-sm);
86
+ --_gcd-p-line-height: var(--wpds-font-line-height-xs);
87
+
62
88
  font-family: var(--wpds-font-family-body);
63
89
  font-size: var(--wpds-font-size-sm);
64
90
  line-height: var(--wpds-font-line-height-xs);
@@ -34,10 +34,7 @@ describe( 'Text', () => {
34
34
  } );
35
35
 
36
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
- );
37
+ render( <Text render={ <h2 /> }>Section title</Text> );
41
38
 
42
39
  expect(
43
40
  screen.getByRole( 'heading', { level: 2, name: 'Section title' } )
package/src/text/text.tsx CHANGED
@@ -3,6 +3,7 @@ import clsx from 'clsx';
3
3
  import { forwardRef } from '@wordpress/element';
4
4
  import { type TextProps } from './types';
5
5
  import styles from './style.module.css';
6
+ import defenseStyles from '../utils/css/global-css-defense.module.css';
6
7
 
7
8
  /**
8
9
  * A text component for rendering content with predefined typographic variants.
@@ -17,7 +18,13 @@ export const Text = forwardRef< HTMLSpanElement, TextProps >( function Text(
17
18
  defaultTagName: 'span',
18
19
  ref,
19
20
  props: mergeProps< 'span' >( props, {
20
- className: clsx( styles[ variant ], className ),
21
+ className: clsx(
22
+ styles.text,
23
+ variant.startsWith( 'heading-' ) && defenseStyles.heading,
24
+ variant.startsWith( 'body-' ) && defenseStyles.p,
25
+ styles[ variant ],
26
+ className
27
+ ),
21
28
  } ),
22
29
  } );
23
30
 
@@ -18,7 +18,10 @@
18
18
  }
19
19
 
20
20
  .description {
21
- margin: 0;
21
+ --_gcd-p-font-size: var(--wpds-font-size-sm);
22
+ --_gcd-p-line-height: var(--wpds-font-line-height-xs);
23
+ --_gcd-p-margin: 0;
24
+
22
25
  font-family: var(--wpds-font-family-body);
23
26
  font-size: var(--wpds-font-size-sm);
24
27
  line-height: var(--wpds-font-line-height-xs);
@@ -9,14 +9,14 @@
9
9
  .outset-ring--focus-within-visible,
10
10
  .outset-ring--focus-parent-visible {
11
11
  @media not ( prefers-reduced-motion ) {
12
+ --_gcd-a-transition: outline 0.1s ease-out;
13
+
12
14
  transition: outline 0.1s ease-out;
13
15
  }
14
16
 
15
17
  /* Outline width must be kept at 0 even with a transparent color,
16
18
  or else the outline will be visible in forced-colors mode. */
17
- outline-width: 0;
18
- outline-style: solid;
19
- outline-color: transparent;
19
+ outline: 0 solid transparent;
20
20
  outline-offset: 1px;
21
21
  }
22
22
 
@@ -27,7 +27,9 @@
27
27
  .outset-ring--focus-within-except-active:focus-within:not(:has(:active)),
28
28
  .outset-ring--focus-within-visible:focus-within:has(:focus-visible),
29
29
  :focus-visible .outset-ring--focus-parent-visible {
30
- outline-width: var(--wpds-border-width-focus);
31
- outline-color: var(--wpds-color-stroke-focus-brand);
30
+ --_gcd-a-outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
31
+ --_gcd-div-outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
32
+
33
+ outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
32
34
  }
33
35
  }
@@ -0,0 +1,117 @@
1
+ /*
2
+ * Unlayered defense against wp-admin global CSS (common.css, forms.css).
3
+ *
4
+ * CSS cascade layers always lose to unlayered styles, so @wordpress/ui's
5
+ * layered component styles get overridden by wp-admin's broad bare-element
6
+ * selectors. This file provides unlayered, class-scoped defenses that
7
+ * restore control to the component.
8
+ *
9
+ * Mechanism (custom property bridge):
10
+ * .input { font-size: var(--_gcd-input-font-size, inherit); }
11
+ *
12
+ * - The class selector beats admin bare-element selectors in specificity.
13
+ * - Each property reads a custom property. The fallback values are what
14
+ * makes sense for @wordpress/ui, to minimize the number of overrides
15
+ * in each individual component stylesheet.
16
+ * - Component CSS modules only need to define the custom properties for
17
+ * the specific properties they want to override.
18
+ * - Custom property definitions aren't suppressed by cascade layers, so the
19
+ * layered value propagates through to the unlayered declaration.
20
+ *
21
+ * Usage: apply the element-specific class directly to each element that
22
+ * needs defense (e.g. defenseStyles.input on an <input>).
23
+ */
24
+
25
+ /*
26
+ * ==========================================================================
27
+ * forms.css defense - https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-admin/css/forms.css
28
+ * ==========================================================================
29
+ */
30
+
31
+ .button {
32
+ box-sizing: var(--_gcd-button-box-sizing, border-box);
33
+ font-family: var(--_gcd-button-font-family, inherit);
34
+ font-size: var(--_gcd-button-font-size, inherit);
35
+ font-weight: var(--_gcd-button-font-weight, inherit);
36
+ }
37
+
38
+ .input {
39
+ box-sizing: var(--_gcd-input-box-sizing, border-box);
40
+ font-family: var(--_gcd-input-font-family, inherit);
41
+ font-size: var(--_gcd-input-font-size, inherit);
42
+ font-weight: var(--_gcd-input-font-weight, inherit);
43
+ margin: var(--_gcd-input-margin, 0);
44
+
45
+ /* Also targets textarea elements, which the `Input` primitive can render as. We combine these textarea defenses
46
+ into the input defenses to minimize the number of tokens, but they should be split if it ever becomes necessary. */
47
+ &:is(textarea, [type="text"], [type="password"], [type="color"], [type="date"], [type="datetime"], [type="datetime-local"], [type="email"], [type="month"], [type="number"], [type="search"], [type="tel"], [type="time"], [type="url"], [type="week"]) {
48
+ box-shadow: var(--_gcd-input-box-shadow, 0 0 0 transparent);
49
+ border-radius: var(--_gcd-input-border-radius, 0);
50
+ border: var(--_gcd-input-border, none);
51
+ background-color: var(--_gcd-input-background-color, transparent);
52
+ color: var(--_gcd-input-color, var(--wpds-color-fg-interactive-neutral));
53
+
54
+ &:focus {
55
+ border-color: var(--_gcd-input-border-color-focus, var(--wp-admin-theme-color));
56
+ box-shadow: var(--_gcd-input-box-shadow-focus, none);
57
+ outline: var(--_gcd-input-outline-focus, none);
58
+ }
59
+
60
+ &:disabled {
61
+ background: var(--_gcd-input-background-disabled, transparent);
62
+ border-color: var(--_gcd-input-border-color-disabled, transparent);
63
+ box-shadow: var(--_gcd-input-box-shadow-disabled, none);
64
+ color: var(--_gcd-input-color-disabled, var(--wpds-color-fg-interactive-neutral-disabled));
65
+ }
66
+
67
+ &::placeholder {
68
+ color: var(--_gcd-input-placeholder-color, var(--wpds-color-fg-interactive-neutral-disabled));
69
+ }
70
+ }
71
+
72
+ &:is(textarea, [type="text"], [type="password"], [type="date"], [type="datetime"], [type="datetime-local"], [type="email"], [type="month"], [type="number"], [type="search"], [type="tel"], [type="time"], [type="url"], [type="week"]) {
73
+ padding: var(--_gcd-input-padding, 0);
74
+ line-height: var(--_gcd-input-line-height, inherit);
75
+ min-height: var(--_gcd-input-min-height, auto);
76
+ }
77
+ }
78
+
79
+ .textarea {
80
+ box-sizing: var(--_gcd-textarea-box-sizing, border-box);
81
+ overflow: var(--_gcd-textarea-overflow, auto);
82
+ resize: var(--_gcd-textarea-resize, block);
83
+ }
84
+
85
+ /*
86
+ * ==========================================================================
87
+ * common.css defense - https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-admin/css/common.css
88
+ * ==========================================================================
89
+ */
90
+
91
+ .div {
92
+ outline: var(--_gcd-div-outline, 0 solid transparent);
93
+ }
94
+
95
+ /* Narrowed down to p elements only, to support element multiplicity of Text component. */
96
+ p.p {
97
+ font-size: var(--_gcd-p-font-size, 13px);
98
+ line-height: var(--_gcd-p-line-height, 1.5);
99
+ margin: var(--_gcd-p-margin, 0);
100
+ }
101
+
102
+ /* Narrowed down to h[1-6] elements only, to support element multiplicity of Text component. */
103
+ :is(h1, h2, h3, h4, h5, h6).heading {
104
+ color: var(--_gcd-heading-color, var(--wpds-color-fg-content-neutral));
105
+ font-size: var(--_gcd-heading-font-size, inherit);
106
+ font-weight: var(--_gcd-heading-font-weight, var(--wpds-font-weight-medium));
107
+ margin: var(--_gcd-heading-margin, 0);
108
+ }
109
+
110
+ .a,
111
+ .a:is(:hover, :focus, :active) {
112
+ outline: var(--_gcd-a-outline, 0 solid transparent);
113
+ color: var(--_gcd-a-color, inherit);
114
+ box-shadow: var(--_gcd-a-box-shadow, none);
115
+ border-radius: var(--_gcd-a-border-radius, 0);
116
+ transition: var(--_gcd-a-transition, none);
117
+ }
@@ -1,12 +1,13 @@
1
- import type { Dialog as _Dialog } from '@base-ui/react/dialog';
1
+ import type { Popover as _Popover } from '@base-ui/react/popover';
2
2
  import { useMemo, useRef } from '@wordpress/element';
3
3
  import { tabbable } from 'tabbable';
4
4
 
5
5
  /**
6
- * Derived from Base UI's `Dialog.Popup.Props['initialFocus']`.
7
- * The same type is shared by all Base UI overlay popups (Dialog, Popover, etc.).
6
+ * The `initialFocus` type shared by Base UI overlay popups (Dialog, Popover,
7
+ * AlertDialog, etc.). We derive it from `Popover.Popup.Props` here, but it
8
+ * is identical across all overlay components.
8
9
  */
9
- type InitialFocus = _Dialog.Popup.Props[ 'initialFocus' ];
10
+ type InitialFocus = _Popover.Popup.Props[ 'initialFocus' ];
10
11
 
11
12
  /**
12
13
  * Options matching Base UI's internal tabbable configuration.