Closed dlibby- closed 6 months ago
There are a lot of issues with Blink's / WebKit's implementation of zoom, including their behavior with stuff like getBoundingClientRect(), etc, last time I looked at it.
zoom also has some very weird effects like affecting the intrinsic sizing of images and such even when no width
/ height
is specified, which are not defined in Rossen's draft (and I just learned by chance).
Here's a super-trivial test-case: data:text/html,<!doctype html><div style="width: 100px; height: 100px; zoom: 2"></div>
document.querySelector("div").getBoundingClientRect()
returns x: 4
and y: 4
, which are completely wrong (they're reverse-zoomed even though the origin doesn't change), and width: 100
and height: 100
(which is really odd because the zoom affects the size of the box, and something like transform-origin: 0 0; transform: scale(2)
instead properly affects the result of getBoundingClientRect()
).
This is just like, the beginning of the issues with zoom, I've seen a lot of bugs go by the Chromium's bug tracker related to it, and the Blink / WebKit implementation is extremely hacky (IMO).
So unless this gets very very very well defined (and my guess is that doing that is going to be very hard because WebKit's implementation is very ad-hoc and patchy in a lot of places), I'd be opposed to do this.
cc @karlcow
One item of particular note is that Gecko does not implement zoom (see https://bugzilla.mozilla.org/show_bug.cgi?id=390936). There are unique challenges given that authors have used a -moz-transform: scale(n) fallback in the absence of zoom. Additionally, I believe there were attempts to implement zoom in terms of transforms but that was not sufficient to alleviate compat concerns.
For reference that's https://bugzilla.mozilla.org/show_bug.cgi?id=1589766. The main idea behind that was to keep sites that used stuff like:
zoom: 2;
-moz-transform-origin: 0 0;
-moz-transform: scale(2);
basically keep working. My gut feeling is that to implement zoom
in any other way we'd have to drop -moz-
prefixed transforms at the same time (which is extra risk). It mostly worked, but the main issue is that stuff that used zoom: 1
and transforms, which then resets the transform-origin and messes up, see https://bugzilla.mozilla.org/show_bug.cgi?id=1599324#c0 which is what made me turn it off.
Another quirk that you don't learn until you try that is that zoom: 0
ends up having to mean zoom: 1
for compat reasons... yay :/
Thanks for those pointers! Sounds like there are two core objections:
I did go through crbug to see I could pull out a signal of the breadth of issues and tagged the ones that I found (searched "css zoom" and skimmed the titles, so probably not exhaustive): https://bugs.chromium.org/p/chromium/issues/list?q=label%3Acss-zoom&can=1
I did a similar exercise in WebKit's bugzilla but did not discover anything unique (though again I could have missed something).
Certainly the getBoundingClientRect()
behavior stands out as particularly weird and difficult to justify the standardization of such strange behavior. I'm not entirely sure the rest would be insurmountable to document/reconcile, and as you mention there is probably other weirdness to find when digging in further.
In any case, I think this is worth having a broader discussion to get consensus on next steps, so tagging for TPAC.
The CSS Working Group just discussed zoom!
.
Based on TPAC discusssion, closing this issue out as there is no support in the WG for specifying zoom
either as-is or prescribing modifications (due to compat concerns). I'll open a new issue for a new proposal for "layout-transforms" or perhaps something more scoped that solves the use-case.
@dlibby- I filed https://bugs.chromium.org/p/chromium/issues/detail?id=1142663 feel free to modify or rephrase it.
@dlibby-
I'll open a new issue for a new proposal for "layout-transforms" or perhaps something more scoped that solves the use-case.
Is there a gh issue or other place to comment on? We're relying on both zoom and scale in our implementation of a browser-based design product, and would be happy to document our use case there if that's of any help. The Chromium bug @karlcow posted appears to be more specific, and the MapML initiative somewhat orthogonal.
@maltenuhn you are welcome to do it here.
Vivliostyle is also relying on zoom, to solve the issue Minimum font size setting in Chrome causes ruby font size problem.
I admit that using zoom: 0.5
instead of font-size: 50%
in ruby-text is an insane way. However, I can't find a better workaround for the problem that font-size: 50%
does not work correctly on Chrome because of the default setting of minimum font size.
See the following test sample, testing font-size: 50%
, zoom: 0.5
, and transform: scale(0.5)
on ruby-text: https://jsbin.com/zewaloc/edit?html,css,output
Result screenshot on Chrome:
On Chrome, with the default setting of minimum font size, ruby is displayed typographically correctly only when zoom is used. (And on Firefox, only font-size: 50%
works fine, of course.)
This zoom usage should never be recommended for normal web pages. However, for typographic apps (for print, in particular) running in browsers, Chrome's zoom property is still needed to workaround Chrome's limitations.
That seems like a Chrome bug that should be reported to them. In fact, I'd argue that the fact that zoom can be used to bypass the minimum font size in Chrome is a bug. Firefox fixed the interaction between min font size and ruby in https://bugzilla.mozilla.org/show_bug.cgi?id=1165538.
@emilio Thanks for the suggestion, I reported https://bugs.chromium.org/p/chromium/issues/detail?id=1195041
Why is there so much opposition to this? I think it's a really good way of implementing high accessibility userstyles, although it is established that other userstyle features such as @document
never went through.
@SNDST00M The way zoom currently works is probably now required for web compatibility - we can’t improve the way it works without breaking pages that rely on its current behavior. And its current behavior is too weird and inconsistent to standardize and recommend for implementation.
But there is no opposition to creating a new layout-affecting transform. It would just likely be a really big thing to get to. @dlibby- have you had a chance to open a new issue for this?
Okay, thanks for suggesting the new property and letting 1% of the internet stay "weird" 😄
FWIW Gmail uses zoom on mobile emails to force parts of them to be responsive, removing it without having a transform-effecting-layout would not be ideal.
I think a plausible path forward is incremental changes to the Webkit and Chromium implementation of zoom
to remove undesired quirks. This would make zoom
more predicable and simple to describe, and at the same time constrain it into a smaller set of behaviors that could potentially be eventually removed, or become an interoperable standard.
which are completely wrong (they're reverse-zoomed even though the origin doesn't change)
To me, this behavior makes sense. It seems that values are returned in transformed coordinate system, where 1px
unit becomes equal to 2px
in the initial (non-zoomed) coordinate system. So 8 non-zoomed pixels are equal to 4 zoomed pixels and 200 non-zoomed pixels that the zoomed element now takes up are still 100px
in the zoomed coordinate system. All CSS properties for the zoomed element, like margins, offsets etc., are set in "zoomed" pixels (e.g., to push it to the top left viewport corner, you would need to set margin: -4px
, not margin: -8px
), so it seems quite reasonable to measure it in the same coordinate system...
Zoom is very useful when showing documents. Transform doesn't work well.
Yes current implementation is hacky - that's because it's not standardized. This conversation is a catch 22. This property is not standardized, so the implementations are hacky, so we cannot standardize.
Was an issue defined for a standardised layout (flow-affecting) transform property?
Reopening to discuss https://github.com/w3c/csswg-drafts/issues/5623#issuecomment-1005274895 further. (Please reopen in the future when adding new actionable comments.)
This conversation is a catch 22. This property is not standardized, so the implementations are hacky, so we cannot standardize.
We can break the catch 22 by creating a entirely new specification from the core functional requirement
Then adding the current behaviors that are both:
A. definable acceptance criteria, and
B. are a superset + union of the core functionality and older spec
Think of the second step as a sort of progressive enhancement.
@zm-cttae I for one would be perfectly happy with a new property, which works "like" zoom
but has a clearly defined, standardized behavior, and is cross browser.
The most important aspect in my use case is the "automatic" scaling of all measurements, such as width
, border-width
, etc., even when they're set to a fractional value, like 0.5mm. Imagine a document with its dimensions defined in millimeters; zoom
allows to render the whole document, applying mm values as px
, and then zoom
as appropriate to make it readable for the user's screen size.
Let's note also that transform origin and perspective origin will need their coord offsets scaled too :)
For Utopia, we'd be strongly supportive of a new property that basically behaves like a "pixel-perfect" (i.e. not layout-affecting!) magnification / inverse - in the manner of design tools like Illustrator / Figma / Sketch.
Most authoring tools on the web would benefit from that, because in the design phase working at 2-4x magnification is frequent: maybe you're looking to tweak that boxShadow just so, or match a translate
to the visual center of an icon, comparing visual weights of fonts but in situ, ... etc. etc.
Happy to discuss this further, but part of the issue with zoom
seems pretty fundamental to me, and is that it breaks the CSS coordinate space in the page, so that CSS pixels mean different things for different elements, and basically no API accounts for that. I'm not sure what can be done to reasonably account for it? Not magically un-zooming in APIs / getComputedStyle / etc is an option, but then you cause the opposite problem, style.width = getComputedStyle().width
not round-tripping for zoomed elements, which is also broken.
For the last comment / use-case here, it seems like rather than zoom
(which behaves like layout zoom), you'd like to expose pinch-zooming (think of the mobile phone zoom) to a subset of the page, right? Though I don't understand why transform
or scale
isn't that? If you need pinch-zoom behavior, an easier solution might be allowing iframes to be pinch-zoomed separately? That doesn't have the same issue zoom
has because the coordinate space would be different across the documents. But that is probably worth a separate issue.
CSS pixels mean different things for different elements, and basically no API accounts for that.
Can we add properties/methods which return adjusted pixels, for these zoom
use cases?
Though I don't understand why transform or scale isn't that?
I tried it and it doesn't work; the dimensions are not multiplied correctly.
Here are some additional thoughts based on my research into zoom, as part of my effort to potentially deprecate and remove CSS zoom from Chromium. The research involved reading the Mozilla issue, relevant Chromium issues, comments on this issue (i.e. #5623) analyzing a number of sites, and conversations with Gmail engineers.
It may not be feasible to get all of these fixed.
Chromium's implementation is not actually all that complex -- it just multiplies all non-percentage lengths, and all font sizes including percents, by the zoom factor, for the subdocument. Nested zoom multiplies.
On all platforms, Chromium actually implements both browser zoom (ctrl-+/-) scale factor and device scale factor by using zoom on the HTML element. (And this is reflected in the computed style of the HTML element presented to JS.) So even if CSS zoom was removed the code in Chromium would not be significantly simplified.
APIs like getBoundingClientRect
and offsetWidth
divide all values by the multiplied zoom across ancestors (including self).
getBoundingClientRect
reports values that don’t make a lot of sense:
data:text/html,<!doctype html><div style="background: lightblue;width: 100px; height: 100px; zoom: 2"></div>
. Should be x=8 and y=8 since those come from margin above the zoomed element.)In general, I found that CSS zoom's superpower is a reliable way to make text, and the elements around it, zoom in or out, while retaining responsive design accessibility principles such as reflow within the content and to adjacent or wrapping content elements, and ensuring content is always readable. You can polyfill it by injecting custom variables into the entire document or with CSS pre-processors, but that may not be feasible for imported external content.
I think the visual implementation of zoom in Chromium works very well, and is consistent across all elements. Evidence includes:
The only significant problems with CSS zoom that I'm aware of are the inconsistent/illogical JS API behavior. (@emilio mentioned other complications that I'm not seeing, so I might be missing something?)
getBoundingClientRect().x
or offsetLeft
should return a physical value that includes zoom. This allows hit testing to work and fixes an existing illogical behavior. (+++)(+) The equivalent feature in Google Sheets / Docs uses custom code that modifies element styles to multiply their sizes by the desired zoom factor.
(++) Imperfect prototype version with transforms - note the background color is wrong in some areas.
(+++) offsetLeft
should only include zoom of ancestors, not self.
zoom also has some very weird effects like affecting the intrinsic sizing of images and such even when no
width
/height
is specified, which are not defined in Rossen's draft (and I just learned by chance).
I'm not totally sure what the weird effects are. @emilio could you clarify?
For what it's worth, #1487 is open for having layout-affecting transforms.
even when no width / height is specified
A rabbit hole of an issue that could bikeshed us here!
The CSS Working Group just discussed Revisiting standardization of the `zoom` property
, and agreed to the following:
RESOLVED: add "zoom" as a real interoperable thing
RESOLVED: Add the zoom property to the device adaptation / viewport spec
I discovered the odd quirk of zoom not inheriting that I was thinking of after the fact. Descendant frames reset the zoom rather than getting the zoomed layout from the iframe element, e.g.
https://jsbin.com/jibiwev/4/edit?html,output
Both of the frames in this example have the same zoom on their contents despite the top one being within a zoom: 2
styled ancestor. I think this is just an implementation quirk because we set the browser zoom as the zoom on the root of each frame so we don't want to scale up the frame by the browser zoom again - but more reasonable behavior would be for the frame's contents to be scaled by the zoom applying to its iframe
element. We can avoid double applying the browser zoom by only applying it to the root frame.
Reposted to Wikimedia Phabricator here - ⚓ T342528 RfC on use cases of standardized zoom CSS property in Wikimedia wikis
FWIW I have an implementation in Firefox in bug 1854441, but I filed #9397 and #9398 on things that make no sense to me.
I'd like to add the following use-case of morphing from one view to the next: https://codepen.io/Schepp/pen/LYNZzWY
If not with zoom
this would also work with View Transitions, but only if there's not multiple morphs running with different delays. And not without JavaScript.
Does anyone know what ended up happening? Is the zoom
here the unchanged behaviour from IE / Chrome?
Does anyone know what ended up happening? Is the
zoom
here the unchanged behaviour from IE / Chrome?
Zoom is now standardized (with some changes from how it's currently implemented in Chromium and WebKit). No browsers ship the standard yet (two are working on it).
As far as I can tell the last time this was discussed in depth was back in 2015. At the time there appeared to be consensus that it would have been better had the property never existed, and there was not a strong preference for standardizing the current implementations. However, usage on the web appears to be non-zero and relatively steady — I'd like to open another discussion on whether there is a path towards standardization.
Here is Chrome's UseCounter data for the percentage of pageloads where a zoom value that is not 1 (or 100%) is applied to any element during the style cascade. As of the opening of this issue it stands around 1% of pageloads.
I'm thinking that this would start mainly as a documentation effort (@atanassov went through this exercise previously here) — given that IE and legacy Edge are on their way out, I'm cautiously optimistic that the WebKit and Blink implementations are similar enough that there will only be a handful of behavioral differences to reconcile, if any.
One item of particular note is that Gecko does not implement zoom (see https://bugzilla.mozilla.org/show_bug.cgi?id=390936). There are unique challenges given that authors have used a
-moz-transform: scale(n)
fallback in the absence of zoom. Additionally, I believe there were attempts to implement zoom in terms of transforms but that was not sufficient to alleviate compat concerns.In terms of use cases, similar to how browsers have both pinch zoom and page zoom, this does seem like a useful primitive for web authors (and clearly there is some usage today). For full disclosure, there are Microsoft web properties that have expressed interest in using the zoom property as a low-cost solution for zooming in certain portions of webpages. They don't want to introduce overflow in the inline direction or redesign their implementation, and the zoom property has provided an almost trivial way to improve these scenarios for their users (they're experimenting with this in production today).
@chrishtr also pointed me to previous discussions related to defining an interaction model driven by maps scenarios (https://github.com/w3c/csswg-drafts/issues/5275). Perhaps that would be another lens through which to have discussions about the use case, but I don't think that would move the needle for the large number of pages that use a property that is currently implemented by more than one browser.