Flexbox, responsive grid of square divs maintaining aspect ratio - css

I'm trying to create a 2x2 grid with divs. Some of the divs might contain an image, but it will probably be set as a background, with the option background-size: cover.
Here's the pen I created: http://codepen.io/qarlo/pen/vLEprq
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: auto;
max-width: 960px;
width: 80%;
}
.container__item {
align-content: center;
border: 1px solid #333;
display: flex;
flex-basis: 1;
font-size: 3em;
justify-content: center;
height: 100%;
margin-bottom: 1em;
min-height: 300px;
width: 47%;
}
<div class="container">
<div class="container__item">?</div>
<div class="container__item">?</div>
<div class="container__item">?</div>
<div class="container__item">?</div>
</div>
I'd like to force the divs to be squares and maintain the aspect ratio when resizing it. I was mistakenly hoping that this would have been straightforward with flexbox, but unless I'm missing something, I was wrong.

To maintain your items aspect ratio, a very simple method is to use CSS Viewport units
I modified your pen to see how this units work: http://codepen.io/vladbicu/pen/wMBmOb
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: auto;
max-width: 960px;
width: 80%;
}
.container__item {
align-content: center;
border: 1px solid #333;
display: flex;
flex-basis: 1;
font-size: 3em;
justify-content: center;
margin-bottom: 1em;
// maintain aspect ratio
width: 30vw;
height: 30vw;
}
Hope it helps.

Use the old "padding-bottom" trick for fixed aspect ratio. Extra divs are reqiured though:
.container {
margin: auto;
width: 80%;
max-width: 960px;
}
.container__square {
float: left;
position: relative;
padding-bottom: 50%;
width: 50%;
background: linear-gradient(45deg, #CCC, #000, #CCC);
}
.container__square__item {
position: absolute;
top: 1em;
bottom: 1em;
left: 1em;
right: 1em;
border: 1px solid #333;
background: #FFF;
}
/* clearfix */
.container::after {
content: "";
display: block;
clear: both;
}
<div class="container">
<div class="container__square">
<div class="container__square__item">?</div>
</div>
<div class="container__square">
<div class="container__square__item">?</div>
</div>
<div class="container__square">
<div class="container__square__item">?</div>
</div>
<div class="container__square">
<div class="container__square__item">?</div>
</div>
</div>

Guess you would have to set at least the min-height to maintain the aspect ration on re-size, if you want to go with a flex-box layout.
Here is a quick and dirty example.
function setCellsMinHeight (parentSelector, cellsMargin, aspect) {
var winWidth = window.innerWidth,
containerList = document.querySelectorAll(parentSelector),
containerArray = Array.prototype.slice.call(containerList),
childMargin = cellsMargin;
containerArray.forEach(function(elem) {
var containerWidth = elem.offsetWidth,
childCount = elem.children.length,
childWidth = (containerWidth - ((childMargin * 2) * childCount)) / childCount,
childMinHeight = (childWidth / 100) * aspect;
for (i = 0; i < childCount; i++) {
elem.children[i].style.margin = childMargin + "px";
elem.children[i].style.minHeight = childMinHeight + "px";
}
});
}
window.onresize = function(event) {
setCellsMinHeight('.container', 4, 100);
};
setCellsMinHeight('.container', 4, 100);
body {
margin: 0;
}
.container {
display: flex;
flex-flow: row wrap;
align-items: stretch;
max-width: 100%;
margin: 0 auto;
}
.content-cell {
flex: 1;
background-color: #ccc;
}
#media (min-width: 801px) {
.container {
max-width: 800px;
}
}
<div class="container">
<div class="content-cell"></div>
<div class="content-cell"></div>
</div>
<div class="container">
<div class="content-cell"></div>
<div class="content-cell"></div>
</div>
Hope it helps.

As i understand i think you want flexible boxes all the time so you can perform this action by using JavaScript.
Find the Highest height of the content
assign that highest width and height for the whole classes
and that's how you can maintain flexible boxes
or you can use external library
Css tricks
JQuery Flexbox

Related

An alternative design without a scrollbar caused by padding

The code that appears below creates the following layout:
The important part is that the content, although centered on the screen when not overflowing, never overlaps the navbar and has its own scrollbar:
The problem is that this layout is achieved with the help of padding (marked by a comment in the code below), which results in the additional scrollbar on the right of the screen.
How can I design the same layout that would have only one scrollbar - the one in the content?
Please note that the solution should not break the following details:
The rounded corners and the shadow.
The title in the content block not participating in scrolling.
The image covering the whole scrollable content, so it scrolls together with the content.
In addition, it would be great if this can be achieved without as many nested div's as I have right now.
Edit: I am ready to go with the suggestion of #JHeth in the comments section. However, I would still be interested if someone can come up with an alternative design that does not rely on padding for centering.
Here is the code (CodePen):
* {
padding: 0;
margin: 0;
}
html {
font-size: 62.5%;
}
:root {
--navbar-height: 3rem;
}
.navbar {
width: 100%;
text-align: center;
font-size: 2rem;
line-height: var(--navbar-height);
background-color: lightgreen;
}
.centering {
position: absolute;
inset: var(--navbar-height) 0 0 0;
display: flex;
flex-direction: column;
align-items: center;
& .auto-margin {
margin: auto;
// For scrollable content
display: flex;
max-height: 100%;
padding-bottom: calc(var(--navbar-height)); // Causes scrolling
}
}
.content-block {
display: flex;
flex-direction: column;
.title {
font-size: 2rem;
position: sticky;
}
.content-outer-container {
display: flex;
flex-direction: column;
overflow-y: auto;
border-radius: 1em;
box-shadow: 0 1em 2em rgba(black, 0.4);
.content-container {
width: 300px;
overflow-y: auto;
.content {
position: relative;
padding: 1em;
&:before {
content: "";
z-index: -1;
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
background-position: center;
background-image:
url(http://www.freegreatpicture.com/files/147/18380-hd-color-background-wallpaper.jpg);
}
}
}
}
}
<div class="navbar">An Amazing Navbar</div>
<div class="centering">
<div class="auto-margin">
<div class="content-block">
<div class="title">My Title</div>
<div class="content-outer-container">
<div class="content-container">
<div class="content">
<h1>Line1</h1>
<h1>Line2</h1>
<h1>Line3</h1>
<h1>Line4</h1>
<h1>Line5</h1>
</div>
</div>
</div>
</div>
</div>
</div>
simply remove max-height:100%; to .centering .auto-margin and it'll works!
.centering .auto-margin {
margin: auto;
display: flex;
/* max-height: 100%; */
padding-bottom: calc(var(--navbar-height));
}

CSS: flex-grow maintain vertical aspect ratio

I am creating a website with a circular menu. The website content should fit all onto the homepage without the need to scroll. The menu needs to fill the remaining space on the homepage. However, I am unsure how to maintain the shape of the circle while filling the remaining space on the homepage using flex-grow: 1. Is there a way I can do this with pure CSS? Setting the menu to a set viewport size is not acceptable, it needs to fill the remaining space. I am not having luck using the traditional padding-top: 100% to maintain aspect ratio. The circle is not quite circular and it takes up twice the remaining space.
body {
height: 100vh;
display: flex;
flex-direction: column;
margin: 0;
}
#title {
font-size: 30px;
}
#circle {
background: black;
border-radius: 50%;
flex-grow: 1;
padding-top: 100%;
}
#footer {
background: black;
color: white;
}
<body>
<div id="title">Title</div>
<div>navigation</div>
<div id="circle"></div>
<div id="footer">Footer</div>
</body>
Edit
I have figured out a way to maintain the aspect ratio of the circle filling the remaining space with flex grow. However, it is what I would consider a hack so I am leaving this question open.
body {
height: 100vh;
display: flex;
flex-direction: column;
margin: 0;
}
#title {
font-size: 30px;
}
#circle {
background: black;
border-radius: 50%;
flex-grow: 1;
/*width: max-content;*/
padding: 0%;
align-self: center;
}
#circle img {
height: 100%;
width: auto;
justify-content: center;
}
#footer {
background: black;
color: white;
}
<body>
<div id="title">Title</div>
<div>navigation</div>
<div id="circle"><img src="https://luxury.zappos.com/search/imgs/blank.20190219170746.png"></div>
<div id="footer">Footer</div>
</body>
Edit 2
It seems I was mislead by caniuse.com. This solution does not seem to work in most browsers besides chrome. Is there another solution?
Put the circle div inside a wrapper div in your HTML:
<div id="circle-wrap">
<div id="circle"></div>
</div>
Then move the flex rule to the wrapper:
#circle-wrap {
flex-grow: 1;
}
body {
height: 100vh;
display: flex;
flex-direction: column;
margin: 0;
}
#title {
font-size: 30px;
}
#circle-wrap {
flex-grow: 1;
}
#circle {
background: black;
border-radius: 50%;
padding-top: 100%;
}
#footer {
background: black;
color: white;
}
<body>
<div id="title">Title</div>
<div>navigation</div>
<div id="circle-wrap">
<div id="circle"></div>
</div>
<div id="footer">Footer</div>
</body>

3 expandable column landscape page in pure CSS

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.

Can it flexbox? Chat window with input at the bottom, chats scrolling up

Seems like it should be possible with flexbox, but I can't figure it out.
http://codepen.io/MichaelJCole/pen/NGBVGe
Goals:
textarea (for typeing in messages) stays at the bottom the whole time.
chats start at the bottom, then scroll up as needed.
If you use the "Google Hangouts", like the message app in that.
Here's the markup:
<div id="chatBar">
<div id="chatList">
<div class="chat mine">hello world</div>
<div class="chat theirs">hello moon</div>
</div>
<input id="chatBarInput" class="form-control" type="textarea">
</div>
And here's the CSS:
html, body { height: 100%; }
#chatBar {
height: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow: none;
}
#chatList {
flex: 0 1 auto;
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow-y: scroll;
}
#chatBarInput {
flex: 1 0 auto;
}
.chat {
flex: none;
align-self: flex-start;
background-color: lightgreen;
}
.chat.mine {
align-self: flex-end;
background-color: pink;
}
I can't get #chatBar to "squeeze" #chatList without setting a height. Which is what I was trying to avoid by using flexbox :-/
Sorry, I'm a backend coder. Tried a bunch of stuff, then pared it down for the CodePen.
Seems like I should be able to tell the inner flexbox to scroll, while leaving the outer alone. Do I have to use position:absolute?
I can't get #chatBar to "squeeze" #chatList without setting a height.
Which is what I was trying to avoid by using flexbox
You had the flex-basis set to auto for all elements. Without explicit height, the flex model will automatically try to accommodate everything inside the available space by shrinking or expanding the elements. This is why you are unable to get the #chatList to work as intended. The div itself as well as the individual chats all expand or shrink within the available space.
What you should do is to start simple:
#chatBar {
height: 100%; overflow: hidden;
display: flex; flex-flow: column;
}
#chatList {
/* grow or shrink as required from flex-basis height of 20% */
flex: 1 1 20%;
display: flex; flex-direction: column;
overflow: auto;
}
/* do not grow or shrink with a flex-basis height of 80% */
#chatBarInput { flex: 0 0 80%; }
And you will be able to see it working. You could then take it further from here.
Your modified codepen: http://codepen.io/Abhitalks/pen/ZbjNvQ/
Goals:
textarea (for typeing in messages) stays at the bottom the whole time.
chats start at the bottom, then scroll up as needed.
If you use the "Google Hangouts", like the message app in that.
The trick would be to use flex-direction: column-reverse and prepend the new messages to the container instead of appending those.
I took an old answer of mine and changed the layout to flex-model for a demo of this purpose. You can peruse the code to see how it's done.
Demo Fiddle: http://jsfiddle.net/abhitalks/khj4903t/
Demo Snippet:
var btn = document.getElementById('btn'),
inp = document.getElementById('inp'),
chats = document.getElementById('chatWindow')
;
btn.addEventListener('click', postMsg);
inp.addEventListener('keyup', function(e) {
if (e.keyCode == 13) { postMsg(); }
});
function postMsg() {
var msg = inp.value,
bubble = document.createElement('div'),
p = document.createElement('p');
if (msg.trim().length <= 0) { return; }
bubble.classList.add('bubble');
bubble.classList.add('right');
p.textContent = msg;
bubble.appendChild(p);
inp.value = '';
chats.insertBefore(bubble, chats.firstChild);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
.wrap {
margin: 8px; height: 90%; width: 50%;
display: flex; flex-direction: column;
}
.container {
flex: 1 1 90%; display: flex; flex-direction: column;
background-color: #eee; border: 1px solid #ccc; overflow: auto;
}
.form { flex: 0 0 32px; display: flex; border: 1px solid #ddd; }
.form > input[type=text] { flex: 1 1 auto; border: 1px solid #eee; }
.form > input[type=button] { flex: 0 0 20%; border: 1px solid #eee; }
.bubble { flex: 1 1 auto; clear: both; } /* clear the floats here on parent */
.bubble p {
border-radius: 5px;
padding: 8px; margin: 8px 12px;
max-width: 80%; /* this will make it not exceed 80% and then wrap */
position: relative; transition: background-color 0.5s;
}
.left p { background-color: #ccc; float: left; } /* floated left */
.right p { background-color: #33c; color: #fff; float: right; } /* floated right */
/* classes below are only for arrows, not relevant */
.left p::before {
content: ''; position: absolute;
width: 0; height: 0; left: -8px; top: 8px;
border-top: 4px solid transparent;
border-right: 8px solid #ccc;
border-bottom: 4px solid transparent;
}
.right p::after {
content: ''; position: absolute;
width: 0; height: 0; right: -8px; bottom: 8px;
border-top: 4px solid transparent;
border-left: 8px solid #33c;
border-bottom: 4px solid transparent;
}
<div class="wrap">
<div id="chatWindow" class="container">
<div class="bubble left"><p>msg</p></div>
<div class="bubble left"><p>long message</p></div>
<div class="bubble right"><p>ultra long message which can wrap at eighty percent </p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
<div class="bubble right"><p>very long message</p></div>
<div class="bubble right"><p>one more message</p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
<div class="bubble right"><p>another message</p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
<div class="bubble right"><p>yet another message</p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
</div>
<div id="inputWindow" class="form">
<input id="inp" type="text" />
<input id="btn" type="button" value="Send" />
</div>
</div>
The vertical scrollbar on the browser exists because you've set a height: 100% to the body, and the user agent stylesheet applies a default margin to the body, typically 8px all around. So, 100% + 16px launches the vertical scroll.
Add this to your CSS: body { margin: 0; }
To apply the scrollbar to the inner flexbox (.chatlist), here are two adjustments:
#chatList {
flex: 0 1 75px; /* specify a height */
display: flex;
flex-flow: column nowrap;
/* justify-content: flex-end; REMOVE */
overflow-y: scroll;
}
DEMO: http://jsfiddle.net/5p2vy31p/1/

Make flex child element occupy 100% height of parent flex [duplicate]

This question already has answers here:
HTML5 flexible box model height calculation
(2 answers)
Closed 4 years ago.
I have a container flex with content flexes. How do i make content flex occupy full width and height of container flex.
<div id="main">
<div id="main-nav">
</div>
<div class="container">
<div class="content"></div>
<div class="content"></div>
</div>
</div>
#main{
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
}
#main-nav{
width: 100%
height: 50px;
}
.container{
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex: 1;
}
.content{
display: flex;
width: 100%;
height: 100%;
}
The above code makes content to occupy 100% width of container but height is based on the text within the content. I tried the solutions mentioned from similar questions but had no luck and it was still the same.
Basically, I want each of the content to occupy the same height as occupied by the container in the viewport height. I also tried jQuery,
var rht = $("#container").height();
$(".content").height(rht);
It changes the height properly but adds a horizontal scroll bar with increase in width.
After several updates to the original question:
* {
box-sizing: borderbox;
margin: 0;
padding: 0;
}
#main {
display: flex;
flex-direction: column;
border: 1px solid red;
min-height: 100vh;
}
#main-nav {
flex: 0 0 50px;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
border: 1px solid green;
}
.content {
flex: 1;
border: 1px solid orange;
}
<div id="main">
<div id="main-nav"></div>
<div class="container">
<div class="content"></div>
<div class="content"></div>
</div>
</div>
JSfiddle Demo
You cannot set width or height of flex's child is bigger (size of flex)/(number of flex's childs) but you can add position: absolute into .content and position: relative into .container then set width and height for .content. First .content is under second .content, you can use propety z-index or display: none to control.
* {
box-sizing: borderbox;
margin: 0;
padding: 0;
}
#main {
display: flex;
flex-direction: column;
background: red;
min-height: 100vh;
}
#main-nav {
flex: 0 0 50px;
}
.container {
position: relative;
display: flex;
flex: 1;
background: green;
}
.content {
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
flex: 1;
background: orange;
}
<div id="main">
<div id="main-nav"></div>
<div class="container">
<div class="content">left</div>
<div class="content">right</div>
</div>
</div>

Resources