CSS3: Vertically align an element while siblings maintain relative position - css

I would like to center an element vertically and horizontally. The catch is that any sibling elements should maintain their position relative to the centered element. If the siblings are large enough, they may overflow the viewport. The siblings have variable heights.
I've started a code sample here: https://jsfiddle.net/hqmoz9xy/2/
html,
body {
height: 100%;
}
body {
margin: 0;
padding: 1em;
}
body,
.container {
box-sizing: border-box;
}
.container {
border: 1px solid red;
padding: 1em;
width: 100%;
height: 100%;
}
.main-display {
width: 100px;
height: 100px;
background-color: #999;
padding: 1em;
}
<div class="container">
<div class="main-display">
Main box: this box should be at the center of the container.
</div>
<ul class="extra-info">
<li>These items should naturally follow the main box and not care about vertical centering.</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
This is easily accomplished using JS and negative margins but I would like to do it only with CSS. Is there a way to do this using flex?

You can use flexbox:
.container {
display: flex; /* Magic begins */
flex-direction: column;
}
.before, .after {
flex: 1; /* If possible, center .main vertically */
min-height: 0; /* Really, don't care about overflow, just center .main vertically */
}
.main {
align-self: center; /* Center .main horizontally */
}
html,
body {
height: 100%;
}
body {
margin: 0;
padding: 1em;
}
body,
.container {
box-sizing: border-box;
}
.container {
border: 1px solid red;
padding: 1em;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.before,
.after {
flex: 1;
min-height: 0;
}
.main {
width: 100px;
height: 100px;
background-color: #999;
padding: 1em;
align-self: center;
}
<div class="container">
<div class="before"></div>
<div class="main">
Main box: this box should be at the center of the container.
</div>
<div class="after">
<ul class="extra-info">
<li>These items should naturally follow the main box and not care about vertical centering.</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</div>

setting negative margin to virtually reduce width down to zero for extra-info, using display:table to shrink on content and and center, you could do something like this :
html,
{
height: 100%;
}
body {
min-height:100%;
border: 1px solid red;
}
body,
.container {
box-sizing: border-box;
}
.container {
padding: 1em;
display: table;
margin: auto;
}
.main-display {
width: 100px;
height: 100px;
background-color: #999;
padding: 1em;
}
.extra-info {
padding:0;
margin:0;
background:lightgray;
margin-right: -50vw;
max-width: 50vw
/* size virtually reduce to zero with equal negative margin value*/
}
<div class="container">
<div class="main-display">
Main box: this box should be at the center of the container.
</div>
<ul class="extra-info">
<li>These items should naturally follow the main box and not care about vertical centering.</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>

I am not sure I quite understand what do you want but maybe ?
#main {
width: 100%;
height: 100px;
border: 0px;
display: -webkit-flex;
display: flex;
}
#main div {
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
<div id="main">
<div style="background-color:white;">empty</div>
<div style="background-color:Blue;">Your box here</div>
<div style="background-color:white;">empty</div>
</div>
You also can do it with margin only by setting left margin to 40% or 35% depending on your box width

Related

button not aligned with image in div

In the following image , the button (Questions) is lower than the image (white rectangle). They both are inside the same div. Why? The page has a top level css-grid with 3 rows.
.grid-container {
display: grid;
grid-template-rows: [nav-row-start]auto [nav-row-end logo-nav-row-start] auto [logo-nav-row-end content-row-start] auto [content-row-end];
}
.nav-style {
height: 5vh;
/*make nav div take 5% of space of viewport*/
background-color: black;
}
.logo-nav-style {
height: 20vh;
/*make logo-nav div take 20% of space of viewport*/
background-color: gray;
}
.nav-flexbox-container {
display: flex;
flex-direction: row-reverse;
}
.content-style {
height: 75vh;
/*make content div take 75% of space of viewport*/
background-color: white;
}
#nav {
grid-row: nav-row-start/nav-row-end;
margin: 0px;
}
#logo-nav {
grid-row: logo-nav-row-start/logo-nav-row-end;
margin: 0px;
}
#content {
grid-row: body-row-start/body-row-end;
margin: 0px;
}
#profile-pic {
margin: 5px;
}
#mail-icon-pic {
margin: 5px;
}
#stats-icon-pic {
margin: 5px;
}
#logo-image {
/*the max width and max height rule will make the image fit inside the div. If the image is bigger than div, the image will
contract, if the image is smaller than the div, the image will expand*/
max-width: 100%;
max-height: 100%;
}
body {
margin: 0px;
}
<div class="grid-container">
<div id="nav" class="nav-style nav-flexbox-container">
<img id="stats-icon-pic" src="stats_icon.png">
<img id="mail-icon-pic" src="mail_icon.png">
</div>
<div id="logo-nav" class="logo-nav-style">
<img id="logo-image" src="example_logo.png"/>
<button type="button">Questions</button>
<!-- this button is lower than the image-->
</div>
<div id="content" class="content-style">body</div>
</div>
I can't answer the "why" part, but one solution is to add display: flex and align-items: flex-end to .logo-nav-style.
Also, I wouldn't lock .content-style to height: 75vh but use min-height: 75vh instead so the content can expand beyond that.
I'm also confused why you got flex-direction: row-reverse; on your .nav-flexbox-container. Why not just put the elements in the correct order to begin with?
.grid-container {
display: grid;
grid-template-rows: [nav-row-start]auto [nav-row-end logo-nav-row-start] auto [logo-nav-row-end content-row-start] auto [content-row-end];
}
.nav-style {
height: 5vh;
/*make nav div take 5% of space of viewport*/
background-color: black;
}
.logo-nav-style {
height: 20vh;
/*make logo-nav div take 20% of space of viewport*/
background-color: gray;
/* Rickard's addition */
display: flex;
align-items: flex-end;
}
.nav-flexbox-container {
display: flex;
flex-direction: row-reverse;
}
.content-style {
min-height: 75vh;
/*make content div take 75% of space of viewport*/
background-color: white;
}
#nav {
grid-row: nav-row-start/nav-row-end;
margin: 0px;
}
#logo-nav {
grid-row: logo-nav-row-start/logo-nav-row-end;
margin: 0px;
}
#content {
grid-row: body-row-start/body-row-end;
margin: 0px;
}
#profile-pic {
margin: 5px;
}
#mail-icon-pic {
margin: 5px;
}
#stats-icon-pic {
margin: 5px;
}
#logo-image {
/*the max width and max height rule will make the image fit inside the div. If the image is bigger than div, the image will
contract, if the image is smaller than the div, the image will expand*/
max-width: 100%;
max-height: 100%;
}
body {
margin: 0px;
}
<div class="grid-container">
<div id="nav" class="nav-style nav-flexbox-container">
<img id="stats-icon-pic" src="stats_icon.png">
<img id="mail-icon-pic" src="mail_icon.png">
</div>
<div id="logo-nav" class="logo-nav-style">
<img id="logo-image" src="example_logo.png"/>
<button type="button">Questions</button>
<!-- this button is lower than the image-->
</div>
<div id="content" class="content-style">body</div>
</div>

Scrollable cards with 100% height container using Flexbox

I would like a , tags which take 100% of the page and my cards to be scrollable when their contents are overflowing. But I can't handle the cards overflow the right way: the scrollbar isn't scrollable. All my containers use flexbox rules.
I've created a codepen to show you the issue:
https://codepen.io/YseopCSS/pen/dBGPYb
Can you help me please ?
<main class="edit-report">
<header class=header>
header
</header>
<section class="main__content">
<div class="global-filter">global-filter</div>
<div class="edit-report__edition">
<div class="edit-report__card data">data</div>
<div class="edit-report__card table">table</div>
<div class="edit-report__card report">
Report<br>
Report<br>
Report<br>
Report<br>
Report<br>
Report<br>
Report<br>
Report<br>
Report<br>
Report<br>
</div>
</div>
</section>
</main>
html, body {
height:100%;
margin: 0;
padding: 0;
}
.edit-report {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-direction: column;
width: 100%;
}
.header {
padding: 15px 50px;
background:green;
}
.main {
&__content {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow: hidden;
background: yellow;
}
}
.global-filter {
height: 100px;
background: purple;
}
.edit-report {
&__edition {
display: flex;
align-items: flex-start;
justify-content: space-between;
flex-grow: 1;
}
&__card {
flex: 1 1 25%;
min-height: 100px;
&.data {
background: pink;
}
&.table {
background: violet;
}
&.report {
background: cyan;
overflow-y: scroll;
}
}
}
After checking your code, I realized the problem was the height on the edit-report__card.report, it does not have a height defined, thanks to that, the current height is "2106px", hence the scroll is not working, even when, you force it with "overflow-y: scroll;", in order to fix it just add this line:
.edit-report__card.report {
background: cyan;
overflow-y: scroll;
height: 100%;
}
CSS,
This pen maybe able to help you.
<ul>
<li>
<img src="http://placekitten.com/280/330"/>
</li>
<li>
<img src="http://placekitten.com/220/300"/>
</li>
<li>
<img src="http://placekitten.com/140/210"/>
</li>
<li>
<img src="http://placekitten.com/140/210"/>
</li>
<li>
<img src="http://placekitten.com/140/210"/>
</li>
<li>
<img src="http://placekitten.com/140/210"/>
</li>
<li>
<img src="http://placekitten.com/140/210"/>
</li>
ul {
display:flex;
flex-flow: row wrap;
/* allow scrolling of flex container */
overflow-x:auto;
/* overwrite list container styles */
margin:2px;
padding:1em;
/* color for some visual of the spacing */
box-shadow:0 0 10px black inset;
}
li {
/* remove list style bullets */
list-style-type:none;
width: 40%;
/* vertical center with 10px on left and right */
width: 23%;
padding: 1%
}
li img {
width: 100%;
}
https://codepen.io/rusticblonde/pen/QXyjKy

Flexbox stretch not stretching far enough

I'm using flexbox to create a basic layout for a web application. I want there to be a menu across the top and, below that, a primary content area on the left and a secondary content area on the right, both of which vertically fill the space left over below the menu. If just the content areas are included in the HTML, the stretch covers everything. When I include the menu, however, I end up with a lot of white space between the menu and content areas.
In the JS fiddle, I added a little JavaScript to remove the menu when you click on either of the links to give a better idea as to how much white space (1rem) I'd like between the menu and the two content areas.
Can this be achieved using flex? Thanks!
$(document).ready(function() {
$("a").click(function() {
$(this).closest(".main-menu").remove();
})
})
html {
height: 100%;
}
body {
display: flex;
min-height: 100%;
width: 100%;
margin: 0;
padding: 0;
border: 0;
flex-wrap: wrap;
align-content: stretch;
}
.main-menu {
width: 90%;
margin: 1rem calc(5% - 1px) 1rem calc(5% - 1px);
padding: 1rem;
border: 1px dashed black;
align-self: flex-start;
}
.main-menu ul {
margin: 0;
border: 0;
padding: 0;
display: inline;
}
.main-menu ul li {
margin: 0;
border: 0;
padding: 0;
display: inline;
}
.primary-stuff {
margin: 1rem 1rem 1rem calc(5% - 1px);
width: calc(75% - 1rem - 1px);
border: 1px dashed black;
}
.secondary-stuff {
margin: 1rem calc(5% - 1px) 1rem 1rem;
width: calc(15% - 1rem - 1px);
border: 1px dashed black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main-menu">
<ul>
<li><a>Link 1</a></li>
<li><a>Link 2</a></li>
</ul>
</div>
<div class="primary-stuff"></div>
<div class="secondary-stuff"> </div>
<div style="position:absolute;background:#FFC; width: calc(5% - 1px); height: 100%; left: 0;"></div>
<div style="position:absolute;background:#FFC; width: calc(5% - 1px); height: 100%; right: 0; top: 0;"></div>
<div style="position:absolute;background:#FFC; height: 1rem; width: 100%; right: 0; top: 0;"></div>
<div style="position:absolute;background:#FFC; height: 1rem; width: 100%; right: 0; bottom: 0;"></div>
Fiddle:
https://jsfiddle.net/don01001100/kdg04ubr/
You can't really do this with flexbox because of the way align-content works with wrap.
However, the layout is simple with CSS Grid layout:
body {
display: grid;
grid-template-columns: 75% 1fr;
grid-template-rows: auto 1fr;
grid-gap: 1rem;
min-height: 100vh;
}
nav {
grid-column: 1 / -1;
}
/* non-essential decorative styles */
body { margin: 0; padding: 1rem 2rem; }
nav { background-color: lightgreen; padding: 1rem; }
.primary-stuff { background-color: lightblue; }
.secondary-stuff { background-color: orange; }
* { box-sizing: border-box; }
<nav>
<a>Link 1</a>
<a>Link 2</a>
</nav>
<div class="primary-stuff"></div>
<div class="secondary-stuff"> </div>
Also, all those calculations you have for margins are not necessary. Here's an easy solution that will simplify your code: Flexbox: 4 items per row
You might find that the Grid Layout is a better fit for this type of layout, as it provides a cleaner method of arranging elements in the way you require, without the need for additional HTML markup.
You could achieve the layout you require using CSS-grid as follows:
html {
height: 100%;
}
body {
/* Cause grid to fill vertical space */
height:100%;
/* Prevent overflow due to padding */
box-sizing:border-box;
/* Use grid display type */
display: grid;
/* Tells grid to cause second row to fill
avaible/remaining vertical space */
grid-template-rows: auto 1fr;
/* Define the grid layout, in terms of areas
that are distributed between 3 colums and
2 rows */
grid-template-areas:
"menu menu menu"
"primary primary secondary";
/* Specify spacing between grid elements */
grid-gap: 1rem;
margin: 0;
padding: 1rem;
background:grey;
}
.main-menu {
background: pink;
/* Accociate the main-menu with the menu area
of your grid-template-areas defined above */
grid-area: menu;
}
.main-menu ul {
margin: 0;
padding: 0;
display: inline;
}
.main-menu ul li {
margin: 0;
padding: 0;
display: inline;
}
.primary-stuff {
background: lightblue;
/* Accociate the primary-stuff with the primary area
of your grid-template-areas defined above */
grid-area: primary;
}
.secondary-stuff {
background: lightgreen;
/* Accociate the secondary-stuff with the secondary area
of your grid-template-areas defined above */
grid-area: secondary;
}
<div class="main-menu">
<ul>
<li><a>Link 1</a></li>
<li><a>Link 2</a></li>
</ul>
</div>
<div class="primary-stuff">
primary content
</div>
<div class="secondary-stuff">
secondary content
</div>

How to get a div to fill the remainder of the screen height [duplicate]

This question already has answers here:
Make a div fill the height of the remaining screen space
(41 answers)
Closed 4 years ago.
I've got a div, I need the bottom div to fill the remainder of the screen and show a scroll bar. The bottom div is not showing a scroll bar.
JSFiddle
.page {
position: fixed;
top: 0;
width: 100%;
height: 100%;
margin: 0;
padding: $menu-height 0 0 0;
box-sizing: border-box;
}
.sidebar {
width: 500px;
float: left;
height: 100%;
box-sizing: border-box;
background: #ddd;
border-right: 1px solid red;
}
.top {
height: 200px;
background: #eee;
}
.bottom {
background: #ccc;
overflow-y: auto;
}
.filler-content {
height: 2000px;
}
<div class="page">
<div class="sidebar">
<div class="top">
top
</div>
<div class="bottom">
<div class="filler-content">
bottom
</div>
</div>
</div>
</div>
If I understood your problem correctly, display: flex is your friend.
Add display: flex; flex-direction: column; to your .sidebar and flex: 1; to your .bottom and that should do it. If I misunderstood, just let me know in a comment and I'll try to help otherwise
Demo: http://jsfiddle.net/qy5fL29t/23/
I would use a flexbox solution as it will make it a lot simpler and get rid of the need for using floats (we shouldn't be abusing them in the day of css3)
html,
body {
height: 100%;
margin: 0;
}
.page {
height: 100%;
display: flex; /* this one is so that you don't need to float the sidebar and can insert a main area that will take up the rest of the width */
flex-direction: column;
flex-wrap: wrap;
}
.sidebar {
width: 500px;
height: 100%;
display: flex; /* this is so we can get bottom to take any height top doesn't need */
flex-direction: column;
background: #ddd;
border-right: 1px solid red;
}
.top {
flex-basis:200px;
min-height: 200px; /* these two are to force top to be 200px otherwise flex may recalculate based on available space */
max-height: 200px;
background: #eee;
}
.bottom {
flex-grow: 1; /* this forces bottom to grow to fill the space top doesn't take */
overflow-y: auto;
}
/* test and example below */
.filler-content {
height:1000px;
}
.main {
flex-grow: 1;
background: white;
}
<div class="page">
<div class="sidebar">
<div class="top">
top
</div>
<div class="bottom">
<div class="filler-content">
bottom
</div>
</div>
</div>
<div class="main">
</div>
</div>
Replace your css with this
.sidebar {
width: 500px;
float: left;
height: 100%;
box-sizing: border-box;
background: #ddd;
border-right: 1px solid red;
}
.top {
height: 200px;
background: #eee;
}
.bottom {
background: #ccc;
overflow-y: scroll;
height:200px
}
.filler-content {
height:2000px;
}
<html>
<body>
<div class="page">
<div class="sidebar">
<div class="top">
top
</div>
<div class="bottom">
<div class="filler-content">
bottom
</div>
</div>
</div>
</body>
</div>
</html>
You can use this code for bottom div srollbar.
.bottom {
background: #ccc;
overflow-y: auto;
height:200px;
}

Sidebar with two flexbox navs top and bottom

I´m trying to align two navigations inside a sidebar –with 100% viewport height – by use of flexbox.
the red box should be placed on the top of it´s sidebar parent
the blue box on the bottom.
In case the red navigation grows and the space between both is to little the sidebar should be scrollable in y-axis. What I´ve tried is setting top and bottom margin for both without luck. Can somebody help me out ?
Thanks!
html, body {
margin: 0;
}
* {
box-sizing: border-box;
}
.sidebar {
height: 100vh;
width: 300px;
background: #ccc;
padding: 10px;
overflow-y: scroll;
}
.sidebar__top {
background: red;
height: 200px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.sidebar__bottom {
background: blue;
height: 100px;
margin-top: auto;
}
<aside class="sidebar">
<nav class="sidebar__top"></nav>
<nav class="sidebar__bottom"></nav>
</aside>
Here is my fiddle: http://jsfiddle.net/1dw7h2sp/1/
There are probably other ways to do this. In short I did the following:
Wrap your elements with a parent that is able to grow in size (.sidebar__wrapper)
Set the min-height instead of height so it can grow
Use flex-grow if you want an element to fill out the remaining space.
html, body {
margin: 0;
}
* {
box-sizing: border-box;
}
.sidebar {
height: 100vh;
width: 300px;
background: #ccc;
overflow-y: scroll;
margin: 0;
padding: 0;
}
/* set up a wrapper that can grow in size */
.sidebar__wrapper {
height: auto;
min-height: 100vh;
width: 100%;
background: #808080;
padding: 10px;
margin: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.sidebar__top {
background: red;
min-height: 200px;
margin-bottom: 10px;
/* this fills up the remaining space */
flex-grow: 1;
}
.sidebar__bottom {
background: blue;
min-height: 100px;
}
<aside class="sidebar">
<div class="sidebar__wrapper">
<nav class="sidebar__top" contenteditable="true">
<p>test</p>
</nav>
<nav class="sidebar__bottom"></nav>
</div>
</aside>

Resources