Skip to content

Commit

Permalink
[css-viewport] [cssom-view-1] Add a definition of the zoom CSS prop…
Browse files Browse the repository at this point in the history
…erty (#9699)

* Add definition of the zoom css property

* Spell out the complete spec

* none

* Avoid zero

* Further clarifications

* Add zoom in more places

* Fixes to formatting

* Fixes to formatting

* Replace 'or zoom' with 'unscaled'

* Fix formatting and add alt text

* Shorten notes

* Shorten heading

* Add background images

* Omit fenced frames; clarify note about iframes

* Fix spec reference type

* Fix spaces and tabs

* Fix some lint issues

* Remove stray character

* Remove more 'and zoom'

* fix unscaled

* Fix newlines

* Special-case 0 and 0%

* HTMLImageElement.{x,y} should be scaled

* Address code review feedback

* Address code review comments

* Fix flat tree

* Add notes about web compat

* Fix references that should say 'scaled'

* Clarify effective zoom

* Fix typo

Co-authored-by: Simon Pieters <[email protected]>

* Switch to used value instead of computed style

* define used value instead of claiming it as a consequence

* Clarify flat tree ancestors

---------

Co-authored-by: Simon Pieters <[email protected]>
  • Loading branch information
chrishtr and zcorpan authored Jan 30, 2024
1 parent 667d335 commit 94cfc4c
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 15 deletions.
2 changes: 2 additions & 0 deletions css-cascade-3/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ Used Values</h3>
the result of taking the <a>computed value</a>
and completing any remaining calculations to make it the absolute theoretical value
used in the formatting of the document.
The effect of the 'zoom' property is applied during this stage,
and before any calculations that require layout.

<p class='example'>
For example, a declaration of ''width: auto'' can't be resolved into a length without knowing the layout of the element's ancestors,
Expand Down
163 changes: 163 additions & 0 deletions css-viewport/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ spec: virtual-keyboard; urlPrefix: https://w3c.github.io/virtual-keyboard
type: attribute; text: overlaysContent; for: VirtualKeyboard; url: dom-virtualkeyboard-overlayscontent
</pre>

<pre class="anchors">
spec: fenced-frames; urlPrefix: https://wicg.github.io/fenced-frame/
type: interface; text: FencedFrames
</pre>

<h2 id="intro">
Introduction</h2>
Expand Down Expand Up @@ -358,6 +362,165 @@ only the value previously set to it.
}
</pre>

<h2 id='zoom-property'>
The 'zoom' property
</h2>

An element becomes zoomed when the 'zoom' property has a positive computed value different than 1
(or when a flat tree ancestor has zoom).

To apply zoom, the [=used value=] of a CSS property is pre-multiplied
(before any other steps in the [=used value=] stage)
by the [=used value=] of 'zoom' for the element.
It also multiplies the [=natural size=] of all replaced elements,
background images,
and nested frames
(except for fenced frames [[!FENCED-FRAME]])
by the [=used value=] of 'zoom'.

Note: This results in a magnification or minification effect.

Note: Since this multiplication is on [=computed value=]s, it applies to
all inherited properties such as 'line-height' and 'font-size'.

Nested values of 'zoom' multiply, resulting in additional scaling of <<length>> values.
The [=used value=] for zoom is always its [=effective zoom=].

The 'zoom' property has no effect on <<length>> property values with computed values that are 'auto' or <<percentage>>.

Note: Unlike 'transform',
scaling the 'zoom' property affects layout.

Note: The computed value of 'font-size' is never <<percentage>>;
thus 'zoom' always applies.

Note: 'zoom' does not affect or prevent 'transform' scaling.

<pre class="propdef">
Name: zoom
Value: <<number>> || <<percentage>>
Initial: 1
Applies to: all <<length>> property values of all elements
Inherited: no
Percentages: Converted to <<number>>
Media: visual
Computed value: as specified, but with <<percentage>> converted to the equivalent <<number>>
Animation type: not animatable
</pre>

The values of this property have the following meanings:

<dl dfn-for="zoom" dfn-type=value>
<dt><dfn><<number>></dfn>
<dd>
Positive floating point number indicating a zoom factor.
Numbers smaller than 1.0 indicate a "zoom out" or minification effect,
while numbers greater than 1.0 indicate a "zoom in" or magnification effect.
A 0 value is treated as if it was 1.

Note: The treatment of 0 is a web compatibility quirk.

<dt><dfn><<percentage>></dfn>
<dd>
Positive floating point number,
followed by a percentage character ("%") which indicates a zoom factor multiplied by 100.
A 0 percentage is treated as if it was 100%.

Note: The treatment of 0 is a web compatibility quirk.

</dl>

Negative values for 'zoom' are illegal.

<div class="example">
Example of the 'zoom' property applied during hover for magnification effect.

<pre>
&lt;div class="messageBox"&gt;
&lt;div class="label"&gt;Text of the label&lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.messageBox {
width: 10em;
padding: 2em;
border: medium solid lightblue;
}

.messageBox:hover {
zoom: 150%;
}

.label {
background: lightgrey;
padding: 1em;
text-align: center;
}
&lt;/style&gt;
</pre>

Here is an llustration of the before and after hover state of the message box element:
<img src="css_zoom_hover_example.png" alt="Two images,
showing the zooming effect before and after zoom has applied. The second is 1.5 larger.">
</div>

<div class="example">
Example of nested zoom.
In this example, "Inner text" is 4x as large as "Outer text",
and "Middle text" is 2x as large as "Outer text".

<pre>
&lt;div style="zoom: 2"&gt;
Middle text
&lt;div style="zoom: 2"&gt;
Inner text
&lt;div&gt;
&lt;div&gt;
Outer text
</pre>
</div>

<div class="example">
Example of replaced elements. In this example,
the image and iframe will be twice as large as their default sizing.

<pre>
&lt;div style="zoom: 2"&gt;
&lt;img src="..."&gt;
&lt;iframe src="..."&gt;&lt;/iframe&gt;
&lt;div&gt;
</pre>
</div>

The <dfn>effective zoom</dfn> of an element is the product of its computed
value of 'zoom' and all flat tree ancestors' computed values of 'zoom'.

The <dfn export>scaled</dfn> value of a CSS length is the [=used value=] of that length;
in particular it includes zoom.

The <dfn export>unscaled</dfn> value of a CSS length relative to an element is the [=scaled=] value divided by the element's [=effective zoom=].

<div class="note">
The [=effective zoom=] of an element in a nested frame may be a value other than 1 even if 'zoom' is never set on an element in that frame. This can be observed by authors via APIs such as {{Window/devicePixelRatio}} and {{Element/getBoundingClientRect}}.
</div>

<h3 id='zoom-om'>
DOM and CSSOM interaction
</h3>

Computed style APIs (i.e., all values returned by {{getComputedStyle()}}) that are non-auto and non-percentage lengths must be [=unscaled=].

The {{Element/getBoundingClientRect}},
{{Element/getClientRects}},
and {{IntersectionObserver}} APIs must return rects with [=scaled=]
lengths.

All other APIs related to element geometries must return [=unscaled=] lengths.
This is explained in detail in [[cssom-view#extensions-to-the-htmlelement-interface]].

The {{Window/devicePixelRatio}} of a frame is multiplied by the [=effective zoom=] inherited by its parent frame.
</h3>

<h2 class="no-num" id="changes">Appendix A. Changes</h2>

This appendix is <em>informative</em>.
Expand Down
Binary file added css-viewport/css_zoom_hover_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 16 additions & 15 deletions cssom-view-1/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ aborting on the first step that returns a value:
1. Return the {{DOMRect}} object in <var>list</var> at index 0.
1. If <a>caret node</a> is a text entry widget that is a replaced element,
and that is in the document,
return a {{DOMRect}} object for the caret in the widget
return a [=scaled=] {{DOMRect}} object for the caret in the widget
as represented by the <a>caret offset</a> value.
The <a>transforms</a> that apply to the element and its ancestors are applied.
1. Return null.
Expand Down Expand Up @@ -1197,7 +1197,7 @@ namely {{CheckVisibilityOptions/opacityProperty}} and {{CheckVisibilityOptions/v
The <dfn method for=Element>getClientRects()</dfn> method, when invoked, must return the result of the following algorithm:

1. If the element on which it was invoked does not have an associated [=CSS/box=] return an empty {{DOMRectList}} object and stop this algorithm.
1. If the element has an associated <a>SVG layout box</a> return a {{DOMRectList}} object containing a single {{DOMRect}} object that describes the bounding box of the element as defined by the SVG specification, applying the <a>transforms</a> that apply to the element and its ancestors.
1. If the element has an associated <a>SVG layout box</a> return a [=scaled=] {{DOMRectList}} object containing a single {{DOMRect}} object that describes the bounding box of the element as defined by the SVG specification, applying the <a>transforms</a> that apply to the element and its ancestors.
1. Return a {{DOMRectList}} object containing {{DOMRect}} objects in content order, one for each <a spec=css-break>box fragment</a>, describing its border area (including those with a height or width of zero) with the following constraints:

* Apply the <a>transforms</a> that apply to the element and its ancestors.
Expand Down Expand Up @@ -1418,24 +1418,24 @@ The <dfn attribute for=Element>scrollHeight</dfn> attribute must return the resu
The <dfn attribute for=Element>clientTop</dfn> attribute must run these steps:

1. If the element has no associated [=CSS/box=] or if the [=CSS/box=] is inline, return zero.
1. Return the computed value of the 'border-top-width' property plus the height of any scrollbar rendered between the top <a>padding edge</a> and the top <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.
1. Return the [=unscaled=] computed value of the 'border-top-width' property plus the height of any scrollbar rendered between the top <a>padding edge</a> and the top <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.

The <dfn attribute for=Element>clientLeft</dfn> attribute must run these steps:

1. If the element has no associated [=CSS/box=] or if the [=CSS/box=] is inline, return zero.
1. Return the computed value of the 'border-left-width' property plus the width of any scrollbar rendered between the left <a>padding edge</a> and the left <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.
1. Return the [=unscaled=] computed value of the 'border-left-width' property plus the width of any scrollbar rendered between the left <a>padding edge</a> and the left <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.

The <dfn attribute for=Element>clientWidth</dfn> attribute must run these steps:

1. If the element has no associated [=CSS/box=] or if the [=CSS/box=] is inline, return zero.
1. If the element is the [=root element=] and the element's <a>node document</a> is not in <a>quirks mode</a>, or if the element is <a>the <code>body</code> element</a> and the element's <a>node document</a> <em>is</em> in <a>quirks mode</a>, return the <a>viewport</a> width excluding the size of a rendered scroll bar (if any).
1. Return the width of the <a>padding edge</a> excluding the width of any rendered scrollbar between the <a>padding edge</a> and the <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.
1. Return the [=unscaled=] width of the <a>padding edge</a> excluding the width of any rendered scrollbar between the <a>padding edge</a> and the <a>border edge</a>, ignoring any <a>transforms</a> or that apply to the element and its ancestors.

The <dfn attribute for=Element>clientHeight</dfn> attribute must run these steps:

1. If the element has no associated [=CSS/box=] or if the [=CSS/box=] is inline, return zero.
1. If the element is the [=root element=] and the element's <a>node document</a> is not in <a>quirks mode</a>, or if the element is <a>the <code>body</code> element</a> and the element's <a>node document</a> <em>is</em> in <a>quirks mode</a>, return the <a>viewport</a> height excluding the size of a rendered scroll bar (if any).
1. Return the height of the <a>padding edge</a> excluding the height of any rendered scrollbar between the <a>padding edge</a> and the <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.
1. Return the [=unscaled=] height of the <a>padding edge</a> excluding the height of any rendered scrollbar between the <a>padding edge</a> and the <a>border edge</a>, ignoring any <a>transforms</a> that apply to the element and its ancestors.


<h3 id=element-scrolling-members>{{Element}} Scrolling Members</h3>
Expand Down Expand Up @@ -1553,6 +1553,7 @@ The <dfn attribute for=HTMLElement>offsetParent</dfn> attribute must return the
1. If <var>ancestor</var> is <a>closed-shadow-hidden</a> from the element and its computed value of the 'position' property is ''position/fixed'', terminate this algorithm and return null.
1. If <var>ancestor</var> is not <a>closed-shadow-hidden</a> from the element and satisfies at least one of the following, terminate this algorithm and return <var>ancestor</var>.
* The element is a containing block of absolutely-positioned descendants (regardless of whether there are any absolutely-positioned descendants).
* The element has a non-default used value of 'zoom'.
* It is <a>the <code>body</code> element</a>.
* The computed value of the 'position' property of the element is ''static'' and the ancestor is one of the following <a>HTML elements</a>: <code>td</code>, <code>th</code>, or <code>table</code>.
1. If there is no more parent of <var>ancestor</var> in the <a>flat tree</a>, terminate this algorithm and return null.
Expand All @@ -1561,8 +1562,8 @@ The <dfn attribute for=HTMLElement>offsetParent</dfn> attribute must return the
The <dfn attribute for=HTMLElement>offsetTop</dfn> attribute must return the result of running these steps:

1. If the element is <a>the <code>body</code> element</a> or does not have any associated [=CSS/box=] return zero and terminate this algorithm.
1. If the {{HTMLElement/offsetParent}} of the element is null return the y-coordinate of the top <a>border edge</a> of the first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any <a>transforms</a> that apply to the element and its ancestors, and terminate this algorithm.
1. Return the result of subtracting the y-coordinate of the top <a>padding edge</a>
1. If the {{HTMLElement/offsetParent}} of the element is null return the [=unscaled=] y-coordinate of the top <a>border edge</a> of the first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any <a>transforms</a>that apply to the element and its ancestors and terminate this algorithm.
1. Return the [=unscaled=] result of subtracting the y-coordinate of the top <a>padding edge</a>
of the first [=CSS/box=] associated with the {{HTMLElement/offsetParent}} of the element
from the y-coordinate of the top <a>border edge</a>
of the first [=CSS/box=] associated with the element,
Expand All @@ -1574,13 +1575,13 @@ The <dfn attribute for=HTMLElement>offsetTop</dfn> attribute must return the res
The <dfn attribute for=HTMLElement>offsetLeft</dfn> attribute must return the result of running these steps:

1. If the element is <a>the <code>body</code> element</a> or does not have any associated [=CSS/box=] return zero and terminate this algorithm.
1. If the {{HTMLElement/offsetParent}} of the element is null return the x-coordinate of the left <a>border edge</a> of the first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any <a>transforms</a> that apply to the element and its ancestors, and terminate this algorithm.
1. Return the result of subtracting the x-coordinate of the left <a>padding edge</a> of the first [=CSS/box=] associated with the {{HTMLElement/offsetParent}} of the element from the x-coordinate of the left <a>border edge</a> of the first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any <a>transforms</a> that apply to the element and its ancestors.
1. If the {{HTMLElement/offsetParent}} of the element is null return the [=unscaled=] x-coordinate of the left <a>border edge</a> of the first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any <a>transforms</a> that apply to the element and its ancestors, and terminate this algorithm.
1. Return the [=unscaled=] result of subtracting the x-coordinate of the left <a>padding edge</a> of the first [=CSS/box=] associated with the {{HTMLElement/offsetParent}} of the element from the x-coordinate of the left <a>border edge</a> of the first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any <a>transforms</a> that apply to the element and its ancestors.

The <dfn attribute for=HTMLElement>offsetWidth</dfn> attribute must return the result of running these steps:

1. If the element does not have any associated [=CSS/box=] return zero and terminate this algorithm.
1. Return the width of the axis-aligned bounding box
1. Return the [=unscaled=] width of the axis-aligned bounding box
of the [=border boxes=]
of all fragments generated by the element's [=principal box=],
ignoring any <a>transforms</a> that apply to the element and its ancestors.
Expand All @@ -1593,7 +1594,7 @@ The <dfn attribute for=HTMLElement>offsetWidth</dfn> attribute must return the r
The <dfn attribute for=HTMLElement>offsetHeight</dfn> attribute must return the result of running these steps:

1. If the element does not have any associated [=CSS/box=] return zero and terminate this algorithm.
1. Return the height of the axis-aligned bounding box
1. Return the [=unscaled=] height of the axis-aligned bounding box
of the [=border boxes=]
of all fragments generated by the element's [=principal box=],
ignoring any <a>transforms</a> that apply to the element and its ancestors.
Expand All @@ -1613,11 +1614,11 @@ partial interface HTMLImageElement {
};
</pre>

The <dfn attribute for=HTMLImageElement>x</dfn> attribute, on getting, must return the x-coordinate of the left <a>border edge</a> of the
The <dfn attribute for=HTMLImageElement>x</dfn> attribute, on getting, must return the [=scaled=] x-coordinate of the left <a>border edge</a> of the
first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any
<a>transforms</a> that apply to the element and its ancestors, or zero if there is no [=CSS/box=].

The <dfn attribute for=HTMLImageElement>y</dfn> attribute, on getting, must return the y-coordinate of the top <a>border edge</a> of the
The <dfn attribute for=HTMLImageElement>y</dfn> attribute, on getting, must return the [=scaled=] y-coordinate of the top <a>border edge</a> of the
first [=CSS/box=] associated with the element, relative to the <a>initial containing block</a> origin, ignoring any
<a>transforms</a> that apply to the element and its ancestors, or zero if there is no [=CSS/box=].

Expand All @@ -1637,7 +1638,7 @@ containing a list of {{DOMRect}} objects in content order that matches the follo

* For each element selected by the range, whose parent is not selected by the range, include the border areas returned by invoking {{Element/getClientRects()}} on the element.
* For each {{Text}} node selected or partially selected by the range (including when the
boundary-points are identical), include a {{DOMRect}} object (for the part that is selected, not
boundary-points are identical), include [=scaled=] {{DOMRect}} object (for the part that is selected, not
the whole line box). The bounds of these {{DOMRect}} objects are computed using font metrics;
thus, for horizontal writing, the vertical dimension of each box is determined by the font
ascent and descent, and the horizontal dimension by the text advance width. If the range covers
Expand Down

0 comments on commit 94cfc4c

Please sign in to comment.