Skip to content
Open
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
6 changes: 6 additions & 0 deletions stripe/controllers/FrmStrpLiteActionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ private static function get_appearance_rules( $settings ) {
'backgroundColor' => $settings['bg_color_active'],
),
'.Label' => array(
'fontFamily' => $settings['font'],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking: .Label is shared by every Stripe element, so this also changes the font of the Payment Element's labels (card number, expiry and so on), not only the Link email label. That is probably what you want (it matches .Input), but the PR description says the change is scoped to the asterisk. Say so in the description, and look at a card form on a theme font before merging.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, the description now says the Payment Element labels pick up the style font too. Not checked on a rendered card form (no Stripe keys here), so worth a look on a theme font before merging.

'color' => $settings['label_color'],
'fontSize' => $settings['font_size'],
'fontWeight' => $settings['weight'],
Expand All @@ -635,6 +636,11 @@ private static function get_appearance_rules( $settings ) {
),
);

if ( '' === (string) $settings['font'] ) {
// Leave the font out so Stripe uses its default stack instead of an empty value.
unset( $rules['.Label']['fontFamily'] );
}

/*
* Filters the appearance rules for Stripe elements.
*
Expand Down
90 changes: 58 additions & 32 deletions stripe/js/frmstrp.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@
colorBackground: maybeAdjustColorForStripe( frm_stripe_vars.appearanceRules[ '.Input' ].backgroundColor ),
fontSmooth: 'auto'
},
rules: frm_stripe_vars.appearanceRules
rules: getAppearanceRules()
};
elements = frmstripe.elements( { clientSecret, appearance } );
isStripeLink = true;
Expand Down Expand Up @@ -752,54 +752,80 @@
requiredIndicator.style.position = 'absolute';
requiredIndicator.style.fontSize = 'var(--font-size)';
requiredIndicator.style.top = '-4px';
requiredIndicator.style.left = `${ getEmailAsteriskOffset( cardElement ) }px`;
requiredIndicator.style.left = `${ getEmailAsteriskOffset() }px`;
requiredIndicator.style.padding = 'var(--label-padding)';
requiredIndicator.style.paddingLeft = '0';
requiredIndicator.setAttribute( 'aria-hidden', 'true' );
authenticationMountTarget.append( requiredIndicator );
} );
}

/**
* Get the appearance rules to send to Stripe.
* The Label font is limited to families the Stripe iframe can render, so the page can measure the same text.
*
* @since 6.35
*
* @return {Object} Appearance rules.
*/
function getAppearanceRules() {
const rules = frm_stripe_vars.appearanceRules;
const label = rules[ '.Label' ];
if ( ! label || ! label.fontFamily ) {
return rules;
}
return Object.assign( {}, rules, { '.Label': Object.assign( {}, label, { fontFamily: removeWebFonts( label.fontFamily ) } ) } );
}

/**
* Remove families that the page loads as web fonts from a font stack.
* The Stripe iframe does not load them, so it falls back to the rest of the stack.
*
* @since 6.35
*
* @param {string} fontFamily
* @return {string} The font stack without web fonts.
*/
function removeWebFonts( fontFamily ) {
const unquote = family => family.trim().replace( /^['"]|['"]$/g, '' ).toLowerCase();
const webFonts = Array.from( document.fonts, font => unquote( font.family ) );
const families = fontFamily.split( ',' ).filter( family => family.trim() && ! webFonts.includes( unquote( family ) ) );
return families.length ? families.map( family => family.trim() ).join( ', ' ) : 'sans-serif';
}

/**
* Create a temporary label element to determine the width of the Email label.
* The asterisk is positioned after the label that Stripe renders inside of the iframe.
* The label is styled from the same appearance rules that Stripe uses, so it does not inherit page styles.
* The probe text is the English word, so the offset can be off when Stripe renders a localized label.
*
* @since 6.35
*
* @param {Element} cardElement
* @return {number} The label width in pixels.
*/
function getEmailAsteriskOffset( cardElement ) {
const label = document.createElement( 'label' );
label.classList.add( 'frm_primary_label', 'form-label' );
label.textContent = 'Email';
label.innerHTML += ' ';

const tempContainer = document.createElement( 'div' );
tempContainer.classList.add( 'with_frm_style' );
tempContainer.style.position = 'absolute';
tempContainer.style.visibility = 'hidden';
tempContainer.style.height = '0';
tempContainer.style.overflow = 'hidden';

const formContainer = cardElement.closest( '.with_frm_style' );
if ( formContainer ) {
each(
formContainer.classList,
function( className ) {
if ( className.startsWith( 'frm_style_' ) ) {
tempContainer.classList.add( className );
return false;
}
}
);
}
function getEmailAsteriskOffset() {
const rules = getAppearanceRules()[ '.Label' ] || {};
const label = document.createElement( 'span' );
label.innerHTML = 'Email ';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking: the probe text is the English word Email, but the Link authentication element renders a localized label (its locale defaults to the visitor's browser locale). On a site where Stripe draws E-Mail or Correo electrónico the offset is still off. This was already true before the PR. If you want to close it, measure the text Stripe actually renders, or note the limit in the docblock.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Took the docblock option: getEmailAsteriskOffset() now says the probe text is English, so the offset can be off when Stripe renders a localized label. Not measuring the rendered text: it lives in the cross-origin iframe and Link has no API to read it.


tempContainer.append( label );
document.body.append( tempContainer );
Object.assign(
label.style,
{
position: 'absolute',
visibility: 'hidden',
whiteSpace: 'nowrap',
fontFamily: rules.fontFamily || 'system-ui, sans-serif',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking: when the style's Font field is blank, PHP now sends .Label.fontFamily = ''. Stripe then draws with its default stack, while this probe falls back to system-ui, sans-serif, which differs from that default on Windows and Linux, so the offset can be a few pixels off. The simplest fix is to skip the fontFamily key in get_appearance_rules() when $settings['font'] is empty, and keep one shared fallback constant in the JS.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 051aea5: get_appearance_rules() drops .Label.fontFamily when the style font is blank, so Stripe uses its default stack and the probe falls back to system-ui, sans-serif only in that case. Checked with a harness: blank font → no fontFamily on .Label (still on .Input, unchanged), Arial → kept; the old code sent an empty string. I did not add a second JS constant: the fallback is already in one place (getEmailAsteriskOffset()), and removeWebFonts() returns sans-serif for a different case (every family was a web font).

fontSize: rules.fontSize,
fontWeight: rules.fontWeight,
padding: rules.padding,
letterSpacing: 'normal',
textTransform: 'none'
}
);

document.body.append( label );
const labelWidth = label.getBoundingClientRect().width;

tempContainer.remove();
label.remove();

return labelWidth;
}
Expand Down
Loading