This question already has answers here:
A grid layout with responsive squares
(5 answers)
Responsive Square Divs Cross Browser Compatible
(3 answers)
css grid of squares with flexbox
(6 answers)
Inner div with square ratio and flexbox [duplicate]
(5 answers)
Closed 4 years ago.
I'm trying to make a grid of resizable squares with some text inside them. Here's the code:
/* Dirty quick CSS reset */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
flex-flow: column;
flex: 1;
background: aliceblue;
}
.row {
display: flex;
flex: 1;
}
.square {
border: 1px solid black;
width: 14.2857%; /* 100% / 7 */
font-size: 18px;
padding: 8px;
/* square-width - font-size - padding-top */
padding-bottom: calc(14.2857% - 18px - 8px);
}
<div class="container">
<div class="row">
<div class="square">1</div>
<div class="square">2</div>
<div class="square">3</div>
<div class="square">4</div>
<div class="square">5</div>
<div class="square">6</div>
<div class="square">7</div>
</div>
</div>
As we can see, there's a row of squares that adapt to the size of the window. The problem is that if we inspect them, we see that they aren't totally squares (they are about 3px taller than wide). It gets worse if we increase the font-size, and as far as I know, the maths is correct.
What's going on here? Why am I getting those extra pixels?
I encountered this problem a while ago, and was able to solve it through this solution by using Pseudo element
/* Dirty quick CSS reset */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
flex-flow: column;
flex: 1;
background: aliceblue;
}
.row {
display: flex;
flex: 1;
}
.square {
border: 1px solid black;
width: 14.2857%;
/* 100% / 7 */
font-size: 18px;
padding: 8px;
}
.square:before {
content: '';
float: left;
padding-top: 100%;
}
<div class="container">
<div class="row">
<div class="square">1</div>
<div class="square">2</div>
<div class="square">3</div>
<div class="square">4</div>
<div class="square">5</div>
<div class="square">6</div>
<div class="square">7</div>
</div>
</div>
The exact calculation should be (14.2857% - 8px - 2px - Lpx) we remove the padding-top and the border and the line-height (not the font-size), so you should know the value of the line-height or you set it:
/* Dirty quick CSS reset */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
flex-flow: column;
flex: 1;
background: aliceblue;
}
.row {
display: flex;
flex: 1;
}
.square {
border: 1px solid black;
width: 14.2857%; /* 100% / 7 */
font-size: 18px;
line-height:1em; /*equal to font-size*/
padding: 8px;
/* square-width - font-size - padding-top */
padding-bottom: calc(14.2857% - 8px - 2px - 18px);
}
<div class="container">
<div class="row">
<div class="square">1</div>
<div class="square">2</div>
<div class="square">3</div>
<div class="square">4</div>
<div class="square">5</div>
<div class="square">6</div>
<div class="square">7</div>
</div>
</div>
If we refer to the documentation the line-height is the value that define the height of the lines and the default value is set to normal:
The line-height CSS property sets the amount of space used for lines,
such as in text.
And
normal
Depends on the user agent. Desktop browsers (including Firefox)
use a default value of roughly 1.2, depending on the element's
font-family.
As you can see the line-height is not necessarily equal to font-size
Accounting for the 2px from the border fixed it for me:
padding-bottom: calc(14.2857% - 18px - 10px);
/* Dirty quick CSS reset */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
flex-flow: column;
flex: 1;
background: aliceblue;
}
.row {
display: flex;
flex: 1;
}
.square {
border: 1px solid black;
width: 14.2857%; /* 100% / 7 */
font-size: 1em;
padding: 8px;
/* square-width - font-size - padding-top */
padding-bottom: calc(14.2857% - 18px - 10px);
}
<div class="container">
<div class="row">
<div class="square">1</div>
<div class="square">2</div>
<div class="square">3</div>
<div class="square">4</div>
<div class="square">5</div>
<div class="square">6</div>
<div class="square">7</div>
</div>
</div>
Related
I have a flex div container with three child divs. For some reason adding padding left to the first container - #testcont causes the other containers to move. Unless I'm not looking carefully, why is this happening when the box-sizing is border box?
Here is my code: https://jsfiddle.net/templar9901/2g3whovc/2/
<div id = "three">
<div id = "testcont">
blahh1
</div>
<div>
blahh2
</div>
<div>
blahh3
</div>
</div>
//CSS
*{
box-sizing: border-box;
}
#three{
border: solid;
display: flex;
justify-content: space-around;
}
#three div{
border: 2px solid red;
/* flex-grow: 1; */
}
#testcont{
padding-left: 20%;
}
Because they all share the same parent container.
You should put each group with a different style separately like this.
<div id = "main-container">
<div class="container1">
<div class="btn" id = "testcont">
blahh1
</div>
</div>
<div class="container2">
<div class="btn">
blahh2
</div>
<div class="btn">
blahh3
</div>
</div>
</div>
CSS
*{
box-sizing: border-box;
}
#main-container{
border: 1px solid;
display: flex;
justify-content: space-around;
}
.container1 {
width: 40%;
border: 1px solid blue;
}
.container2 {
width: 60%;
border: 1px solid green;
display: flex;
}
.btn{
border: 2px solid red;
padding: 0 5px;
/* flex-grow: 1; */
}
#testcont{
padding-left: 30%;
}
hope this help.
If this can be achieved in CSS:
When not hovered: 3 columns split in average width
When hovered on one of the column: that column expands and squeezes other 2 columns
Here's what I've been trying:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* vertical 1:2:1 */
html,
body {
height: 100%;
}
.vertical-divider {
display: flex;
flex-flow: column nowrap;
height: 100%;
}
/* container in page center */
.container {
display: flex;
flex-flow: row nowrap;
background-color: #eee;
flex: 2;
}
.container>.item {
display: flex;
flex-flow: column;
justify-content: left;
align-content: left;
align-items: left;
transition: .3s;
max-width: 50%;
padding-top: 24px;
padding-left: 12px;
background-color: #ccc;
min-width: 10%;
flex: 1;
text-align: left;
}
.container>.item:hover {
transition: .3s;
max-width: 80% !important;
background: #333;
flex: 4;
cursor: pointer;
color: #fff;
}
<!doctype html>
<html>
<head>
</head>
<body>
<div class="vertical-divider">
<div class="container">
<div class="item">
Column 1
</div>
<div class="item">
Column 2
</div>
<div class="item">
Column 3
</div>
</div>
</div>
</body>
</html>
But responsive design (e.g. If I want to just put them vertically if the screen is narrow) seems hard to achieve. So I'm asking if there is a better solution.
Flexbox offers a clean, modern solution. We can transition on the flex property. If you want to make the hovered div take up more room, simply adjust the value to a higher number.
.container {
display: flex;
}
.container > div {
flex: 1;
border-right: 2px solid black;
height: 300px;
padding: 10px;
transition: 0.5s flex;
}
.container > div:hover {
flex: 3;
}
.container > div:last-child {
border-right: 0;
}
<div class="container">
<div>col 1</div>
<div>col 2</div>
<div>col 3</div>
</div>
Edit A new requirement has emerged: make it responsive. Flexbox makes this an easy addition by changing the flex-direction property inside a simple media query.
#media screen and (max-width: 700px) {
.container {
flex-direction: column;
height: 100vh;
}
.container > div {
border-right: none;
border-bottom: 2px solid;
}
}
With the media query in place, our example is now complete.
Have a look.
I need to preserve the aspect ratio of several divs using flex, cross browser. The divs contain charts and diagrams as SVGs, not IMGs.
I have a preferred solution working in firefox (https://jsfiddle.net/2d5hcfbo/4/), and another working in IE (https://jsfiddle.net/229oo3br/2/), but neither solution works in both. These were based on this answer. When looking at the Jsfiddles, if you increase the width of the output window (by dragging the middle column boundary to the left) you'll see the yellow divs turn pink and a Filter column is added (#media queries).
In both cases, the problem is that the divs seem to default to text height + padding. They need to stay oblong, broadly 1.5 times as wide as high. Also in IE the divs overlap each other and the font aligns low.
The FF solution uses flex-basis: 30vw; to set the height based on the width (flex-direction = column). (Height: 30vw doesn't work, not sure why.) This works in Chrome too.
The IE solution uses padding-top: 16.67%; to affect the height. This method has never been intuitive to me but I'd use it if it worked in FF.
I'm using IE 11 and FF45.9. I understand IE11 has/had a bug in this area(https://github.com/philipwalton/flexbugs/issues/71) but I can't avoid the browser. Thanks for any help!
Edit: I can make both declarations. But is there a better way?
CSS:
div#container {
/*position: relative;*/
padding-top: 50px;
display: flex;
/*flex-direction: row wrap;*/
/*align-items: stretch;*/
}
div#column1 {
flex: 0 0 auto;
background-color: white;
box-shadow: 3px 0px 10px #bebebe;
z-index: 9999;
overflow-y: scroll;
}
div#column2 {
display: flex;
flex-direction: column;
}
.row { display: flex; }
.row--top { flex: 2;}
.row--bottom { flex: 1; }
.cell {
flex: 1;
padding: 0.5em;
background-color: white;
margin: 1em;
box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 6px, rgba(0, 0, 0, 0.12) 0px 1px 4px;
}
.cell-wrap {
flex-basis: 31%;
display: flex;
flex-direction: column;
}
.cell-wrap div {
margin-left:0;
}
div.row--top div#cell1,
div.row--top div.cell-wrap div {
margin-bottom: 0;
}
div.fullwidth { width: 100%; }
div.fullheight { height: 100%; }
#media screen and (max-width: 1100px) {
#container {
height: auto;
}
.row { flex-direction: column; }
.cell {
flex-grow: 1;
background-color: pink;
/* flex-basis: 30vw; */
padding-top: 16.67%;
}
/*.flex.padding div {
padding-top: 16.67%;
}*/
#cell4 {
margin-bottom: 0;
}
.cell-wrap {
width: 100%;
flex-direction: column;
}
.cell-wrap div {
margin-left:1em;
}
}
#media screen and (max-width: 700px) {
.cell {
/*flex-grow: 0;*/
background-color: yellow;
padding-top: 16.67%;
/* flex-basis: 50vw; */
}
div#column1 {
display: none;
}
}
HTML:
<div id="container" class="fullheight fullwidth">
<div class="fullheight" id="column1">
<div id="filterRow">
<div class="selectHolder" id="filters"><h1>Filter</h1><div class="spanHolder">
</div></div>
</div>
</div>
<div class="fullheight fullwidth" id="column2">
<div class="row row--top">
<div class="cell" id="cell1">cell one</div>
<div class="cell-wrap">
<div class="cell" id="cell2">cell two</div>
<div class="cell" id="cell3">cell three</div>
</div>
</div>
<div class="row row--bottom">
<div class="cell" id="cell4">cell four</div>
<div class="cell-wrap">
<div class="cell" id="cell5">cell five</div>
</div>
</div>
<!-- </div> -->
</div>
</div>
Perhaps IE requires re-declaration of the default flex property within media queries. Adding back default declaration flex: 0 1 auto did the trick.
Thanks to Michael_B for the pointer. Fix here: https://jsfiddle.net/2d5hcfbo/9/
I have a simple table structure made up of divs:
$(document).ready(function() {
$("button").on("click", function() {
$(".cell").outerWidth(500);
})
})
div {
border: 1px solid black;
box-sizing: border-box;
}
.container {
width: 400px;
height: 100%;
padding: 5px;
overflow: auto;
}
.row {
min-width: 100%;
width: auto;
display: inline-flex;
padding: 5px;
margin-bottom: 5px;
border-color: red;
}
.cell {
flex: 0 0 auto;
border-right: 1px solid black;
overflow: hidden;
min-width: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="cell">x</div>
</div>
<div class="row">
<div class="cell">x</div>
</div>
<div class="row">
<div class="cell">x</div>
</div>
</div>
<button type="button">Change width</button>
The rows need to be vertically stacked, each having the (unknown) height of their content and be at least as wide as the container. The container has to scroll if the content does not fit. The width of the cells will be interactively changed using JS and the rows should expand to fit the whole content. For this reason, the rows have the following style:
.row {
min-width: 100%;
width: auto;
display: inline-flex;
}
The flex part is needed for the cells and is outside of the scope of this question. Being an inline element, the row will grow with the content in all major browsers but not in Internet Explorer 11. Check out the fiddle and click the button to change the width of the cells. The border helps to visualize the behaviour. The image below shows the expected behaviour (top) and how Internet Explorer interprets it (bottom):
What kind of bug is this (couldn't figure it out from the list of flexbugs) and how can I make it work in Internet Explorer?
In IE11 the behavior is as wanted:
The default flex behavior of flex items has changed. In Internet
Explorer 10, flex items that didn't fit their containers overflowed
the margins of the container or clipped to the margins of the
container. Starting with IE11, these items now shrink to fit their
containers (up to the min-width value, if specified). Use the
flex-shrink property to change this behavior.
https://msdn.microsoft.com/en-us/library/dn265027(v=vs.85).aspx
So, the following .cell rules should solve the issue
.cell {
flex: 0 0 auto;
-ms-flex: 0 1 auto; /* overwrites the previous rule only in IE11 */
border-right: 1px solid black;
overflow: hidden;
min-width: 200px;
}
Here's a solution I've come up with... that doesn't use Flex at all.
Updated:
Simplified the CSS to handle the margins and padding better. When you click the button to make the cell grow larger, because of the fixed width of the container, there is no margin between the row and the container.
$(document).ready(function() {
$("button").on("click", function() {
$(".cell").width(500);
})
})
html, body { width: 100%; height: 100%; margin:0; padding:0; }
div {
border: 1px solid black;
/* box-sizing: border-box; */
}
.container {
width: 400px;
padding: 5px;
margin:10px;
background: green;
overflow: auto;
}
.container::after, .row::after {
content: " ";
visibility: hidden;
display: block;
height: 0;
width: 0;
clear:both;
}
.row {
min-width: calc(100% - 22px);
padding: 5px;
margin: 5px;
border-color: red;
background: pink;
float:left;
}
.container > *:last-child {
/* margin: 0; */
}
.cell {
padding: 5px;
margin:5px;
border-right: 1px solid black;
overflow: hidden;
width: calc(200px - 22px);
background: orange;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="cell">x</div>
</div>
<div class="row">
<div class="cell">x</div>
</div>
<div class="row">
<div class="cell">x</div>
</div>
</div>
<button type="button">Change width</button>
The problem:
div {
box-sizing: border-box;
}
.cell {
flex: 0 0 auto;
}
The solution:
If you don't want to change this part of css, i suggest you to avoid setting width, instead of setting min-width
$(document).ready(function() {
$("button").on("click", function() {
$(".cell").css("min-width","500px");
})
})
I use to play with both display: flex and margin: auto to have this kind of layouts:
This works well on every browser supporting Flexbox, even IE.
However, it would have been too easy if there hadn't had a little exception: min-height.
You can find a simple working example here. When using min-height on my wrapper, the last element is not pushed to the bottom of this wrapper (IE only).
I can't get this to works, do you girls/guys have any idea? Thanks.
Testing on IE11
.wrapper {
display: flex;
flex-direction: column;
min-height: 300px;
border: 1px solid grey;
padding: 5px;
}
.element {
height: 35px;
border: 1px solid grey;
margin: 5px;
}
.element:last-child {
margin-top: auto;
}
<div class="wrapper">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>
This is a bug in IE's flexbox implementation:
In all other browsers that support flexbox, a flex-direction:column based flex container will honor the containers min-height to calculate flex-grow lengths. In IE10 & 11-preview it only seems to work with an explicit height value.
Bug report - (https://connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview#tabs)
It appears that this is on Microsoft's radar and will be fixed some point in the future:
Unfortunately, we are not able to address this feedback in our upcoming release. We will consider your feedback for a future release. We will keep this connect feedback bug active to track this request.
Reply from Microsoft - (https://connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview#tabs)
For now the simple solution is to use height:
.wrapper {
border: 1px solid grey;
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 300px;
padding: 5px;
}
.element {
border: 1px solid grey;
height: 35px;
margin: 5px;
}
.element:last-child {
margin-top: auto;
}
<div class="wrapper">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>
But this has the limitation that the box wont grow if more .elements are added so probably isn't what you are after.
There does appear to be a somewhat hacky way of achieving this although it does require an extra containing element:
.container {
display: table;
min-height: 300px;
width: 100%;
}
.wrapper {
border: 1px solid grey;
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
min-height: 300px;
padding: 5px;
}
.element {
border: 1px solid grey;
height: 35px;
margin: 5px;
}
.element:last-child {
margin-top: auto;
}
<div class="container">
<div class="wrapper">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>
</div>
This adds a container (.container), sets it to display: table; and gives it max-height: 300px;. height: 100%; is then added to .wrapper to get it to fit the full height of .container (effectively 300px) thus making IE behave the same as other browsers.
Compliant browsers ignore this and will continue to follow the min-height: 300px; rule set on .wrapper.
Here's another solution:
Adding an additional container with 2 elements:
an element with an height of "300px"
your ".wrapper"
.container {
display: flex;
}
.min-height-fix {
flex: 0 0 auto;
height: 300px; /* the "min-height" */
width: 1px; /* DEBUG */
background: red; /* DEBUG */
}
.wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: column;
/*min-height: 300px;*/
border: 1px solid grey;
padding: 5px;
flex-wrap: wrap;
}
.element {
height: 35px;
border: 1px solid grey;
margin: 5px;
}
.element:last-child {
margin-top: auto;
}
<div class="container">
<div class="min-height-fix">
</div>
<div class="wrapper">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</div>
</div>