I asked a question today about good and bad practises in CSS/HTML/jQuery and when it is appropriate to use jQuery to set container dimensions. I got some good answers
So, understanding that jQuery is not the best option, I decided to ask maybe some of you can give some input about this "problem"
So, I have a page put together with php. I have one header for all of my pages and content is being changed with php (I am saying this only to let you guys know that wrapping header and div in one container is not an option):
include ("header.php");
include ("$lang/$section.php");
include ("footer.php");
I have a header with fixed hight (100px + 100px margin-bottom) and after that I have a div which on screens smaller than 768px(height) I want to be no longer than the remaining space. If the screen is larger, I want my div to be
max-height: 420px;
with
padding: 100px 0;
Inside of this div I have 3 floated columns. I need them to fill the space in the parent div.
What I would usually do is- use jQuery and calculate screen height and subtract header height and all the margins and paddings. But as I've learned today, that is not a good practise.
So, to wrap it up: I NEED THE DIV TO FILL THE SPACE BETWEEN HEADER AND BOTTOM OF THE SCREEN FOR VIEWPORT HEIGHT SMALLER THAN 768px. MAX-HEIGHT FOR THIS DIV IS 420px. With jQuery it is super easy but I can't figure out the clean css way.
Maybe some of you have an idea?
Here is my fiddle, so you guys don't have to type out all of the code.
Thank you in advance!
You can use calc() and vh (viewport height).
calc() browser support: http://caniuse.com/#search=calc
vh browser support: http://caniuse.com/#search=vh
So we use calc(100vh - 200px) being 100vh the height of the viewport and 200px the height of the header.
Also, we add a media query so that when the screen is bigger than 768px height we limit the height to 420px.
Try this:
header { height: 100px; background: #ccc; margin-bottom: 100px; box-sizing: border-box; }
section { width: 100%; height: calc(100vh - 200px); padding: 50px 0; background: yellow; box-sizing: border-box; }
.col1, .col2, .col3 { float: left; width: 33%; }
.colPadding { padding: 25px; background: blue; }
.cb { width: 100%; height: 1px; clear: both; }
body {
margin: 0;
}
#media screen and (min-height: 768px) {
section {
max-height: 420px;
}
}
<header>
This is my header with 100px bottom margin
</header>
<section>
<div class="col1">
<div class="colPadding">
section with padding: 50px 0; and max-height: 420px;
</div>
</div>
<div class="col2">
<div class="colPadding">
Column 2
</div>
</div>
<div class="col3">
<div class="colPadding">
Column 3
</div>
</div>
<div class="cb"></div>
</section>
Gave it a shot with CSS3 flex-box model and screen media queries. Here is my fiddle.
I used 300px instead of 764px for the fiddle. (you can change it if you want, I just used 300px so that it's easier to test)
Applied CSS
* { box-sizing: border-box; } /* force sizing based on border */
body {
display: flex; /* flex for body since wrapping header and section is not allowed */
flex-flow: column wrap;
}
header {
height: 100px;
background: #ccc;
margin-bottom: 100px;
flex: 0 0 auto; /* make header size fixed */
}
section {
width: 100%;
max-height: 420px;
padding: 50px 0;
background: yellow;
/* to occupy remaining space */
flex: 1 1 auto;
/* for columns inside to occupy full width */
display: flex;
flex-flow: row wrap;
/* for immediate children to stretch to max height possible */
align-items: stretch;
}
.col1, .col2, .col3 {
float: left;
/* to occupy remaining width */
flex: 1 0 auto;
}
.colPadding {
padding: 25px;
background: blue;
}
.cb {
width: 100%;
height: 1px;
clear: both;
}
/* Custom CSS */
/* style to apply when the screen is less than or equal to 300px (you can change this to 768px) */
#media screen and ( max-height: 300px ){
body {
height: 100vh; /* for body to have a size of the full screen */
}
header {
margin: 0px; /* remove margin bottom */
}
section {
padding: 0px; /* remove margin bottom and top/bottom padding */
margin: 0px;
}
}
More on CSS3 flex-box here.
Related
I want to center the text (namely h1) both vertically and horizontally using vh and vw units only.
My HTML body looks like this,
<section class="centered_text">
<h1>Centered text.</h1>
</section>
My simple css (scss) looks like this,
* {
margin: 0;
padding: 0;
}
.centered_text {
margin: 50vh 50vw;
}
The problem is that the text goes more to the right side of a screen. I figured out that when I set the h1 width to the fixed one and subtract half of that width from the margin with the calc function, it seems like it's working, but I don't really know why. The code is this,
* {
margin: 0;
padding: 0;
}
.centered_text {
margin: 50vh calc(50vw - 75px);
h1 {
width: 150px;
}
}
My two questions are,
Why does it work?
How to do it otherwise, in a more convenient way (EDIT: but still using the vh and vw units)?
Thanks in advance.
It works because the position in css is the top-left position of the box where your text is in. 50vw is exactly half the view-width, so the text starts in the middle (and is not centered), removing half the width of the text fixes that.
For centering horizontally I usually use:
css:
.centered_text {
margin-left:auto;
margin-right:auto;
h1 {
width: 150px;
}
}
50vw/50vh is exactly half of the view area. The child element placed at that coordinate starts at it's top left corner, so if you subtract half of it's width, it gets centered.
If a parent container has display: flex;, you can center it's child element with simple margin: auto:
section
{
border: 1px solid red;
text-align: center; /* center wrapped text */
display: flex;
}
h1
{
margin: auto; /* center element */
}
.centered_text
{
width: 100vw;
height: 100vh;
}
html,body
{
height: 100vh; /* set height so we can use % values */
}
.containerBox
{
width: 60%; /* 60% of body width (100vw) */
height: 60%;
}
.container
{
width: 80%; /* 80% of parent width (60% of 100vw) */
height: 80%;
}
<div class="containerBox">
<section class="container">
<h1>Centered text in 80% of 60% box</h1>
</section>
</div>
<section class="centered_text">
<h1>Centered text in 100vh box</h1>
</section>
This is my first week working with flexbox, and I like it a lot until now. I am doing everything I can to get away of floating elements. Therefore my main purpose is not a solution there is floating elements in. I run into 2 problems, that I am not quite sure of the correct saolution.
Problem 1:
When I hit around 1200px I can see the 2 columns is starting moving together. How can that be?
Problem 2:
Why is my columns not fitting on viewport under 768px? I can see on the mobile the 900x200 is going over the edge of the max width on telephone.
Example page of my code here.
.column-layout {
max-width: 1200px;
background-color: #fff;
margin: 40px auto 0 auto;
}
.column-layout-one {
max-width: 1200px;
background-color: #fff;
margin: 40px auto 0 auto;
}
.header-item-one {
order: 1;
}
.header-item-two {
order: 2;
}
#media (min-width: 768px) {
.column-layout-one {
display: flex;
justify-content: space-between;
}
}
<div class="column-layout-one">
<div class="header-item-one">
<img src="http://placehold.it/280x200">
</div>
<div class="header-item-two">
<img src="http://placehold.it/900x200">
</div>
</div>
.column-layout {
max-width: 1200px;
background-color: #fff;
margin: 40px auto 0 auto;
}
img {max-width: 100%;}
.column-layout-one {
max-width: 1200px;
background-color: #fff;
margin: 40px auto 0 auto;
}
.header-item-one {
order: 1;
}
.header-item-two {
order: 2;
}
#media (min-width: 768px) {
.column-layout-one {
display: flex;
justify-content: space-between;
}
}
<div class="column-layout-one">
<div class="header-item-one">
<img src="http://placehold.it/280x200">
</div>
<div class="header-item-two">
<img src="http://placehold.it/900x200">
</div>
</div>
When I hit around 1200px I can see the 2 columns is starting moving together. How can that be?
Your .column-layout-one has max width 1200 set, inner elements has 280+900 = 1180 px, and you have used space-between on its parent. so, when the parent has space more than its defined width, inner items will be seprated by diffrence of 1200-1180 = 20px; as soon as its parent will shrink this space will reduce, because its the space left by these 2 divs,
Space-between = outerWidth - (Total of inner width)
if you want your images to fit in screen on mobile, then provide it
max-width: 100%, if you will not do so, itwill take its orignal width and distort your design.
Set your .header-item-one and -two to flex: 1; to fill up the whole space in your flex box. Then, set max-width values for the expected results.
.header-item-one {
flex: 1;
max-width: 200px;
background-color: red;
height: 300px;
}
.header-item-two {
flex: 1;
max-width: 900px;
background-color: blue;
height: 300px;
}
About the other problem -- the divs go over the viewport boundaries, because you have placeholder images. Images are fixed-width inline block elements, meaning they won't fit the user's viewport, unless you crop them out with overflow-x: hidden; in the parent div or do something like div img { max-width: 100%; }.
Remove the images and try out that css. Fiddle: https://jsfiddle.net/59meh6vs/
I'm using flexbox to align my content blocks in the middle of the page, now i'm willing to align the header with the outer content blocks
In the image you can see what needs to happen
This is my current result:
And this is how it need's to look like
So when scaling the browser the content is going to the first line if there is space, the header needs to grow with this at that moment.
Here is a codepen with the flexbox in it
.flex {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
http://codepen.io/Dirkandries/pen/XmpGzw
Is this possible without using media queries and keeping the content boxes the same size?
*The padding and margin can be removed
Challenge #1
In looking at your code, you have a 10px margin applied to each box:
section {
background: red;
width: 300px;
height: 200px;
margin: 10px;
}
So one problem you will encounter in trying to align the header edges with the box edges is that there's an additional 10px of transparent space beyond the border of each box. But you're asking for the header to align with the border of the box. So we can either remove the margin from each box, or adjust the width of the header. I've gone with the latter in my solution below.
Challenge #2
You've specified a fixed width for each box (300px). This makes it difficult to match the header width with the row of boxes. What happens when the screen is 750px or 1150px wide? The boxes don't stretch to fill the width, a gap is created as a result, and the box row doesn't align with the header.
The box row width is 960px but the header width is 1150px.
One possible solution (or step in the right direction)
Align the flexbox in a column direction to vertically stack the header and the boxes (which are wrapped in a new container)
Use a nested flexbox to align the boxes in a row
Use calc for width values
Use media queries for screen adjustments
HTML
<article class="flex">
<header>
Header needs to be alignd with the container outer part
</header>
<div id="nested-inner-container"><!-- new container for nested flexbox -->
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
</div>
</article>
CSS
body { margin: 0; }
.flex {
display: flex;
flex-direction: column; /* main groups (header and div) in column direction */
align-items: center;
}
header {
height: 50px;
width: calc(100% - 20px); /* width accounts for left and right box margins */
background-color: blue;
}
#nested-inner-container {
display: flex; /* box group (flex item) becomes flex container, as well */
justify-content: center;
flex-wrap: wrap;
width: 100%; /* width equal to header width */
}
section {
flex: 1 1 calc(25% - 80px); /* flex basis equals four boxes per row less margins */
height: 200px;
margin: 10px;
background: red;
}
#media screen and (max-width: 1500px) {
section { flex-basis: calc(33.33% - 60px); } /* three boxes per row less margins */
}
#media screen and (max-width: 1250px) {
section { flex-basis: calc(50% - 40px); } /* two boxes per row less margins */
}
#media screen and (max-width: 1000px) {
section { flex-basis: calc(100% - 20px); } /* one box per row less margins */
}
DEMO: http://codepen.io/anon/pen/wKgVYb
NOTE: This answer may or may not be exactly what you're looking for. The question didn't address all the details (like "can the margins be adjusted?", "are the box widths adjustable?", "are media queries not an option or just something you're hoping to avoid?"). So my goal in this answer was to offer up some concepts that hopefully get you where you want to go. AT A MINIMUM, the header and the boxes align in all screen sizes :-)
As far as I know, there is no solution to your question using only CSS and the flex box model.
If what you want is just to align the background of the header, you can fake it.
You need another flex container, that will give you a background with the same rules as the real one. (And that will be used only for this... not really semantic)
.flex{
display: flex;
flex-wrap: wrap;
justify-content: center;
position: relative;
}
header{
width: 100%;
background: lightblue;
height: 50px;
margin: 0px 170px;
text-align: center;
}
section{
background: red;
width: 300px;
height: 200px;
margin: 10px;
}
.headerbkg {
position: absolute;
left: 0px;
right: 0px;
top: 0px;
height: 50px;
display: flex;
flex-wrap: wrap;
justify-content: center;
overflow: hidden;
z-index: -1;
}
.headerbkg div {
background: lightblue;
width: 300px;
height: 50px;
margin: 0px 10px;
}
<article class="flex">
<header>
Header needs to be alignd with the container outer part
</header>
<div class="headerbkg">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
</article>
The real header is given a margin that will keep it always narrower that the background, so that won't spoli the effect.
To make this less evident, I have given it a centered text
The following are my adressment of the question.
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<meta name = "viewport"
content = "width = device-width, initial-scale = 1.0"
>
<link href = "CSS/Example.css"
rel = "stylesheet"
type = "text/css"
>
</head>
<body>
<header>
Header needs to be alignd with the container outer part
</header>
<div class="flex">
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
</div>
</body>
</html>
CSS
html,
*
{
border : 0;
box-sizing : border-box;
margin : 0;
padding : 0;
}
.flex
{
display : flex;
flex-wrap : wrap;
justify-content : center;
}
header
{
background : blue;
display : block;
height : 50px;
margin : auto;
width : 300px;
}
#media only screen and (min-width : 620px)
{
body header
{
width : 620px;
}
}
#media only screen and (min-width : 940px)
{
body header
{
width : 940px;
}
}
#media only screen and (min-width : 1260px)
{
body header
{
width : 1260px;
}
}
#media only screen and (min-width : 1580px)
{
body header
{
width : 1580px;
}
}
#media only screen and (min-width : 1900px)
{
body header
{
width : 1900px;
}
}
section
{
background : red;
display : block;
width : 300px;
height : 200px;
margin : 10px;
}
Firstly, in the CSS file there is the segment...
*
{
border : 0;
box-sizing : border-box;
margin : 0;
padding : 0;
}
By setting border, margin and padding to 0 for all elements you can eliminate any default settings for those properties that some or all browsers might display for an element. By adding box-sizing : border-box; any border or padding settings you make will be contained within the width of an element rather than without, making laying out a page much simpler.
I tend to include this snippet as a precaution.
As for setting the width of your header, it will need to be set to a fixed width that will need to be changed whenever the concentration of section's (themselves of a fixed width) to a line changes, which will occur whenever the viewport reaches certain widths.
The width of the header, and the width of the viewport at which it is changed, will equal the total width of the sections in one full line plus the total width of the margins between them, nnamely 300px, 620px, 940px, 1260px, 1580px, and 1900px. I have used media queries to perform the changeover at those points.
Note : Because of specificity issues caused when seeking to override a property using media queries I have placed body in front of the definition of header in each of the media queries.
Since the width of header isn't flexible and flex is mainly there to distribute the section's, I have placed header outside of flex.
If you have any questions or comments about my answer, then please feel free to reply.
It is much easier then it looks. Here is the CodePen: http://codepen.io/mikestratton/pen/PPmpKQ
Add the following two classes to your CSS:
.sleft {
background: red;
width: 50%;
height: 200px;
margin: 0;
float: left;
}
.sright{
background: red;
width: 50%;
height: 200px;
margin: 0;
float: left;
}
Then change your HTML to:
<article class="flex">
<header>
Header needs to be alignd with the container outer part
</header>
<section class="sleft">content</section>
<section class="sright">content</section>
<section class="sleft">content</section>
<section class="sright">content</section>
<section class="sleft">content</section>
<section class="sright">content</section>
</article>
Its Not the same as you want but i tried to do some little hacks & tweeks
http://codepen.io/anon/pen/GpEdKq
Check is this ok or you can use grids instead and it will be responsive too, Sorry if it doesn't help you Thanks
Html Code:
<article class="flex">
<div class="wrapper">
<header>
Header needs to be alignd with the container outer part
</header>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
<section>content</section>
</div>
</article>
css code:
body{
margin: 0;
}
.wrapper{width:80%; margin-left:10%;margin-right:15%;}
.flex{
display: flex;
flex-wrap: wrap;
justify-content: center;
}
header{
width: 100%;
background: blue;
height: 50px;
}
section{
background: red;
width: 300px;
height: 200px;
margin: 10px;
float:left;
}
I want to obtain this layout:
<------------ Browser Width 100% ------------>
[left][----- center: fixed width -----][right]
The center column has a fixed pixel width
The left and right columns fill in the remaining viewport width equally
The example below breaks when the viewport width is not wide enough, and getting the correct percentage width is hard because of the fixed width center column.
div {
display: inline-block;
background: #F90;
height: 100px;
width: 20%;
}
.center {
width: 500px;
background: #FF0;
}
<div class="left">left (fill available space)</div>
<div class="center">fixed width</div>
<div class="right">right (fill available space)</div>
Three ways to achieve a fluid / fixed column layout
Method #1 - with display: table
This is one of the easiest methods and has good browser support.
Compatibility: IE8 + and all modern browsers
body gets display: table - this could also be applied to a div wrapper instead.
table-layout: fixed ensures the middle column remains fixed width
the direct div children of body get display: table-cell
the body gets a min-width to ensure the left and right columns do not get too small
the middle column is fixed at your desired width (500px in this example)
the left and right columns inherit the remaining page width
#1 - Working Example
html {
height: 100%;
}
body {
display: table;
height: 100%;
margin: 0;
width: 100%;
table-layout: fixed;
min-width: 800px;
}
body > div {
display: table-cell;
}
.left {
background: #000;
}
.middle {
background: #F00;
width: 500px;
}
.right {
background: #F90
}
<div class="left">
</div>
<div class="middle">
</div>
<div class="right">
</div>
Method #2 - with display: inline-block and width: calc(x - y)
Compatibility: Calc is compatible in IE 9 + and most modern browsers. There are javascript fallbacks available as well.
The direct div children of body are given display: inline-block and vertical-align: top. They will align themselves to the top of the browser window on the same line
The middle column gets its fixed width
The left and right columns are given calc(50% - 250px); this calculates 50% of the page width minus half of the width of the fixed middle column.
box-sizing: border-box incorporates padding and borders into the width and height
#2 - Working Example
Note how the closing and opening divs tags have no gaps between them; this is to prevent an inline gap between elements.
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
}
body {
min-width: 800px;
}
body > div {
display: inline-block;
width: calc(50% - 250px);
height: 100%;
vertical-align: top;
}
.left {
background: #000;
}
.middle {
background: #F00;
width: 500px;
}
.right {
background: #F90
}
<div class="left"></div><div class="middle"></div><div class="right"></div>
Method #3 - with display: flex
This is a really awesome method, but is not supported with older browsers :(
Compatibility: IE11 and most modern browsers
The body gets display: flex and height: 100vh (100% of the viewport height)
The direct children get flex: 1 and will grow and shrink
The middle column gets its fixed width and flex: 0 0 auto; it will not grow or shrink
Here is a useful guide to Flexbox.
#3 - Working Example
body {
display: flex;
height: 100vh;
margin: 0;
min-width: 800px;
}
body > div {
flex: 1;
}
.left {
background: #000;
}
.middle {
background: #F00;
width: 500px;
flex: 0 0 auto;
}
.right {
background: #F90
}
<div class="left">
</div>
<div class="middle">
</div>
<div class="right">
</div>
I have a container div with a floating left-hand navigation pane and a content pane to the right:
<div id="header"></div>
<div id="container">
<div id="leftnav"></div>
<div id="content"></div>
<div class="clearfix"></div>
</div>
<div id="footer"></div>
CSS:
body
{
text-align: center; /* IE center div fix */
}
#container
{
width: 800px; /* site width */
background-color: red; /* so I can see it */
text-align: left; /* undo text-align: center; */
margin: 0 auto; /* standards-compliant centering */
}
#leftnav
{
float: left;
width: 200px;
}
#content
{
height: 100%;
width: 600px;
margin-left: 200px;
background-color: green; /* so I can see it */
}
.clearfix { clear: both; }
The #container div stretches to the full height of the floating #leftnav div, but the contained #content div does not stretch to 100% of the height. I've read elsewhere that this is due to the parent #container not having a specified height (defaults to auto) and therefore the 100% is not based on that container; however, I can't specify the height because the left navigation pane height isn't constant.
How can I get the #content div to be 100% of the height of the #container div when the #container div's height is defined by the floating #leftnav?
This is similar to the 3 column liquid "holy grail" CSS layout that has been plaguing people for years (though has been solved in the past couple years, though many of the solutions required browser hacks or Javascript to function).
I'd highly suggest you not reinvent the wheel here as it is difficult to get CSS to perform exactly as you're describing. Here is a good resource for this layout and many other similar liquid layouts:
http://matthewjamestaylor.com/blog/perfect-2-column-left-menu.htm
The easy way would be to use JS to set the height of #content to the height of #leftnav. You can use faux columns on #container and make a slice/gif of the green background and repeat it vertically on #container along with the red however you have it but I'm not sure if it fits your needs.
try this CSS
body
{
text-align: center; /* IE center div fix */
}
#container
{
width: 800px; /* site width */
background-color: red; /* so I can see it */
text-align: left; /* undo text-align: center; */
margin: 0 auto; /* standards-compliant centering */
}
#leftnav
{
float: left;
width: 200px;
}
#content
{
height: 100%;
width: 600px;
background-color: green; /* so I can see it */
float:right;
}
.clearfix { clear: both; }
I would also suggest using a line break with a clear both rather than a div.