CSS Scroll Snapping Change Proposal

Unofficial Proposal Draft,

This version:
https://drafts.csswg.org/css-scroll-snap/
Feedback:
www-style@w3.org with subject line “ [css-scroll-snap] … message topic … ” ( archives )
Issue Tracking:
GitHub
Inline In Spec
Editors:
Tab Atkins-Bittner ( Google )
fantasai ( Invited Expert )

Abstract

A brief description of an alternate model for scroll-snapping.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, in speech, etc.

Status of this document

Table of Contents

1. Introduction

We think scroll snapping is a great idea, and fully support exposing this functionality through CSS. However, a major weakness of the current spec is the way it conceives snapping on a coordinate model rather than a box model. This requires a lot of manual calculations in figuring out the correct coordinates from the box model; and also makes sensible scroll-snap settings dependent on the relative sizes of the viewport and the snappable contents, causing problems for users are unexpectedly large and/or small screens (a problem commonly ignored by many authors).

This proposal builds off of roc’s model, using an area-snapping model to intelligently handle adaptation to multiple screen sizes. It also adds group alignment as a built-in concept, rather than requiring authors to build one in JavaScript.

2. Use Cases

Use Case 1: Snapping to the start or middle of each box e.g. address book (start) or photo album (middle)
  1. Snapping to 0.25rem above the top of each heading

    :root { scroll-snap-type: proximity; }
    h1, h2, h3, h4, h5, h6 {
      scroll-snap-align: start;
      scroll-snap-area: 0.25em;
    }
  2. Snapping to the center of each photo

    :root { scroll-snap-type: mandatory; }
    img { scroll-snap-align: center; }
Use Case 2: Snapping to the start or middle of a group of boxes, where the number of boxes depends on how many fit in the viewport e.g. scrolling image galleries
  1. Snapping to the top of each "page" of address book entries in a list of entries

    :root {
      scroll-snap-type: proximity;
      scroll-group-align: start;
    }
    article {
      scroll-snap-align: group;
    }
Use Case 3: Snapping to boxes (or points) in 2D e.g. on a map, where you want to snap points of interest to the center, or a flow-chart diagram, where you want to snap the edges of each box into the visible area. In both cases, you don’t want objects wholly outside the visible area to influence snapping.
  1. Snapping each flow chart entry to within the viewport when it falls near the edge:

    :root {
      scroll-snap-type: proximity;
    }
    li {
      scroll-snap-align: edges;
    }
  2. Snapping each city on a map to the center of the viewport, but only once it gets near the center in both dimensions:

    :root {
      scroll-snap-type: proximity;
    }
    .city {
      scroll-snap-align: center;
    }
Use Case 4: Slideshow, where successive slides are arranged horizontally, and sometimes "detail" slides are placed below the "main" slide for that point.
 class="slides">
   class="slide">...
class="slide">... class="slide details"> class="slide">... class="slide">... class="slide">...

3. Overview of Change

On the scroll container:

Spec Proposal Priority
scroll-snap-type scroll-snap-type (no change) High priority
''scroll-snap-destination: '' ''scroll-snap-destination: auto | '' Keep this property only if needed for compat.
scroll-snap-points-x dropped High priority to drop. :)
scroll-snap-points-y dropped High priority to drop. :)
n/a ''scroll-group-align: [ start | end | edges ]{1,2} | #'' Low priority

On the children:

Spec Proposal Priority
''scroll-snap-coordinate: #'' ''scroll-snap-align: [ none | start | end | edges ]{1,2} | | group'' High priority; simpler version adds a center keyword to the bracketed set instead of allowing the full # syntax inherited from scroll-snap-coordinate . group keyword is low-priority.
n/a ''scroll-snap-area: [ border-box | margin-box ] || {1,4}'' High priority

4. Element-based Snapping

4.1. Scroll Snapping Area: the scroll-snap-area property

Name: scroll-snap-area
Value: [ border-box | margin-box ] || {1,4}
Initial: border-box
Applies to: all elements
Inherited: no
Percentages: n/a
Media: visual
Computed value: as specified, with lengths made absolute
Animatable: yes, if border-box / margin-box are constant

Specifies the box that is used for snapping. values give offsets (similar to margin or border-image-outset ).

Note: This functionality effectively replaces scroll-snap-destination , in a way that allows for more control in mixed-content environments (each element can specify its own offsets from the viewport edges) and also plays better with smaller viewports (see §4.2.1 Snap Alignment in Small Viewports ).

4.2. Scroll Snapping Alignment: the scroll-snap-align property

Name: scroll-snap-align
Value: [ none | start | end | edges | center ] {1,2}
Initial: none
Applies to: all elements
Inherited: no
Percentages: n/a
Media: visual
Computed value: as specified
Animatable: no

Specifies the element’s snap position as an alignment of its box within the viewport.

The first value gives alignment in the inline axis; the second value gives alignment in the block axis. If one value is specified, it is duplicated.

center keyword can be replaced by # to maintain compat, if necessary.

4.2.1. Snap Alignment in Small Viewports

The snapped position of a box is given by its scroll-snap-align property. However,if the scroll-snap-area is larger than the viewport...

  • Inertial scrolling (and scroll-adjustment caused by snapping) continues to align to the snap-position as normal.

  • For explicit/programmatic (non-fling) scrolling:

    • While the area fully fills the viewport in a given axis, snapping is ignored in that axis: the container can be scrolled arbitrarily and will not react.

    • If the container is scrolled such that the area no longer fully fills the viewport in an axis, the area acts as if it has both-edges snapping in that axis, resisting outward scrolling until you fling out or pull it sufficiently to trigger snapping to a different snap-position (with either proximity or mandatory behavior as appropriate).

As an example, imagine a photo as the area, or a slide in a slideshow. You want mandatory snapping from item to item, but if the item happens to be larger than your viewport, you want to be able to scroll around the whole thing once you’re over it.

5. Scope of Snapping Influence

Current spec doesn’t define how to select which snap-point to snap to. See https://lists.w3.org/Archives/Public/www-style/2015Jul/0325.html for a proposal to ignore snap positions far outside the viewport.

UAs should be encouraged to ignore snap positions that require scrolling in two dimensions when a one-dimensional scroll is triggered.

Define that snap-point selection is based on the final scroll position that the scroll physics would land the scroller in after a fling.

6. Group-based Snapping

Collects all snapping areas of group-snapped boxes, segments them into groups that will fit within the viewport, then creates snap areas that capture the specified alignment of each such group. (Note that such areas may overlap, if group-snapped boxes are arranged in an overlapping pattern.)

This is a simple form of "scrolling by pages".

Use Case 1: Snapping to the top of each "page" of address book entries in a list of entries.
:root {
  scroll-snap-type: proximity;
  scroll-group-align: start;
}
article {
  scroll-snap-align: group;
}
Use Case 2: Scrolling an article to the first paragraph that hasn’t been completely read.
article {
  scroll-snap-type: proximity;
  scroll-group-align: start;
}
article > * {
  scroll-snap-align: group;
}
Use Case 3: Scrolling image gallery, a la Pinterest, where images are packed tightly on the page.
.gallery {
  scroll-snap-type: proximity;
  scroll-group-align: center;
}
.gallery > img {
  scroll-snap-align: group;
}

6.1. Turning On Group Snapping: the group value of scroll-snap-align

Name: scroll-snap-align
New values: group

The group value specifies that this element’s scroll snap area should be group-aligned to the viewport.

6.2. Aligning the Group: the scroll-snap-group property

Name: scroll-snap-group
Value: [ start | end | edges | center ] {1,2}
Initial: start
Applies to: all elements
Inherited: no
Percentages: n/a
Media: visual
Computed value: as specified
Animatable: no

Specifies the alignment of a group-snapped group’s area within the viewport.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words "for example" or are set apart from the normative text with class="example" , like this:

This is an example of an informative example.

Informative notes begin with the word "Note" and are set apart from the normative text with class="note" , like this:

Note, this is an informative note.

Advisements are normative sections styled to evoke special attention and are set apart from other normative text with , like this: UAs MUST provide an accessible alternative.

Conformance classes

Conformance to this specification is defined for three conformance classes:

style sheet
A CSS style sheet .
renderer
A UA that interprets the semantics of a style sheet and renders documents that use them.
authoring tool
A UA that writes a style sheet.

A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.

A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)

An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.

Partial implementations

So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate ) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.

Experimental implementations

To avoid clashes with future CSS features, the CSS2.1 specification reserves a prefixed syntax for proprietary and experimental extensions to CSS.

Prior to a specification reaching the Candidate Recommendation stage in the W3C process, all implementations of a CSS feature are considered experimental. The CSS Working Group recommends that implementations use a vendor-prefixed syntax for such features, including those in W3C Working Drafts. This avoids incompatibilities with future changes in the draft.

Non-experimental implementations

Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.

To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.

Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at http://www.w3.org/Style/CSS/Test/ . Questions should be directed to the public-css-testsuite@w3.org mailing list.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification . 7 June 2011. REC. URL: http://www.w3.org/TR/CSS2
[CSS-2D-TRANSFORMS-1]
CSS 2D Transforms Module Level 1 URL: http://www.w3.org/TR/css3-2d-transforms/
[CSS-BACKGROUNDS-3]
CSS Backgrounds and Borders Module Level 3 URL: http://www.w3.org/TR/css3-background/
[CSS-SNAPPOINTS-1]
Matt Rakow; Jacob Rossi. CSS Scroll Snap Points Module Level 1 . 26 March 2015. WD. URL: http://www.w3.org/TR/css-snappoints-1/
[CSS-VALUES]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3 . 11 June 2015. CR. URL: http://www.w3.org/TR/css-values/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels . March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119

Property Index

Name Value Initial Applies to Inh. %ages Media Animatable Computed value
scroll-snap-area [ border-box | margin-box ] || {1,4} border-box all elements no n/a visual yes, if border-box/margin-box are constant as specified, with lengths made absolute
scroll-snap-align [ none | start | end | edges | center ]{1,2} none all elements no n/a visual no as specified
scroll-snap-group [ start | end | edges | center ]{1,2} start all elements no n/a visual no as specified

Issues Index

center keyword can be replaced by # to maintain compat, if necessary.
Current spec doesn’t define how to select which snap-point to snap to. See https://lists.w3.org/Archives/Public/www-style/2015Jul/0325.html for a proposal to ignore snap positions far outside the viewport.
UAs should be encouraged to ignore snap positions that require scrolling in two dimensions when a one-dimensional scroll is triggered.
Define that snap-point selection is based on the final scroll position that the scroll physics would land the scroller in after a fling.