diff --git a/lib/css/components/expandable.less b/lib/css/components/expandable.less index 5e7679eb6d..6676198f13 100644 --- a/lib/css/components/expandable.less +++ b/lib/css/components/expandable.less @@ -1,104 +1,20 @@ -// see http://stackoverflow.com/a/43965099 for how this works: - -// Notes on the clip-path stuff: What we would really like is overflow: hidden during the transition, -// but not when the element is expanded. Unfortunately, although the CSS spec provides for it, there's -// no browser support yet for transitioning non-interpolatable properties, and therefore we cannot say -// "change overflow to visible *after* the transition". -// -// So we use clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%), which is essentially the same -// as overflow: hidden, but unlike overflow, clip-path is interpolatable -- but only if the opposite state -// is also a polygon, and has the same vertice count. So our version of overflow: visible is a rectangle -// of 2x2 million pixels, which should be enough for everybody (TM). -// -// At the time of writing, clip-path works in Chrome, Firefox Beta, and Safari (with vendor prefix). // -// In browsers that do not support this yet, we have to have another way to prevent the expandable content -// from being visible below the bottom edge during the transition, and the best I could come up with is -// transitioning to scaleY(0) with a timing function that's faster than the height-reducing transitions. -// This does kinda look like a deliberate effect, even more so if we also transition to transparency -// (which we therefore do), so I feel it's an okay fallback. +// STACK OVERFLOW +// EXPANDABLE // -// We still set overflow to hidden in the collapsed state (which also applies to the collapsing transition), -// we just can't do that for the expanding transition. +// This CSS comes from Stacks, our CSS & Pattern library for rapidly building +// Stack Overflow. For documentation of all these classes and how to contribute, +// visit https://stackoverflow.design/ // -// A major drawback of clip-path is that even though the content is clipped (not visible), it will still -// contribute to the document height. This caused a jumping around of the scrollbar (and possibly even -// of the viewport) when the expandable was at the bottom of the page, because once the negative bottom -// margin of the -expandable-group exceeds the actual height, any additional pixels were added on to the -// bottom. The fix is to set the -expandable-group's flex alignment to flex-start, which forces the -// (no longer visible) element itself to remain at the top, thereby forcing the excess pixels to be added -// above the top, not below the bottom. And because extending content above the document top will not do -// anything to the document height, there is no jumping during the transition. - -@stacks-internals-s-expandable-transition-duration: 100ms; - -// Per the answer referenced above, the component can only guarantee smooth transitions if above a minimum -// height and can only guarantee the element will be hidden is below a maximimum height. -// The minimum height has been set at 10px because that's below the height of a single line of text in an s-description. -@stacks-internal-s-expandable-min-expected-height: 10px; -@stacks-internal-s-expandable-max-expected-height: 1500px; .s-expandable { - display: flex; - -webkit-clip-path: polygon(-1000000px -1000000px, 1000000px -1000000px, 1000000px 1000000px, -1000000px 1000000px); - clip-path: polygon(-1000000px -1000000px, 1000000px -1000000px, 1000000px 1000000px, -1000000px 1000000px); - align-items: flex-start; // see comment above - transition: clip-path 0s @stacks-internals-s-expandable-transition-duration, -webkit-clip-path 0s @stacks-internals-s-expandable-transition-duration; - - &:after { - content: ''; - -ms-flex-preferred-size: 0; - flex-basis: 0; - height: @stacks-internal-s-expandable-min-expected-height; - max-height: 0; - transition: - height @stacks-internals-s-expandable-transition-duration linear, - max-height 0s @stacks-internals-s-expandable-transition-duration linear; - } -} - -.s-expandable--content { - -ms-flex-preferred-size: 100%; - flex-basis: 100%; - max-height: 1000000px; - margin-bottom: 0; - -webkit-transform-origin: 0 0; - transform-origin: 0 0; - transition: - margin-bottom @stacks-internals-s-expandable-transition-duration cubic-bezier(0, 0, 0, 1), - transform @stacks-internals-s-expandable-transition-duration cubic-bezier(1, 0, 1, 1), - opacity @stacks-internals-s-expandable-transition-duration cubic-bezier(1, 0, 1, 1); -} - -.s-expandable:not(.is-expanded) { + height: 0; + opacity: 0; + visibility: hidden; overflow: hidden; - -webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); - clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); - transition: none; - - .s-expandable--content { - visibility: hidden; - max-height: 0; - margin-bottom: -@stacks-internal-s-expandable-max-expected-height; - opacity: 0; - -webkit-transform: scaleY(0); - transform: scaleY(0); - transition: - margin-bottom @stacks-internals-s-expandable-transition-duration cubic-bezier(1, 0, 1, 1), - visibility 0s @stacks-internals-s-expandable-transition-duration, - max-height 0s @stacks-internals-s-expandable-transition-duration, - transform @stacks-internals-s-expandable-transition-duration cubic-bezier(0, 1, 1, 1), - opacity @stacks-internals-s-expandable-transition-duration cubic-bezier(0, 1, 1, 1); - @supports ((-webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)) or (clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%))) { - opacity: 1; - -webkit-transform: none; - transform: none; - } - } - - &:after { - height: 0; - max-height: @stacks-internal-s-expandable-min-expected-height; - transition: height @stacks-internals-s-expandable-transition-duration linear; - } } +.s-expandable.is-expanded { + height: auto; + visibility: visible; + opacity: 1; +} \ No newline at end of file