[css-sticky-scrollbars] feedback and variations on the concept

Hi Tab,

I've been looking at this proposal of yours
discussed in https://tabatkins.github.io/specs/css-sticky-scrollbars/

I think it is overall a good idea addressing valid use cases, but I have some
disagreements about the design, so I've been thinking of how to tweak it to be
more to my liking. Also I have some use-cases that are not explicitely
addressed by your proposal which should be covered as well.

If you just want my proposal, jump all the way to the bottom of this mail, to
SUMMARY. Otherwise keep on reading for the rationale building up to that


First, I am not sure that anchoring at a distance from the edge is what we actualy
need. I absolutely see that in in the chat example, if you're in the middle of
the scroller, you want to stay there no matter how much content gets added to
the end. But if you scroll to 1em of the end, with a scroll-anchor distance of
2em, do you want to stay forever at 1em from the end? You might fail to notice
and forever miss one line as new messages get added. It seems to me it would be
more friendly to snap to the end, and anchor there. Maybe both are useful and we
can have a switch, but if we only get one, I think it should be the one that gets
you to the edge, not the one that keeps you near it.

Also if you're not within the scroll-anchor distance from the edge, and the
content grows, it would be useful to be able to say from which edge of the
scroller you maintain a constant distance. In the classical chat example, or
similar situations where content gets added to the bottom, you want to
maintain the offset from the top. But when new content comes at the top,
as it does for example in twitter, you want the other way around.

This, together with the "go-to-the-edge-if-you-are-close" behavior mean that
specifying the behavior both on the start and end edge are useful. Under your
proposal only one side (start or end) per axis can have an anchor zone.

With names to be bikesheded, that gives me the following properties instead of

 scroll-offset-anchor: [ start | end ]{1,2}
 scroll-edge-behavior: [ inert | [ glue | magnet ] <length>? ]#{1,4}
or maybe
 scroll-edge-behavior: inert | [ glue | magnet ] <length>{0,4}
if the previous one is overkill

scroll-offset-anchor tells you if you maintaint the distance from the start or
end edge of the scroller when it grows and you're somewhere in the middle. If
you have 1 value, it applies in the inline and block directions, and if you
have 2, the first is block and the second is inline.

scroll-edge-behavior is similar to your overflow-anchor, and tells you what
happens if you get within the scroll-anchoring distance of an edge. ''inert''
get you what we have now without any special property, ''glue'' what you
specified, and ''magnet'' scrolls you all the way in. The optional length
works the same as in your spec to set the scroll anchoring distance. If you
set 1 value, it applies to all 4 edges, and 2 to 4 values work the usual way:
top-bottom left-right, top left-right bottom, top right bottom left.

Another consideration is that if instead of manual scrolling (scrollbar,
mousewheel, touchscreen...), what is trigering the scrolling is moving the
text insersion caret inside a editable text field, it's a bit more tricky.
Let's say your scroll-anchoring distance is equivalent to 3 lines' height. At
any point in the editable field other than the last 3 lines, if you move the
caret down one line and that line was the last visible line, your scroller
moves by 1 line.  If you move to caret to the 3rd-to-last line and we're in
glue mode, nothing special happens, since caret movements explicitly change
the scroll position. In magnet mode though, we jump all the way to the edge,
which may be jarring. What we could do instead is always mainting a
scroll-anchoring distance worth of space between the caret and the visible
edge of the scroller. That way, when you're within the last 3 lines, the
result is the same and you're scrolled all the way, but this happens without
discontinuity in the amount of scrolling you get when you move the caret by
one line.

It also seems that this "keep a bit of context around the caret" effect can
also be quite useful even if we're not anchoring at the edge, so maybe it
should be a separate property. This is not an essential part of the proposal,
but it seems better to me with it.

scroll-context: auto | <length>{1,4}

auto gets the value from the scroll-anchoring distance (maybe only in magnet
mode?), the other values let you set it up explicitely.

Besides caret based scrolling, this could also apply to scrolling triggered by
focusing elements. If you focus (by pressing the tab key, programatically...)
a button or something that is in the scroller, but out of view, the scroll
offset is adjusted to make it visible. So far so good. But if you have for
example a 15px box shadow around your buttons that you would like to be
visible when focusing them, you could set "scroll-context: 15px" to make sure
the whole thing is in view. Which makes me think that maybe scroll-context
should also apply to focusable children of the scroller, to let you
override element by element the values set on the scroller itself.

== Demos ==
Here are a few JS-bins to play around with. They illustrates situations other
than the chat or twitter examples when these properties would help.

* Demo 1

Problems with a scroller with padding that contains an editable thing, and an
overlay over the scroller meant to fit in the padding when you're scrolled all
the way down.

Would be made better with either "scroll-context: 1em" directly, or getting it
through "scroll-edge-behavior: magnet 1em".

* Demo 2

Similar to demo 1, except the padding overlay is in the scroller instead of
over it.

Would also be made better with either scroll-context or
scroll-edge-behavior:magnet, potentially with different values on different
edges. (scroll-edge-behavior: magnet 1em, magnet 1em, magnet 2em).

In these 2 examples, glue mode (or equivalently, the overflow-anchor behavior)
wouldn't help: if you have scrolled all the way down, and type text to grow the
content, the caret movements will set the scroll position, overriding the glue
effect. While the caret would be kept in the visible area of the scroller,
it would not account for the overlays, which would move out of view in demo 2,
or obscure the content in demo 1.

* Demo 3

This one is arguably less disfunctional than the first 2, but may suffers
from hidden content if you navigate the content of the scroller by tabing
around instead of scrolling around, depending on exactly how the UA choses to
bring the focused element into view. (On this example, webkit/blink do well
even without the property, but Firefox and IE do not).

This also would benefit from using either properties, in the same way the
preceding 2 examples did.

In all 3 demos, if you don't expect the content to be programatically grown,
using either property gets you the same thing, but if you do, using
scroll-edge-behavior is likely preferable.

Replace overflow-anchor by scroll-edge-behavior, and add scroll-offset-anchor
and scroll-context.

scroll-edge-behavior: [ inert | [ glue | magnet ] <length>? ]#{1,4}
scroll-edge-behavior: inert | [ glue | magnet ] <length>{0,4}
Initial: inert
Applies to: scrollable elements
Definition: sets up scroll-anchor areas, and the behavior in them: stay where
            you are, or scroll to the edge and stay there.

scroll-offset-anchor: [ start | end ]{1,2}
Initial: start
Applies to: scrollable elements
Definition: From which edge of the scroller should the distance be maintained
            the scroller grows if we're not in a scroll-anchor area.

scroll-context: auto | <length>{1,4}
Initial: auto
Applies to: Scrollable elements and focusable descendants of scrollable
Definition: When the scroll position is moved by caret movements or by
	    focusing elements, how much room should be kept between this
            active element and the visible edges of the scroller. auto
            computes to the scroll-anchor distance of magnet-mode edges.

 - Florian

Received on Monday, 29 June 2015 21:06:06 UTC