I am trying to put this circle in the center of the triangle. I set the class triangle to display flex but it didn't work. Please help me.
The code:
body {
display: flex;
align-items: center;
justify-content: center;
}
.triangle {
position: relative;
width: 0;
height: 0;
border-left: 300px solid transparent;
border-right: 300px solid transparent;
border-bottom: 300px solid black;
}
.circle {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: blue;
}
<div class="triangle">
<div class="circle">
</div>
</div>
One of the easiest – non SVG – ways to achieve this is as below, with explanatory comments in the code:
.collage {
/* using grid layout means we can easily
position the elements in the same place
without nesting them: */
display: grid;
/* effectively the same as:
justify-content: center;
align-contents: center;
to place the items in the center along
the block and inline axes: */
place-items: center;
}
.triangle {
/* allows us to set the aspect-ratio, which causes
the browser to compute one axis of the element
based on the value we specify for 'other' axis;
here we specify a height of 300px, so the browser
calculates the other axis to 600px, making the
triangle-shape twice as wide as its height: */
aspect-ratio: 2 / 1;
background-color: #000;
/* using clip-path, with the CSS polygon() function,
to specify a list of coordinates outside of which
the element is clipped, instead of using the
border hack to create a triangle: */
clip-path: polygon(50% 0, 100% 100%, 0 100%);
/* positions the element in the first grid-row
and first grid-column: */
grid-area: 1 / 1;
height: 300px;
z-index: 1;
}
.circle {
/* a shorthand for an aspect-ratio of: 1 / 1,
which causes the browser to calculate the
unknown axis to be same length as the
specified axis (again, the height): */
aspect-ratio: 1;
background-color: #00f;
border-radius: 50%;
grid-area: 1 / 1;
height: 210px;
/* to place the element higher in the visual
stack, 'closer' to the viewer: */
z-index: 10;
}
<div class="collage">
<div class="triangle"></div>
<div class="circle"></div>
</div>
JS Fiddle demo.
Of course, if you're prepared to use SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" xml:space="preserve">
<!-- this element is the background upon which
the triangle and circle appear: -->
<rect id="background"
<!-- we fill the shape with black, the 'fill'
being the 'background-color' -->
style="fill: #fff;"
<!-- these attributes determine the placement of
the element, the x and y coordinates
of the upper-left corner: -->
x="-300" y="-300"
<!-- determines the width and height: -->
width="600" height="600"
<!-- moves the element across the SVG, in the
x and y axes: -->
transform="translate(300 300)" />
<path id="triangle"
style="fill: #000;"
<!-- this is the path of the triangle, the enclosed
space being the filled portion: -->
d="M-37.43 32.41 0-32.41l37.43 64.82z"
transform="matrix(7.36 0 0 4.99 300 300)" />
<circle id="circle"
style="fill: #00f;"
<!-- we specify the radius of the <circle>: -->
r="35"
<!-- and move it within the SVG for positioning: -->
transform="matrix(3.01 0 0 3.01 300 300)" />
</svg>
JS Fiddle demo.
SVG is a little complex to explain, so unfortunately I've largely abdicated from that responsibility and left a link – in the bibliography below – from which you (and others) can learn more about it.
It's also worth stating that my own knowledge of SVG is limited, and I tend to use a program to create them, such as InkScape (other programs are, of course, available) or an online generator, as I did here.
References:
align-contents.
aspect-ratio.
background-color.
clip-path.
display.
grid-area.
height.
justify-content.
place-items.
<SVG>.
SVG element reference.
z-index.
Bibliography:
"A Complete Guide to Grid," CSS-Tricks.
"CSS Grid Layout," Mozilla Developer Network.
"SVG Tutorial," Mozilla Developer Network.
You can consider one element to draw both shapes:
.box {
width: 400px;
aspect-ratio: 2;
clip-path: polygon(50% 0, 100% 100%, 0 100%);
background: radial-gradient(17% 34% at 50% 60%, blue 98%,#000); /* 34 = 17*2 */
}
<div class="box"></div>
You must to set top, right, left, bottom for your circle class in css.
For example:
.circle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%) //optional
width: 200px;
height: 200px;
border-radius: 50%;
background: blue;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
.triangle {
position: relative;
width: 0;
height: 0;
border-left: 300px solid transparent;
border-right: 300px solid transparent;
border-bottom: 300px solid black;
}
.circle {
width: 200px;
height: 200px;
border-radius: 50%;
background: blue;
position: absolute;
top: 75px;
right : 50%;
transform: translateX(50%);
}
<div class="triangle">
<div class="circle">
</div>
</div>
Related
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>
Please refer to my fiddle. I was aiming to create a triangle (to be placed inside a div), and make it fit exactly (corner to corner).
Here are the rules outlined:
Place the triangle inside all the div elements.
The bottom left corner of the triangle should fit the bottom left corner inside all the divs.
The top right corner of the triangle should fit the top right corner inside all the divs.
The divs has fixed width and height BUT in real life they are all unknown, inherited from a parent.
The angle of the diagonal will be different for every div but that is ok.
Use borders, gradients, transform or SVG to solve the problem. I would not like to use fixed pixel solutions like canvas or PNG.
.one {
width: 100px;
/* Unknown */
height: 30px;
/* Unknown */
background: #ccc;
}
.two {
width: 40px;
/* Unknown */
height: 90px;
/* Unknown */
background: #aaa;
}
.three {
width: 70px;
/* Unknown */
height: 70px;
/* Unknown */
background: #aaa;
}
.triangle {
width: 0;
height: 0;
border-style: solid;
border-width: 0 0 50px 50px;
border-color: transparent transparent #007bff transparent;
}
<div class="one"></div>
<br>
<div class="two"></div>
<br>
<div class="three"></div>
<br>
<div class="triangle"></div>
JSFiddle Reference
Achieving this effect with border will not be possible for dynamically sized containers because they cannot take percentage values or adapt based on a change in dimension of the container. They can only use either pixel or viewport units. Both those wouldn't be of much use for a dynamic container.
Transforms can be used by placing a pseudo-element on top of the containers but they would need calculations for height and width of the element based on trigonometric equations. The simpler ones would be the gradient and SVG approaches.
Using Gradient:
You can do this using a gradient with to [side] [side] syntax. This is responsive and would work for all container sizes. The only drawback is that there would be jagged lines in some cases where the width or height is too large compared to the other.
One advantage of this is that it doesn't require any extra elements (SVG or HTML, not even pseudos) but the drawback will be when hover and click effects are required for the triangle alone (restricted to the triangle's boundaries). Since the element is still a rectangle/square, the hover or click effect will be triggered even when the mouse is outside the triangle but within the container.
.one {
width: 100px;
height: 30px;
background-color: #ccc;
}
.two {
width: 40px;
height: 90px;
background-color: #aaa;
}
.three {
width: 70px;
height: 70px;
background-color: #aaa;
}
div {
background-image: linear-gradient(to top left, blue 50%, transparent 51%);
}
<div class="one"></div>
<br>
<div class="two"></div>
<br>
<div class="three"></div>
<br>
Using SVG:
You could also do it with SVG path element like in the below snippet. The SVG has to be positioned absolutely with respect to the container and have 100% of the parent's width and height.
The advantage of using SVG for the triangle over gradients is that hover and click effects can be added to the triangle alone.
.one {
width: 100px;
height: 30px;
background-color: #ccc;
}
.two {
width: 40px;
height: 90px;
background-color: #aaa;
}
.three {
width: 70px;
height: 70px;
background-color: #aaa;
}
div{
position: relative;
}
div > svg {
position: absolute;
height: 100%;
width: 100%;
}
svg path{
fill: #0007bf;
}
svg path:hover{
fill: crimson;
}
<div class="one">
<svg viewBox='0 0 100 100' preserveAspectRatio='none'>
<path d='M100,0 L100,100 0,100z' />
</svg>
</div>
<br>
<div class="two">
<svg viewBox='0 0 100 100' preserveAspectRatio='none'>
<path d='M100,0 L100,100 0,100z' />
</svg>
</div>
<br>
<div class="three">
<svg viewBox='0 0 100 100' preserveAspectRatio='none'>
<path d='M100,0 L100,100 0,100z' />
</svg>
</div>
<br>
in gradient
style
.triangle {
width: 100%;
height: 100%;
background: linear-gradient(to bottom right, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%, #007bff 50%, #007bff 100%)
}
html
<div class="one">
<div class="triangle"></div>
</div><br>
<div class="two">
<div class="triangle"></div>
</div><br>
<div class="three">
<div class="triangle"></div>
</div>
I have a standard image (that is responsive because of bootstap) and would like to overlay a standard grid 5% x 5% on top of it.
I need to pick points on the images and need the grid to be visible as well as the image.
I upload the image via form and it show up in a form location on the html. I would like to know how to add additional css on top it - to get the grid on top it.
See the image below for a sample (I only put 8 grid - but I would like 5% spacing or 10% spacing both x and y).
Using two linear-gradients on pseudo-elements of the div, one on ::before and one on ::after, we can create two simple lines which are then repeated every nth-percent with background-size. The ::after pseudo-element is rotated 90deg to create the horizontal lines. It looks like this:
.grid::before,
.grid::after {
background: linear-gradient(to right, #000 2px, transparent 2px);
background-size: 10%;
}
.grid::after {
transform: rotate(90deg);
}
The two gradients create two intersecting lines which are a percentage size long, like this:
These lines are repeated with the default background-repeat: repeat, which creates a grid, like this:
When the ::before and ::after pseudo elements are placed over the image we get this:
You can create a fixed grid size, using a fixed pixel background-size:
.fixed::before,
.fixed::after {
background-size: 23px;
}
Example
Note how the entire grid is given an outline using box-shadow: inset 0px 0px 0 2px #000; on ::before.
*,
*::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.grid img {
display: block;
}
.grid {
display: inline-block;
position: relative;
margin: 10px;
vertical-align: top;
}
.grid::before,
.grid::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to right, #000 2px, transparent 2px);
background-size: 10%;
}
.grid::before {
background-color: rgba(255, 0, 0, 0.56);
box-shadow: inset 0px 0px 0 2px #000;
}
.grid::after {
transform: rotate(90deg);
}
.fixed::before,
.fixed::after {
background-size: 23px;
}
<div class="grid">
<img src="https://dummyimage.com/300x300/ccc" width="300" height="300" />
</div>
<div class="grid">
<img src="https://dummyimage.com/200x200/ccc" width="200" height="200" />
</div>
<div class="grid">
<img src="https://dummyimage.com/100x100/ccc" width="100" height="100" />
</div>
<div class="grid fixed">
<img src="https://dummyimage.com/500x500/ccc" width="500" height="500" />
</div>
I am currently trying to make a square be 4 triangles of equal size that have hover events on them.
I am creating the triangles like this,
.right, left, .top, .bottom {
position: relative;
width: 26px;
}
.right:before{
position: absolute;
display: inline-block;
border-top: 26px solid transparent;
border-right: 26px solid #eee;
border-bottom: 26px solid transparent;
left: 26px;
top: 0px;
content: '';
}
What I am finding is that each triangle sits above one another meaning only one triangle can be hovered, here is my example,
http://codepen.io/anon/pen/qdmbKz
As you can see only the bottom triangle (hover at the bottom of the square) is hoverable. What am I doing wrong? Is there a better way of doing this?
As you have already indicated in your question, the reason why the hover works only on the bottom triangle and not the others is because the container of the bottom triangle is placed on top of the container of the other three triangles.
While using the border trick to produce triangles, the actual shape is still a square. It gets the triangle appearance only because the other three borders are transparent. Now when you hover on the shape you are actually hovering the transparent areas of the bottom triangle and not the containers of the other triangles which is why their respective hover events don't get triggered.
I would personally recommend using SVG for these type of things but the shape is not all that complex to achieve with CSS either.
SVG:
In SVG, you could make use of the polygon elements to create four triangles within the square and each polygon is hover-able separately. If they should have their own target links, you can also enclose the polygons within an a (anchor) tag.
In the snippet, I have implemented the anchor only for one triangle
.square {
height: 400px;
width: 400px;
overflow: hidden;
}
svg {
height: 100%;
width: 100%;
}
polygon {
fill: aliceblue;
stroke: crimson;
stroke-linejoin: round;
}
polygon:hover {
fill: cornflowerblue;
}
<div class='square'>
<svg viewBox='0 0 100 100'>
<a xlink:href='http://google.com'>
<polygon points='5,5 50,50 95,5' />
</a>
<polygon points='5,5 50,50 5,95' />
<polygon points='5,95 50,50 95,95' />
<polygon points='95,5 50,50 95,95' />
</svg>
</div>
CSS:
This is an adaptation of the answer posted by here by web-tiki. I am posting a separate answer because the shape in this question is much simpler and doesn't require as much work as the other one.
The square is split into four equal sized hover-able triangles using the following method:
The container is a square and has borders on all its sides. The borders are required on the parent because diagonal lines on the triangle are much much more difficult to achieve with CSS.
Four child elements are added to the container whose height and width are calculated using Pythagoras theorem. They are then positioned such that their top left corner is on the center point of the square (to help with the rotation).
All the child elements are rotated by the appropriate angles to form the triangles. The transform-origin is set as top left to have the rotation done with the parent square's center point as the axis.
The parent has overflow: hidden to prevent the other half of each square from being visible.
Note that adding text into the 4 triangles will not be straight-forward because they would also be rotated. The text would have to be put inside a child element which must either be counter rotated.
Note: The script included in the demo is the prefix free library which is used to avoid browser prefixes.
.square {
position: relative;
height: 200px;
width: 200px;
border: 2px solid crimson;
overflow: hidden;
transition: all 1s;
}
.top,
.left,
.right,
.bottom {
position: absolute;
height: calc(100% / 1.414);
width: calc(100% / 1.414);
top: 50%;
left: 50%;
border: 1px solid crimson;
transform-origin: 0% 0%;
}
.right {
transform: rotate(-45deg);
}
.bottom {
transform: rotate(45deg);
}
.top {
transform: rotate(-135deg);
}
.left {
transform: rotate(135deg);
}
.square > div:hover {
background: tomato;
}
/*Just for demo*/
.square:hover{
height: 300px;
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='square'>
<div class="top"></div>
<div class="right"></div>
<div class="bottom"></div>
<div class="left"></div>
</div>
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 the top-attribute and the element-height to a percentage value. Is there any way to do that?
Schematic representation:
In the image, arrows of the same color are in golden ratio to each other (38.2% : 61.8%). The dotted line is only imaginary.
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: #ddd;
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 divs, 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;
}