Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions packages/ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

## Unreleased

### Bug Fixes

- `VisuallyHidden`: Add `word-break: normal` to prevent text wrapping issues in screen reader content ([#75539](https://github.com/WordPress/gutenberg/pull/75539)).

### New Features

- Add `Text` primitive with predefined typographic variants (`heading-2xl` through `heading-sm`, `body-xl` through `body-sm`) built on design tokens ([#75870](https://github.com/WordPress/gutenberg/pull/75870)).
- Add `Card` and `CollapsibleCard` primitives ([#76252](https://github.com/WordPress/gutenberg/pull/76252)).

### Bug Fixes

- `VisuallyHidden`: Add `word-break: normal` to prevent text wrapping issues in screen reader content ([#75539](https://github.com/WordPress/gutenberg/pull/75539)).

### Enhancements

- `Notice`: Improve narrow layout by letting description and actions span the icon column when a title is present ([#76202](https://github.com/WordPress/gutenberg/pull/76202)).
- `Notice`: Use `Text` component for `Title` and `Description` typography ([#75870](https://github.com/WordPress/gutenberg/pull/75870)).

## 0.8.0 (2026-03-04)

Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export * from './icon-button';
export * as Notice from './notice';
export * from './stack';
export * as Tabs from './tabs';
export * from './text';
Comment thread
ciampo marked this conversation as resolved.
export * as Tooltip from './tooltip';
export * from './visually-hidden';
26 changes: 12 additions & 14 deletions packages/ui/src/notice/description.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import { forwardRef } from '@wordpress/element';
import { useRender, mergeProps } from '@base-ui/react';
import clsx from 'clsx';
import type { DescriptionProps } from './types';
import { Text } from '../text';
import styles from './style.module.css';

/**
* The description text for a notice.
*/
export const Description = forwardRef< HTMLDivElement, DescriptionProps >(
function NoticeDescription( { render, ...props }, ref ) {
const element = useRender( {
defaultTagName: 'div',
render,
ref,
props: mergeProps< 'div' >(
{ className: styles.description },
props
),
} );

return element;
export const Description = forwardRef< HTMLSpanElement, DescriptionProps >(
function NoticeDescription( { className, ...props }, ref ) {
return (
<Text
ref={ ref }
variant="body-md"
className={ clsx( styles.description, className ) }
{ ...props }
/>
);
}
);
8 changes: 0 additions & 8 deletions packages/ui/src/notice/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@

padding-block: var(--text-vertical-padding);

font-family: var(--wpds-font-family-heading);
font-size: var(--wpds-font-size-md);
font-weight: var(--wpds-font-weight-medium);
line-height: var(--wpds-font-line-height-sm);
color: var(--wp-ui-notice-text-color);
}

Expand All @@ -44,10 +40,6 @@

padding-block: var(--text-vertical-padding);

font-family: var(--wpds-font-family-body);
font-size: var(--wpds-font-size-md);
font-weight: var(--wpds-font-weight-regular);
line-height: var(--wpds-font-line-height-sm);
text-wrap: pretty;
color: var(--wp-ui-notice-text-color);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/notice/test/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe( 'Notice', () => {
it( 'forwards ref', () => {
const rootRef = createRef< HTMLDivElement >();
const titleRef = createRef< HTMLSpanElement >();
const descriptionRef = createRef< HTMLDivElement >();
const descriptionRef = createRef< HTMLSpanElement >();
const actionsRef = createRef< HTMLDivElement >();
const actionButtonRef = createRef< HTMLButtonElement >();
const actionLinkRef = createRef< HTMLAnchorElement >();
Expand Down Expand Up @@ -36,7 +36,7 @@ describe( 'Notice', () => {
);
expect( rootRef.current ).toBeInstanceOf( HTMLDivElement );
expect( titleRef.current ).toBeInstanceOf( HTMLSpanElement );
expect( descriptionRef.current ).toBeInstanceOf( HTMLDivElement );
expect( descriptionRef.current ).toBeInstanceOf( HTMLSpanElement );
expect( actionsRef.current ).toBeInstanceOf( HTMLDivElement );
expect( actionButtonRef.current ).toBeInstanceOf(
HTMLButtonElement
Expand Down
21 changes: 11 additions & 10 deletions packages/ui/src/notice/title.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { forwardRef } from '@wordpress/element';
import { useRender, mergeProps } from '@base-ui/react';
import clsx from 'clsx';
import type { TitleProps } from './types';
import { Text } from '../text';
import styles from './style.module.css';

/**
* A short heading that communicates the main message of the notice.
*/
export const Title = forwardRef< HTMLSpanElement, TitleProps >(
function NoticeTitle( { render, ...props }, ref ) {
const element = useRender( {
defaultTagName: 'span',
render,
ref,
props: mergeProps< 'span' >( { className: styles.title }, props ),
} );

return element;
function NoticeTitle( { className, ...props }, ref ) {
return (
<Text
ref={ ref }
variant="heading-md"
className={ clsx( styles.title, className ) }
{ ...props }
/>
);
}
);
2 changes: 1 addition & 1 deletion packages/ui/src/notice/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface TitleProps extends ComponentProps< 'span' > {
children?: ReactNode;
}

export interface DescriptionProps extends ComponentProps< 'div' > {
export interface DescriptionProps extends ComponentProps< 'span' > {
/**
* The description text of the notice.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Text } from './text';
68 changes: 68 additions & 0 deletions packages/ui/src/text/stories/index.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable jsx-a11y/heading-has-content */
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Text } from '../index';
import { Stack } from '../../stack';

const meta: Meta< typeof Text > = {
title: 'Design System/Components/Text',
component: Text,
};
export default meta;

type Story = StoryObj< typeof Text >;

export const Default: Story = {
args: {
variant: 'body-md',
children: 'The quick brown fox jumps over the lazy dog.',
},
};

export const AllVariants: Story = {
render: () => (
<Stack
direction="column"
gap="lg"
style={ { color: 'var(--wpds-color-fg-content-neutral)' } }
>
{ (
[
'heading-2xl',
'heading-xl',
'heading-lg',
'heading-md',
'heading-sm',
'body-xl',
'body-lg',
'body-md',
'body-sm',
] as const
).map( ( variant ) => (
<Stack key={ variant } direction="column" gap="xs">
<Text variant="heading-sm">{ variant }</Text>
<Text variant={ variant }>
The quick brown fox jumps over the lazy dog.
</Text>
</Stack>
) ) }
</Stack>
),
};

export const WithRenderProp: Story = {
render: () => (
<Stack direction="column" gap="md">
<Text variant="heading-2xl" render={ <h1 /> }>
Page Title
</Text>
<Text variant="heading-xl" render={ <h2 /> }>
Section Heading
</Text>
<Text variant="body-md" render={ <p /> }>
A paragraph of body text rendered as a semantic paragraph
element.
</Text>
</Stack>
),
};
/* eslint-enable jsx-a11y/heading-has-content */
67 changes: 67 additions & 0 deletions packages/ui/src/text/style.module.css
Comment thread
jameskoster marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides;

@layer wp-ui-components {
.heading-2xl {
font-family: var(--wpds-font-family-heading);
font-size: var(--wpds-font-size-2xl);
line-height: var(--wpds-font-line-height-2xl);
font-weight: var(--wpds-font-weight-medium);
}

.heading-xl {
font-family: var(--wpds-font-family-heading);
font-size: var(--wpds-font-size-xl);
line-height: var(--wpds-font-line-height-md);
Comment thread
ciampo marked this conversation as resolved.
font-weight: var(--wpds-font-weight-medium);
}

.heading-lg {
font-family: var(--wpds-font-family-heading);
font-size: var(--wpds-font-size-lg);
line-height: var(--wpds-font-line-height-sm);
font-weight: var(--wpds-font-weight-medium);
}

.heading-md {
font-family: var(--wpds-font-family-heading);
font-size: var(--wpds-font-size-md);
line-height: var(--wpds-font-line-height-sm);
font-weight: var(--wpds-font-weight-medium);
}

.heading-sm {
font-family: var(--wpds-font-family-heading);
font-size: var(--wpds-font-size-xs);
line-height: var(--wpds-font-line-height-xs);
font-weight: var(--wpds-font-weight-medium);
text-transform: uppercase;
}

.body-xl {
font-family: var(--wpds-font-family-body);
font-size: var(--wpds-font-size-xl);
line-height: var(--wpds-font-line-height-xl);
font-weight: var(--wpds-font-weight-regular);
}

.body-lg {
font-family: var(--wpds-font-family-body);
font-size: var(--wpds-font-size-lg);
line-height: var(--wpds-font-line-height-md);
font-weight: var(--wpds-font-weight-regular);
}

.body-md {
font-family: var(--wpds-font-family-body);
font-size: var(--wpds-font-size-md);
line-height: var(--wpds-font-line-height-sm);
font-weight: var(--wpds-font-weight-regular);
}

.body-sm {
font-family: var(--wpds-font-family-body);
font-size: var(--wpds-font-size-sm);
line-height: var(--wpds-font-line-height-xs);
font-weight: var(--wpds-font-weight-regular);
}
}
46 changes: 46 additions & 0 deletions packages/ui/src/text/test/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render, screen } from '@testing-library/react';
import { createRef } from '@wordpress/element';
import { Text } from '../index';

describe( 'Text', () => {
it( 'forwards ref', () => {
const ref = createRef< HTMLSpanElement >();

render( <Text ref={ ref }>Content</Text> );

expect( ref.current ).toBeInstanceOf( HTMLSpanElement );
} );

it( 'renders children', () => {
render( <Text>Hello world</Text> );

expect( screen.getByText( 'Hello world' ) ).toBeVisible();
} );

it( 'forwards props to the rendered element', () => {
render( <Text data-testid="text">Content</Text> );

expect( screen.getByTestId( 'text' ) ).toBeInTheDocument();
} );

it( 'forwards the className to the rendered element', () => {
render(
<Text data-testid="text" className="custom-class">
Content
</Text>
);

expect( screen.getByTestId( 'text' ) ).toHaveClass( 'custom-class' );
} );

it( 'supports the render prop', () => {
render(
// eslint-disable-next-line jsx-a11y/heading-has-content
<Text render={ <h2 /> }>Section title</Text>
);

expect(
screen.getByRole( 'heading', { level: 2, name: 'Section title' } )
).toBeVisible();
} );
} );
25 changes: 25 additions & 0 deletions packages/ui/src/text/text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useRender, mergeProps } from '@base-ui/react';
import clsx from 'clsx';
import { forwardRef } from '@wordpress/element';
import { type TextProps } from './types';
import styles from './style.module.css';

/**
* A text component for rendering content with predefined typographic variants.
* Built on design tokens for consistent typography across the UI.
*/
export const Text = forwardRef< HTMLSpanElement, TextProps >( function Text(
{ variant = 'body-md', render, className, ...props },
ref
) {
const element = useRender( {
render,
defaultTagName: 'span',
ref,
props: mergeProps< 'span' >( props, {
className: clsx( styles[ variant ], className ),
} ),
} );

return element;
} );
25 changes: 25 additions & 0 deletions packages/ui/src/text/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type ComponentProps } from '../utils/types';

export interface TextProps extends ComponentProps< 'span' > {
/**
* The typographic variant to apply, controlling font family, size,
* line height, and weight.
*
* @default "body-md"
*/
variant?:
| 'heading-2xl'
| 'heading-xl'
| 'heading-lg'
| 'heading-md'
| 'heading-sm'
| 'body-xl'
| 'body-lg'
| 'body-md'
| 'body-sm';

/**
* The content to be rendered inside the component.
*/
children?: React.ReactNode;
}
Loading