I want to position an HTML element in the horizontal middle and vertical golden ratio with CSS only. The height of the element must be absolutly flexible, so I can not just set
I believe the math works out to be this (vertical centering only discussed here):
See Fiddle (thanks to Daniel for making that)
CSS
div {
position: absolute; /* or fixed */
top: 23.6%; /* height is 100% of viewport * 38.2% (blue) * 61.8% (red) */
bottom: 38.2%; /* height is 100% of viewport * 61.8% (blue) * 61.8% (red) */
}
That should get your ratio correct for green. Height is flexible to viewport size.
EDIT: More explanation. Note that the blue arrows start out dividing 100% of the height into 38.2 and 61.8. The red top arrow is going to be 61.8 of the top blue section (38.2), hence 23.6. The red bottom arrow is going to be 61.8 of the bottom blue section (61.8), hence 38.2. Now the double check: 23.6 + 38.2 = 61.8 (that is the TOTAL distance of the two red arrows added together). So their ratio is 23.6/61.8 = 38.2% and 38.2/61.8 = 61.8% (red arrows meet your golden ratio). The green is 100 - 61.8 (the red arrow totals) = 38.2 (the total green area). The top green arrow is 38.2 (top blue) - 23.6 (top red) = 14.6. The bottom green area is 61.8 (bottom blue) - 38.2 (bottom red) = 23.6 (bottom green). Let's check the green ration: 14.6/38.2 = 38.2% and 23.6/38.2 = 61.8% (green arrows meet your golden ratio). Here's a golden ratio addict html/css version of your picture (I know your picture was for illustration purposes, but this was fun):
HTML
<div class="golden"></div>
<div class="dotted"></div>
<div class="blue top arrow"></div>
<div class="blue bottom arrow"></div>
<div class="red top arrow"></div>
<div class="red bottom arrow"></div>
<div class="green top arrow"></div>
<div class="green bottom arrow"></div>
CSS
html {
background-color: #000;
width: 100%;
height: 100%;
}
body {
background-color: #fff;
width: 38.2%;
height: 100%;
margin: 0 30.9%;
position: relative;
}
.golden {
position: absolute;
top: 23.6%;
bottom: 38.2%;
width: 38.2%;
background-color: #ffffd;
left: 50%;
margin-left: -19.1%;
}
.dotted {
position: absolute;
top: 38.2%;
height: 0;
width: 100%;
border-top: 1px dotted #444;
}
.blue {
position: absolute;
right: 14.6%;
width: 2px;
background-color: #00f;
}
.blue.top {
height: 38.2%;
top: 0;
}
.blue.bottom {
height: 61.8%;
bottom: 0;
}
.red {
position: absolute;
right: 38.2%;
width: 2px;
background-color: #f00;
}
.red.top {
height: 23.6%;
top: 0;
}
.red.bottom {
height: 38.2%;
bottom: 0;
}
.green {
position: absolute;
right: 50%;
width: 2px;
background-color: #83f92c;
border-color: #83f92c;
}
.green.top {
height: 14.6%;
top: 23.6%;
}
.green.bottom {
height: 23.6%;
bottom: 38.2%;
}
.arrow:before,
.arrow:after {
content: '';
position: absolute;
display: block;
left: 0;
width: 0;
height: 0;
margin-left: -6px;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
}
.arrow:before {top: 0;}
.arrow:after {bottom: 0;}
.blue:before {border-bottom: 10px solid #00f;}
.red:before {border-bottom: 10px solid #f00;}
.green:before {border-bottom: 10px solid #83f92c;}
.blue:after {border-top: 10px solid #00f;}
.red:after {border-top: 10px solid #f00;}
.green:after {border-top: 10px solid #83f92c;}
EDIT 11-10-11: Based on the questioner's comment that my first interpretation above was not correct. I offer the fact that my solution will still work assuming the white area is the content container controlling the height (in case this is useful for anyone). In such a case, do this:
HTML
<div class="content">
...place some arbitrary length content here...
[copy above HTML here]
</div>
CSS--first, remove html
and body
CSS from above. Then add:
.content {
position: relative;
/* this is the only vital point, you can also style it
similar to the body css in the first version above, minus the height */
}
Finally I found an answer, after eight years :D
Because of new CSS technologies :)
see my CodePen: https://codepen.io/eHtmlu/pen/ExjZrQb
or the same live example here on stackoverflow:
/***********************************/
/* Here is where the magic happens */
.container {
display: flex; /* we need the flex technique */
flex-direction: column; /* and we need it vertically */
align-items: center; /* horizontally we just center the box */
}
.container::before {
content: " ";
flex-grow: .38196601; /* This is the magic number that places the box vertically in the golden ratio */
}
/* That's it!! */
/* except you want to place it relatively to the viewport - see below, where we position the container element */
/***********************************/
/* To place the container at the golden ratio of the viewport, we need to set the height of "html" and "body" to 100% and margin to 0. Then we use the same technique as we used for the box. */
html,
body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
body::before {
content: " ";
flex-grow: .38196601;
}
/* The rest are just a few environmental and styling settings */
.container {
border: #000 solid 1px;
height: 20em;
width: 30em;
margin: 0 auto;
}
.box {
width: 10em;
padding: 1em;
border-radius: .5em;
box-shadow: 0 0 1em rgba(0, 0, 0, .5);
text-align: center;
}
<div class="container">
<div class="box">This box is located vertically in the golden ratio of the container element.</div>
</div>
Okay, I've tested this and it appears to work. The trick though requires the two div
s, inner
and dummy
to have exactly the same contents. dummy
is used to give the outer
div the appropriate height so that the inner
div can be positioned by a percentage of that height. A bit hacky but no javascript.
http://jsfiddle.net/fVQeC/4/
<div class="outer">
<div class="inner">
Something<br>too<br>more<br>more<br>more<br>more
</div>
<div class="dummy">
Something<br>too<br>more<br>more<br>more<br>more
</div>
</div>
.outer{
position: absolute;
top: 38.2%;
}
.inner{
width: 200px;
background-color: blue;
position: absolute;
top: -38.2%;
}
.dummy{
width: 200px;
visibility: hidden;
}