So I currently find myself needing to make something like this.
My first thought was to use clip-path, but the rounded corners would be hard to pull off, and it would be hard to maintain the 22.5 degrees when the button changes width because of its contents.
So I ended up making each button 2 divs, with 1 div being skewed by 22.5 degrees and being overlapped by the regular rectangular div. Then I added border radius to both.
body {
line-height: 0;
font-size: 16px;
background-color: black;
}
.cta-button-group {
display: flex;
gap: 2rem;
align-items: center;
}
.button-angular-wrapper-left {
display: flex;
isolation: isolate;
position: relative;
height: 40px;
width: fit-content;
}
.button-angular-wrapper-left .button-angular-main {
border-radius: 7px 0 0 7px;
height: 100%;
display: inline-grid;
place-items: center;
padding-inline: 8px 16px;
margin-right: 13px;
transition: background-color 50ms;
}
.button-angular-wrapper-left .button-angular-slant {
border-radius: 0 7px 7px 0;
height: 100%;
width: 24px;
position: absolute;
right: 0;
top: 0;
bottom: 0;
z-index: -1;
transition: background-color 50ms;
}
.button-angular-wrapper-left .button-angular-slant.back-slash {
transform: skewX(22.5deg);
}
.button-angular-wrapper-left .button-angular-slant.forward-slash {
transform: skewX(-22.5deg);
}
.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-main,
.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-slant {
background: white;
border: 3px solid white;
color: blue;
}
.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-main {
border-right: none;
}
.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-slant {
border-left: none;
}
.button-angular-wrapper-right {
display: flex;
isolation: isolate;
position: relative;
height: 40px;
width: fit-content;
}
.button-angular-wrapper-right .button-angular-main {
border-radius: 0 7px 7px 0;
height: 100%;
display: inline-grid;
place-items: center;
padding-inline: 8px 16px;
margin-left: 13px;
}
.button-angular-wrapper-right .button-angular-slant {
border-radius: 7px 0 0 7px;
height: 100%;
width: 24px;
position: absolute;
left: 0;
top: 0;
bottom: 0;
z-index: -1;
}
.button-angular-wrapper-right .button-angular-slant.back-slash {
transform: skewX(22.5deg);
}
.button-angular-wrapper-right .button-angular-slant.forward-slash {
transform: skewX(-22.5deg);
}
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main,
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-slant {
border: 3px solid white;
}
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main {
border-left: none;
}
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main .icon-call {
color: white;
}
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main .cta-text {
color: white;
}
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-slant {
border-right: none;
}
<div class="cta-button-group">
<div class="button-angular-wrapper-left button-angular-color-solid-white" href="">
<div class="button-angular-main">
<span class="cta-text">
Learn More Today
</span>
</div>
<div class="button-angular-slant back-slash">
</div>
</div>
<div class="button-angular-wrapper-right button-angular-color-outline-white" href="">
<div class="button-angular-main">
<span class="cta-text tel-link-no">
1800-1-5555
</span>
</div>
<div class="button-angular-slant back-slash">
</div>
</div>
</div>
Codepen: https://codepen.io/katylar/pen/yLRjKaO
It works, but it’s not perfect. I notice significant artifacts and weird corners/edges on some browsers at some resolutions.
Does anyone have a good solution? that doesn’t involved masks (which I always have a hard time with, sizing-wise)?
3
4 Answers
4
Reset to default
Highest score (default)
Trending (recent votes count more)
Date modified (newest first)
Date created (oldest first)
I’ve tried this approach with pseudo elements. The left side of this shape is a ::before
element and for the hover effect I turned specific sides on the button and pseudo element invisible and also changed the border raduis of specific corners.
.button {
color: white;
background-color: black;
text-align: center;
text-transform: uppercase;
padding: 5px 10px;
margin: 10px;
display: inline-block;
border-radius: 4px;
border: 2px solid black;
-ms-transform: skewX(-20deg);
-webkit-transform: skewX(-20deg);
transform: skewX(-20deg);
}
.button-left::before {
content: " ";
display: block;
position: absolute;
top: -2px;
left: -7px;
z-index: -10;
background-color: black;
width: 20px;
height: 100%;
-ms-transform: skewX(20deg);
-webkit-transform: skewX(20deg);
transform: skewX(20deg);
border-radius: 4px;
border: 2px solid black;
}
.button-left:hover {
background: rgba(0,0,0,0);
box-sizing: border-box;
border: 2px solid black;
border-left: 2px solid rgba(0,0,0,0);
color: black;
border-top-left-radius: 0;
}
.button-left:hover::before {
border-right: 2px solid rgba(0,0,0,0);
background: rgba(0,0,0,0);
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.button-content {
-ms-transform: skewX(20deg);
-webkit-transform: skewX(20deg);
transform: skewX(20deg);
display: inline-block;
}
<div class="button button-left">
<span class="button-content">Slanted Button</span>
</div>
6
-
1
Unrelated to the answer but, you don't need to use
rgba
anymore,rgb
accepts the same parameters (including the alpha value)– rishiyesterday
-
1
oh that's cool and it has wide support (except IE, but usage of IE is quite rare nowadays) too.
– Apodemusyesterday
-
3
Technically, the pseudo-element is
::before
, not:before
. The spec says:
are pseudo-classes,::
are pseudo-elements. Unfortunately, the spec didn’t originally make this distinction, and so most browsers accept both:
and::
—for the original pseudo-elements. But for consistency with later pseudo-elements (which often must be::
), we should probably stick to the spec, unless you specifically need legacy browser support (I don’t know that IE ever got around to supporting::
).– KRyan21 hours ago
-
@KRyan I'll update it
– Apodemus21 hours ago
-
1
Nice! To be clear, I’d already upvoted, but I appreciate the update.
– KRyan21 hours ago
One suggestion, if you’re totally against masks, is to use perspective
and rotate one div in 3D to give an angled element.
Use a container and make it display: inline-block
so the angled div is always at the right hand side. That makes it, when you rotate it on the X axis, lean ‘backwards’ to the right. We create a new stacking context in the container to hide the last div below the parent. Use css custom properties to make sure the final div matches the width of the parent. Personally I’d use a mask but this could be used instead, certainly for elements with a solid background. I don’t know how you’d do it with border as the border width will also change with transform: rotateX()
property. It’s also a bit fragile for varying heights amd widths but it might be a useful starting point.
Anyway, see what you think.
body {
background: black;
color: black;
}
.container {
--height: 4rem;
perspective: 100px;
isolation: isolate;
display: inline-flex;
}
.main,
.angle {
display: inline-flex;
align-items: center;
padding-left: 1rem;
background-color: white;
border-radius: 0.5rem;
}
.main {
height: var(--height);
width: 200px;
}
.angle {
position: relative;
right: 30px;
width: 30px;
transform-origin: top center;
transform: rotateX(20deg);
height: calc(var(--height) * 0.96);
z-index: -1;
}
<div class="container">
<div class='main'>Learn more today!</div><div class="angle"></div>
</div>
This version does not require any extra HTML mark-up; it applies the appropriate slant effect automatically, using pseudo-elements. It also works with any number of buttons, and automatically adds slants only between elements, leaving the first element without a left slant and the last element without a right slant.
It also uses several CSS variables to allow it to be customized by a given use-case.
.slant-between:not(:first-child) {
margin-top: 2rem;
}
.slant-between {
white-space: nowrap;
--border-width: 0.25rem;
--border-radius: 0.5rem;
--bg-color: black;
--fg-color: white;
--skew-angle: 25deg;
--height: 2rem;
}
.slant-between>* {
box-sizing: border-box;
position: relative;
z-index: 0;
border: transparent;
background: transparent;
color: var(--fg-color);
padding: 0 1rem;
height: var(--height);
line-height: var(--height);
font-weight: bold;
cursor: pointer;
}
.slant-between>:hover {
color: var(--bg-color);
}
.slant-between>::before {
content: ' ';
position: absolute;
z-index: -1;
background: var(--bg-color);
border: var(--border-width) solid var(--bg-color);
border-radius: var(--border-radius);
transform: skew(var(--skew-angle));
top: 0;
bottom: 0;
}
.slant-between>:not(:first-child)::before {
left: 0;
}
.slant-between>:not(:last-child)::before {
right: 0;
}
.slant-between>:hover::before {
background: var(--fg-color);
}
.slant-between>:first-child::before,
.slant-between>:first-child::after,
.slant-between>:last-child::before,
.slant-between>:last-child::after {
--skew-tangent: tan(var(--skew-angle));
--abs-skew-tangent: max(var(--skew-tangent), -1 * var(--skew-tangent));
--safe-overlap-width: calc(var(--height)/2 * var(--abs-skew-tangent) + var(--border-radius))
}
.slant-between>:first-child::before {
left: var(--safe-overlap-width);
}
.slant-between>:last-child::before {
right: var(--safe-overlap-width);
}
.slant-between>:first-child::before {
border-left: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.slant-between>:last-child::before {
border-right: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.slant-between>:first-child::after, .slant-between>:last-child::after {
content: ' ';
position: absolute;
z-index: -2;
top: 0;
bottom: 0;
background: var(--bg-color);
border: var(--border-width) solid var(--bg-color);
}
.slant-between>:first-child:hover::after, .slant-between>:last-child:hover::after {
background: var(--fg-color);
}
.slant-between>:first-child::after {
left: 0;
right: var(--safe-overlap-width);
border-right: 0;
border-top-left-radius: var(--border-radius);
border-bottom-left-radius: var(--border-radius);
}
.slant-between>:last-child::after {
right: 0;
left: var(--safe-overlap-width);
border-left: 0;
border-top-right-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
<div class="slant-between">
<button>Foo</button>
<button>Bar</button>
<button>Baz</button>
</div>
<div class="slant-between" style="--skew-angle: 15deg; --bg-color: green; --fg-color: blue; padding: 0.5rem; background: blue">
<button>🜃</button>
<button>🜂</button>
<button>🜁</button>
<button>🜄</button>
<button>♥</button>
</div>
<div class="slant-between" style="--skew-angle: -45deg">
<button style="--bg-color: pink">Hearts</button>
<button style="--bg-color: yellow; --fg-color: black">Stars</button>
<button style="--bg-color: purple">Horseshoes</button>
<button style="--bg-color: green">Clovers</button>
<button style="--bg-color: blue">Blue Moons</button>
<button style="--bg-color: gold">Pots of Gold</button>
<button style="--bg-color: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
)">Rainbows</button>
<button style="--bg-color: red">A Red Balloon</button>
</div>
It can handle a variety of skew angles, color schemes, border widths and radii, and so on.
The primary limitation of the code here is that you need to define the height of the buttons in the CSS, with the --height
variable. In a lot of cases, all the trigonometry around --height
can just be hard-coded with estimated values—there’s a lot of wiggle room on that.
It also doesn’t really work for backgrounds other than solid colors, as you can see with the “Rainbows” example, since you can’t use gradients or images or whatever for text or border colors. Still amused me to try it. I got the rainbow gradient from @Bartek’s answer.
4
-
Note that approach causes artifacts if the browser zoom is anything other than 100%.
– Brian15 hours ago
-
Also causes artifacts on 4K monitors due to OS-level scaling.
– RiverTam12 hours ago
-
@Brian Good point; overhauled the approach to address that, and added a bunch of variables for tweaking it.
– KRyan8 hours ago
-
@RiverTam Good point; overhauled the approach to address that, and added a bunch of variables for tweaking it.
– KRyan8 hours ago
This question made me think of Eric Meyer‘s slantastic in css/edge from 20 years ago.
Anyway, I created this approach while trying to resolve the glitch that causes the "artifacts" when zooming on KRyan’s original answer. I started out using a nested div, then span, but reverted to pseudo-elements because the results were identical.
I wanted to use relative lengths instead of absolute lengths and logical properties instead of physical properties. I wound up with an improvement in Chrome/Edge but not Firefox.
While I don’t think this is better than KRyan’s reworked answer, the other answers that currently have more upvotes fail to show the second part of the requested box, which is not as simple as reversing the first part (in my testing.)
.slanted {
display: flex;
justify-content: center;
padding: 1em;
background-color: #2a63b2;
font-family: sans-serif;
}
.slanted div {
position: relative;
margin-inline: 1em;
padding: .5em;
white-space: nowrap;
border: .25em solid white;
}
.slanted .first {
color: #2a63b2;
background-color: white;
border-radius: .4em 1em 0 .4em;
z-index: 0;
}
.slanted .second {
padding-inline-start: 0;
color: white;
background-color: #2a63b2;
border-inline-start-width: 0;
border-radius: 0 .4em .4em .3em;
}
.slanted .first::after,
.slanted .second::before {
content: ' ';
position: absolute;
top: -.25em;
bottom: -.25em;
width: 2em;
border: .25em solid white;
transform: skew(25deg);
}
.slanted .first::after {
right: -.5em;
border-radius: 0 .4em .4em 0;
background-color: white;
z-index: -1;
}
.slanted .second::before {
left: -.7em;
border-inline-end-width: 0;
border-radius: .4em 0 0 .4em;
}
.slanted .second:hover {
color: #2a63b2;
background-color: white;
}
.slanted .second:hover::before {
background-color: white;
clip-path: polygon(0 0, 60% 0, 35% 50%, 35% 100%, 0 100%);
}
</style>
<div class="slanted">
<div class="first">Learn more today!</div>
<div class="second">1800-1-5555</div>
</div>
Your Answer
Exito is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
Post as a guest
Required, but never shown
Post as a guest
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy
Not the answer you're looking for? Browse other questions tagged
or ask your own question.
or ask your own question.
I think this might be possible with a pseudo element like
:before
yesterday
@Apodemus Pretty sure the OP knows about the existence of pseudo elements. The question is how you would use it.
yesterday
I've posted an answer now with how I would have done it with pseudo elemetns
yesterday
|