I have a 3 column element on a page
<main>
<aside class="left-sidebar"></aside>
<section class="main-content"></section>
<aside class="right-sidebar"></aside>
</main>
And have the following css rules:
main {
display:flex;
flex-wrap:wrap;
margin-left: -20px;
}
main > * {
margin-left: 20px;
}
aside.left-sidebar {
max-width: 320px;
width: calc(25% - 20px);
}
.main-content {
width: calc(100% - 800px);
margin: 0 auto;
padding-top: 65px;
max-width: 1200px;
}
aside.right-sidebar {
max-width: 400px;
width: calc(25% - 20px);
}
This works fine on Chrome and Firefox but not on Microsoft Edge, anything am missing?
One thing I noticed on Edge upon checking its DevTools is that it will reverse the operation on the calc() function so instead of width: calc(25% - 20px); it'll convert it to calc(-20px + 50%)
unsure if that's the culprit tho the result is the same I think.
UPDATE: Forgot to include that there's a max-width set on columns and max-width:100% is called when screen size is #media screen and (max-width: 1440px).
Possible workaround that shouldn't be used
This only appears to be a problem with percentage widths, not viewport width units (vw). Here is a workaround that shouldn't be used:
body {
margin: 0;
}
main {
display: flex;
flex-wrap: wrap;
height: 100vh;
}
aside.left-sidebar {
background: red;
max-width: 100%;
width: calc(25vw - 20px);
}
.main-content {
background: green;
max-width: 100%;
width: calc(50vw - 20px);
margin: 0 20px;
}
aside.right-sidebar {
background: blue;
max-width: 100%;
width: 25vw;
}
<main>
<aside class="left-sidebar"></aside>
<section class="main-content"></section>
<aside class="right-sidebar"></aside>
</main>
What you should use
Since you're using flex, use flex:
First and third column are given flex: 1
Center column is given flex: 2
Place the left and right margin on the center column
The columns can be given individual max-widths
No widths are given
This gives you the same ratio, no need for calc.
Example
Note: I have removed the default margin on the body and given main 100vh so it has a height for this example.
body {
margin: 0;
}
main {
display: flex;
flex-wrap: wrap;
height: 100vh;
}
aside.left-sidebar {
background: red;
flex: 1;
max-width: 320px;
}
.main-content {
background: green;
flex: 2;
max-width: 1200px;
margin: 0 40px;
}
aside.right-sidebar {
background: blue;
flex: 1;
max-width: 400px;
}
<main>
<aside class="left-sidebar"></aside>
<section class="main-content"></section>
<aside class="right-sidebar"></aside>
</main>
Related
I'm developing an app with the interface that is supposed to fit the page (only some internal elements may have scrolling). The basic layout consists of a header and the main section:
<div class="page">
<Navigation/> <!-- a Vue component -->
<main class="page__main">
...
</main>
</div>
currently, CSS has hardcoded height of the header (Navigation):
.page {
height: 100vh;
}
.page__main {
height: calc(100vh - 80px); /* 80px is the height of the header */
}
I'd like to get rid of this hardcoded bit but make sure .page__main's height gets no larger than 100vh - height of Navigation. Is there a way to do this without JS? I suspect that there are some options that can be used with
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
but just using that with
.page__main {
flex-shrink: 1;
}
doesn't work: .page__main has children which use height in percents and once I set flex-shrink: 1; instead of height: calc(100vh - 80px); those grow and the interface is broken.
To illustrate the problem better, here's the current state:
body { padding: 0; margin: 0; }
.page {
height: 100vh;
background: blue;
}
.page__navigation {
height: 80px;
background: gray;
}
.page__main {
height: calc(100vh - 80px);
}
.part1 {
height: 50%;
background: #eeeeee;
overflow-y: scroll;
}
.part2 {
height: 50%;
background: #cccccc;
}
<div class="page">
<div class="page__navigation">nav stuff</div>
<main class="page__main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</main>
</div>
and here's what happen when I try to "set height" via flex:
body { padding: 0; margin: 0; }
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.page__navigation {
height: 80px;
background: gray;
}
.page__main {
flex-shrink: 1;
}
.part1 {
height: 50%;
background: #eeeeee;
overflow-y: scroll;
}
.part2 {
height: 50%;
background: #cccccc;
}
<div class="page">
<div class="page__navigation">nav stuff</div>
<main class="page__main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</main>
</div>
You can consider a nested flexbox container and don't forget the use of min-height:0; to allow the elements to shrink.
body { padding: 0; margin: 0; }
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.page__navigation {
height: 80px;
background: gray;
}
.page__main {
flex-grow: 1; /* Fill the remaining space*/
display:flex; /* Nested Container*/
flex-direction:column;
min-height:0; /* Allow the element to shrink */
}
.part1 {
flex-basis: 50%;
background: #eeeeee;
overflow-y: scroll; /* Allow the element to shrink */
}
.part2 {
flex-basis: 50%;
min-height:0; /* Allow the element to shrink */
background: #cccccc;
}
<div class="page">
<div class="page__navigation">nav stuff</div>
<main class="page__main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</main>
</div>
Use flex-grow. Keep everything as the second one (flex one) and change:
Edit
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.page__main {
height: 100%;
min-height: 0;
flex: 1 1 auto;
}
Three value flex means flex: flex-grow | flex-shrink | flex-basis.
Flex-grow tells our element whether or not it can take up additional space.
Flex-shrink works very similarly to flex-grow, only instead of dealing with extra space, it deals with space not needed by an elements content.
Flex basis is best used when in conjunction with either flex-shrink or flex-grow.
You can check this article to understand better.
I would suggest css-grid approach : -
.page {
background: gray;
display: grid;
grid-template-rows: 100px auto;
height: 100vh;
color: white;
}
.nav {
grid-row: 1/2;
background: brown;
}
.main {
grid-row: 2/3;
background: green;
display: grid;
grid-template-rows: 30% 70%;
}
.part1 {
overflow: auto
}
.part2 {
background: blue
}
<div class="page">
<div class="nav">Nav</div>
<div class="main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</div>
</div>
</div>
Backstory: The following flexbox based layout works as expected. As I shrink the width of the browser all three images scale down at equal rates. If you run the code snippet please click the Full page link and scale the browser to see this simple effect.
.flexParent {
display: flex;
margin: 0 auto;
width: 80%;
}
.flexChildOne {
margin-right: 16px;
}
.flexChildOne img {
width: 100%;
height: auto;
}
.flexInterior {
display: flex;
overflow: hidden;
}
.flexChildTwo {
width: 400px;
margin-right: 16px;
}
.flexChildTwo img {
width: 100%;
height: auto;
}
.flexChildThree {
width: 400px;
}
.flexChildThree img {
width: 100%;
height: auto;
}
<div class="flexParent">
<div class="flexChildOne"><img src="https://i.stack.imgur.com/9lzSH.jpg"></div>
<div class="flexInterior">
<div class="flexChildTwo"><img src="https://i.stack.imgur.com/8Q4Q7.jpg"></div>
<div class="flexChildThree"><img src="https://i.stack.imgur.com/5vBC6.jpg"></div>
</div>
</div>
However when I add flex-direction: column; to the flexInterior class the effect doesn't work as needed. The left image scales as before but the two images inside flexInterior remain static. Their right edges get progressively cut off as the browser shrinks instead of scaling. This is because of the line overflow: hidden; which is needed in the original row layout to cause the scaling effect but creates this new cutting problem in the column layout version.
Question: In the column layout how do I make the images scale similar to the row layout? This is a simplified example. In the actual version I'm using media queries to display either the column or row version depending on the width of the browser.
.flexParent {
display: flex;
margin: 0 auto;
width: 80%;
}
.flexChildOne {
margin-right: 16px;
}
.flexChildOne img {
width: 100%;
height: auto;
}
.flexInterior {
display: flex;
flex-direction: column; /* Only line that was changed */
overflow: hidden;
}
.flexChildTwo {
width: 400px;
margin-right: 16px;
}
.flexChildTwo img {
width: 100%;
height: auto;
}
.flexChildThree {
width: 400px;
}
.flexChildThree img {
width: 100%;
height: auto;
}
<div class="flexParent">
<div class="flexChildOne"><img src="https://i.stack.imgur.com/9lzSH.jpg"></div>
<div class="flexInterior">
<div class="flexChildTwo"><img src="https://i.stack.imgur.com/8Q4Q7.jpg"></div>
<div class="flexChildThree"><img src="https://i.stack.imgur.com/5vBC6.jpg"></div>
</div>
</div>
The child elements of .flexInterior are wider than their parent
Remove the width from .flexChildTwo and .flexChildThree and add it to .flexInterior
.flexInterior {
display: flex;
flex-direction: column;
overflow: hidden;
width: 400px
}
Example: https://codepen.io/IanJohnson/pen/QzqggE?editors=1100
You will also want to get rid of the margin-right when its in the column layout
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>
This question already has answers here:
Maintain aspect ratio of div but fill screen width and height in CSS?
(9 answers)
Closed 7 years ago.
I want to create an HTML page with:
A fixed-width, full-height Navigation pane on the left
A square element in the centre of the remaining area
I want this square to be as big as possible, expanding to fill the area not taken up by the navigation pane.
I have a JavaScript solution for this (see below and as a jsFiddle), but I'm hoping that it is possible to do this as a CSS only solution.
<!DOCTYPE html>
<html lang=en>
<head>
<style>
html, body {
margin: 0;
height: 100%;
background-color: #fff;
}
nav {
height: 100%;
width: 96px;
background-color: #666;
}
main {
position: absolute;
background-color: #000;
color: #fff;
}
</style>
</head>
<body>
<nav>
Navigation
</nav>
<main>
This should be square
</main>
<script>
;(function createSquareArea() {
var main = document.querySelector("main")
var nav = document.querySelector("nav")
var navWidth = nav.getBoundingClientRect().width
var debounceDelay = 100
var timeout
window.onresize = windowResized
maintainRatio()
function windowResized() {
if (timeout) {
window.clearTimeout(timeout)
}
timeout = window.setTimeout(maintainRatio, debounceDelay)
}
function maintainRatio() {
timeout = 0
var windowHeight = window.innerHeight
var mainWidth = window.innerWidth - navWidth
var minDimension = Math.min(windowHeight, mainWidth)
var left = (mainWidth - minDimension) / 2 + navWidth
var top = (windowHeight - minDimension) / 2
main.style.left = left + "px"
main.style.top = top + "px"
main.style.width = minDimension + "px"
main.style.height = minDimension + "px"
}
})()
</script>
</body>
</html>
JSFiddle: https://jsfiddle.net/ymzq6zm0/7/
HTML
<nav>
Navigation
</nav>
<main>
<div class="sc">
This should be square
</div>
</main>
CSS
html, body {
margin: 0;
height: 100%;
background-color: #fff;
}
.wrapper {
display: flex;
align-items: center;
height: 100%;
}
nav {
float: left;
height: 100%;
width: 96px;
background-color: #666;
}
main {
float: left;
width: calc(100% - 96px);
height: 100vmin;
max-height: calc(100vw - 96px);
}
.sc {
margin: 0 auto;
background-color: #000;
color: #fff;
height: 100vmin;
width: 100vmin;
max-width: 100%;
max-height: calc(100vw - 96px);
}
How it works
Our main objective here is to:
Center align .sc
Align .sc vertically center
Make sure .sc is always a sqaure
Make .sc responsive
The square is highly responsive as it changes its height and width according to the window's or view port's height and width. We need to use vw (viewport's width) and vmin (lowest value between viewport's height and width). Read more about these units here: https://css-tricks.com/viewport-sized-typography/
To make .sc a square, we need to make sure its width and height are always equal. Since the ratio of height and width of viewport is not always the same, we need to find out the lowest value between these two and assign them to .sc which can be done using the vmin unit mentioned above.
The square should always remain centered in the remaining area after the navigation on the left, never cross the remaining area and resize accordingly.
This can be accomplished the following codes:
nav {
float: left;
height: 100%;
width: 96px;
background-color: #666;
}
main {
float: left;
width: calc(100% - 96px);
height: 100vmin;
max-height: calc(100vw - 96px);
}
.sc {
margin: 0 auto;
background-color: #000;
color: #fff;
height: 100vmin;
width: 100vmin;
max-width: 100%;
max-height: calc(100vw - 96px);
}
main is the remaining area after nav. We make sure of this by using the calc property to subtract the nav width from 100%.
The .sc is placed inside main and we have added the extra max-width and max-height properties to make sure .sc always resizes itself according to main.
max-height: calc(100vw - 96px); property of .sc is always equal to width: calc(100% - 96px); property of main. They both calculate the same values.
By adding max-width: 100%; to .sc we make sure it's maximum width is equal to that of main.
Now since, both the max-height and max-width along with width and height of .sc remain the same, it will always be a square.
At the end we put both nav and main inside .wrapper which is a flexbox and has align-items: center; property. This will ensure that the square is always vertically centered with respect to the nav.
This can be accomplished with viewport units.
HTML
<div class='page-wrapper'>
<nav>
</nav>
<div class='content'>
<div class='square'>
</div>
</div>
</div>
CSS
body, html
{
height: 100%;
}
.page-wrapper
{
display: flex;
height: 100%;
width: 100%;
}
nav
{
height: 100%;
width: 200px;
background-color: #444;
}
.content
{
flex-grow: 1;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.square
{
width: 100vh;
height: 100vh;
background-color: #111;
max-width: calc( 100vw - 200px );
max-height: calc( 100vw - 200px );
}
See this jsFiddle https://jsfiddle.net/ryannwarner/hugt40cm/
Viewport units and calc can do a lot of this but I'm not sure on your final desired result.
If you want both dimension to recalculate you will need javascript.
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
background-color: #fff;
}
nav {
height: 100%;
width: 96px;
background-color: #666;
}
main {
position: absolute;
background-color: lightgreen;
color: #fff;
top: 0;
left: 96px;
height: calc(100vw - 96px);
max-height: 100vh;
width: calc(100vw - 96px);
max-width: 100vh;
}
<nav>
Navigation
</nav>
<main>
This should be square
</main>
JSfiddle demo with no max-height...I think you can see the problem with a CSS method.
I'm trying to make my own responsive layout using percentages. I managed to calculate the columns that I wanted to use but I can't work out how to put like a margin (gutter) in between columns. If you check the codepen code there is no spacing in between the contents.
Codepen
.container{
width: 90%;
max-width: 1140px;
margin: 0 auto;
/*background: #333;*/
}
.container .columns{
float: left;
background: #ccc;
margin: 0 0 1em ;
padding-right: 1em;
padding-left: 1em;
border-left-width:12px;
}
.row{
float: left;
clear: both;
width: 100%;
}
.container .columns.col-1 { width: 8.33333333333%; }
.container .columns.col-2 { width: 16.6666666667%; }
.container .columns.col-3 { width: 25%; }
.container .columns.col-4 { width: 33.3333333333%; }
.container .columns.col-5 { width: 41.6666666667%; }
.container .columns.col-6 { width: 50%; }
.container .columns.col-7 { width: 58.3333333333%; }
.container .columns.col-8 { width: 66.6666666667%; }
.container .columns.col-9 { width: 75%; }
.container .columns.col-10{ width: 83.3333333333%; }
.container .columns.col-11{ width: 91.6666666667%; }
.container .columns.col-12{ width: 100%; }
I would personally shy away from Calc as it's still not fully supported but up to you — http://caniuse.com/#feat=calc
I would recommend wrapping all of your content in another set of elements that way you can use padding for spacing and margin for alignment. Check out the demo.
<div class="columns col-6"><div>6</div></div>
DEMO
Instead of giving padding give margin
.container .columns{
float: left;
background: #ccc;
margin: 0 0 1em ;
margin-right: 1em;
margin-left: 1em;
border-left-width:12px;
}
Use calc() method to calculate margin from width.For example for .col-3 the CSS would be
.container .columns.col-3 {
width: calc(25% - 5px);
margin-right:5px;
}
make sure you use calc() in right way like this
calc([first value][space][operator][space][second value])
If you see your columns width, You have divided 100% of the viewport width equally.
For example:
.container .columns.col-6 {
width: 50%;
}
So in that case, you won't have any space between two blocks.
So while mentioning the width for the columns, you need to consider margin as well.
so you can use either of the following two approaches:
.container .columns.col-6 {
width: calc(50% - 10px); // 10px represents the margin / space
margin-right:10px;
}
or
.container .columns.col-6 {
width: 46%;
margin-right: 1%;
}
The first approach is better.