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
1 change: 1 addition & 0 deletions packages/global-styles-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Revisions: close the screen on the first click of the back arrow after a revision has been selected, instead of requiring a second click ([#81897](https://github.com/WordPress/gutenberg/pull/81897)).
- Revisions: close the screen after applying a revision, which stopped happening when the screen moved into this package ([#81897](https://github.com/WordPress/gutenberg/pull/81897)).
- Color palette panel: do not render the theme colors wrapper when the theme provides no colors, which left an empty gap above the Custom section ([#81894](https://github.com/WordPress/gutenberg/pull/81894)).
- Font Library: Resolve variable font weight ranges to a single value so the previews no longer fall back to the surrounding font weight ([#81748](https://github.com/WordPress/gutenberg/pull/81748)).

### Enhancements

Expand Down
5 changes: 2 additions & 3 deletions packages/global-styles-ui/src/font-library/font-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { __experimentalText as WCText } from '@wordpress/components';
import { useContext, useEffect, useState, useRef } from '@wordpress/element';
import type { FontFamily, FontFace } from '@wordpress/core-data';
import { FontLibraryContext } from './context';
Expand Down Expand Up @@ -102,12 +101,12 @@ function FontDemo( { font, text }: FontDemoProps ) {
className="font-library__font-variant_demo-image"
/>
) : (
<WCText
<span
style={ textDemoStyle }
className="font-library__font-variant_demo-text"
>
{ text }
</WCText>
</span>
) }
</div>
);
Expand Down
63 changes: 42 additions & 21 deletions packages/global-styles-ui/src/font-library/utils/preview-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,43 @@ function findNearest( input: number, numbers: number[] ) {
return numbers[ 0 ];
}

function extractFontWeights( fontFaces: FontFace[] ): number[] {
const result: number[] = [];

fontFaces.forEach( ( face ) => {
const weights = String( face.fontWeight ).split( ' ' );
const FONT_WEIGHT_KEYWORDS: Record< string, number | undefined > = {
normal: 400,
bold: 700,
};

function isValidWeight( weight: number | undefined ): weight is number {
return (
weight !== undefined &&
Number.isFinite( weight ) &&
weight >= 1 &&
weight <= 1000
);
}

if ( weights.length === 2 ) {
const start = parseInt( weights[ 0 ] );
const end = parseInt( weights[ 1 ] );
/*
* Resolve a font-weight range (e.g. "200 900") to a single value.
* Ranges that cover 400 resolve to 400; otherwise to the closest end.
*/
function resolveFontWeight( fontWeight: FontFace[ 'fontWeight' ] ): string {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to support non-numerical values like bold or auto?

It might be a good idea to have unit tests for this function.

@juanfra juanfra Aug 20, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! auto was going through. Added a few guardrails now for that with keyword mapping and fallbacks.

Added tests here 7ced102

const weights = String( fontWeight ?? '' )
.trim()
.toLowerCase()
.split( /\s+/ )
.filter( Boolean )
.map( ( value ) => FONT_WEIGHT_KEYWORDS[ value ] ?? Number( value ) );

const [ start, end ] = weights;

if ( ! isValidWeight( start ) ) {
return '400';
}

for ( let i = start; i <= end; i += 100 ) {
result.push( i );
}
} else if ( weights.length === 1 ) {
result.push( parseInt( weights[ 0 ] ) );
}
} );
if ( weights.length !== 2 || ! isValidWeight( end ) ) {
return String( start );
}

return result;
return String( Math.min( Math.max( 400, start ), end ) );
}

/*
Expand Down Expand Up @@ -134,16 +152,19 @@ export function getFamilyPreviewStyle(
);
if ( normalFaces.length > 0 ) {
style.fontStyle = 'normal';
const normalWeights = extractFontWeights( normalFaces );
const nearestWeight = findNearest( 400, normalWeights );
style.fontWeight = String( nearestWeight ) || '400';
const normalWeights = normalFaces.map( ( face ) =>
Number( resolveFontWeight( face.fontWeight ) )
);
style.fontWeight = String(
findNearest( 400, normalWeights ) ?? 400
);
} else {
style.fontStyle =
( family.fontFace.length && family.fontFace[ 0 ].fontStyle ) ||
'normal';
style.fontWeight =
( family.fontFace.length &&
String( family.fontFace[ 0 ].fontWeight ) ) ||
resolveFontWeight( family.fontFace[ 0 ].fontWeight ) ) ||
'400';
}
}
Expand All @@ -155,6 +176,6 @@ export function getFacePreviewStyle( face: FontFace ): CSSProperties {
return {
fontFamily: formatFontFamily( face.fontFamily ),
fontStyle: face.fontStyle || 'normal',
fontWeight: face.fontWeight || '400',
fontWeight: resolveFontWeight( face.fontWeight ),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { getFacePreviewStyle, getFamilyPreviewStyle } from '../preview-styles';

describe( 'getFacePreviewStyle', () => {
it.each( [
[ '700', '700' ],
[ '200 900', '400' ],
[ '500 900', '500' ],
[ '100 300', '300' ],
[ 'bold', '700' ],
[ '500 bold', '500' ],
[ 'auto', '400' ],
[ undefined, '400' ],
] )( 'resolves %p to %p', ( fontWeight, expected ) => {
expect(
getFacePreviewStyle( { fontFamily: 'Inter', fontWeight } )
).toEqual( expect.objectContaining( { fontWeight: expected } ) );
} );
} );

describe( 'getFamilyPreviewStyle', () => {
it( 'resolves the range of the normal faces', () => {
expect(
getFamilyPreviewStyle( {
fontFamily: 'Inter',
fontFace: [ { fontStyle: 'normal', fontWeight: '150 700' } ],
} )
).toEqual( expect.objectContaining( { fontWeight: '400' } ) );
} );

it( 'picks the weight closest to regular across the normal faces', () => {
expect(
getFamilyPreviewStyle( {
fontFamily: 'Inter',
fontFace: [
{ fontStyle: 'normal', fontWeight: '700' },
{ fontStyle: 'normal', fontWeight: '500' },
],
} )
).toEqual( expect.objectContaining( { fontWeight: '500' } ) );
} );

it( 'falls back to the first face when none are normal', () => {
expect(
getFamilyPreviewStyle( {
fontFamily: 'Inter',
fontFace: [ { fontStyle: 'italic', fontWeight: '200 900' } ],
} )
).toEqual(
expect.objectContaining( {
fontStyle: 'italic',
fontWeight: '400',
} )
);
} );
} );
5 changes: 0 additions & 5 deletions tools/eslint/suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1636,11 +1636,6 @@
"count": 6
}
},
"packages/global-styles-ui/src/font-library/font-demo.tsx": {
"@wordpress/use-recommended-components": {
"count": 1
}
},
"packages/global-styles-ui/src/font-library/google-fonts-confirm-dialog.tsx": {
"@wordpress/use-recommended-components": {
"count": 5
Expand Down
Loading