Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change minimum viewport width to be exclusive whereas the maximum width remains inclusive #1839

Merged
merged 5 commits into from
Feb 3, 2025

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Feb 1, 2025

Summary

This is a dependency to fix #1696 and blocks #1833 from being merged. This change allows for creating media queries that avoid any 1px gaps. The key code change is in OD_URL_Metric_Group::is_viewport_width_in_range() (and isViewportNeeded in detect.js). This also avoid the need to add the number 1 to the minimum viewport width which can be confusing given the known breakpoints. This instead moves the addition of 1 at the moment to od_generate_media_query() but this will be removed with resolution of #1696.

As part of this, the viewport width and height for a URL Metric are now required to be at least 1px, whereas before they could theoretically be 0px (which doesn't make sense).

While auditing the types after the above change, I also improved the type specificity with PHPStan's richer types.

Note that all of the snapshot tests for Optimization Detective, Image Prioritizer, and Embed Optimizer are all passing without any modification in this PR. This shows that the code change does not impact the output.

…th remains inclusive

This will allow for creating media queries that avoid any 1px gaps. The key code change is in `OD_URL_Metric_Group::is_viewport_width_in_range()` (and `isViewportNeeded` in `detect.js`). This also avoid the need to add the number 1 to the minimum viewport width which can be confusing given the known breakpoints. This instead moves the addition of 1 at the moment to `od_generate_media_query()` but this will be removed with resolution of #1696.

Require that a viewport width and height for a URL Metric be at least 1px.
@westonruter westonruter added [Type] Enhancement A suggestion for improvement of an existing feature [Plugin] Optimization Detective Issues for the Optimization Detective plugin labels Feb 1, 2025
Copy link

github-actions bot commented Feb 1, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: westonruter <[email protected]>
Co-authored-by: felixarntz <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

$last_link['maximum_viewport_width'] + 1 === $link['minimum_viewport_width']
$last_link['maximum_viewport_width'] === $link['minimum_viewport_width']
Copy link
Member Author

Choose a reason for hiding this comment

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

Key change. The addition of 1 is no longer needed because the previous maximum is intended to equal the next minimum.

Comment on lines 247 to 254
$groups = array();
$min_width = 0;
foreach ( $this->breakpoints as $max_width ) {
$groups[] = new OD_URL_Metric_Group( array(), $min_width, $max_width, $this->sample_size, $this->freshness_ttl, $this );
$min_width = $max_width + 1;
$groups = array();
$min_width_exclusive = 0;
foreach ( $this->breakpoints as $max_width_inclusive ) {
$groups[] = new OD_URL_Metric_Group( array(), $min_width_exclusive, $max_width_inclusive, $this->sample_size, $this->freshness_ttl, $this );
$min_width_exclusive = $max_width_inclusive;
}
$groups[] = new OD_URL_Metric_Group( array(), $min_width, PHP_INT_MAX, $this->sample_size, $this->freshness_ttl, $this );
$groups[] = new OD_URL_Metric_Group( array(), $min_width_exclusive, PHP_INT_MAX, $this->sample_size, $this->freshness_ttl, $this );
Copy link
Member Author

Choose a reason for hiding this comment

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

Key change. Note how there is no longer the need to add 1 to the maximum width to become the minimum width of the next group.

We could consider replacing PHP_INT_MAX here with null, to allow the maximum viewport width to be positive-int|null. The logic would then need to check if OD_URL_Metric_Group::is_viewport_width_in_range() the maximum_viewport_width is null, and if so it could always return true if the provided viewport width is greater than the minimum_viewport_width. This would match what I've implemented client-side in the isViewportNeeded() function to avoid exporting PHP_INT_MAX to the frontend.

Copy link
Member Author

Choose a reason for hiding this comment

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

Note that od_generate_media_query() supports this behavior, where you can either supply 0 or null to omit as the minimum viewport width to omit the min-width media query, whereas you can also supply null or PHP_INT_MAX as the maximum viewport width to omit the max-width.

Copy link
Member

Choose a reason for hiding this comment

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

I like the idea of getting rid of the need for PHP_INT_MAX here, to no longer have to export it, which in practice feels excessive anyway. I don't anticipate any viewports being wider than, let's say 10,000 px, and that's probably already excessive. I'm sure there are such cases, but in any case that's still miles away from PHP_INT_MAX :)

So no longer using a concrete number here and instead truly indicating "unbounded" makes more sense to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've implemented this in b4ea6bd

Comment on lines 220 to 221
$viewport_width >= $this->minimum_viewport_width &&
$viewport_width > $this->minimum_viewport_width &&
Copy link
Member Author

Choose a reason for hiding this comment

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

Key change!

Comment on lines -252 to +257
'minimum' => 0,
'minimum' => 1,
),
'height' => array(
'type' => 'integer',
'required' => true,
'minimum' => 0,
'minimum' => 1,
Copy link
Member Author

Choose a reason for hiding this comment

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

Important: Note how the minimum width/height for a URL Metric is now 1px (i.e. positive-int)

Comment on lines +101 to +125
* The comparison logic here corresponds with the PHP logic in `OD_URL_Metric_Group::is_viewport_width_in_range()`.
*
* @param {number} viewportWidth - Current viewport width.
* @param {URLMetricGroupStatus[]} urlMetricGroupStatuses - Viewport group statuses.
* @return {boolean} Whether URL Metrics are needed.
*/
function isViewportNeeded( viewportWidth, urlMetricGroupStatuses ) {
let lastWasLacking = false;
for ( const { minimumViewportWidth, complete } of urlMetricGroupStatuses ) {
if ( viewportWidth >= minimumViewportWidth ) {
lastWasLacking = ! complete;
} else {
break;
if ( viewportWidth === 0 ) {
return false;
}

for ( const {
minimumViewportWidth,
maximumViewportWidth,
complete,
} of urlMetricGroupStatuses ) {
if (
viewportWidth > minimumViewportWidth &&
( null === maximumViewportWidth ||
viewportWidth <= maximumViewportWidth )
) {
return complete;
}
}
return lastWasLacking;
return false;
Copy link
Member Author

Choose a reason for hiding this comment

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

Key change.

Copy link

codecov bot commented Feb 1, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 65.82%. Comparing base (d94977f) to head (b4ea6bd).
Report is 6 commits behind head on trunk.

Additional details and impacted files
@@            Coverage Diff             @@
##            trunk    #1839      +/-   ##
==========================================
- Coverage   65.89%   65.82%   -0.07%     
==========================================
  Files          88       88              
  Lines        6878     6865      -13     
==========================================
- Hits         4532     4519      -13     
  Misses       2346     2346              
Flag Coverage Δ
multisite 65.82% <100.00%> (-0.07%) ⬇️
single 38.48% <23.07%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

return null;
}
$media_attributes = array();
if ( null !== $minimum_viewport_width && $minimum_viewport_width > 0 ) {
$media_attributes[] = sprintf( '(min-width: %dpx)', $minimum_viewport_width );
$media_attributes[] = sprintf( '(min-width: %dpx)', $minimum_viewport_width + 1 );
Copy link
Member Author

@westonruter westonruter Feb 1, 2025

Choose a reason for hiding this comment

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

The addition of 1 here can be redone as part of #1833. /cc @SohamPatel46

Copy link
Member

Choose a reason for hiding this comment

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

On the issue you mentioned potential issues with half pixels. By no means a blocker, but I would think that doing this here still leaves that problem unresolved for the largest viewport's minimum width.

Is there no way to use an exclusive minimum without providing a maximum with the new range syntax?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but I figured to do that as part of #1833 since doing that change here would cause all of the snapshots to be invalidated.

Copy link
Member Author

Choose a reason for hiding this comment

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

@westonruter westonruter force-pushed the update/viewport-handling branch from ef74635 to 0dafbb7 Compare February 2, 2025 23:36
Comment on lines -246 to +247
array_map(
static function ( OD_URL_Metric $url_metric ): array {
return $url_metric->jsonSerialize();
},
$group_collection->get_flattened_url_metrics()
),
JSON_UNESCAPED_SLASHES // No need for escaped slashes since not printed to frontend.
$group_collection->get_flattened_url_metrics(),
JSON_UNESCAPED_SLASHES // No need for escaping slashes since this JSON is not embedded in HTML.
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a bonus change referenced in #1156 (comment)

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

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

@westonruter This looks great, just a few minor comments. I think it would be great to implement getting rid of PHP_INT_MAX, see https://github.com/WordPress/performance/pull/1839/files#r1939774669.

Comment on lines 101 to 104
* @phpstan-param int<0, max> $minimum_viewport_width
* @phpstan-param int<1, max> $maximum_viewport_width
* @phpstan-param int<1, max> $sample_size
* @phpstan-param int<0, max> $freshness_ttl
Copy link
Member

Choose a reason for hiding this comment

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

Neat, I didn't know you could do this :)

*/
function od_get_cache_purge_post_id(): ?int {
$queried_object = get_queried_object();
if ( $queried_object instanceof WP_Post ) {
if ( $queried_object instanceof WP_Post && $queried_object->ID > 0 ) {
Copy link
Member

Choose a reason for hiding this comment

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

Is this just to satisfy PHPStan? Seems a bit odd of a check.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it is for the sake of PHPStan. It's so we can say that the function returns a positive-int. Technically $queried_object->ID can be zero or negative since at the the_post action it can be overridden. In fact, the Customizer does this for nav menus which aren't yet saved in the DB: it provides negative values for the IDs to indicate nav menu items that haven't yet been inserted.

return null;
}
$media_attributes = array();
if ( null !== $minimum_viewport_width && $minimum_viewport_width > 0 ) {
$media_attributes[] = sprintf( '(min-width: %dpx)', $minimum_viewport_width );
$media_attributes[] = sprintf( '(min-width: %dpx)', $minimum_viewport_width + 1 );
Copy link
Member

Choose a reason for hiding this comment

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

On the issue you mentioned potential issues with half pixels. By no means a blocker, but I would think that doing this here still leaves that problem unresolved for the largest viewport's minimum width.

Is there no way to use an exclusive minimum without providing a maximum with the new range syntax?

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

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

@westonruter Thanks, LGTM!

@westonruter westonruter merged commit 5a1e472 into trunk Feb 3, 2025
22 of 24 checks passed
@westonruter westonruter deleted the update/viewport-handling branch February 3, 2025 23:03
( null === maximumViewportWidth ||
viewportWidth <= maximumViewportWidth )
) {
return complete;
Copy link
Member Author

Choose a reason for hiding this comment

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

Oops. This logic should be inverted as return ! complete;

Fixed in 4c01373 as part of #1833.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Enhancement A suggestion for improvement of an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider switch to CSS range syntax for media queries in Optimization Detective
2 participants