From 9b7d4ebcdb94a9bc6db30a4782b8cb6939158e79 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Wed, 24 Dec 2025 03:55:26 +0900 Subject: [PATCH 1/7] Add `Field` primitives --- package-lock.json | 1 + packages/ui/package.json | 1 + .../ui/src/form/primitives/field/control.tsx | 9 ++ .../src/form/primitives/field/description.tsx | 18 +++ .../ui/src/form/primitives/field/details.tsx | 36 +++++ .../ui/src/form/primitives/field/index.ts | 8 + .../ui/src/form/primitives/field/item.tsx | 9 ++ .../ui/src/form/primitives/field/label.tsx | 21 +++ .../ui/src/form/primitives/field/root.tsx | 31 ++++ .../primitives/field/stories/index.story.tsx | 137 ++++++++++++++++++ .../form/primitives/field/test/index.test.tsx | 36 +++++ .../ui/src/form/primitives/field/types.ts | 80 ++++++++++ packages/ui/src/form/primitives/index.ts | 1 + packages/ui/src/index.ts | 1 + packages/ui/src/utils/css/field.module.css | 28 ++++ packages/ui/src/utils/css/resets.module.css | 13 ++ storybook/main.js | 18 +++ 17 files changed, 448 insertions(+) create mode 100644 packages/ui/src/form/primitives/field/control.tsx create mode 100644 packages/ui/src/form/primitives/field/description.tsx create mode 100644 packages/ui/src/form/primitives/field/details.tsx create mode 100644 packages/ui/src/form/primitives/field/index.ts create mode 100644 packages/ui/src/form/primitives/field/item.tsx create mode 100644 packages/ui/src/form/primitives/field/label.tsx create mode 100644 packages/ui/src/form/primitives/field/root.tsx create mode 100644 packages/ui/src/form/primitives/field/stories/index.story.tsx create mode 100644 packages/ui/src/form/primitives/field/test/index.test.tsx create mode 100644 packages/ui/src/form/primitives/field/types.ts create mode 100644 packages/ui/src/form/primitives/index.ts create mode 100644 packages/ui/src/utils/css/field.module.css create mode 100644 packages/ui/src/utils/css/resets.module.css diff --git a/package-lock.json b/package-lock.json index 9b2592f77c29b3..e09ce96c0773ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56192,6 +56192,7 @@ "dependencies": { "@base-ui/react": "^1.0.0", "@wordpress/element": "file:../element", + "@wordpress/i18n": "file:../i18n", "clsx": "^2.1.1" }, "devDependencies": { diff --git a/packages/ui/package.json b/packages/ui/package.json index f0be6636430a18..c5ae8d2265e491 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -46,6 +46,7 @@ "dependencies": { "@base-ui/react": "^1.0.0", "@wordpress/element": "file:../element", + "@wordpress/i18n": "file:../i18n", "clsx": "^2.1.1" }, "devDependencies": { diff --git a/packages/ui/src/form/primitives/field/control.tsx b/packages/ui/src/form/primitives/field/control.tsx new file mode 100644 index 00000000000000..291522ccc7ccbf --- /dev/null +++ b/packages/ui/src/form/primitives/field/control.tsx @@ -0,0 +1,9 @@ +import { Field as _Field } from '@base-ui/react/field'; +import { forwardRef } from '@wordpress/element'; +import type { FieldControlProps } from './types'; + +export const Control = forwardRef< HTMLInputElement, FieldControlProps >( + function Control( props, ref ) { + return <_Field.Control ref={ ref } { ...props } />; + } +); diff --git a/packages/ui/src/form/primitives/field/description.tsx b/packages/ui/src/form/primitives/field/description.tsx new file mode 100644 index 00000000000000..9ecef06d862214 --- /dev/null +++ b/packages/ui/src/form/primitives/field/description.tsx @@ -0,0 +1,18 @@ +import clsx from 'clsx'; +import { Field as _Field } from '@base-ui/react/field'; +import { forwardRef } from '@wordpress/element'; +import fieldStyles from '../../../utils/css/field.module.css'; +import type { FieldDescriptionProps } from './types'; + +export const Description = forwardRef< + HTMLParagraphElement, + FieldDescriptionProps +>( function Description( { className, ...restProps }, ref ) { + return ( + <_Field.Description + ref={ ref } + className={ clsx( fieldStyles.description, className ) } + { ...restProps } + /> + ); +} ); diff --git a/packages/ui/src/form/primitives/field/details.tsx b/packages/ui/src/form/primitives/field/details.tsx new file mode 100644 index 00000000000000..f30725b9e3f142 --- /dev/null +++ b/packages/ui/src/form/primitives/field/details.tsx @@ -0,0 +1,36 @@ +import clsx from 'clsx'; +import { Field as _Field } from '@base-ui/react/field'; +import { forwardRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import fieldStyles from '../../../utils/css/field.module.css'; +import type { FieldDetailsProps } from './types'; +import { VisuallyHidden } from '../../../visually-hidden'; + +/** + * A component for showing additional information about the field, + * styled similarly to a normal `Field.Description`. + * Unlike a normal description, it can include links and other semantic elements. + * + * Although this content is not associated with the field using direct semantics, + * it is made discoverable to screen reader users via a visually hidden description, + * alerting them to the presence of additional information below. + * + * If the content only includes plain text, use `Field.Description` instead, + * so the readout is not unnecessarily verbose for screen reader users. + */ +export const Details = forwardRef< HTMLDivElement, FieldDetailsProps >( + function Details( { className, ...restProps }, ref ) { + return ( + <> + <_Field.Description render={ }> + { __( 'More details follow the field.' ) } + +
+ + ); + } +); diff --git a/packages/ui/src/form/primitives/field/index.ts b/packages/ui/src/form/primitives/field/index.ts new file mode 100644 index 00000000000000..a2a2ce945dd7d1 --- /dev/null +++ b/packages/ui/src/form/primitives/field/index.ts @@ -0,0 +1,8 @@ +import { Root } from './root'; +import { Item } from './item'; +import { Label } from './label'; +import { Description } from './description'; +import { Details } from './details'; +import { Control } from './control'; + +export { Root, Item, Label, Description, Details, Control }; diff --git a/packages/ui/src/form/primitives/field/item.tsx b/packages/ui/src/form/primitives/field/item.tsx new file mode 100644 index 00000000000000..e2430b1967d93f --- /dev/null +++ b/packages/ui/src/form/primitives/field/item.tsx @@ -0,0 +1,9 @@ +import { Field as _Field } from '@base-ui/react/field'; +import { forwardRef } from '@wordpress/element'; +import type { FieldItemProps } from './types'; + +export const Item: React.ForwardRefExoticComponent< + FieldItemProps & React.RefAttributes< HTMLDivElement > +> = forwardRef( function Item( props, ref ) { + return <_Field.Item ref={ ref } { ...props } />; +} ); diff --git a/packages/ui/src/form/primitives/field/label.tsx b/packages/ui/src/form/primitives/field/label.tsx new file mode 100644 index 00000000000000..d85560b8117bd5 --- /dev/null +++ b/packages/ui/src/form/primitives/field/label.tsx @@ -0,0 +1,21 @@ +import clsx from 'clsx'; +import { Field as _Field } from '@base-ui/react/field'; +import { forwardRef } from '@wordpress/element'; +import fieldStyles from '../../../utils/css/field.module.css'; +import type { FieldLabelProps } from './types'; + +export const Label = forwardRef< HTMLLabelElement, FieldLabelProps >( + function Label( { className, variant, ...restProps }, ref ) { + return ( + <_Field.Label + ref={ ref } + className={ clsx( + fieldStyles.label, + variant && fieldStyles[ `is-${ variant }` ], + className + ) } + { ...restProps } + /> + ); + } +); diff --git a/packages/ui/src/form/primitives/field/root.tsx b/packages/ui/src/form/primitives/field/root.tsx new file mode 100644 index 00000000000000..b924384437f868 --- /dev/null +++ b/packages/ui/src/form/primitives/field/root.tsx @@ -0,0 +1,31 @@ +import clsx from 'clsx'; +import { Field as _Field } from '@base-ui/react/field'; +import { forwardRef } from '@wordpress/element'; +import resetStyles from '../../../utils/css/resets.module.css'; +import type { FieldRootProps } from './types'; +import { Stack } from '../../../stack'; + +const DEFAULT_RENDER = ( props: React.ComponentProps< typeof Stack > ) => ( + +); + +/** + * A low-level component that associates an accessible label and description + * with a single form control element. + * + * Simply wrapping a control with this component does not guarantee + * accessible labeling. See examples for how to associate the label in different cases. + */ +export const Root = forwardRef< HTMLDivElement, FieldRootProps >( function Root( + { className, render = DEFAULT_RENDER, ...restProps }, + ref +) { + return ( + <_Field.Root + ref={ ref } + className={ clsx( resetStyles[ 'box-sizing' ], className ) } + render={ render } + { ...restProps } + /> + ); +} ); diff --git a/packages/ui/src/form/primitives/field/stories/index.story.tsx b/packages/ui/src/form/primitives/field/stories/index.story.tsx new file mode 100644 index 00000000000000..36065859aa611b --- /dev/null +++ b/packages/ui/src/form/primitives/field/stories/index.story.tsx @@ -0,0 +1,137 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { useId } from '@wordpress/element'; +import '@wordpress/theme/design-tokens.css'; +import { Field } from '../../../..'; + +const meta: Meta< typeof Field.Root > = { + title: 'Design System/Components/Form/Primitives/Field', + component: Field.Root, + subcomponents: { + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Item: Field.Item, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Label: Field.Label, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Control: Field.Control, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Description: Field.Description, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + Details: Field.Details, + }, +}; +export default meta; + +/** + * If your control component forwards refs, as well as the `aria-labelledby` and `aria-describedby` props + * to the actual underlying HTML element to be labeled, + * you can simply place your control in the `render` prop of `Field.Control`. + */ +export const Default: StoryObj< typeof Field.Root > = { + args: { + children: ( + <> + Label + } + /> + + The accessible description. + + + ), + }, +}; + +const MyNonRefForwardingControl = ( + props: React.ComponentProps< 'input' > +) => { + return ; +}; + +/** + * If your control component does not forward refs, but does forward the `id` prop + * to the actual underlying HTML element to be labeled, use the `htmlFor` prop + * of the `Field.Label` component to associate the label with the control. + * + * This is preferred over `aria-labelledby` because it allows users to click the + * label to focus the control. + */ +export const UsingHtmlFor: StoryObj< typeof Field.Root > = { + name: 'Using htmlFor', + render: ( args ) => { + const controlId = useId(); + const descriptionId = useId(); + + return ( + + Label + + + The accessible description. + + + ); + }, +}; + +/** + * If your control component does not forward refs nor the `id` prop, but does + * forward the `aria-labelledby` prop to the actual underlying HTML element to be + * labeled, use the `id` prop of the `Field.Label` component to associate the + * label with the control. + */ +export const UsingAriaLabelledby: StoryObj< typeof Field.Root > = { + name: 'Using aria-labelledby', + render: ( args ) => { + const labelId = useId(); + const descriptionId = useId(); + + return ( + + Label + + + The accessible description. + { ' ' } + + ); + }, +}; + +/** + * To add rich content (such as links) to the description, use `Field.Details`. + * + * Although this content is not associated with the field using direct semantics, + * it is made discoverable to screen reader users via a visually hidden description, + * alerting them to the presence of additional information below. + * + * If the content only includes plain text, use `Field.Description` instead, + * so the readout is not unnecessarily verbose for screen reader users. + */ +export const WithDetails: StoryObj< typeof Field.Root > = { + args: { + children: ( + <> + Label + } + /> + + Details can include{ ' ' } + + links to more information + { ' ' } + and other semantic elements. + + + ), + }, +}; diff --git a/packages/ui/src/form/primitives/field/test/index.test.tsx b/packages/ui/src/form/primitives/field/test/index.test.tsx new file mode 100644 index 00000000000000..4363826d12e527 --- /dev/null +++ b/packages/ui/src/form/primitives/field/test/index.test.tsx @@ -0,0 +1,36 @@ +import { render } from '@testing-library/react'; +import { createRef } from '@wordpress/element'; +import * as Field from '../index'; + +describe( 'Field', () => { + it( 'forwards ref', () => { + const rootRef = createRef< HTMLDivElement >(); + const itemRef = createRef< HTMLDivElement >(); + const controlRef = createRef< HTMLInputElement >(); + const labelRef = createRef< HTMLLabelElement >(); + const descriptionRef = createRef< HTMLParagraphElement >(); + const detailsRef = createRef< HTMLDivElement >(); + + render( + + + Field Label + } /> + + Field description + + + Field details + + + + ); + + expect( rootRef.current ).toBeInstanceOf( HTMLDivElement ); + expect( itemRef.current ).toBeInstanceOf( HTMLDivElement ); + expect( controlRef.current ).toBeInstanceOf( HTMLInputElement ); + expect( labelRef.current ).toBeInstanceOf( HTMLLabelElement ); + expect( descriptionRef.current ).toBeInstanceOf( HTMLParagraphElement ); + expect( detailsRef.current ).toBeInstanceOf( HTMLDivElement ); + } ); +} ); diff --git a/packages/ui/src/form/primitives/field/types.ts b/packages/ui/src/form/primitives/field/types.ts new file mode 100644 index 00000000000000..6cebda9e72e482 --- /dev/null +++ b/packages/ui/src/form/primitives/field/types.ts @@ -0,0 +1,80 @@ +import type { Field } from '@base-ui/react/field'; +import type { ComponentProps } from '../../../utils/types'; + +export type FieldRootProps = Omit< + ComponentProps< typeof Field.Root >, + | 'disabled' + // TODO: Maybe allow these when we have validation support ready. + | 'dirty' + | 'invalid' + | 'touched' + | 'validate' + | 'validationDebounceTime' + | 'validationMode' +> & { + children?: Field.Root.Props[ 'children' ]; + /** + * Whether the field is disabled. + */ + disabled?: Field.Root.Props[ 'disabled' ]; +}; + +export type FieldItemProps = ComponentProps< typeof Field.Item > & { + children?: React.ReactNode; +}; + +export type FieldLabelProps = ComponentProps< typeof Field.Label > & { + /** + * The label string, or the string and the element to associate it with. + * + * To keep things accessible, do not include other interactive + * elements such as links or buttons. + */ + children?: Field.Label.Props[ 'children' ]; + /** + * The visual variant of the label. + * + * Use 'plain' for controls like checkboxes and radio buttons. + * + * @default 'default' + */ + variant?: 'default' | 'plain'; +}; + +export type FieldControlProps = Omit< + ComponentProps< typeof Field.Control >, + 'defaultValue' +> & { + children?: Field.Control.Props[ 'children' ]; + /** + * The default value to use in uncontrolled mode. + */ + defaultValue?: Field.Control.Props[ 'defaultValue' ]; +}; + +export type FieldDescriptionProps = ComponentProps< + typeof Field.Description +> & { + /** + * The accessible description, associated using `aria-describedby`. + * + * For screen reader accessibility, this should only contain plain text, + * and no semantics such as links. + */ + children?: string; +}; + +export type FieldDetailsProps = ComponentProps< 'div' > & { + /** + * Additional information about the field, which unlike a normal description, + * can include links and other semantic elements. + * + * Although this content is not associated with the field using direct semantics, + * it is made discoverable to screen reader users via a visually hidden description, + * alerting them to the presence of additional information below. + * + * Do not use this component when the details content is only plain text, + * as it makes the readout unnecessarily verbose for screen reader users. + */ + children?: React.ReactNode; +}; diff --git a/packages/ui/src/form/primitives/index.ts b/packages/ui/src/form/primitives/index.ts new file mode 100644 index 00000000000000..090e4e93b84d07 --- /dev/null +++ b/packages/ui/src/form/primitives/index.ts @@ -0,0 +1 @@ +export * as Field from './field'; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 2f06a9456b4aa0..8efe7bdc93fcaa 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,4 +1,5 @@ export * from './box'; export * from './badge'; +export * from './form/primitives'; export * from './stack'; export * from './visually-hidden'; diff --git a/packages/ui/src/utils/css/field.module.css b/packages/ui/src/utils/css/field.module.css new file mode 100644 index 00000000000000..5c2bc2d68806dd --- /dev/null +++ b/packages/ui/src/utils/css/field.module.css @@ -0,0 +1,28 @@ +@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides; + +@layer wp-ui-utilities { + .label { + --wp-ui-field-label-line-height: var(--wpds-line-height-x-small); + + font-family: var(--wpds-font-family-body); + font-size: var(--wpds-font-size-x-small); + line-height: var(--wp-ui-field-label-line-height); + font-weight: 500; /* TODO: Use variable? */ + text-transform: uppercase; + color: var(--wpds-color-fg-content-neutral); + + &.is-plain { + font-size: var(--wpds-font-size-medium); + font-weight: 400; + text-transform: none; + } + } + + .description { + margin: 0; + font-family: var(--wpds-font-family-body); + font-size: var(--wpds-font-size-small); + line-height: var(--wpds-line-height-small); + color: var(--wpds-color-fg-content-neutral-weak); + } +} diff --git a/packages/ui/src/utils/css/resets.module.css b/packages/ui/src/utils/css/resets.module.css new file mode 100644 index 00000000000000..4c003ee93f30ed --- /dev/null +++ b/packages/ui/src/utils/css/resets.module.css @@ -0,0 +1,13 @@ +@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides; + +@layer wp-ui-utilities { + .box-sizing { + box-sizing: border-box; + + *, + *::before, + *::after { + box-sizing: inherit; + } + } +} diff --git a/storybook/main.js b/storybook/main.js index a7f0740c743404..f722c855d06f78 100644 --- a/storybook/main.js +++ b/storybook/main.js @@ -74,6 +74,24 @@ module.exports = { docs: {}, typescript: { reactDocgen: 'react-docgen-typescript', + // Should match defaults in Storybook except for the propFilter. + // https://github.com/storybookjs/storybook/blob/3e34a288c8fabc7d5b5cc43b28ae9d674c48e3ea/code/core/src/core-server/presets/common-preset.ts#L162-L168 + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + shouldRemoveUndefinedFromOptional: true, + propFilter: ( prop ) => { + if ( ! prop.parent ) { + return true; + } + + if ( /@base-ui|@ariakit/.test( prop.parent.fileName ) ) { + return true; + } + + return ! /node_modules/.test( prop.parent.fileName ); + }, + savePropValueAsString: true, + }, }, webpackFinal: async ( config ) => { // Find the `babel-loader` rule added by `@storybook/addon-webpack5-compiler-babel` From a7d0462be8cd8dc373336790ff90046ca676f07d Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Wed, 24 Dec 2025 08:30:03 +0900 Subject: [PATCH 2/7] Add changelog --- packages/ui/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index db3b650f1e42c9..28fb2b0d196bea 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -10,3 +10,4 @@ - Add `Stack` component ([#73928](https://github.com/WordPress/gutenberg/pull/73928)). - Add `VisuallyHidden` component ([#74189](https://github.com/WordPress/gutenberg/pull/74189)). +- Add `Field` primitives ([#74190](https://github.com/WordPress/gutenberg/pull/74190)). From acbec00dcd33a5c24055e7dd18b7bf8857b7760e Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Wed, 24 Dec 2025 23:05:41 +0900 Subject: [PATCH 3/7] Fix tsconfig --- packages/ui/tsconfig.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index a7d445ffa2cbbb..b853d74120bdfa 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -4,6 +4,10 @@ "compilerOptions": { "types": [ "node", "jest", "@testing-library/jest-dom" ] }, - "references": [ { "path": "../element" }, { "path": "../theme" } ], + "references": [ + { "path": "../element" }, + { "path": "../i18n" }, + { "path": "../theme" } + ], "exclude": [] } From 6de3303c9df9340925d969869cbcda63642ce443 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Tue, 30 Dec 2025 08:03:22 +0900 Subject: [PATCH 4/7] Add default value for `disabled` --- packages/ui/src/form/primitives/field/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/ui/src/form/primitives/field/types.ts b/packages/ui/src/form/primitives/field/types.ts index 6cebda9e72e482..30bd8bd35ed4a8 100644 --- a/packages/ui/src/form/primitives/field/types.ts +++ b/packages/ui/src/form/primitives/field/types.ts @@ -15,6 +15,8 @@ export type FieldRootProps = Omit< children?: Field.Root.Props[ 'children' ]; /** * Whether the field is disabled. + * + * @default false */ disabled?: Field.Root.Props[ 'disabled' ]; }; From 08004df4922e529ccf1035531bdfc268f395bd5c Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Tue, 30 Dec 2025 23:12:00 +0900 Subject: [PATCH 5/7] Revert changes to Storybook main.js --- storybook/main.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/storybook/main.js b/storybook/main.js index f722c855d06f78..a7f0740c743404 100644 --- a/storybook/main.js +++ b/storybook/main.js @@ -74,24 +74,6 @@ module.exports = { docs: {}, typescript: { reactDocgen: 'react-docgen-typescript', - // Should match defaults in Storybook except for the propFilter. - // https://github.com/storybookjs/storybook/blob/3e34a288c8fabc7d5b5cc43b28ae9d674c48e3ea/code/core/src/core-server/presets/common-preset.ts#L162-L168 - reactDocgenTypescriptOptions: { - shouldExtractLiteralValuesFromEnum: true, - shouldRemoveUndefinedFromOptional: true, - propFilter: ( prop ) => { - if ( ! prop.parent ) { - return true; - } - - if ( /@base-ui|@ariakit/.test( prop.parent.fileName ) ) { - return true; - } - - return ! /node_modules/.test( prop.parent.fileName ); - }, - savePropValueAsString: true, - }, }, webpackFinal: async ( config ) => { // Find the `babel-loader` rule added by `@storybook/addon-webpack5-compiler-babel` From b2b9b62d4d95f5b225fb92ce3feb301773d2702f Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Wed, 31 Dec 2025 02:10:11 +0900 Subject: [PATCH 6/7] Fix line height tokens --- packages/ui/src/utils/css/field.module.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/utils/css/field.module.css b/packages/ui/src/utils/css/field.module.css index 5c2bc2d68806dd..bc2f2e686faa77 100644 --- a/packages/ui/src/utils/css/field.module.css +++ b/packages/ui/src/utils/css/field.module.css @@ -2,7 +2,7 @@ @layer wp-ui-utilities { .label { - --wp-ui-field-label-line-height: var(--wpds-line-height-x-small); + --wp-ui-field-label-line-height: var(--wpds-font-line-height-x-small); font-family: var(--wpds-font-family-body); font-size: var(--wpds-font-size-x-small); @@ -22,7 +22,7 @@ margin: 0; font-family: var(--wpds-font-family-body); font-size: var(--wpds-font-size-small); - line-height: var(--wpds-line-height-small); + line-height: var(--wpds-font-line-height-x-small); color: var(--wpds-color-fg-content-neutral-weak); } } From 52139a7b64a72dff3d0cd6f0a890a527451bf122 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Wed, 31 Dec 2025 02:13:59 +0900 Subject: [PATCH 7/7] Adjust font weight for fallback --- packages/ui/src/utils/css/field.module.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ui/src/utils/css/field.module.css b/packages/ui/src/utils/css/field.module.css index bc2f2e686faa77..dd5c15f1f8142f 100644 --- a/packages/ui/src/utils/css/field.module.css +++ b/packages/ui/src/utils/css/field.module.css @@ -7,13 +7,12 @@ font-family: var(--wpds-font-family-body); font-size: var(--wpds-font-size-x-small); line-height: var(--wp-ui-field-label-line-height); - font-weight: 500; /* TODO: Use variable? */ + font-weight: 499; /* TODO: Use variable? */ text-transform: uppercase; color: var(--wpds-color-fg-content-neutral); &.is-plain { font-size: var(--wpds-font-size-medium); - font-weight: 400; text-transform: none; } }