英文:
How can I create a box with an angle one side and rounded corners via CSS?
问题
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 two divs, with one div being skewed by 22.5 degrees and being overlapped by the regular rectangular div. Then I added border radius to both.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
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.
Is there a good solution? That doesn't involved masks (which I always have a hard time with, sizing-wise)?
英文:
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 two divs, with one div being skewed by 22.5 degrees and being overlapped by the regular rectangular div. Then I added border radius to both.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
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.
Is there a good solution? That doesn't involved masks (which I always have a hard time with, sizing-wise)?
答案1
得分: 14
.button {
color: white;
background-color: black;
text-align: center;
text-transform: uppercase;
padding: 5px 10px;
margin: 11px;
display: inline-block;
border-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
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: -12px;
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>
英文:
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.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.button {
color: white;
background-color: black;
text-align: center;
text-transform: uppercase;
padding: 5px 10px;
margin: 11px;
display: inline-block;
border-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
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: -12px;
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;
}
<!-- language: lang-html -->
<div class="button button-left">
<span class="button-content">Slanted Button</span>
</div>
<!-- end snippet -->
答案2
得分: 9
以下是翻译好的内容:
如果您完全不喜欢使用口罩的话,一个建议是使用perspective
,并在3D中旋转一个div
以创建一个倾斜的元素。
使用一个容器,并将其设置为display: inline-block
,使倾斜的div
始终位于右侧。当您在X轴上旋转它时,它会向右侧倾斜。我们在容器中创建一个新的堆叠上下文,以隐藏父元素下面的最后一个div
。使用CSS自定义属性来确保最终的div
与父元素的宽度匹配。个人建议使用蒙版,但对于具有纯色背景的元素,这也可以使用。我不知道如何处理具有边框的情况,因为边框宽度也会随着transform: rotateX()
属性的变化而改变。对于不同高度和宽度的情况,这也有些脆弱,但可能是一个有用的起点。
无论如何,看看您的想法。
<!-- 开始代码段: js 隐藏: false 控制台: true Babel: false -->
<!-- 语言: lang-css -->
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;
}
<!-- 语言: lang-html -->
<div class="container">
<div class='main'>Learn more today!</div><div class="angle"></div>
</div>
<!-- 结束代码段 -->
英文:
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.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
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;
}
<!-- language: lang-html -->
<div class="container">
<div class='main'>Learn more today!</div><div class="angle"></div>
</div>
<!-- end snippet -->
答案3
得分: 5
这个版本不需要任何额外的HTML标记;它会自动应用适当的斜线效果,使用伪元素。它还适用于任意数量的按钮,并且自动在元素之间添加斜线,使第一个元素没有左斜线,最后一个元素没有右斜线。
它还使用了几个CSS变量,允许根据给定的用例进行自定义。
.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>* {
// ...(CSS样式)
}
<div class="slant-between">
<button>Foo</button>
<button>Bar</button>
<button>Baz</button>
</div>
<!-- 还有其他示例,请参考原文 -->
这段代码可以处理各种倾斜角度、颜色方案、边框宽度和半径等。这里的主要限制是需要在CSS中定义按钮的高度,使用 --height
变量。在许多情况下,围绕 --height
的三角法可以使用估算值进行硬编码,有很大的余地。
此外,它不适用于除了纯色背景之外的背景,正如“Rainbows”示例中所示,因为无法使用渐变或图像等来设置文本或边框颜色。仍然很有趣尝试。您可以从@Bartek's answer获取了彩虹渐变的代码。
英文:
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.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.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);
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
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
得分: 4
这个问题让我想起了20年前Eric Meyer的css/edge中的slantastic。
无论如何,在尝试解决导致在KRyan的原始答案缩放时出现“伪影”时,我创建了这种方法。我起初使用了嵌套的div,然后是span,但最终转回了伪元素,因为结果是相同的。
我想使用相对长度而不是绝对长度,逻辑属性而不是物理属性。最终在Chrome/Edge中取得了改进,但在Firefox中没有。
虽然我认为这不如KRyan的重新制作的答案好,但目前具有更多赞的其他答案未能显示请求框的第二部分,这不像翻转第一部分那么简单(根据我的测试)。
英文:
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.)
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.slanted {
--border-width: .25em;
display: flex;
justify-content: center;
padding: 1em;
font-family: sans-serif;
background-color: #2a63b2;
white-space: nowrap;
}
.slanted div {
position: relative;
margin-inline: 1em;
padding: .5em;
border: var(--border-width) 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: calc(-1 * var(--border-width));
bottom: calc(-1 * var(--border-width));
width: 2em;
border: var(--border-width) 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>
<!-- language: lang-html -->
<div class="slanted">
<div class="first">Learn More Today!</div>
<div class="second">1800-1-5555</div>
</div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论