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>
Related
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>
How to achieve something like the above image. The below gray color block is cropped in the middle with semi circle to accommodate the image. Should I use CSS animations or anything else to get this effect? So that the background of the below element can be cropped as per the image shape.
Use position:absolute; for the image icon and place it between two divs and instead of cropping you can give a background:#fff and padding so it looks like that.
See code Below:
.main{
position:relative;
height: auto;
}
.upper {
background: #eee;
height: 100px;
}
.lower {
background: #ccc;
height: 100px;
}
img.icon {
position: absolute;
top: 40%;
left:50%;
background: #fff;
border-radius:50%;
padding:2px;
}
<div class="main">
<div class="upper"></div>
<img class="icon" src="https://cdn2.iconfinder.com/data/icons/web-and-mobile-ui-volume-24/48/1200-512.png" width="50" />
<div class="lower"></div>
</div>
JsFiddle: https://jsfiddle.net/75euot8d/1/
I have created the below arrow div with CSS pseudo elements. It is working fine for the fixed height but when I set its height to auto and increase the text it becomes like this. Now is it to set the arrow in a way that it increases its height with the text.
We can do this by using jQuery but is it possible to do it in CSS only?
.label-box-st1::after {
border-bottom: 73px solid #800080;
border-right: 45px solid rgba(0, 0, 0, 0);
border-top: 73px solid #800080;
content: "";
position: absolute;
right: -43px;
top: 0;
width: 20px;
}
Using Linear Gradients:
You can do it using a couple of angled linear-gradients like in the below snippet. As you can see from the snippet, it can adapt to any height even if the content wraps around (or) if it spans multi-lines.
The shape is created as follows:
Couple of linear-gradients (with to [side] [side] syntax) which is colored for almost 50% and transparent for the remaining 50%. Both these gradients have 50% size in Y-axis (that is, half of the element's height) and has 20px size in X-axis (meaning it has a fixed width for the triangle).
These linear gradients are positioned at the right top and right bottom of the element to produce the triangle effect.
One more linear-gradient (which is actually a solid color) whose size in Y-axis is 100% height of the parent and size in X-axis is 20px less than 100% (using calc). This produces the colored area aside from the triangular area.
The advantages of this approach are as follows:
It doesn't require any extra elements (real or pseudo) and hence there is no unwanted clutter in the markup and the pseudo-elements can be used for other things.
As can be seen in the last div, it would adapt itself even if the width of the div changes also.
The triangular cut at the right side is transparent and so the background of the page can also be seen through the cut, if required.
The only two drawbacks of this approach are as follows:
Gradients have a lower browser support compared to pseudo-elements (works only in IE10+) and
At some very wide angles, the angled sides will be a bit jagged.
.shape {
position: relative;
width: 200px;
color: white;
padding: 8px;
margin: 4px;
background: linear-gradient(to bottom right, rgb(128, 0, 128) 49%, transparent 51%), linear-gradient(to top right, rgb(128, 0, 128) 49%, transparent 51%), linear-gradient(to right, rgb(128, 0, 128), rgb(128, 0, 128));
background-size: 20px 50%, 20px 50%, calc(100% - 20px) 100%;
background-position: 100% 0%, 100% 100%, 0% 0%;
background-repeat: no-repeat;
}
.shape.wide {
width: 300px;
}
<div class='shape'>Some div with small content</div>
<div class='shape'>Some div with large content that wraps around into multiple lines</div>
<div class='shape'>Some div with large content that wraps
<br>around into multiple lines
<br>even spanning multiple lines</div>
<div class='shape'>Some div with
<br>large content that wraps
<br>around into multiple lines
<br>even spanning
<br>multiple lines</div>
<div class='shape wide'>Some div with
<br>large content that wraps
<br>around into multiple lines
<br>even spanning
<br>multiple lines</div>
Using SVG: (recommended approach but added below as question asks for CSS)
The same shape can also be achieved using SVG. With SVG all that we need to do is create one path using SVG's path element and then position the path absolutely with respect to the element (along with z-index: -1 to put it behind the text). SVGs are by nature scalable and so they can adapt even if the container's width and/or height increases.
Advantages of SVG are almost similar to that of the gradient based approach. The way where SVG is better than gradient based approach is that this has better browser support (should work in IE9+) and the jagged edges are also less pronounced.
.shape {
position: relative;
width: 200px;
color: white;
padding: 8px;
margin: 4px;
}
.shape.wide {
width: 300px;
}
.shape svg {
position: absolute;
height: 100%;
width: 100%;
top: 0px;
left: 0px;
z-index: -1;
}
.shape path {
fill: rgb(128, 0, 128);
}
<div class='shape'>
<svg viewBox='0 0 1 1' preserveAspectRatio='none'>
<path d='M0,0 1,0 0.9,0.5 1,1 0,1z' />
</svg>
Some div with small content</div>
<div class='shape'>
<svg viewBox='0 0 1 1' preserveAspectRatio='none'>
<path d='M0,0 1,0 0.9,0.5 1,1 0,1z' />
</svg>
Some div with large content that wraps around into multiple lines</div>
<div class='shape'>
<svg viewBox='0 0 1 1' preserveAspectRatio='none'>
<path d='M0,0 1,0 0.9,0.5 1,1 0,1z' />
</svg>
Some div with large content that wraps
<br>around into multiple lines
<br>even spanning multiple lines</div>
<div class='shape'>
<svg viewBox='0 0 1 1' preserveAspectRatio='none'>
<path d='M0,0 1,0 0.9,0.5 1,1 0,1z' />
</svg>
Some div with
<br>large content that wraps
<br>around into multiple lines
<br>even spanning
<br>multiple lines</div>
<div class='shape wide'>
<svg viewBox='0 0 1 1' preserveAspectRatio='none'>
<path d='M0,0 1,0 0.9,0.5 1,1 0,1z' />
</svg>
Some div with
<br>large content that wraps
<br>around into multiple lines
<br>even spanning
<br>multiple lines</div>
Note: You can read more about SVG's path element and its commands (like M, z, L, A etc) in this MDN Tutorial. I would personally recommend you to have a look at SVG because it helps creating a lot of complex shapes with very minimal effort :)
In Addition to Harry's answer, you could also use skew'd pseudo elements in order to create this kind of shape.
div {
height: 20vh;
width: 50%;
background: #800080;
position: relative;
transition: all 0.4s;
cursor:pointer;
}
div:before,
div:after {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 50%;
background: #800080;
z-index: -1;
}
div:before {
top: 0;
transform: skewX(-45deg);
transform-origin: bottom left;
}
div:after {
top: 50%;
transform: skewX(45deg);
transform-origin: top left;
}
/*demo only*/
div:hover {
width: 80%;
height: 50vh;
}
<div>Hover to see resized element</div>
I have a round image (a .png file) which is transparent in the middle. I need to make the background inside the image a solid color. To do this, I made the background solid, and then put border-radius:50%, but this creates an ugly small white line. Is there anyway to get rid of this, or would I have to manually color the image in an image editor?
div {
width: 500px;
height: 500px;
background: black;
}
div img {
margin: 100px;
max-width: 50%;
background: white;
border-radius: 50%;
}
<div>
<img src="http://i.imgur.com/sDU7Lhz.png">
</div>
Fiddle here: https://jsfiddle.net/h3nwkoe1/
The problem is not with the image. The image is a transparent one and has no background to it at all. The problem is caused by the background: white and the border-radius: 50% added to the image element. It is due to the anti-aliasing pixel in browsers and is the same issue described in this thread.
The solution would be to use some method to fill the background partially to the element and not fully (that is, just enough to cover till the black circle that is already present on the image). Since the img tag cannot have pseudo-elements (atleast it won't work cross-browser), the best option is to use a radial-gradient for the background like in the below snippet.
Note: The thick green border is only for demo and can be removed without any side effect.
div {
width: 500px;
height: 500px;
background: black;
}
div img {
margin: 100px;
max-width: 50%;
background: radial-gradient(circle at center, white 60%, transparent 61%);
border-radius: 50%;
overflow: hidden;
border: 4px solid green;
}
<div>
<img src="http://i.imgur.com/sDU7Lhz.png">
</div>
I totally agree with Harry's explanation.
Another workaround could be to enclose the image in a div slightly smaller than the image (like 1px on each side), so that the circle formed using border-radius is smaller than the external black circle on the image.
It is a bit messier than the solution proposed by Harry. But it could be an alternative to gradient.
div#black {
width:500px;
height:500px;
background:black;
border: solid black 1px;
box-sizing: border-box;
}
div#circle {
margin: 100px;
width: 250px;
height: 250px;
background: white;
border-radius: 50%;
text-align: center;
}
div#circle img {
width: 252px;
height: 252px;
margin-left: -1px;
margin-top: -1px;
}
<div id="black">
<div id="circle">
<img src="http://i.imgur.com/sDU7Lhz.png">
</div>
</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>