Reputation: 116
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.
Upvotes: 3
Views: 635
Reputation: 89750
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>
Upvotes: 7
Reputation: 287
You have border with static width:
border-width: 19px 0 19px 11px;
but the height of element changes according to number of rows or text size, so if you update to
border-width: 30px 0 30px 11px;
it will solve the problem for 2 lines text but will not be good for one line text. You will have to check with JS heigt of element and the according to it update the :
border-width: 'dinamic result from JS'px 0 'dinamic result from JS'px 11px;
Upvotes: 0