mirror of
https://github.com/rio-labs/rio.git
synced 2026-04-30 16:09:26 -05:00
120 lines
3.8 KiB
SCSS
120 lines
3.8 KiB
SCSS
@import "../utils";
|
|
|
|
// ScrollContainer
|
|
//
|
|
// Scrolling is super weird. You'd think scrolling would be easy to achieve, but
|
|
// no. I don't really understand what makes it so difficult, but here are some
|
|
// of the issues I struggled with:
|
|
//
|
|
// 1. The parent element refuses to become smaller than its content even when
|
|
// scrolling is enabled
|
|
// 2. You can force the parent element to become smaller, but depending on how
|
|
// you do it, you may end up shrinking its content as well.
|
|
// 3. If multiple elements can scroll, we want the innermost one to do it (and
|
|
// not the <html> element).
|
|
// 4. When scrolling is only enabled in one direction, the parent may need to
|
|
// grow larger in the other direction in order to fit the scroll bar.
|
|
// 5. The element must have a minimum size so that the scroll bars are usable.
|
|
//
|
|
// The solution I've found requires 3 nested elements. From the outside in:
|
|
//
|
|
// 1. An element with `position: relative`, so that its child can size itself
|
|
// with `100%`.
|
|
//
|
|
// 2. This is where the CSS `overflow` setting is applied. We also override the
|
|
// child's size request by explicitly setting the `width`/`height` to the
|
|
// minimum size where the scroll bars are still usable. Finally, we force the
|
|
// element to fill its parent by setting `min-width`/`min-height` to `100%`.
|
|
//
|
|
// 3. For some reason, when an element scrolls horizontally, it overrides the
|
|
// width of its child, which can lead to the child being smaller than its
|
|
// content. This element exists solely to fix that with a `min-width:
|
|
// max-content`.
|
|
|
|
.rio-scroll-container {
|
|
pointer-events: auto;
|
|
|
|
$min-scroll-size: 5rem; // Minimum size where the scroll bar is still usable
|
|
|
|
// Set the `overflow` appropriately
|
|
&[data-scroll-x="auto"] > * {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
&[data-scroll-x="always"] > * {
|
|
overflow-x: scroll;
|
|
}
|
|
|
|
&[data-scroll-x="never"] > * {
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
&[data-scroll-y="auto"] > * {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
&[data-scroll-y="always"] > * {
|
|
overflow-y: scroll;
|
|
}
|
|
|
|
&[data-scroll-y="never"] > * {
|
|
overflow-y: hidden;
|
|
}
|
|
|
|
// Kill the size request depending on the scroll direction
|
|
&[data-scroll-x="auto"] > *,
|
|
&[data-scroll-x="always"] > * {
|
|
width: $min-scroll-size;
|
|
min-width: 100%;
|
|
}
|
|
|
|
&[data-scroll-y="auto"] > *,
|
|
&[data-scroll-y="always"] > * {
|
|
height: $min-scroll-size;
|
|
min-height: 100%;
|
|
}
|
|
|
|
// For some reason, when scrolling is enabled in the horizontal direction,
|
|
// the child element's width gets set to the width of the scrolling element.
|
|
// This can cause it to become smaller than its content. We have to override
|
|
// this.
|
|
&[data-scroll-x="auto"] > * > *,
|
|
&[data-scroll-x="always"] > * > * {
|
|
width: max-content;
|
|
}
|
|
|
|
// Stretch to fill the parent (Note: This is the
|
|
// .rio-scroll-container-column)
|
|
& > * > * {
|
|
min-width: 100%;
|
|
min-height: 100%;
|
|
}
|
|
|
|
// 'auto'-scrolling in the y direction has a unique problem: Because the
|
|
// width of an element is decided before its height, the browser doesn't
|
|
// know whether a vertical scroll bar will be needed until it's too late. If
|
|
// it turns out that the parent didn't allocate enough width for the child
|
|
// *and* the vertical scroll bar, it will suddenly start scrolling in *both*
|
|
// directions. That's not what we want - we want to increase the parent's
|
|
// width instead.
|
|
&[data-scroll-x="never"][data-scroll-y="auto"] > * {
|
|
scrollbar-gutter: stable !important;
|
|
}
|
|
}
|
|
|
|
.rio-scroll-container-column {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.rio-scroll-container-child-container {
|
|
flex-grow: 1;
|
|
overflow-anchor: none;
|
|
|
|
@include single-container();
|
|
}
|
|
|
|
.rio-scroll-container-anchor {
|
|
height: 1px;
|
|
}
|