I have a responsive website with a two-column layout in large browser windows. The two-column layout is currently implemented using float. On smaller screens I'd like to have just one column. The content of the other column should be displayed between the two elements of the main column, like shown here:
<div class="two-columns">
<div class="main-column">
<div class="red-element"></div>
<div class="yellow-element"></div>
</div>
<div class="sidebar-column">
<div class="green-element"></div>
</div>
</div>
I tried using a flex-box-based approach, basically the one described in this question, but flex-basis still seems to be unsupported in Safari when flex-direction is column. Proper Safari support is a must as Safari is the main browser of my visitors.
Is there a way this can be achieved using CSS only without having to place the green element twice in my markup?
Here's a general solution using one flex container:
<div class="container">
<div class="box"> ... </div><!-- red box -->
<div class="box"> ... </div><!-- green box -->
<div class="box"> ... </div><!-- yellow box -->
</div>
Starting with small screens (for no particular reason), stack them in a column:
.container {
display: flex;
flex-direction: column;
}
.box {
height: 100px;
width: 100%;
}
Re-arrange the layout for wider screens:
#media (min-width: 800px) {
.container {
flex-direction: row;
flex-wrap: wrap;
}
.box {
flex-basis: 45%;
}
}
On screens wider than 800px, the container lines the items in a row and enables wrapping. Each box is given a large enough width (flex-basis) for only two to fit on a line.
Full demo:
* {
box-sizing: border-box;
}
.container {
display: flex;
flex-direction: column;
padding: 5px 0;
background-color: #f5f5f5;
border: 1px solid #aaa;
}
.box1 { background-color: red; }
.box2 { background-color: lightgreen; }
.box3 { background-color: yellow; }
.box {
height: 100px; /* `flex-basis: 100px` would also work */
width: calc(100% - 20px);
margin: 5px 10px;
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2em;
}
#media (min-width: 800px) {
.container {
flex-direction: row;
flex-wrap: wrap;
}
.box {
flex-basis: 45%;
}
}
<div class="container">
<div class="box box1"><span>1</span></div>
<div class="box box2"><span>2</span></div>
<div class="box box3"><span>3</span></div>
</div>
jsFiddle
From your question:
...but flex-basis still seems to be unsupported in Safari when flex-direction is column
I'm not sure this is correct (caniuse.com).
However, you can always use width or height properties instead of flex-basis (more details: What are the differences between flex-basis and width?).
Using Bootstrap,
<div class="row">
<div class="col-md-8">
<div class="red-element"></div>
</div>
<div class="col-md-4">
<div class="green-element"></div>
</div>
<div class="col-md-8">
<div class="yellow-element"></div>
</div>
<div class="clearfix"></div>
</div>
This uses float methods and works on all browsers.
you should using #media via margin-top.on specific screen width (via #media), change margin-top of the green-element to -200%. and change margin-top of yellow-element to 100%.they change their position very nice :)
please see this link:
http://jsbin.com/xozeviseka/edit?html,output
You need to change some html structure so then you can do this.
*,*:after,*:before {
box-sizing:border-box;
}
.two-columns {
position:relative;
background:#EFEFEF;
border:1px solid #000;
}
.red-element,
.green-element,
.yellow-element {
margin-bottom:30px;
}
.red-element {
height:70px;
background:#FF0004;
}
.green-element {
height:70px;
background:#7ED321;
}
.yellow-element {
height:100px;
background:#F8E71C;
}
#media (min-width:767px) {
.main-column {
width:70%;
padding:10px;
}
.sidebar-column {
position:absolute;
right:0;
top:0;
width:30%;
padding:10px;
}
}
<div class="two-columns">
<div class="main-column">
<div class="red-element"></div>
<div class="sidebar-column">
<div class="green-element"></div>
</div>
<div class="yellow-element"></div>
</div>
</div>
Or if you don't want to change html structure you have to take another element that only show in mobile for example
*,*:after,*:before {
box-sizing:border-box;
}
.two-columns {
background: #EFEFEF;
border: 1px solid #000;
overflow: hidden;
}
.red-element,
.green-element,
.yellow-element {
margin-bottom:30px;
}
.red-element {
height:70px;
background:#FF0004;
}
.green-element {
height:70px;
background:#7ED321;
}
.yellow-element {
height:100px;
background:#F8E71C;
}
.hideMobile{
display:none;
}
#media (min-width:767px) {
.main-column {
width: 70%;
float: left;
padding: 10px;
}
.sidebar-column {
float: right;
width: 30%;
padding: 10px;
}
.showMobile {
display:none;
}
.hideMobile {
display:block;
}
}
<div class="two-columns">
<div class="main-column">
<div class="red-element"></div>
<div class="green-element showMobile"></div><!--only for mobile-->
<div class="yellow-element"></div>
</div>
<div class="sidebar-column hideMobile"><!--hide in mobile-->
<div class="green-element"></div>
</div>
</div>
Related
In the following html I want the txt-box div to be centered in the container, overlay the image, and expand to fill the container. It should have a margin of equal width on all sides allowing part of the image to show like a thick border.
The html shown is passable for what I want except the vertical vs. horizontal margins are always slightly different as the browser window is resized.
I feel like what I have here is a hack and that I am using flex-grow incorrectly. I understand flex-grow works to allow the txt-box div to expand since it is the only element with a grow value. If I can get that resolved I should be able to simply set a margin on txt-box and it should work.
What am I not understanding about flex-grow?
.container {
display: flex;
align-items: center;
justify-content: center;
border: solid 2px red;
position: relative;
}
.container img {
width: 100%;
flex-grow: 0;
flex-shrink: 0;
}
.txt-box {
background-color: white;
display: flex;
padding: 5px;
border: solid 2px blue;
flex-grow: 1;
position: absolute;
width: 90%;
height: 80%;
}
<div class="container">
<img src="blocks.png" />
<div class="txt-box">
hello world
</div>
</div>
Thanks to Michael Benjamin for putting me on the path to enlightenment. I finally got it figured out. My original question was actually a portion of what I was trying to accomplish. The answers are to use background-image:url('...') and make sure the table and row elements are display:flex.
JSFiddle
<html>
<head>
<style>
.flex-table {
flex-flow:column;
}
.flex-row {
flex-flow:row;
}
.container {
align-items: center;
justify-content: center;
padding: 20px;
border: solid 2px red;
background-image:url('https://i.imgur.com/BF3ty6o.jpg');
background-size:cover;
background-repeat:no-repeat;
max-width:500px;
}
.txt-box {
justify-self:stretch;
align-self:stretch;
border: solid 2px blue;
background-color: rgba(192,192,192,0.5);
}
body, .flex-table, .flex-row, .container, .txt-box {
display:flex;
flex-grow:1;
}
#media (max-width: 768px) {
.flex-row {
flex-flow:column;
}
}
</style>
</head>
<body>
<div class="flex-table">
<div class="flex-row">
<div class="container">
<div class="txt-box">
hello world 1
</div>
</div>
<div class="container">
<div class="txt-box">
hello world 2
</div>
</div>
<div class="container">
<div class="txt-box">
hello world 3
</div>
</div>
</div>
<div class="flex-row">
<div class="container">
<div class="txt-box">
hello world 4
</div>
</div>
<div class="container">
<div class="txt-box">
hello world 5
</div>
</div>
<div class="container">
<div class="txt-box">
hello world 6
</div>
</div>
</div>
</div>
</body>
</html>
What am I not understanding about flex-grow?
Flex properties don't work on absolutely positioned children of a flex container.
ยง 4.1. Absolutely-Positioned Flex
Children
As it is out-of-flow, an absolutely-positioned child of a flex
container does not participate in flex layout.
Therefore, flex-grow: 1 on txt-box is not doing anything. It's just being ignored.
Considering that you want the image simply laying in the background, while the text box has more requirements, I would suggest absolutely positioning the image and leaving the text box in the normal flow.
Then give the text box full width and height, with equal padding on the primary container to keep uniform "margins" across screen sizes.
Here's a demo, with a few extra features to help illustrate the concepts involved.
body {
height: 100vh;
display: flex;
margin: 0;
padding: 10px;
}
.container {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
position: relative;
border: solid 2px red;
}
img {
position: absolute;
height: 100%;
width: 100%;
object-fit: contain; /* also try 'cover' for demo */
}
.txt-box {
z-index: 1;
width: 100%;
height: 100%;
border: solid 2px blue;
background-color: rgba(192,192,192,0.5);
}
* {
box-sizing: border-box;
}
<div class="container">
<img src="http://i.imgur.com/60PVLis.png">
<div class="txt-box">hello world</div>
</div>
jsFiddle demo
This question already has answers here:
Why doesn't percentage padding / margin work on flex items in Firefox and Edge?
(2 answers)
Closed 5 years ago.
I built a simple page using FlexBox CSS, and I don't understand why when I use a percent margin on one of the items, the width of the container is not expanding.
Note: Problems exists in Chrome / FireFox.
Code Pen: https://codepen.io/dsomekh/pen/QvGvrq
Example:
<html>
<style>
.page{
display:flex;
/*border: 5px solid orange;*/
}
.container{
display:flex;
border: 5px solid orange;
}
.item_left{
display:flex;
margin-right:25%;
}
.item_right{
display:flex;
}
</style>
<div class="page">
<div class="container">
<div class="item_left">Left</div>
<div class="item_right">Right</div>
</div>
</div>
</html>
You're using flexbox wrong.
try
.container{
display:flex;
}
.item_left {
flex: 1;
border: 5px solid orange;
margin-right:25%;
}
.item_right {
flex: 1;
border: 5px solid orange;
}
<div class="page">
<div class="container">
<div class="item_left">Left</div>
<div class="item_right">Right</div>
</div>
</div>
As Michael Coker commented, Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers. (CSS Flexbox margins)
When using percent we often relate that to the viewport width, so with that in mind, viewport units vw/vh can be a good option, since it works similar (responsive).
Stack snippet made of your Codepen sample
.page {
display: flex;
/*border: 5px solid orange;*/
}
.container {
display: flex;
border: 5px solid orange;
}
.item_left {
flex: 1;
margin-right: 25vw;
}
.item_right {
display: flex;
flex: 1;
}
<div class="page">
<div class="container">
<div class="item_left">Left</div>
<div class="item_right">Right</div>
</div>
</div>
Stack snippet made of your questions code
.page {
display: flex;
/*border: 5px solid orange;*/
}
.container {
display: flex;
border: 5px solid orange;
}
.item_left {
display: flex;
margin-right: 25vw;
}
.item_right {
display: flex;
}
<div class="page">
<div class="container">
<div class="item_left">Left</div>
<div class="item_right">Right</div>
</div>
</div>
I'm using flexbox to place some elements side by side
<div class="artist-wrapper">
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
</div>
.artist-wrapper {
display: flex;
height: 100%;
width: 100%;
}
.artist {
flex: 1;
height: 100%;
}
It works quite well! On small screens (like mobile) the `.artist-elements shouldn't stay side by side but rather among each other. Is there a way to do this by using flexbox?
You need media queries and then allow wrapping in the parent.
JSFiddle Demo
.artist-wrapper {
display: flex;
height: 100%;
width: 100%;
}
.artist {
flex: 1;
height: 100px;
border: 1px solid grey;
}
#media screen and (max-width: 480px) {
.artist-wrapper {
flex-wrap: wrap;
}
.artist {
flex: 0 0 100%
}
}
<div class="artist-wrapper">
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
</div>
Is this what you are after? This is using display: inline-block
But here is a solution using flexbox, the property for this is flex-wrap: wrap, if you remove min-width and min-height you can have a more fluid layout.
You may use min-width instead or with mediaqueries.
(see comments)
.artist-wrapper {
display: flex;
flex-wrap:wrap;
}
.artist {
flex:1;
min-width:250px;/* whatever breaking points you look for : here we have for 3 elements a first-breakpoint at 500px then another at 250 */
min-height: 50px;/* demo purpose use content instead*/
box-shadow:0 0 0 1px gray;
}
/* extra */
.artist-wrapper {
margin:1em;
}
.artist {
color:#444;
font-size:1.5em;
font-family:'lucida console', courier;
background:tomato;
display:flex;
align-items:center;
justify-content:center;
}
.artist:nth-child(odd) {
background:orange
}
.artist:nth-child(4n) {
background:turquoise;
min-width: 500px;
max-width:100%;/* allows it to shrink on small device , can be set to all of them */
}
<div class="artist-wrapper">
<div class="artist"></div>
<div class="artist">breaks at 250px;</div>
<div class="artist"> breaks at 500px </div>
</div>
<div class="artist-wrapper">
<div class="artist"></div>
<div class="artist">breaks at 250px;</div>
<div class="artist">breaks at 500px;</div>
<div class="artist"> breaks at 1250px;</div>
<div class="artist"> and so on ... </div>
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
<div class="artist"></div>
</div>
I am working on a responsive site and came across an interesting problem. I have some divs side by side. There could be anywhere from 2 to 6 or so of them. When the screen isn't wide enough to show all the content properly, the divs stack vertically. Simple enough to do with CSS.
The problem is, I need them to be in a different order depending on the layout. This is easy to do with 2 or 3 divs (Changing divs order based on width), but significantly more challenging when you add a fourth.
I could use position: absolute; and manually set the position, however this causes the parent to shrink and not contain them properly.
To make this even more complicated, I can't use JavaScript.
Working with two columns:
(untested)
HTML:
<div id="container">
<div class="column-half column-half-2">
First div on mobile, right div on desktop
</div>
<div class="column-half column-half-1">
Second div on mobile, left div on desktop
</div>
</div>
CSS:
.container {
width: 80%;
max-width: 1200px;
margin: 0 auto;
padding-bottom: 20px;
position: relative;
}
.column-half {
display: table-cell;
padding: 25px;
vertical-align: top;
width: 40%;
}
.column-half-1 {
float: left;
}
.column-half-2 {
float: right;
}
HTML, with 4 columns:
<div id="container">
<div class="column-quarter column-quarter-3">
First div on mobile, third div on desktop
</div>
<div class="column-quarter column-quarter-2">
Second div on mobile, second div on desktop
</div>
<div class="column-quarter column-quarter-1">
Third div on mobile, first div on desktop
</div>
<div class="column-quarter column-quarter-4">
Fourth div on mobile, fourth div on desktop
</div>
</div>
This is doable in CSS thanks to the wonderful flexbox spec. Using the order and flex-flow properties, we can achieve what you want. Unprefixed, IE11 and all evergreen browsers will support this. IE10 prefixes -ms-order and doesn't support flex-flow.
The solution takes into consideration all the constraints you listed:
Have a list of elements in a given order displayed as a row.
When the window is too small, change them to display in a column.
Change the order of the elements when they are displayed in a column.
Because of the limitations of Stack Snippets, you'll need to view the demo in Full page mode, and resize your browser to see the effect.
.container div {
width: 100px;
height: 50px;
display: inline-block;
}
.one { background: red; }
.two { background: orange; }
.three { background: yellow; }
.four { background: green; }
.five { background: blue; }
#media screen and (max-width: 531px) {
.container { display: flex; flex-flow: column; }
.five { order: 1; }
.four { order: 2; }
.three { order: 3; }
.two { order: 4; }
.one { order: 5 }
}
<div class="container">
<div class="one">I'm first</div>
<div class="two">I'm second</div>
<div class="three">I'm third</div>
<div class="four">I'm fourth</div>
<div class="five">I'm fifth</div>
</div>
Alternatively, here is a JSFiddle demo.
You can also simply use flex-flow: column-reverse without the order property assigned to each div, if you are so inclined against verbose CSS. The same demo restrictions apply; view this demo in full screen and resize the browser window accordingly.
.container div {
width: 100px;
height: 50px;
display: inline-block;
}
.one { background: red; }
.two { background: orange; }
.three { background: yellow; }
.four { background: green; }
.five { background: blue; }
#media screen and (max-width: 531px) {
.container { display: flex; flex-flow: column-reverse; }
}
<div class="container">
<div class="one">I'm first</div>
<div class="two">I'm second</div>
<div class="three">I'm third</div>
<div class="four">I'm fourth</div>
<div class="five">I'm fifth</div>
</div>
It's worth pointing out that flex-flow is a shorthand property encompassing both flex-direction and flex-wrap properties.
I'm facing a bootstrap problem.
In my html page, I used different containers but I'm not able to re-arrange and re-organize them as I want in mobile screens.
Here my Bootply.
And to be more clear, I want it to look like this:
Containers 1 and 5 are fluid, instead 2, 3, 4 are not.
How can I move container 1 and 2 after 3 and 4 in small screens?
Thank you in advance for your reply!
Cheers!
This is not possible without rearranging your content.
One way is to make two versions of the area you want to rearrange and hide them based on the width of the browser. This is bad practice, especially if you have a whole website you want to rearrange on resize, but for a small div with 5 divs inside it would be an acceptable solution.
Here is the adapted HTML
<div class="desktopwrapper"> <!-- added a desktop wrapper -->
<div class="container-fluid green"></div>
<div class="container red"></div>
<div class="container ">
<div class="row">
<div class="col-sm-8 yellow"></div>
<div class="col-sm-4 fuxia"></div>
</div>
</div>
<div class="container-fluid blue"></div>
</div>
<div class="mobilewrapper"> <!-- added a mobile wrapper and rearranged content -->
<div class="container ">
<div class="row">
<div class="col-sm-8 yellow"></div>
<div class="col-sm-4 fuxia"></div>
</div>
</div>
<div class="container-fluid green"></div>
<div class="container red"></div>
<div class="container-fluid blue"></div>
</div>
And I have added these lines to CSS:
#media screen and (max-width: 766px) {
.desktopwrapper {
display:none;
}
}
#media screen and (min-width: 767px) {
.mobilewrapper {
display:none;
}
}
What this basically does, is hide one arrangement when the screen gets resized to 766px wide and will display the other. And of course the other way around.
You can try it out here.
Another way would be to put everything in a wrapper, position the wrapper relative, all the divs inside absolute and just place them with using px. This is however really not useful when divs have changing heights depending on the content. The best way would be to do like the example I have.
flexbox proof of concept.
* {
margin: 0;
padding: 0;
}
html {
height: 100%;
color: white;
text-align: center;
}
body {
height: 100%;
}
h2 {
display: inline-block;
background: #000;
padding: .25em;
}
.page {
min-height: 100%;
display: flex;
flex-direction: column;
}
header {
flex: 0 0 75px;
background: darkgreen;
}
.banner {
flex: 0 0 100px;
width: 80%;
margin: auto;
background: darkred;
}
main {
flex: 1;
width: 80%;
margin: auto;
display: flex;
}
.content {
width: 75%;
background: yellow;
}
aside {
width: 25%;
background: fuchsia;
}
footer {
flex: 0 0 100px;
background: lightblue;
}
#media screen and (max-width: 768px) {
.banner,
main {
width: 100%;
}
main {
flex-direction: column;
order: -1;
}
.content,
aside {
flex: 1;
width: 100%;
}
aside {
flex: 0 0 150px
}
}
<div class="page">
<header>
<h2>1</h2>
</header>
<div class="banner">
<h2>2</h2>
</div>
<main>
<div class="content">
<h2>3</h2>
</div>
<aside>
<h2>4</h2>
</aside>
</main>
<footer>
<h2>5</h2>
</footer>
</div>
Codepen Demo