Sticky element inside flexbox (not in combination with) - css

Many similar questions have been posted to try and achieve position sticky with an element that has flex rules applied (e.g. this post is one of many that I tried the answers from), but my aim is to apply position:sticky to a child of a flex element.
I have a two-column layout with a menu on the left. The idea is that when the page is scrolled down, the blue-colored menu stays at the top (although the image above should scroll upwards, leaving just the menu visible in it's place).
However, no matter of which combinations of align-self I apply, the menu still disappears vertically upwards with the image.
If its possible to combine flex with sticky, then I'm hoping there's also a solution for the child element.
Fiddle here using SCSS
.PageContainer {
.OuterContainer {
display: flex;
flex-wrap: nowrap;
gap: 1em;
.SideOne {
background-color:#fee;
align-self: flex-start /* Solution from https://stackoverflow.com/questions/44446671 */;
width: 10em;
.CompanyLogo {
img {
width: 100%;
}
}
.MyStickMenu {
border:1px solid blue;
background-color:#eef;
position: sticky;
top: 0;
}
}
.SideTwo {
background-color:#ddd;
flex-grow: 1;
flex-shrink: 1;
}
}
}
<div class="PageContainer">
<div class="OuterContainer">
<div class="SideOne">
<div class="CompanyLogo">
<img src="https://via.placeholder.com/250x100.png" alt="" />
</div>
<div class="MyStickMenu">
<h2>
Not-so Sticky Menu
</h2>
<ul>
<li>
Home
</li>
<li>
Away
</li>
</ul>
</div>
</div>
<div class="SideTwo">
<h1>
Scrollable page
</h1>
<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
</div>
</div>
</div>

Your issue is not with the sticky property but with the expectations you have of it. A sticky element is only sticky within its parent and so the Side One's height limits how far the menu can go.
You're going to have to put the Menu as a sibling to the "sides".
Alternatively you could divide Side One into two separate sides one for the menu and one for the rest, applying the position: sticky to the new side as it is the one who is supposed to cover its siblings while scrolling.

Related

Chrome not stretching display:table elements in flex container

There are a lot of questions about vertical centering, the occasional flexbox-stretching oddity, or the difference between block and table elements, but I couldn't find anything about the (unexpected) behavior of this particular combination.
I'm in a bit of a tricky situation with the markup bootstrap-vue is giving me:
#navbar-container {
/* Given */
position: relative;
display: flex;
width: 100%;
}
#navbar {
/* Given */
flex-grow: 1;
display: flex;
flex-direction: row;
/* For illustration */
background-color: red;
}
.nav-item {
/* Given */
flex-grow: 1;
text-align: center;
/* Added to center .nav-link content vertically */
display: table;
/* Without this Chrome and Edge don't stretch to the height of #navbar(-container), but Firefox does */
/*height: 100%;*/
}
.nav-link {
/* Added to center content vertically */
display: table-cell;
vertical-align: middle;
/* Should cover #navbar's red background */
background-color: lightgreen;
}
<div id="navbar-container">
<div id="navbar">
<div id="brand">
<!-- Gives height to #navbar -->
<img src="https://via.placeholder.com/100">
</div>
<div class="nav-item">
<a class="nav-link">
Item 1
</a>
</div>
<div class="nav-item">
<a class="nav-link">
Item 2
</a>
</div>
<div class="nav-item">
<a class="nav-link">
Item 3
</a>
</div>
</div>
</div>
I'm trying to vertically center the content of .nav-link across the full height of #navbar.
Keep in mind that I can't change anything about the markup here, it's given to me by the framework.
Since #navbar (implicitly) has align-items: stretch, I would assume that .nav-item is stretched to the height of #navbar. Firefox agrees, but Chrome and Edge don't.
The interesting part is that this happens only with display: table elements.
Any other display value (correctly) stretches .nav-item to the height of #navbar.
Note that .nav-link doesn't automatically stretch along in that case, but that's to be expected.
Adding height: 100% to .nav-item fixes the problem, but I can't understand why this is necessary.
I'm not looking for alternative vertical-centering solutions, adding the height works fine.
Using nested flexbox or line-height poses other (architectural) issues.
I suppose at this point I'm mostly curious why this happens, and perhaps more importantly: which browser is in the wrong here?
Here you go with officlal doc.
Table elements have an internal 'table wrapper box'. This wrapper is stretched, but the actual table itself isn't stretched to that box. The table height only grows with its content, or when you specify a height.
It's unclear why, but Firefox apparently does stretch the actual table automatically.

How to nest *position: sticky* inside of mat-expansion-panel => css grid

Inside of an accordion, I have a grid with 2 columns (really more but for simplicity here) both of variable length (dynamic form creator). In the left column I have some buttons I want to stay on the screen when scrolling down the right column. Is css grid or expansion panels contradicted with sticky? I tried to find overflow being hidden as I read you couldn't do that, but I did not find that. Do you see what I'm missing and why my sticky doesn't stay?
.html
...
<mat-expansion-panel>
<mat-expansion-panel-header>
Header
</mat-expansion-panel-header>
<div class="grid">
<div id="left">
<div class=sticky>
stay on screen
</div>
</div>
<div id="right">
some other really long content
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
...
.css
.sticky {
position: sticky;
top:0;
}
.grid {
display: grid;
grid-template-columns: 2fr 8fr;
}
I ran into a situation where I needed the expanded mat-expansion-panel-header to stick to the top of the page as you scrolled.
#Mateusz Budzisz's answer talked about using overflow: inherit !important on the .mat-expansion-panel, but this would leave the page with a ton of empty space and scrollbars resulting from the overflowed (but height: 0'd) expansion panel body.
I solved this issue by adding this rule:
.mat-expansion-panel {
overflow: inherit !important;
}
.mat-expansion-panel-body {
overflow: hidden; // This rule fixes extra whitespace
}
.mat-expansion-panel-header.mat-expanded {
position: sticky;
top: 0;
z-index: 1000;
}
Now all expanded panel headers stick to the top of the page as they should and there is no empty space when the panels are closed.
I originally tried adding the overflow: hidden; rule to just the .mat-expansion-panel-content, but this made the expand/close animations of the panel behave weird.
<mat-expansion-panel> has overflow: hidden; You can fix position sticky by removing it, like this:
.mat-expansion-panel {
overflow: inherit !important;
}
But it will result in empty space after panel if any panel is collapsed. More about this css issue: https://github.com/w3c/csswg-drafts/issues/865

CSS Position element on bottom of container without removing it from flow

I have a container with 3 children elements.
<div class="container">
<img />
<div class="element1"></div>
<div class="element2 bottom"></div>
</div>
They must be positioned as shown on the diagram below:
image is in the top of the left column and nothing goes below it (it is the only element in the left column)
element1 is in the top of the right column
element2 is stick to the bottom of the right column (and must not collide with the element1 which is above it)
Does somebody know how to achieve such layout using pure CSS? Ideally I wouldn't like to add any markup, but I can do that if that's the only possible way.
The biggest problem I'm facing here is how to stick that second element (non-image) to the bottom of the container without removing it from the flow. Because if I use position: absolute and remove it from the flow, the elment above it can collide with it (both elements have unknown height).
Here's a pen to work on: http://codepen.io/anon/pen/yNwGvQ
I would suggest you to use two columns in your html and then use the property display: flex; for your right column as suggested in the article A Complete Guide to Flexbox.
http://codepen.io/AlexisBertin/pen/QboYyY
All the HTML:
<div class="container">
<div class="column column-left">
<div class="image">This is an image</div>
</div>
<div class="column column-right">
<div class="element1">This container has dynamic content so it's height is unknown and may change.<br/><br/> Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger.</div>
<div class="element2">This container also has dynamic content so it's height is unknown and may change</div>
</div>
</div>
Part of this CSS:
.column {
float: left;
height: 100%;
}
.column.column-left { width: 100px; }
.column.column-right {
width: calc(100% - 100px);
display: flex;
flex-direction: column;
justify-content: space-between;
}
Hope you get the idea. Good Luck'.
EDIT:
The easiest way to achieve this without declaring height to the container seems to only create a third parent div to the first block of the second column and define it as flex: 1; while the second block of this same second column would be define as flex: 0;.
http://codepen.io/anon/pen/yNwZmJ
More details explained in the comments.
The easiest solution I figured out is this one:
First you create this CSS:
.container {
width: 400px;
padding: 10px;
border: 1px solid red;
background-color: white;
}
.container > img {
float: left;
}
.container > div {
position: relative;
overflow: auto;
padding-left: 5px;
min-height: 120px;
}
.container > div > .bottom{
position: absolute;
bottom: 0;
display: block;
}
And then use these divs, depending on your content. The first one you use when you know your text is short:
<div class="container">
<img src="http://placehold.it/120x120">
<div>
<div>
<p>This container has dynamic content so it's height is unknown and may change.</p>
</div>
<div class="bottom">
<p>This container also has dynamic content so it's height is unknown and may change</div>
</div>
</div>
The second one you use when you know your text is long
<div class="container">
<img src="http://placehold.it/120x120">
<div>
<div>
<p>This container has dynamic content so it's height is unknown and may change.</p>
<p>Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger.</p>
</div>
<div>
<p>This container also has dynamic content so it's height is unknown and may change</p>
</div>
</div>
</div>
The difference is that you remove bottom class from the last div in your div that has long text.
Also in your CSS you can see .container > div{... min-height: 120px; ...}, you should set it to height of your image. In case you want the bottom text more down then you have to increase min-height to be bigger than your image height.
Here is it in action: http://codepen.io/anon/pen/YXgBXx

Inline block challenges and suggestions for the layout

I keep reading articles that floats are outdated and that using inline-block solves problems such as having to use clearfix and a few more. These articles go on to justify inline-block by showing the same example: three squares that are aligned middle. In trying to use inline-block to create a navbar, I come across many problems. My navbar layout looks like such:
<nav id="main-nav" class="navbar">
<div class="logo">
<!-- image -->
</div><!--
--><div class="navbar-header"><!--
--><button type="button" class="navbar-toggle closed">
<span class="sr-only">Toggle navigation</span>
<i class="fa fa-bars"></i>
</button>
</div>
<div class="navbar-collapse navbar-sidebar">
<ul class="navbar-nav">
<!-- list-items -->
</ul>
</div>
</nav>
In order to align the logo left and the navbar-toggle button right, I had to use text-align justify and some special markup, which I find just as obtrusive as clearfix (Align two inline-blocks left and right on same line):
.header {
text-align: justify;
/* ie 7*/
*width: 100%;
*-ms-text-justify: distribute-all-lines;
*text-justify: distribute-all-lines;
}
.header:after{
content: '';
display: inline-block;
width: 100%;
height: 0;
font-size:0;
line-height:0;
}
.logo {
display: inline-block;
vertical-align: top;
}
.navbar-header {
display: inline-block;
vertical-align: middle;
}
My navbar is very similar to Bootstrap's. At small screen sizes, I want my navbar-toggle button to be centered in the navbar area. Vertical align: middle, however, would align this button to the middle my logo, which will be shorter or taller than the navbar, and which I also want aligned to the top of the navbar. Inline-block doesn't allow me to vertically align my content to the parent container, which seems to make it a non-viable option in many layouts. Is there some sort of solution where I can align my content to the container, rather than the sibling elements? I've been experimenting with setting different line heights and vertical-aligns.
If you have followed the comments above, there are many question being asked. I'll try to summaries most of it.
For display:inline-block, the vertical-algin property only affects the position of the element itself, and relative to the position of the siblings (the tallest sibling especially).
Percentage height like height:100%, only works with fixed height of parent container, or all percentage height that is set all the way back to <html> tag. But excluding positioned (relative, absolute etc.) elements, and viewport units vh, and maybe some other cases.
For display:table-cell, the vertical-algin property affects all the child elements, again excluding some positioned ones.
I think CSS table is easiest way to get your desired layout done in this case. Since you can easily have both vertical and horizontal alignments set on it. Here is a simplified workaround.
JsFiddle Demo
.nav {
border: 1px solid red;
display: table;
width: 100%;
}
.nav > div {
display: table-cell;
vertical-align: middle;
}
.logo img {
display: block;
}
.menu {
text-align: right;
}
.menu span {
border: 1px solid blue;
}
<div class="nav">
<div class="logo">
<img src="//dummyimage.com/50"/>
</div>
<div class="menu">
<span>Menu</span>
</div>
</div>

position: absolute inside position: relative causes content to overlap

I have a main element I have set to position: relative. This contains two divs that I then apply position: absolute on. This then causes the header and footer that sandwich the main element to then bump up against each other. How can I stop this?
Using floats and clearing the footer seems to give the two column layout I want. But I'm not sure how “sturdy” a solution that is and what'll happen on IE6/7.
Code on codepen.
All you elements in main are absolutely positioned, so main's height computes to zero, so the bottom edge of the header is next to the top edge of the footer. If you add a height to main you will open up space between the header and footer.
Given the following HTML:
<header>Header</header>
<main>
<div id="text">
<p>Some text</p>
</div>
<div id="links">
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</div>
</main>
<footer>
<p>Footer</p>
</footer>
You can realize a two-column layout using floats as shown in the following CSS:
main {
position: relative;
height: auto;
overflow: auto;
border: 1px solid blue;
}
#text {
float: left;
width: 500px
}
#links {
float: left;
width: 400px;
}
You need to set overflow: auto on your main container to contain the floats (equivalent to clearing them).
Also, make sure that the widths of the floated element are not too wide or else they will wrap to a 2nd line if the screen size is too narrow.
See demo at http://codepen.io/anon/pen/gGsjd
Footnote: Using overflow:auto versus clear:both
I tend to use overflow: auto but in some cases the the clear property is what is needed. At some point, read up about "block formatting contexts" at http://www.w3.org/TR/CSS2/visuren.html#block-formatting The reasons to pick one approach over the other are a bit subtle and the choice depends on the details of the layout that you are trying to achieve, how it behaves in a responsive manner and so on.

Resources