Skip to content

Commit ab9bf10

Browse files
ciampomirka
andauthored
Avatar: Add colored fallbacks with foreground tinting (#84824)
* Avatar: Explore colored fallbacks and foreground tinting * Avatar: Link fallback exploration changelog entry * Avatar: Cover colors near the contrast crossover * Avatar: Compare translucent fallback foregrounds * Avatar: Remove opacity exploration and improve neutral icon contrast * Avatar: Compare capped and uncapped fallback tinting * Avatar: Warn about unsupported fallback colors * Avatar: Target 3:1 contrast for person fallbacks * Avatar: Use uncapped tinting and simplify color stories * Avatar: Match fallback borders to the person icon tint * Avatar: Remove borders from colored fallbacks * Avatar: Consolidate colored examples in Storybook * Avatar: Simplify colored Storybook examples * Avatar: Keep color documentation focused on consumer use * Avatar: Reduce stories and cover color behavior in browser tests * Avatar: Let Storybook infer available controls * Avatar: Keep shared story args in the default example * Avatar: Define the default render in its story * Avatar: Use only inferred Storybook controls * Avatar: Simplify the color prop description * Avatar: Use a typed Vitest mock for color warnings --- Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: mirka <0mirka00@git.wordpress.org>
1 parent b660fbb commit ab9bf10

8 files changed

Lines changed: 310 additions & 246 deletions

File tree

‎packages/ui/CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
### Enhancements
2121

22+
- `Avatar`: Use `color` for fallback backgrounds with contrasting text and icons ([#84824](https://github.com/WordPress/gutenberg/pull/84824)).
2223
- `Avatar`: Add an optional `color` prop for an internal ring, preserving the avatar's layout size and neutral fallbacks while leaving room for an external focus ring ([#84822](https://github.com/WordPress/gutenberg/pull/84822)).
2324
- `SelectControl`: Add a `trigger` slot and `SelectControl.Trigger` subcomponent for customizing trigger props ([#84828](https://github.com/WordPress/gutenberg/pull/84828)).
2425
- `Link`: Use a small border radius for consistent focus rings in the default variant ([#84697](https://github.com/WordPress/gutenberg/pull/84697)).
@@ -32,6 +33,7 @@
3233

3334
### Bug Fixes
3435

36+
- `Avatar`: Use the strong neutral stroke color for the person fallback to provide sufficient non-text contrast ([#84824](https://github.com/WordPress/gutenberg/pull/84824)).
3537
- `Tabs`: Expand vertical tab interaction backgrounds and focus rings across the row ([#84821](https://github.com/WordPress/gutenberg/pull/84821)).
3638
- Keep the `Link` new-tab indicator with the preceding text when wrapping ([#84806](https://github.com/WordPress/gutenberg/pull/84806)).
3739
- `Select`: Keep long popup lists scrollable when item alignment falls back to anchored positioning or the popup opens through touch ([#84682](https://github.com/WordPress/gutenberg/pull/84682)).
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import warning from '@wordpress/warning';
2+
import type { RootProps } from './types';
3+
4+
export function warnAvatarColor( color: RootProps[ 'color' ] ): void {
5+
if ( color === undefined ) {
6+
return;
7+
}
8+
9+
// Skip comments and strings, and keep custom-property names as whole tokens.
10+
// This is a best-effort scan; it does not decode CSS escapes or resolve var().
11+
const tokens = color.match(
12+
/\/\*[\s\S]*?(?:\*\/|$)|"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|(?:[-\w\u0080-\uFFFF]|\\(?:[\da-fA-F]{1,6}[ \t\r\n\f]?|[^\r\n\f]))+/g
13+
);
14+
if ( tokens?.some( ( token ) => token.toLowerCase() === 'currentcolor' ) ) {
15+
warning(
16+
'Avatar.Root: `color` contains `currentColor`, which can produce insufficient contrast in colored fallbacks. Use a color that does not depend on the foreground.'
17+
);
18+
return;
19+
}
20+
21+
if ( typeof CSS === 'undefined' || ! CSS.supports ) {
22+
return;
23+
}
24+
25+
if ( ! CSS.supports( 'color', color ) ) {
26+
warning(
27+
'Avatar.Root: The browser does not support the `color` value. Use a valid CSS color supported by this browser.'
28+
);
29+
} else if (
30+
// Browsers without relative colors retain the neutral fallback.
31+
CSS.supports( 'color', 'rgb(from red r g b / 1)' ) &&
32+
! CSS.supports( 'color', `rgb(from ${ color } r g b / 1)` )
33+
) {
34+
warning(
35+
'Avatar.Root: `color` cannot be used as a colored fallback fill. Use an explicit CSS color instead of a CSS-wide keyword such as `inherit`.'
36+
);
37+
}
38+
}

‎packages/ui/src/avatar/root.tsx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Avatar as _Avatar } from '@base-ui/react/avatar';
22
import clsx from 'clsx';
33
import { forwardRef } from '@wordpress/element';
44
import type { RootProps } from './types';
5+
import { warnAvatarColor } from './dev-warnings';
56
import resetStyles from '../utils/css/resets.module.css';
67
import defenseStyles from '../utils/css/global-css-defense.module.css';
78
import styles from './style.module.css';
@@ -27,6 +28,10 @@ export const Root = forwardRef< HTMLSpanElement, RootProps >(
2728
{ size = 'md', shape = 'circle', color, className, style, ...props },
2829
ref
2930
) {
31+
if ( process.env.NODE_ENV !== 'production' ) {
32+
warnAvatarColor( color );
33+
}
34+
3035
const customProperties =
3136
color === undefined
3237
? style

0 commit comments

Comments
 (0)