I am trying to make a responsive triangle for one border, which will be visible only on hover
. I tried the below code but it doesn't work because it uses static border-width
. When I have one-string (single line) link it's perfect, but when strings more (more than one line), it's failing.
Code here:
<ul>
<li><a href="#">Lorem ipsum</a></li>
<li><a href="#">Lorem ipsum</a></li>
<li><a href="#">Lorem ipsum dolor sit amet</a></li>
</ul>
SCSS here:
ul{
width:120px;
li{
list-style-type: none;
a{
position:relative;
display:block;
padding:10px;
color:#00f;
text-decoration: none;
&:hover{
color:#fff;
background:#00f;
&:after{
content:'';
position: absolute;
top: 0;
right: -5px;
height: 0;
border-style: solid;
border-width: 19px 0 19px 11px;
border-color: #fff #fff #fff #00f;
}
}
}
}
}
The last link fails. Using an image or picture is not a solution for the problem.
You cannot use the border
triangle method to create the triangles here because the height
of your element is dynamic. Instead you could use any of the following alternatives:
Inline SVG + Clip Path: Recommended
You can make use of inline SVG and clip-path
to produce the bar with a triangle effect. The clip-path
is applied only while hovering on the a
tag and so the normal state remains unaffected. The browser support for this is much better than the CSS equivalent.
ul {
width: 120px;
}
li {
list-style-type: none;
}
a {
position: relative;
display: block;
padding: 10px;
color: #00f;
text-decoration: none;
}
a:hover {
-webkit-clip-path: url(#clip-shape);
-moz-clip-path: url(#clip-shape);
clip-path: url(#clip-shape);
background: crimson;
}
<svg width="0" height="0">
<defs>
<clipPath id="clip-shape" clipPathUnits="objectBoundingBox">
<polygon points="0,0 0.8,0 1,0.5 0.8,1 0,1" />
</clipPath>
</defs>
</svg>
<ul>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum dolor sit amet</a>
</li>
</ul>
Transforms:
You can use transform: rotate(45deg)
on a pseudo-element to produce the triangle and then position it at the end of the a
to produce the shape. The parent has a overflow: hidden
setting to cut out the portions of the triangle that is not required to be displayed.
ul {
width: 120px;
}
li {
list-style-type: none;
}
a {
position: relative;
display: inline-block;
color: blue;
text-decoration: none;
padding: 5px 25px 5px 5px;
overflow: hidden;
}
a:after {
content: '';
position: absolute;
top: 50%;
right: 0%;
height: 100%;
width: 100%;
background-color: inherit;
transform-origin: 100% 0;
transform: rotate(45deg);
z-index: -1;
}
a:before {
position: absolute;
content: '';
top: 0px;
left: 0px;
height: 100%;
width: calc(100% - 25px);
background-color: inherit;
z-index: -1;
}
a:hover {
background: crimson;
background-clip: content-box;
color: beige;
}
<!-- Library included just to avoid prefixes so that users with older browser can view -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<ul>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum dolor sit amet</a>
</li>
</ul>
Alternately, you could use two pseudo-elements with a transform: skew(45deg)
applied to them (in opposite directions) to get the triangle shape. Here also the parent has overflow: hidden
setting.
ul {
width: 120px;
}
li {
list-style-type: none;
margin-bottom: 10px;
}
a {
position: relative;
display: block;
padding: 10px;
color: #00f;
text-decoration: none;
overflow: hidden;
}
a:before,
a:after {
position: absolute;
content: '';
height: 50%;
width: 100%;
left: -15%;
z-index: -1;
}
a:before {
top: 0px;
transform: skew(45deg);
}
a:after {
bottom: 0px;
transform: skew(-45deg);
}
a:hover:after,
a:hover:before {
background: crimson;
}
a:hover{
color: beige;
}
<!-- Library included just to avoid prefixes so that users with older browser can view -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<ul>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum dolor sit amet</a>
</li>
</ul>
These are probably the closest to pure CSS solutions with good browser support. However, they still needs some tweaking of the properties like padding-right
(for rotate
method) and left
(for skew
method) etc when the height increases further and hence is not recommended.
CSS Clip Path:
You can make use of a polygonal clip-path
to create a bar with a triangle effect on hover
. The drawback here is the poor browser support for CSS clip-path.
ul {
width: 120px;
}
li {
list-style-type: none;
}
a {
position: relative;
display: block;
padding: 10px;
color: #00f;
text-decoration: none;
}
a:hover {
-webkit-clip-path: polygon(0% 0%, 80% 0%, 100% 50%, 80% 100%, 0% 100%);
background: crimson;
}
<ul>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum dolor sit amet</a>
</li>
</ul>
Linear Gradients:
You can also use linear-gradient
and pseudo-element combination like in the below snippet but gradients are known to produce jagged edges and are not really recommended.
ul {
width: 120px;
}
li {
list-style-type: none;
}
a {
position: relative;
display: block;
padding: 10px;
color: #00f;
text-decoration: none;
}
a:after {
content: '';
display: none;
position: absolute;
right: -25px;
top: 0px;
width: 50px;
height: 100%;
background: linear-gradient(to top left, rgba(0, 0, 0, 0) 50%, crimson 50%), linear-gradient(to top right, crimson 50%, rgba(0, 0, 0, 0) 50%);
background-size: 50% 50%;
background-repeat: no-repeat;
background-position: 100% 100%, 100% 0%;
z-index: -1;
}
a:hover {
background: crimson;
}
a:hover:after {
display: block;
}
<!-- Library included just to avoid prefixes so that users with older browser can view -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<ul>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum</a>
</li>
<li><a href="#">Lorem ipsum dolor sit amet</a>
</li>
</ul>