I have a <main> HTML element that takes up 80% of the <body> element on a page and it has a max-width value of max-width: 1220px.
Outside of this <main> element I have a small SVG arrow - is it possible with the CSS calc() function to have this so it is always in the middle of the area outside the main element on the left hand side?
If I didn't have a max-width property I could just do left: 10% but this only works up until the main element hits it max-width.
The full code is below and I've managed to get it so it aligns with the left hand side of the main element, but I can't get it so it is halfway across the white space on the left hand side. I'm thinking it may not be possible in CSS.
Codepen: https://codepen.io/emilychews/pen/bGEJjwX
Note 1: When viewing the snippet below you'll need to view it full-page because of the max-width value.
Note 2: What I am trying to do is illustrated in the below image.
main {
position: relative;
margin: 0 auto;
width: 80%;
padding: 6rem 0;
background: red;
height: 100vh;
max-width: 1220px;
}
.down-arrow {
position: absolute;
width: 1rem;
height: 1rem;
bottom: 25vh;
/* THIS IS THE BIT I NEED HELP WITH */
left: calc((100% - 1220px) / 2);
}
<main>
<div class="row"></div>
</main>
<svg class="down-arrow" aria-hidden="true" viewBox="0 0 410.95 355.89">
<polygon fill="#000" points="205.47 354.89 410.08 0.5 0.87 0.5 205.47 354.89" /></svg>
Because the arrow is absolutely positioned, it makes it a lot more complicated to position it relative to the main element. Instead, you can achieve the same effect (if i'm understand what you're looking for correctly!) using a wrapping container and Flexbox (or default CSS to vertically and horizontally center the child elements, I just prefer flex).
What I did was wrap the main element and the arrow in a div, labeled with a class of container. This way, we can position the main and svg relative to each other while still maintaining the flow of the application.
Display flex automatically aligns child elements in a row, which puts the svg and main elements next to each other. Adding align-items and justify-content center ensures that everything remains vertically and horizontally centered. I removed the margin: 0 auto; from main and the absolute positioning from the svg since it's no longer necessary.
Pen with changes, or see below: https://codepen.io/xenvi/pen/OJMGweV
body {
margin: 0;
height: 100vh;
}
.container {
display: flex;
}
main {
position: relative;
width: 80%;
padding: 6rem 0;
background: red;
height: 100vh;
max-width: 1220px;
}
.down-arrow {
width: 1rem;
height: 1rem;
bottom: 25vh;
}
.arrow-container, .end-container {
display: flex;
align-items: center;
justify-content: center;
flex-grow: 1;
}
<div class="container">
<div class="arrow-container">
<svg class="down-arrow" aria-hidden="true" viewBox="0 0 410.95 355.89">
<polygon fill="#000" points="205.47 354.89 410.08 0.5 0.87 0.5 205.47 354.89" /></svg>
</div>
<main>
<div class="row"></div>
</main>
<div class="end-container"></div>
</div>
You need to use left: calc((100% - 1220px)/4);. From 100% we remove the width to get the white space. Then we divide by 2 to get only the left part and we divide again by 2 to get half of it.
You can also use min() to make it working for small screen too:
main {
position: relative;
margin: 0 auto;
width: 80%;
padding: 6rem 0;
background: red;
height: 100vh;
max-width: 1220px;
}
.down-arrow {
position: absolute;
width: 1rem;
height: 1rem;
top: 25vh;
left: calc((100% - min(1220px,80%))/4);
transform:translate(-50%); /* don't forget this to get a perfect centring */
}
<main>
<div class="row"></div>
</main>
<svg class="down-arrow" aria-hidden="true" viewBox="0 0 410.95 355.89">
<polygon fill="#000" points="205.47 354.89 410.08 0.5 0.87 0.5 205.47 354.89" /></svg>
Related
I have 2 container: I want to put the first on top and center it (no problem to do this with margin:0 auto;).
However, I'm not able to put the second in the bottom center.
I'm trying to align a div to the center in bottom position (like a footer div but not at width 100%).
My div has a width of 90% and it is impossible to center it. It is always aligned to the left.
If I use margin: 0 auto; it goes to the center but not at the bottom of the page. If I use position: absolute; bottom:0; it goes in bottom of the page but not aligned to the center.
Does anyone have an explanation?
PS: I would like to keep the HTML unchanged and not create another div to contain the second div.
.a {
width: 90%;
height: 10%;
background-color: beige;
margin: 0 auto;
}
.b {
position: fixed;
width: 90%;
height: 10%;
margin: 0 auto;
bottom: 0;
background-color: aliceblue;
}
<div class="a">A</div>
<div class="b">B</div>
I don't have high enough reputation to comment on answers, but you can use
left: 0; right: 0;
on your .b div without having to put a container around it. I've tried it out and is working under Google Chrome.
There's some more explanation here - CSS Fixed position with Auto Margin
So with position: fixed you've effectively detached that element from the DOM flow so margin: 0 auto is now irrelevant, instead you'll want to do one of several other options to accomplish your goal. A couple examples (Note: the snippet editor won't do position: fixed well either so you'll want to try it locally);
.a {
width: 90%;
height: 10%;
background-color: beige;
margin: 0 auto;
}
.b {
position: fixed;
bottom: 0;
left: 5%;
right: 5%;
height: 10%;
background-color: aliceblue;
}
<div class="a">A</div>
<div class="b">B</div>
OR,
.a {
width: 90%;
height: 10%;
background-color: beige;
margin: 0 auto;
}
.b-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
.b {
margin: 0 auto;
width: 90%;
height: 10%;
background-color: aliceblue;
}
<div class="a">A</div>
<div class="b-container">
<div class="b">B</div>
</div>
Hope this helps, cheers.
I'm not sure what other HTML you have in place besides the two divs. Will you have a div in-between "a" and "b"? Will there be a wrapper div around all the divs? You could use CSS grid for your solution.
Here's the CSS:
/* ideally this would be on a wrapper div instead of the body tag */
body {
display: grid;
grid-template-rows: 90px 1fr 90px; /*This is the row heights*/
grid-template-columns: 1fr;
height: 100vh;
width:90%;
margin:0 auto;
}
.a {
grid-row: 1 / 2;
background-color: beige;
}
.b {
grid-row: 3 / 4;
background-color: aliceblue;
}
Then your markup would stay the same:
<div class="a">A</div>
<div class="b">B</div>
Give .b the following:
left: 5%;
margin: auto won't work on absolutely-positioned elements, only static ones.
Do you want a header and a footer? And the footer always on bottom?
Use a more modern flex layout to do this.
https://stackblitz.com/edit/html-header-main-footer?embed=1&file=style.css
<body>
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
</body>
html, body {
width: 100%;
height: 100%;
margin: 0;
}
body {
display: flex;
align-items: center;
flex-direction: column;
}
header, main, footer {
width: 100%;
max-width: 600px;
text-align: center;
}
main {
flex: 1;
}
Q: How does it work?
A: The main is a flex spacer.
PS: header, main and footer is just a suggestion. You also can use just divs.
#chris..... solution A not work and B I want to keep unchange html #utkanos.....left: 5% is good but i don't want to calculate the space beetween (image if the width was 775 px) #Dominik.... div are not fully width and the 2 container could be different width
I have an SVG graphic that is the child element of a container.
If I want it to fit the full width of this container I normally do preserveAspectRatio="none" and set the width to 100% (with an optional height value if I want to add some extra height to the SVG).
However, I would like to have it so the SVG fits the container, but when I drag the window to a smaller size the wedge shape (i.e. diagonal angle of the SVG), stays at the same angle, whilst also still filling the container.
In the sample code I've kept the preserveAspectRatio=none code to show the general effect I would like (the difference being of course I would like the angle of the wedge to stay the same as the window is reduced in size).
Is this possible? I'm struggling to think of a way to get this to work.
Here is a Codepen: https://codepen.io/emilychews/pen/pGbPby
body {
margin: 0;
padding: 0;
display: flex;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 10rem;
background: lightblue;
position: relative;
}
.wedge {
width: 100%;
left: 0;
bottom: 0;
height: 4rem;
position: absolute
}
<div class="container">
<svg class="wedge" preserveAspectRatio="none" aria-hidden="true" viewBox="0 0 376.9 122.7">
<polyline fill="#000" points="376.9 122.69 0 122.35 376.55 0 376.9 122.69"/>
</svg>
</div>
If you want the SVG to keep its aspect ratio, and not stretch, then don't use preserveAspectRatio="none". Use a different preserveAspectRatio value. For example "xMinYMax slice".
body {
margin: 0;
padding: 0;
display: flex;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 10rem;
background: lightblue;
position: relative;
}
.wedge {
width: 100%;
left: 0;
bottom: 0;
height: 4rem;
position: absolute
}
<div class="container">
<svg class="wedge" preserveAspectRatio="xMinYMax slice" aria-hidden="true" viewBox="0 0 376.9 122.7">
<polyline fill="#000" points="376.9 122.69 0 122.35 376.55 0 376.9 122.69"/>
</svg>
</div>
However you are obviously going to have to decide what you want to happen if the page gets wide enough that the wedge gets so tall that the top gets clipped off. For example in your example (and in my example above) you have limited the height of the SVG to 4em. So you are only seeing the bottom of the wedge.
If I change the SVG height to 100%, you see more of it.
body {
margin: 0;
padding: 0;
display: flex;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 10rem;
background: lightblue;
position: relative;
}
.wedge {
width: 100%;
left: 0;
bottom: 0;
height: 100%;
position: absolute
}
<div class="container">
<svg class="wedge" preserveAspectRatio="xMinYMax slice" aria-hidden="true" viewBox="0 0 376.9 122.7">
<polyline fill="#000" points="376.9 122.69 0 122.35 376.55 0 376.9 122.69"/>
</svg>
</div>
Of course, you may also want to reduce the angle of the wedge.
If you want a box centred with margin: 0 auto, but you want to slightly adjust its position by few pixel. is there a way? In another words content centred plus x pixel to the left/right?
I know I can add left: x. But that would not do what I want, I want to make sure the element is centered with only few pixel to the left. if I adjust jsut the left element without having it centered. it could ended up being in different location in different screen sizes.
CSS
.test{
width: 100px;
height: 100px;
margin: 0 auto;
/*Plus code for x pixel to left/right.*/
}
You can do it using translate property
.test{
width: 100px;
height: 100px;
margin: 0 auto;
/*Plus code for x pixel to left/right.*/
background:red;
transform:translateX(10px);
}
}
<div class="test"></div>
You can do it by using position:relative and a negative value on 'left' or 'right' property. Look at this snippet I made.
.parent {
width: 100%;
background: red;
padding: 50px 0;
box-sizing: border-box;
}
.child {
position: relative;
width:50%;
margin: 0 auto 0 auto;
height: 200px;
background: #fff;
right: -50px;
}
<div class="parent">
<div class="child">
</div>
</div>
In the code sample I did:
reset all margins/paddings to 0
set body height to 100%
set flex container height to 96%
set flex container margin-top to 2%
Now this gives me a scroll on the body even if the flex containers height + margin-top only sums up to 98%, so my question is, can't I use margin-top is this way and where does the extra space come from forcing the body to scroll?
Setting the body to overflow:hidden removes the scroll, but that feels more like a band-aid and not considered as a solution (unless this is a "behavior-by design" which needs that in this case).
Edit
Ways like remove the margin-top on the flex container and then set a padding-top: 2%; on the body or use position: relative; top: 2%; on the container or with absolute: position; I can make it work as expected though, but the case here is why margin-top: 2% doesn't do it.
* {
box-sizing: border-box;
padding: 0;
margin: 0
}
html, body {
background-color: gray;
height: 100%;
}
.outer {
display: flex;
flex-flow: column;
margin: 0 auto;
height: 96%;
width: 50%;
margin-top: 2%;
}
.top {
background-color: lightgray;
}
.middle {
background-color: darkgray;
}
.the-rest {
flex: 1 0 auto;
background-color: lightgray;
}
<div class="outer">
<div class="top">
top
</div>
<div class="middle">
middle
</div>
<div class="the-rest">
content
</div>
</div>
This is because percentage margins are based on the width of the containing block / element...in this case, the body.
W3C Spec
MDN
A <percentage> relative to the width of the containing block. Negative values are allowed.
I usually use vh and vw on html and body. In this demo I applied vh only since it looked like a vertically oriented demo. I also make the body and html position: relative. With vw and vh there's a real measured length that other elements (children of ::root) can actually set their relative measurements of 100%. I use position: relative because it makes the html, body sit rigidly inside the view port and the viewport units keep the body, html on the edge at 100vh and 100vw.
UPDATE
I think this behavior is due to collapsing-margins So if there's an illogical margin-top behavior, keep that in mind. There are several very specific circumstances that result in this odd behavior and there are a few solutions as well. The solution for these circumstances are as follows:
body, html { height: 100vh; } /* no relative or absolute positioning */
.outer { min-height: 100%; margin-top: -2%; } /* It's explained that the positive numbered margin-top is not effective and yet the negative value works but not like a normal negative value!? o_0
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
position: relative;
background-color: gray;
height: 100vh;
}
.outer {
display: flex;
flex-flow: column;
margin: 0 auto;
min-height: 100%;
width: 50%;
margin-top: - 2%;
border-top: 0;
}
.top- {
background-color: lightgray;
}
.middle {
background-color: darkgray;
}
.the-rest {
flex: 1 0 auto;
background-color: lightgray;
}
.marker {
position: absolute;
outline: 1px solid red;
}
<span class="marker" style="top: 0; left: 0;">top:0|left:0</span>
<span class="marker" style="top: 0; right: 0;">top:0|right:0</span>
<div class="outer">
<div class="top">
top
</div>
<div class="middle">
middle
</div>
<div class="the-rest">
content
</div>
</div>
<span class="marker" style="bottom: 0; left: 0;">bottom:0|left:0</span>
<span class="marker" style="bottom: 0; right: 0;">bottom:0|right:0</span>
This is a little tricky to explain, but: I want a responsive-height div (height: 100%) that will scale the width proportional to the height (not vice versa).
I know of this method utilising a padding-top hack to make the height proportional to the width, but I need it to work the other way around. Having said that, I'm not hugely keen on the additional requirement of absolutely-positioned elements for the content in that method, so I realise I may well be asking for the moon on a stick here.
To help visualise, here is an image:
...and here is a jsFiddle, illustrating pretty much the same thing.
It is worth noting that I am already using the :before and :after pseudo-elements to vertically-align the content of the box I want to scale proportionally.
I would really enjoy not having to revert to jQuery, just because there's going to be an inherent requirement for resize handlers and generally more debugging all round... but if that's my only choice, then fiat.
I've been wondering about a pure-css solution to this problem for a while. I finally came up with a solution using ems, which can be progressively enhanced using vws:
See codepen link for full working demo and explanation:
http://codepen.io/patrickkunka/pen/yxugb
Simplified version:
.parent {
font-size: 250px; // height of container
height: 1em;
}
.child {
height: 100%;
width: 1em; // 100% of height
}
Oh,you could probably use that "padding-top" trick.
width: 50%;
height: 0;
padding-bottom: 50%;
http://absolide.tumblr.com/post/7317210512/full-css-fluid-squares
Or:
.square-box{
position: relative;
width: 50%;
overflow: hidden;
background: #4679BD;
}
.square-box:before{
content: "";
display: block;
padding-top: 100%;
}
http://codeitdown.com/css-square-rectangle/
The vertical padding in CSS is related to the width of the element, not the height.
The font solution requires that the height is known. I have found a solution for making an element proportional inside a parent div with unknown widths and heights. Here is a demo.
The trick I'm using is to have an image used as a spacer. The code explained:
<div class="heightLimit">
<img width="2048" height="2048" class="spacer"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAA
P///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
<div class="filler">
<div class="proportional">
</div>
</div>
</div>
So it is not the prettiest with two extra divs and a useless image. But it could be worse. The image element needs to have width and height with the desired dimensions. Width and height need to be as large as the maximum size allowed (a feature!).
The css:
.heightLimit {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: auto;
max-width: 100%;
overflow: hidden;
}
This element is to limit the height, but to expand horizontally (width: auto) although never beyond the parent (max-width). Overflow needs to be hidden because some children will protrude outside the div.
.spacer {
width: auto;
max-height: 100%;
visibility: hidden;
}
This image is invisible and scaled proportionally to the height, while the width is adjusted and forces the width of the parent to also be adjusted.
.filler {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
This element is required to fill the space with an absolutely positioned container.
.proportional {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
}
And here our proportional element gets a height proportional to the width with the familiar padding-bottom trick.
Unfortunately, there is a bug in Chrome and IE so if you modify the parent element using Javascript, such as in my demo, the dimensions will not be updated. There is a hack that can be applied to solve that, as shown in my demo.
You can use view height (vh) as the unity for the width.
Here is an example with the 20px margin you asked for.
.parent {
margin : 20px;
}
.child {
width: calc(100vh - 40px);
height : calc(100vh - 40px);
margin:0 auto;
background: red;
box-sizing:border-box;
padding:10px;
}
See the fiddle :
https://jsfiddle.net/svobczp4/
Based off of #kunkalabs's answer (which is really smart) I've come up with a solution that lets you preserve the inherited font-size.
HTML:
<div id='rect'>
<div id='content'>Text</div>
</div>
CSS:
#rect {
font-size: 1000%;
height: 1em;
width: 1em;
position: relative;
}
#content {
font-size: 10%;
}
So basically the font-size of #content is (100 / $rectFontSize) * 100 percent of the rectangle. If you need a definite pixel size for the rectangle, you can set the #rect's parent's font-size…otherwise just adjust the font-size until it's about where you want it to be (and enrage your designer in the process).
You can achieve that by using SVG.
It depends on a case, but in some it is really usefull. As an example - you can set background-image without setting fixed height or use it to embed <iframe> with ratio 16:9 and position:absolute.
For 3:2 ratio set viewBox="0 0 3 2" and so on.
Example:
div{width:35%;background-color:red}
svg{width:100%;display:block;visibility:hidden}
<div>
<svg viewBox="0 0 3 2"></svg>
</div>
On newer browsers, we can use aspect-ratio with a fixed height, and the width will be calculated accordingly.
img {
aspect-ratio: 1.2;
height: 250px;
max-width: 500px;
}
But the browser support for aspect-ratio is not good enough. I liked the SVG solution proposed by #Jakub Muda, except for the fact that it requires modifying the markup. I have moved the SVG to CSS by including it using content property. On newer browsers, it disables the SVG hack and switches to aspect-ratio property.
document.querySelector('.nav').addEventListener('click', function(e) {
var index = parseInt(e.target.dataset.index);
if (!index) {
return;
}
var elements = document.querySelectorAll('.box');
for (var i = elements.length; i > 0; i--) {
elements[i - 1].classList.toggle('hide', i !== index);
}
});
.wrapper {
max-width: 500px;
margin: 0 auto;
width: 100%;
height: 250px;
text-align: center;
background: green;
}
.box {
display: inline-flex;
position: relative;
max-width: 100%;
}
/* SVG Hack */
.box::before {
display: block;
line-height: 0;
max-width: 100%;
content: 'test';
}
[data-aspect-ratio="1"]::before {
content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1' height='250'></svg>");
}
[data-aspect-ratio="2"]::before {
content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2 1' height='250'></svg>");
}
[data-aspect-ratio="3"]::before {
content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3 1' height='250'></svg>");
}
#supports (aspect-ratio1: 1) {
/* Modern browsers */
.box {
height: 100%;
background: green;
}
.box::before {
display: none;
}
[data-aspect-ratio="1"] {
aspect-ratio: 1;
}
[data-aspect-ratio="2"] {
aspect-ratio: 2;
}
[data-aspect-ratio="3"] {
aspect-ratio: 2;
}
}
.content {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.content>svg {
display: block;
width: 100%;
position: absolute;
top: 50%;
left: 50%;
height: auto;
transform: translate(-50%, -50%);
}
.nav {
text-align: center;
}
.hide {
display: none;
}
<!doctype html>
<html lang="en">
<head>
<title>Width proportional to height in CSS</title>
</head>
<body>
<div class="wrapper">
<div class="box" data-aspect-ratio="1">
<div class="content">
<svg viewBox="0 0 100 100" width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="96" height="96" style="fill:#DEDEDE;stroke:#555555;stroke-width:2"/><text x="50%" y="50%" font-size="18" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#555555">100×100</text></svg>
</div>
</div>
<div class="box hide" data-aspect-ratio="2">
<div class="content">
<svg viewBox="0 0 200 100" width="200" height="100" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="196" height="96" style="fill:#DEDEDE;stroke:#555555;stroke-width:2"/><text x="50%" y="50%" font-size="18" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#555555">200×100</text></svg>
</div>
</div>
<div class="box hide" data-aspect-ratio="3">
<div class="content">
<svg viewBox="0 0 300 100" width="300" height="100" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="296" height="96" style="fill:#DEDEDE;stroke:#555555;stroke-width:2"/><text x="50%" y="50%" font-size="18" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#555555">300×100</text></svg>
</div>
</div>
</div>
<div class="nav">
<button data-index="1">1</button>
<button data-index="2">2</button>
<button data-index="3">3</button>
</div>
</body>
</html>
Make the parent DIV behave like a table cell and align the child element vertically. No need to do any padding tricks.
HTML
<div class="parent">
<img src="foo.jpg" />
</div>
CSS
.parent { width:300px; height:300px; display:table-cell; vertical-align:middle; }