diff --git a/css-overflow-4/Overview.bs b/css-overflow-4/Overview.bs index 0a4260d25ef8..ad9f7eee0167 100644 --- a/css-overflow-4/Overview.bs +++ b/css-overflow-4/Overview.bs @@ -233,8 +233,9 @@ provided by the 'overflow' property. <pre class="propdef"> Name: scrollbar-gutter -Value: auto | [ stable | always ] && both? && force? +Value: auto | stable && both? Initial: auto +Applies to: [=scroll containers=] Inherited: no Computed value: specified keyword(s) Animation type: discrete @@ -262,20 +263,29 @@ consuming space when present, are called <dfn>classic scrollbars</dfn>. Such scrollbars are usually opaque. -Whether <a>classic scrollbars</a> or <a>overlay scrollbars</a> are used is UA defined. - -The appearance and size of the scrollbar is UA defined. - -Whether scrollbars appear on the start or end edge of the box is UA defined. - -For <a>classic scrollbars</a>, -the width of the <a>scrollbar gutter</a> is the same as the width of the scrollbar. -For <a>overlay scrollbars</a>, -the width of the <a>scrollbar gutter</a>, if present, is UA defined. -However, it must not be 0, -and it must not change based on user interactions with the page or the scrollbar -even if the scrollbar itself changes. -Also, it must be the same for all elements in the page. +Whether <a>classic scrollbars</a> or <a>overlay scrollbars</a> are used, +the appearance and size of the scrollbar, +and whether scrollbars appear on the start or end edge of the box, +is UA defined. + +Note: Which side a scrollbar appears on may depend on +operating system conventions, +[=bidirectionality=], +or other ergonomic considerations. + +In the case of <a>classic scrollbars</a>, +the width of the <a>scrollbar gutter</a>, +if present (see below), +is the same as the width of the scrollbar. +In the case of <a>overlay scrollbars</a>, +no <a>scrollbar gutter</a> is present. + +Note: There are known use cases that could be addressed +by enabling <a>scrollbar gutters</a> +for <a>overlay scrollbars</a>, +but no satisfactory design has been agreed to so far. +This could be addressed by future extensions of this property. +See [[#sbg-ext]]. The values of this property have the following meaning: @@ -287,30 +297,26 @@ The values of this property have the following meaning: <a>Overlay scrollbars</a> do not consume space. <dt><dfn>stable</dfn> - <dd>The <a>scrollbar gutter</a> is present when - the scrollbar is a <a>classic scrollbar</a> + <dd>The <a>scrollbar gutter</a> is present + for <a>classic scrollbars</a> when 'overflow' is ''overflow/hidden', ''overflow/scroll'', or ''overflow/auto'', regardless of whether the box is actually overflowing. - The <a>scrollbar gutter</a> is not present - when the scrollbar is an <a>overlay scrollbar</a>. + <a>Overlay scrollbars</a> do not consume space. - <dt><dfn>always</dfn> - <dd>The <a>scrollbar gutter</a> is always present when - 'overflow' is ''overflow/scroll'' or ''overflow/auto'', - regardless of the type of scrollbar or - of whether the box is overflowing. + Note: This does not change whether the scrollbar itself is visible, + only the presence of a gutter is affected. <dt><dfn>both</dfn> <dd>If a <a>scrollbar gutter</a> would be present on one of the inline start edge or the inline end edge of the box, another <a>scrollbar gutter</a> must be present on the opposite edge as well. - <dt><dfn>force</dfn> - <dd>When the ''scrollbar-gutter/force'' keyword is present - ''scrollbar-gutter/stable'' and ''scrollbar-gutter/always'' take effect - when 'overflow' is ''overflow/visible'', ''overflow/hidden'' or ''overflow/clip'' - in addition ''overflow/auto'' or ''overflow/scroll''. - This does not cause a scrollbar to be displayed, only a <a>scrollbar gutter</a>. + Issue: ''both'' refers to both-sides, + but that may not be obvious to authors without reference to the specification or documentation. + <code>both-sides</code> would be clearer, but longer. + <code>symmetrical</code> is also clearer, but also longer and harder to spell. + While still not as short, + <code>mirror</code> might be a good compromise between length and understandability. </dl> When the <a>scrollbar gutter</a> is present but the scrollbar is not, @@ -319,106 +325,67 @@ the background of the <a>scrollbar gutter</a> must be painted as an extension of <div class=note> Note: The following table summarizes the interaction of 'overflow' and 'scrollbar-gutter' -for different types of scrollbars, -showing in which case space is reserved for the <a>scrollbar gutter</a>. +showing in which case space is reserved for a <a>classic scrollbar</a>'s <a>scrollbar gutter</a>. + +<style> +.data.complex tbody th:last-of-type { + border-right: solid black 2px; +} +.data.complex tbody tr:nth-of-type(2n) td, +.data.complex tbody tr:nth-of-type(2n) th:not([rowspan]) { + background: rgba(127, 127, 127, 0.25); +} +</style> -<table class=data> +<table class="data complex"> <caption> - Should space be reserved for the [=scrollbar gutter=]?</caption> + Should a <a>classic scrollbar</a>'s <a>scrollbar gutter</a> be present?</caption> <thead> - <tr> - <td> - <td> - <th colspan=2>Classic scrollbars - <th colspan=2>Overlay scrollbars <tr> <th>'overflow' <th>'scrollbar-gutter' <th>Overflowing <th>Not overflowing - <th>Overflowing - <th>Not overflowing </thead> <tr> - <th rowspan=3>''overflow/scroll'' + <th rowspan=2>''overflow/scroll'' <th>''scrollbar-gutter/auto'' <td>yes <td>yes - <td> - <td> <tr> <th>''scrollbar-gutter/stable'' <td>yes <td>yes - <td> - <td> <tr> - <th>''scrollbar-gutter/always'' - <td>yes - <td>yes - <td>yes - <td>yes - <tr> - <th rowspan=3>''overflow/auto'' + <th rowspan=2>''overflow/auto'' <th>''scrollbar-gutter/auto'' <td>yes <td> - <td> - <td> <tr> <th>''scrollbar-gutter/stable'' <td>yes <td>yes - <td> - <td> - <tr> - <th>''scrollbar-gutter/always'' - <td>yes - <td>yes - <td>yes - <td>yes <tr> - <th rowspan=3>''overflow/hidden'' + <th rowspan=2>''overflow/hidden'' <th>''scrollbar-gutter/auto'' <td> <td> - <td> - <td> <tr> <th>''scrollbar-gutter/stable'' <td>yes <td>yes - <td> - <td> - <tr> - <th>''scrollbar-gutter/always'' - <td>if ''force'' - <td>if ''force'' - <td>if ''force'' - <td>if ''force'' <tr> - <th rowspan=3>''overflow/visible'', ''overflow/clip'' + <th rowspan=2>''overflow/visible'', ''overflow/clip'' <th>''scrollbar-gutter/auto'' <td> <td> - <td> - <td> <tr> <th>''scrollbar-gutter/stable'' - <td>if ''force'' - <td>if ''force'' <td> <td> - <tr> - <th>''scrollbar-gutter/always'' - <td>if ''force'' - <td>if ''force'' - <td>if ''force'' - <td>if ''force'' </table> </div> - <h2 id="text-overflow" caniuse="text-overflow">Overflow Ellipsis: the 'text-overflow' property</h2> <pre class="propdef"> @@ -1480,8 +1447,378 @@ that effect. </td></tr></table> </div> -Privacy and Security Considerations {#priv-sec} -=============================================== +<h2 id=sbg-ext class=nonum> +Appendix A: Possible extensions for ''scrollbar-gutter''</h2> + +<i>This section is non-normative.</i> + +<div class=non-normative> + +Issue: This section documents current attempts +at extending the 'scrollbar-gutter' property +to solve additional use cases. +However, it does not currently have consensus. +It is presented here to encourage discussion, +but non-experimental implementation is not recommended. + + <div class=example> + This example exercises all the additional values of the 'scrollbar-gutter' property: + * ''scrollbar-gutter: always force'' on the header/toolbar + * ''scrollbar-gutter: always'' on the scroll container + * ''scrollbar-gutter: match-parent'' on each row inside the scroll container + + <figure> + <figcaption>With classic scrollbars</figcaption> + <img srcset="images/scrollbar-gutter-complex-classic.png 2x"> + </figure> + <figure> + <figcaption>With overlay scrollbars</figcaption> + <img srcset="images/scrollbar-gutter-complex-overlay.png 2x"> + </figure> + </div> + +<pre class="propdef partial"> +Name: scrollbar-gutter +New Values: auto | [ [ stable | always ] && both? && force? ] || match-parent +Applies to: all elements +</pre> + +For <a>overlay scrollbars</a>, +the precise width of the <a>scrollbar gutter</a>, if present, is UA defined. +However, it must not be 0, +and it must not change based on user interactions with the page or the scrollbar +even if the scrollbar itself changes, +with the expectation that it covers +the width of the overlay scrollbar in its widest form, +to the extent that this is well defined. + +The new values of this property have the following meaning: + +<dl dfn-for="scrollbar-gutter" dfn-type=value> + <dt><dfn>always</dfn> + <dd> + The <a>scrollbar gutter</a> is always present when + 'overflow' is ''overflow/scroll'', ''overflow/hidden'', or ''overflow/auto'', + regardless of the type of scrollbar or + of whether the box is overflowing. + + <div class=example> + ''scrollbar-gutter: always'' can be bused to solve the problem + of (small) interactive elements near the edge of the element + getting covered by an appearing overlay scrollbar. + A representative case would be a basic todo list, + with each line starting with some text and ending with a right-aligned checkbox. + With a classic scrollbar, everything is fine, + but an overlay scrollbar could obscure the check boxes and make them hard to interact with. + + <figure> + <figcaption>Checkboxes adjacent to a classic scrollbar</figcaption> + <img + src=images/todo-classic.png + alt=" + A scrollable todo list with checkboxes on the right edge, + adjecent to the scrollbar. + This situation poses no particular problem."> + </figure> + <figure> + <figcaption>Checkboxes and an overlay scrollbar classic scrollbar</figcaption> + <img + src=images/todo-overlay.png + alt=" + A scrollable todo list with checkboxes on the right edge. + When the overlay scrollbar is hidden, + the situation poses no particular problem, + but when it pops in, + it covers the checkboxes, + getting in the way of interacting with them."> + </figure> + + Overlay Scrollbar are typically transient and disappear when not interacted with, + so the checkboxes they cover are not impossible to use. + But when the scrollbar is shown it does get in the way, + and that makes for an awkward interaction. + The author might try and solve the problem by adding some right padding, + but (1) how much?, + and (2) that padding isn't needed in the case of classic scrollbars. + ''scrollbar-gutter: always'' solves this problem, + yielding an identical result in first case of classic scrollbars, + but adding the desired gutter with overlay scrollbars: + + <figure> + <figcaption>Checkboxes and an overlay scrollbar and ''scrollbar-gutter: always''</figcaption> + <img + src=images/todo-overlay-always.png + alt=" + A scrollable todo list with checkboxes on the right edge, + shifted left by a gutter. + Whether the overlay scrollbar is hidden or visible, + the checkboxes remain uncovered, + and can be interacted with."> + </figure> + </div> + + <div class=issue> + Apple is reluctant to add this value, + as authors may use it too broadly, + inserting gutters with overlay scrollbars even when not justified by interactive elements, + defeating the space-saving advantage of overlay scrollbars. + + An alternative solution has been suggested: + as the focus is interactive elements, + maybe we could have a property that applies to the elements + that needs to avoid being under the scrollbar. + When turned on, it would enlarge the right or left margin of the element as appropriate + by just the right value + to push it out from under an overlay scrollbar if that's where it would end up, + but would leave the element unchanged otherwise. + + Possibly, an addition toggle would cause the element + to enlarge both its inline-end and inline-start margins or neither, + rather than just one. + This could typically be useful for block-level descendants of the scroller + with visible borders or background: + adding space on one side to avoid collisions with the overlay scrollbar + would make them look off-center when the scrollbar disapears. + Increasing the margin on both sides avoids that. + + Yet another possibility is to have a choice between growing the margin to protect the element, + or growing the padding to protect the element's content. + + The syntax could be something like <code>scrollbar-avoid: none | [self | content] && mirror?</code>. + + An interesting consideration is that this may alleviate the need for ''scrollbar-gutter: match-parent'', + as it seems that situations that would have been addressed by ''scrollbar-gutter: stable'' or ''scrollbar-gutter: always'' on the parent + and ''scrollbar-gutter: match-parent'' on the select children + could instead be addressed by leaving the parent as ''scrollbar-gutter: auto'' + and using <code>scrollbar-avoid: self</code> or <code>scrollbar-avoid: content</code> on the relevant children. + </div> + + <dt><dfn>force</dfn> + <dd> + When the ''scrollbar-gutter/force'' keyword is present + ''scrollbar-gutter/stable'' and ''scrollbar-gutter/always'' take effect + when 'overflow' is ''overflow/visible'', ''overflow/hidden'' or ''overflow/clip'' + in addition ''overflow/auto'' or ''overflow/scroll''. + This does not cause a scrollbar to be displayed, only a <a>scrollbar gutter</a>. + + <div class=example> + This value enables authors to reserve + the same amount of space on the edges of an element that is adjacent to a scroller + as is being reserved in the scroller itself, + so that their content would visually line up. + The same effect could be achieved by adding padding for classic scrollbars and none for overlay scrollbars, + but without this there is no reliable way for authors to know if they should be adding that padding, + and if so how much. + + A concrete example can be seen (at the time of writing) in the UI of Gmail: + it attempts, not always successfully, + to line up the controls in the toolbar above the list of mails with those in the list of mails. + Here are two screenshots, + one with classic scrollbars turned on (at the OS level), + where Gmail correctly guesses how much padding it should be adding to the toolbar above the list, + and one with overlay scrollbars turned on, + where Gmail incorrectly adds padding, + throwing things out of alignment + (orange dotted line added manually to highlight the point under discussion). + + <figure> + <figcaption>The Gmail UI with classic scrollbars</figcaption> + <img srcset="images/gmail-classic.png 2x" + alt=" + Icons in and out of the scroller are properly aligned: + those in the scroller are shifted left from the right edge of their container due to the scrollbar + while those outside of it are shifted left by the same amount through padding."> + </figure> + + <figure> + <figcaption>The Gmail UI with overlay scrollbars</figcaption> + <img srcset="images/gmail-overlay.png 2x" + alt=" + Icons in and out of the scroller are not aligned: + those in the scroller are flush to the right edge of the container + while those outside of it are shifted left due to unnecessary padding."> + </figure> + + Creating this spacing in the toolbar + using ''scrollbar-gutter: stable force'' rather than padding + would keep icons in alignment in both cases, + as well as on systems where the scrollbar has an atypical size. + </div> + + Issue: It is because of this value that the property has been be made to apply to all elements, + rather than merely to [=scroll containers=], + so that it can apply to ''overflow: visible'' elements as shown in the example. + This could pause implementations difficulties, + as user agents cannot just rely on existing code to place the gutter, + since they may have to do so on elements which previously could not have one. + Restricting it to all elements where 'overflow' applies + would probably have no negative effect on use cases + and could make implementation easier; + however, even that may still be more difficult + that limiting it to [=scroll containers=]. + + Issue: In addition to the implementation challenges mentioned above, + it is not clear that this value solves the problem as reliably as it intends to. + As the size and side of the scrollbar and its gutter are UA defined, + they may vary between different elements. + Since the appearance and position of scrollbars are up to the user agent, + there is no limit to the list of properties that might influence them. + It is probable that setting properties such as 'direction' (via setting the HTML <a for=HTMLElement attribute>dir</a> attribute) + or 'scrollbar-width' + could give enough context to the user agent to know how it would create a scrollbar on that element + if it were scrollable, + and therefore to create a gutter of the same size at the right place, + but it cannot be guaranteed. + + Issue: In early iterations of the specification, + this value was the only way to achieve the effect described in the example. + However, since then, ''scrollbar-gutter: stable'' has been made to apply to ''overflow: hidden'' elements. + While applying ''overflow: hidden'' has other effects which may be undesirable in that context, + the combination of ''scrollbar-gutter: stable'' and ''overflow: hidden'' + does add spacing in the same way as ''scrollbar-gutter: stable force'', + and may prove to be a sufficient workaround, + particularly considering the other issues described above. + + <dt><dfn>match-parent</dfn> + <dd> + On a [=block-level=] box whose parent has a [=scrollbar gutter=] (or mirrored gutters), + this causes the box to have [=scrollbar gutter=] + on the same side(s) and of the same width as its parent’s gutter(s). + Moreover, + that gutter is made to overlap that of the parent box. + + <figure> + <img + srcset="images/match-parent.png 2x" + alt="The gutter of a ''scrollbar-gutter: match-parent'' box overlaps with that of its parent."> + </figure> + + If the box with ''scrollbar-gutter: match-parent'' has a non-zero border or margin + on the side where the gutter is expected, + then the size of that box's gutter is <code>parent.gutter - child.border - child.margin</code>, + and the gutter+border+margin is what collapses with the parent's gutter. + + If the box with ''scrollbar-gutter/ match-parent'' is itself a [=scroll container=], + depending on the type of scrollbars, + on its 'overflow' property, + and on the other values of the 'scrollbar-gutter' property, + it may need additional gutter for its own scrollbars. + This comes in addition to the amount of gutter added for the sake of the ''scrollbar-gutter/match-parent'' value + and does not collapse with the parent's gutter. + + <div class=example> + <figure> + <figcaption>A scroller with an child whose background intrudes into the gutter, thanks to ''scrollbar-gutter/match-parent''</figcaption> + <img srcset="images/match-parent-ex1.png 4x"> + </figure> + <figure> + <figcaption>A ''scrollbar-gutter/match-parent'' box inside a scroll container with classic scrollbars, ''overflow: auto'', and ''scrollbar-gutter: stable''</figcaption> + <img srcset="images/match-parent-classic.png 2x" alt="The background of the match-parent element is visible in the gutter when the scrollbar isn't there."> + </figure> + <figure> + <figcaption>A scrollable box with ''scrollbar-gutter/match-parent'' inside another scroll container</figcaption> + <img srcset="images/match-parent-nested.png 2x" alt="The element has a double gutter, one for its own scrollbar, one to match its parent's."> + </figure> + <figure> + <figcaption>A scrollable box with ''scrollbar-gutter/match-parent'' inside another scroll container, with bidi</figcaption> + <img srcset="images/match-parent-bidi.png 2x" alt="The element has a two gutters, one for its own scrollbar, one to match its parent's, on opposite sides."> + </figure> + <figure> + <figcaption>A scrollable box with ''scrollbar-gutter:match-parent stable'' inside another scroll container, with bidi</figcaption> + <img srcset="images/match-parent-stable.png 2x" alt="The element has a two gutters, one for its own scrollbar (not shown, as it's not overflowing), one to match its parent's, on opposite sides."> + </figure> + </div> +</dl> + +<div class=note> +Note: The following table summarizes the interaction of 'overflow' and 'scrollbar-gutter' +for different types of scrollbars, +showing in which case space is reserved for the <a>scrollbar gutter</a>. + +<table class="data complex"> + <caption> + Should space be reserved for the [=scrollbar gutter=]?</caption> + <thead> + <tr> + <th rowspan=2>'overflow' + <th rowspan=2>'scrollbar-gutter' + <th colspan=2>Classic scrollbars + <th colspan=2 rowspan=2>Overlay scrollbars <small>(whether overflowing or not)</small> + <tr> + <th>Overflowing + <th>Not overflowing + </thead> + <tr> + <th rowspan=3>''overflow/scroll'' + <th>''scrollbar-gutter/auto'' + <td>yes + <td>yes + <td> + <tr> + <th>''scrollbar-gutter/stable'' + <td>yes + <td>yes + <td> + <tr> + <th>''scrollbar-gutter/always'' + <td>yes + <td>yes + <td>yes + <tr> + <th rowspan=3>''overflow/auto'' + <th>''scrollbar-gutter/auto'' + <td>yes + <td> + <td> + <tr> + <th>''scrollbar-gutter/stable'' + <td>yes + <td>yes + <td> + <tr> + <th>''scrollbar-gutter/always'' + <td>yes + <td>yes + <td>yes + <tr> + <th rowspan=3>''overflow/hidden'' + <th>''scrollbar-gutter/auto'' + <td> + <td> + <td> + <tr> + <th>''scrollbar-gutter/stable'' + <td>yes + <td>yes + <td> + <tr> + <th>''scrollbar-gutter/always'' + <td>yes + <td>yes + <td>yes + <tr> + <th rowspan=3>''overflow/visible'', ''overflow/clip'' + <th>''scrollbar-gutter/auto'' + <td> + <td> + <td> + <tr> + <th>''scrollbar-gutter/stable'' + <td>if ''force'' + <td>if ''force'' + <td> + <tr> + <th>''scrollbar-gutter/always'' + <td>if ''force'' + <td>if ''force'' + <td>if ''force'' +</table> +</div> + +</div> + +<h2 id=priv-sec class=nonum> +Appendix B: Privacy and Security Considerations</h2> This specification introduces no new security considerations. diff --git a/css-overflow-4/images/gmail-classic.png b/css-overflow-4/images/gmail-classic.png new file mode 100644 index 000000000000..35fa77515d71 Binary files /dev/null and b/css-overflow-4/images/gmail-classic.png differ diff --git a/css-overflow-4/images/gmail-overlay.png b/css-overflow-4/images/gmail-overlay.png new file mode 100644 index 000000000000..f499c3d61b74 Binary files /dev/null and b/css-overflow-4/images/gmail-overlay.png differ diff --git a/css-overflow-4/images/match-parent-bidi.png b/css-overflow-4/images/match-parent-bidi.png new file mode 100644 index 000000000000..a5865a613a1c Binary files /dev/null and b/css-overflow-4/images/match-parent-bidi.png differ diff --git a/css-overflow-4/images/match-parent-classic.png b/css-overflow-4/images/match-parent-classic.png new file mode 100644 index 000000000000..2a0a1980f800 Binary files /dev/null and b/css-overflow-4/images/match-parent-classic.png differ diff --git a/css-overflow-4/images/match-parent-ex1.png b/css-overflow-4/images/match-parent-ex1.png new file mode 100644 index 000000000000..7870718275bb Binary files /dev/null and b/css-overflow-4/images/match-parent-ex1.png differ diff --git a/css-overflow-4/images/match-parent-nested.png b/css-overflow-4/images/match-parent-nested.png new file mode 100644 index 000000000000..cc23ab2796cc Binary files /dev/null and b/css-overflow-4/images/match-parent-nested.png differ diff --git a/css-overflow-4/images/match-parent-stable.png b/css-overflow-4/images/match-parent-stable.png new file mode 100644 index 000000000000..5790a374f519 Binary files /dev/null and b/css-overflow-4/images/match-parent-stable.png differ diff --git a/css-overflow-4/images/match-parent.png b/css-overflow-4/images/match-parent.png new file mode 100644 index 000000000000..a9e9c6db8b34 Binary files /dev/null and b/css-overflow-4/images/match-parent.png differ diff --git a/css-overflow-4/images/scrollbar-gutter-complex-classic.png b/css-overflow-4/images/scrollbar-gutter-complex-classic.png new file mode 100644 index 000000000000..3eff28fc38c6 Binary files /dev/null and b/css-overflow-4/images/scrollbar-gutter-complex-classic.png differ diff --git a/css-overflow-4/images/scrollbar-gutter-complex-overlay.png b/css-overflow-4/images/scrollbar-gutter-complex-overlay.png new file mode 100644 index 000000000000..4f9227705323 Binary files /dev/null and b/css-overflow-4/images/scrollbar-gutter-complex-overlay.png differ diff --git a/css-overflow-4/images/todo-classic.png b/css-overflow-4/images/todo-classic.png new file mode 100644 index 000000000000..480986bfa117 Binary files /dev/null and b/css-overflow-4/images/todo-classic.png differ diff --git a/css-overflow-4/images/todo-overlay-always.png b/css-overflow-4/images/todo-overlay-always.png new file mode 100644 index 000000000000..86cbc65a0ccc Binary files /dev/null and b/css-overflow-4/images/todo-overlay-always.png differ diff --git a/css-overflow-4/images/todo-overlay.png b/css-overflow-4/images/todo-overlay.png new file mode 100644 index 000000000000..4f9eefcf295e Binary files /dev/null and b/css-overflow-4/images/todo-overlay.png differ