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.
Related
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.
This question already has answers here:
Make a div fill the height of the remaining screen space
(41 answers)
Closed 2 years ago.
I'm trying to make the tab content take up 100% of the available space. but for some reason it is generating a vertical scroll because apparently it is adding the height of the tab. How can I make the content of the tab occupy the available space without generating the vertical scroll?
I am using ng-bootstrap, if you are not familiar with angular don't worry, in this live code you should only see the styles.css file and /app/app.component.html: https://stackblitz.com/edit/angular-ng-bootstrap-9obrp7?file=app%2Fapp.component.html
Note: I can fix this with an overflow, but I want to know why I am getting this problem?
Additional note: ngb-tabset adds 2 elements with class .tab-content and .tab-pane, I set them 100% height, otherwise the tab content would not grow.
This is my code:
<ngb-tabset [destroyOnHide]="false">
<ngb-tab id="nav-tabContent" class="h-100 border1" title="Relación causa con Emoción">
<ng-template ngbTabContent>
<div class="border2 h-100">holi</div>
</ng-template>
</ngb-tab>
</ngb-tabset>
body,html{
height:100%;
margin:0px;
padding:0px;
}
.h-100{
height:100%;
}
.tab-content {
height:100%;
}
.tab-pane {
height: 100%;
}
The reason it's happening is because the total height is actually getting calculated as 100% + the height of the tab element.
You can use flexbox to overcome this quite easily. You can make the container a flex container, and give it the 100% height. Then you use the flex property on it's children to determine whether they will stretch to use the space.
I've used the ngb-tabset element in your code as the container, and added the following CSS to make it take up 100% of the space and display the nav-tabs and tab-content in a column inside it:
ngb-tabset {
display: flex;
flex-direction: column; /* show the child elements in a column */
height: 100%; /* Use the full available height */
}
Then we add the following to tell the tab-content to stretch to the full available height after the nav-tabs, and to stop the nav-tabs from stretching:
ngb-tabset .nav-tabs {
flex: 0; /* do not grow or shrink */
}
ngb-tabset .tab-content {
flex: 1; /* grow or shrink to fill the remaining space */
}
(Note that I only needed to add the CSS for the container to your live code so some other CSS might be having the same effect, but you would usually need the other CSS also.)
Working Example: (I've added a container with id ngb-tabset to replace the ngb-tabset element in your Angular code)
body,html {
height: 100%;
margin: 0px;
padding: 0px;
}
#ngb-tabset, ngb-tabset {
display: flex;
flex-direction: column;
height: 100%;
}
#ngb-tabset .nav-tabs, ngb-tabset .nav-tabs {
flex: 0; /* do not grow or shrink */
}
#ngb-tabset .tab-content, ngb-tabset .tab-content {
flex: 1; /* grow or shrink to fill the remaining space */
}
.tab-pane {
height: 100%;
}
.border2 { border: 1px solid blue;}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<div id="ngb-tabset">
<ul class="nav nav-tabs justify-content-start">
<li class="nav-item">
<a class="nav-link active" href="" role="tab" id="nav-tabContent" aria-controls="nav-tabContent-panel" aria-expanded="true" aria-disabled="false">
Relación causa con Emoción
</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="nav-tabContent-panel" aria-expanded="true">
<div class="border2 h-100">holi</div>
</div>
</div>
</div>
Firstly, let me say that unfortunately I do have to support IE11 still and I don't believe this is a duplicate question, although I have found a few that were kinda similar.
I have a simple modal window which contains 3 flexible components in a column, header, footer and main.
The plan is that the outer box should grow as the content grows, until it is 80% of the height of the screen, at which point the middle section of the modal which is set to overflow-y:auto should get a scrollbar and the main modal will not get any taller.
Here is my markup
<div class="modal-wrapper">
<div class="modal">
<div class="modal-header">Header</div>
<div class="modal-main">
<div>Content goes here, could get very long</div>
</div>
<div class="modal-footer">Footer</div>
</div>
</div>
Fairly standard stuff. The modal is set to flex and the header and footer are fixed height. The middle section is set to grow and shrink as necessary. The main thing is that the .modal should never overflow the .modal-wrapper.
I have a jsfiddle set up and it's tested in Chrome, Firefox, Safari and iOS and it's working fine if you drag the bottom right box height up and down you'll see how it is supposed to behave. IE11 though is a mess.
https://jsfiddle.net/jonhobbs/sf6untnt/3/
Now, I have a feeling it may be related to the min-height bug here:
https://connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview
but I'm not convinced it's exactly that bug because none of the workarounds for that bug seem to work (e.g. using min-height:1px instead of 0, wrapping in another flexbox etc).
Hopefully somebody on SO can take a look at the jsfiddle and see an obvious problem
Maybe if you make it a flex child and use flex:0 1 80%; , it should fixe your trouble with IE :
example
html, body{
height: 100%;
display:flex;
flex-flow:column;
}
.modal-wrapper{
display: flex;
flex-direction: column;
width: 70%;
margin: 0 auto;
flex:0 1 80%;/* IE gets it , because the flow is column */
max-height:80%;/* others such as FF gets it */
background: white;
}
.modal{
display: flex;
flex-glow: 1;/* doesn't exist */
flex/*-shrink*/: 1; /* good enough */
flex-direction: column;
min-height: 1px;
}
.modal-main{
flex: 1;/* good enough */
min-height: 1px;
overflow-y: auto;
padding: 20px;
}
.modal-header, .modal-footer{
flex-grow: 0;
flex-shrink: 0;
height: 60px;
color: white;
line-height: 60px;
text-align: center;
background: dodgerblue;
}
<div class="modal-wrapper">
<div class="modal">
<div class="modal-header">Header</div>
<div class="modal-main">
<div>This content could get very long so I'm going to put a big long div in it</div>
<div style=" width:100px; height:1000px; background-color:red; opacity:0.1;"></div>
</div>
<div class="modal-footer">Footer</div>
</div>
</div>
https://jsfiddle.net/sf6untnt/7/
I am trying to achive the last example on the following image, using flex-box.
From what I see, the align-items: baseline; property works great when the blocks only have 1 line.
The property align-items: flex-end; creates some issues mainly because the left and right items have different font-sizes and line-heights. Although the edges of the items are aligned, the white space created by the font size and line-height differences looks really bad when the item has no borders.
I'm trying to find a good all-around solution without any JS.
Thanks in advance.
You can wrap the contents of the flex items inside inline-block wrappers:
.flex {
display: flex;
align-items: baseline;
}
.inline-block {
display: inline-block;
}
.item { border: 1px solid red; }
.item:first-child { font-size: 200%; }
.flex::after { content: ''; position: absolute; left: 0; right: 0; border-top: 1px solid blue; }
<div class="flex">
<div class="item">
<div class="inline-block">Lorem<br />Ipsum<br />Dolor</div>
</div>
<div class="item">
<div class="inline-block">Foo bar</div>
</div>
</div>
That will work because, according to CSS 2.1,
The baseline of an 'inline-block' is the baseline of its last line box
in the normal flow, unless it has either no in-flow line
boxes or if its 'overflow' property has a computed value other than
'visible', in which case the baseline is the bottom margin
edge.
At the time of writing the CSS box model alignment working draft proposes a ‘first’ and ‘last’ value to be added to ‘align-items’. The would allow:
align-items: last baseline
Current it only appears to be supported by Firefox so is one for the future.
https://developer.mozilla.org/en-US/docs/Web/CSS/align-items
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>