Files
rio/frontend/css/components/scroll_container.scss
T
2025-03-21 12:39:48 +01:00

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;
}