哦,制作三角形面包屑丝带的多种方法
让我们看看如何创建一行链接,这些链接以类似于 Chevron 的形状互相连接,每个块都有一个缺口,就像您在层次化的面包屑导航中看到的那样。
您可能经常看到这种模式。它经常出现在多步骤表单和网站面包屑中。为了我们的目的,我们将这些称为“丝带”,这样我们就知道我们在谈论什么。

就像网络上的很多东西一样,我们可以用很多方法制作这样的丝带!我创建了一个 演示页面 ,它将各种丝带整合在一起,例如使用 CSS 三角形 、SVG 背景和 CSS clip-path
属性。
从 HTML 结构开始
对于每个演示,HTML 结构在很大程度上都是相同的,其中我们有一个充当父元素的<nav>
,然后是其中的链接作为子元素。
<nav class="ribbon ribbon--modifier" role="navigation" aria-label="breadcrumbs">
<a class="ribbon__element" href="https://www.silvestar.codes/">Home</a>
<a class="ribbon__element" href="https://www.silvestar.codes/categories/articles/">Blog</a>
<a class="ribbon__element" href="https://www.silvestar.codes/articles/building-an-animated-sticky-header-with-custom-offset/" aria-current="page">Post</a>
</nav>
请注意,根据 A11y 风格指南网站 ,这些元素应该是可访问的。遵循一个好的规则,即从可访问性方面构建组件,并在一开始就引入可访问性,这是防止经典“我忘记使其可访问”情况的最佳方法。
让我们创建一些基线样式
对于像这样的东西,我们要确保元素的大小正确。为此,我们将定义.ribbon
(我们将这些称为)包装元素的字体大小,然后在子元素(即链接本身)上使用em
单位。
/* Define font size of the wrapper element */
.ribbon {
font-size: 15px;
}
/* Use ems to define the size of the ribbon element */
.ribbon__element {
font-size: 1.5em;
letter-spacing: 0.01em;
line-height: 1.333em;
padding: 0.667em 0.667em 0.667em 1.333em;
}
这种特定技术对于定义每个丝带的三角形形状的大小非常有用,因为我们将使用相同的大小来计算三角形。由于我们使用em
单位来计算丝带元素的大小,我们可以通过重新定义包装元素上的font-size
来调整所有元素的大小。
让我们使用 CSS 网格 来进行布局,因为,好吧,我们可以。我们可以用一种提供更深层浏览器支持的方式来做到这一点,但我们会根据您的支持要求将此留给您。
我们将定义四列
- 三列用于丝带元素
- 一列用于修复间距问题。就目前而言,右箭头形状将被放置在丝带组件的外部,这可能会破坏原始布局。
/* The wrapper element
* We're using CSS Grid, but ensure that meets your browser support requirements.
* Assuming the use of autoprefixer for vendor prefixes and properties.
*/
.ribbon {
display: grid;
grid-gap: 1px;
grid-template-columns: repeat(auto-fill, 1fr) 1em; /* Auto-fill the three ribbon elements plus one narrow column to fix the sizing issues */
}
如果您想避免拉伸丝带元素,则可以以不同的方式定义网格。例如,我们可以使用max-content
来根据内容大小调整列。(但是请注意,max-content
在某些关键浏览器中尚未得到很好的支持。)
/* Make ribbon columns adjust to the maximum content size */
.ribbon--auto {
grid-template-columns: repeat(3, max-content) 1em;
}
我相信有很多不同的方法可以进行布局。我喜欢这种方法,因为它定义了丝带元素之间的确切间距,而无需复杂的计算。
可访问性不仅仅是添加 aria 属性。它还包括颜色对比度和可读性,以及添加悬停和聚焦状态。如果您不喜欢outline
样式,可以使用其他 CSS 属性,例如box-shadow
。
/* Use current link color, but add underline on hover */
.ribbon__element:hover,
.ribbon__element:active {
color: inherit;
text-decoration: underline;
}
/* Clear default outline style and use inset box shadow for focus state */
.ribbon__element:focus {
box-shadow: inset 0 -3px 0 0 #343435;
outline: none;
}
创建独特的三角形形状
在定义每个丝带末端的三角形时,我们有多种选择。我们可以
- 我们可以使用伪元素创建 使用边框的三角形
- 我们可以使用伪元素的 SVG 背景图像
- 我们可以使用内联 SVG 图像
- 我们可以使用
polygon()
函数创建clip-path
让我们深入研究每一个。
选项 1:边框方法
首先,我们应该将元素的宽度和高度设置为零,这样它就不会妨碍我们用来绘制边框三角形的伪元素。然后我们应该使用边框绘制三角形,具体而言,通过定义与背景颜色匹配的实心左边框,使其与丝带的其余部分融为一体。从那里,让我们定义顶部和底部边框,并使它们透明。这里的诀窍是计算边框的大小。
我们的丝带元素具有line-height
值加上顶部和底部填充的内容大小
1.333em + 0.667em + 0.667em = 2.667em
这意味着我们的顶部和底部边框应该为该大小的一半。唯一剩下的事情是将元素绝对定位到组件的正确侧面。
/* The left arrow */
.ribbon--alpha .ribbon__element:before {
/* Make the content size zero */
content: '';
height: 0;
width: 0;
/* Use borders to make the pseudo element fit the ribbon size */
border-bottom: 1.333em solid transparent;
border-left: 0.667em solid #fff;
border-top: 1.333em solid transparent;
/* Position the element absolutely on the left side of the ribbon element */
position: absolute;
top: 0;
bottom: 0;
left: 0;
}
/* The right arrow */
.ribbon--alpha .ribbon__element:after {
/* Make the content size zero */
content: '';
height: 0;
width: 0;
/* Use borders to make the pseudo-element fit the ribbon size */
border-bottom: 1.333em solid transparent;
border-left: 0.667em solid;
border-top: 1.333em solid transparent;
/* Position the element absolutely on the right side of the ribbon element and push it outside */
position: absolute;
top: 0;
right: 0;
bottom: 0;
-webkit-transform: translateX(0.667em);
transform: translateX(0.667em);
}
由于右三角形应该与丝带的背景颜色匹配,因此请记住为每个丝带的伪元素添加正确的边框颜色。
/* The right arrow of the first element */
.ribbon--alpha .ribbon__element:nth-child(1):after {
border-left-color: #11d295;
}
/* The right arrow of the second element */
.ribbon--alpha .ribbon__element:nth-child(2):after {
border-left-color: #ef3675;
}
/* The right arrow of the third element */
.ribbon--alpha .ribbon__element:nth-child(3):after {
border-left-color: #4cd4e9;
}
就这样!
查看 CodePen 上的
CSS 网格丝带 – Alpha ,作者是 Silvestar Bistrović (@CiTA)
在 CodePen 上。
选项 2:背景图像方法
我们也可以使用背景图像创建三角形。这需要创建一个与设计匹配的图像,这有点繁琐,但仍然完全有可能。我们将在这里使用 SVG,因为它在任何分辨率下都很平滑。
与边框三角形方法不同,我们希望使伪元素的高度与丝带元素的高度匹配,即 100%。组件的宽度应该与边框三角形的左边框宽度匹配,在本例中为 0.666666em。然后,我们应该在左侧三角形上使用白色三角形作为背景图像,然后使用有颜色的三角形图像作为右侧三角形的背景图像。同样,我们使用绝对定位将三角形放置到丝带元素的正确侧面。
/* The left arrow */
.ribbon--beta .ribbon__element:before {
/* Define the arrow size */
content: '';
height: 100%;
width: 0.666666em;
/* Define the background image that matches the background color */
background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBmaWxsPSIjZmZmIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=);
background-position: center left;
background-repeat: no-repeat;
background-size: 100%;
/* Position the element absolutely on the left side of the ribbon element */
position: absolute;
bottom: 0;
top: 0;
left: 0;
}
/* The right arrow */
.ribbon--beta .ribbon__element:after {
/* Define the arrow size */
content: '';
height: 100%;
width: 0.667em;
/* Define the background image attributes */
background-position: center left;
background-repeat: no-repeat;
background-size: 100%;
/* Position the element absolutely on the right side of the ribbon element and push it outside */
position: absolute;
top: 0;
right: 0;
bottom: 0;
-webkit-transform: translateX(0.667em);
transform: translateX(0.667em);
}
/* Define the background image that matches the background color of the first element */
.ribbon--beta .ribbon__element:nth-child(1):after {
background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsPSIjMTFkMjk1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=);
}
/* Define the background image that matches the background color of the second element */
.ribbon--beta .ribbon__element:nth-child(2):after {
background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsPSIjZWYzNjc1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=);
}
/* Define the background image that matches the background color of the third element */
.ribbon--beta .ribbon__element:nth-child(3):after {
background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsPSIjNGNkNGU5IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=);
}
就这样!
查看 CodePen 上的
CSS 网格丝带 – Beta ,作者是 Silvestar Bistrović (@CiTA)
在 CodePen 上。
选项 3:内联 SVG 方法
与其为每个背景图像加载不同的 SVG 三角形,我们可以在 HTML 中直接使用内联 SVG。
这种特定方法允许我们使用 CSS 控制每个 SVG 箭头的fill
颜色。箭头大小由丝带大小计算得出。再次,我们使用em
单位来定义大小,并且箭头被绝对定位,就像我们到目前为止看到的其他方法一样。
/* Position arrows absolutely and set the correct size */
.ribbon--gamma .ribbon__element svg {
height: 2.667em;
position: absolute;
top: 0;
width: 0.667em;
}
/* The left arrow */
.ribbon--gamma .ribbon__element svg:first-child {
fill: #fff; /* Define the background image that matches the background color */
left: 0; /* Stick left arrows to the left side of the ribbon element */
}
/* The right arrow */
.ribbon--gamma .ribbon__element svg:last-child {
left: 100%; /* Push right arrows outside of the ribbon element */
}
/* Define the fill color that matches the background color of the first element */
.ribbon--gamma .ribbon__element:nth-child(1) svg:last-child {
fill: #11d295;
}
/* Define the fill color that matches the background color of the second element */
.ribbon--gamma .ribbon__element:nth-child(2) svg:last-child {
fill: #ef3675;
}
/* Define the fill color that matches the background color of the third element */
.ribbon--gamma .ribbon__element:nth-child(3) svg:last-child {
fill: #4cd4e9;
}
查看 CodePen 上的
CSS 网格丝带 – Gamma ,作者是 Silvestar Bistrović (@CiTA)
在 CodePen 上。
选项 4:clip-path 方法
我们可以使用多边形来创建丝带三角形,该多边形屏蔽了背景。 Firefox 的形状编辑器 是一个很棒的工具,可以使用 GUI 直接在浏览器中绘制形状,就像 Clippy 一样。
由于多边形必须使用百分比创建,因此我们应该尽力匹配边界三角形的尺寸。另外,请注意基于百分比的多边形在某些视窗上可能看起来有点奇怪,尤其是在元素尺寸根据其周围环境(例如包装元素)进行调整时。请考虑为不同的视窗重新定义多边形。
.ribbon--delta .ribbon__element {
clip-path: polygon(95% 0, 100% 50%, 95% 100%, 0% 100%, 5% 50%, 0% 0%);
}
由于我们使用 CSS 网格定义了包装元素,因此我们应该扩展色带元素,但将最后一个元素保持在多边形三角形的尺寸(在本例中为 5%)。最后一个色带元素的宽度应该比边界三角形宽度大,以匹配前两个示例。
/* Make all ribbon elements (except the last one) wider by the size of the polygon triangle */
.ribbon--delta .ribbon__element:not(:last-child) {
width: 105%;
}
/* Make the last ribbon element wider by the size of the border triangle */
.ribbon--delta .ribbon__element:last-child {
width: calc(100% + .667em);
}
查看 Pen
CSS 网格色带 - Delta by Silvestar Bistrović (@CiTA)
在 CodePen 上。
这些选项的变体
现在我们已经了解了如何用几种不同的方法创建面包屑色带,我们可以对它进行一些调整,例如添加阴影或渐变以及不同的尺寸。
添加阴影
我们可以在色带元素上添加阴影。确保避免在色带元素的左侧或右侧添加阴影。
/* Add shadow under each ribbon element */
.ribbon--shadow .ribbon__element {
box-shadow: 1px 3px 3px -3px black;
}
查看 Pen
CSS 网格色带 - 阴影 by Silvestar Bistrović (@CiTA)
在 CodePen 上。
使用渐变进行颜色
我们可以在色带元素上添加渐变。在这样做时,请确保与右三角形的颜色相匹配。此外,请确保符合 对比度可访问性。
例如,如果我们要使用边界方法或背景图像方法,我们应该主要使用水平(即从左到右)渐变(某些经过仔细计算的角度渐变除外)。如果我们使用 clip-path
方法,我们可以使用我们想要的任何渐变版本。
/* Add gradient to the first ribbon element */
.ribbon--gradient .ribbon__element:nth-child(1) {
background-image: linear-gradient(to right, #11ced2, #11d295);
}
/* Add gradient to the second ribbon element */
.ribbon--gradient .ribbon__element:nth-child(2) {
background-image: linear-gradient(to right, #ef36b2, #ef3675);
}
/* Add gradient to the third ribbon element */
.ribbon--gradient .ribbon__element:nth-child(3) {
background-image: linear-gradient(to right, #4c9fe9, #4cd4e9);
}
查看 Pen
CSS 网格色带 - 渐变 by Silvestar Bistrović (@CiTA)
在 CodePen 上。
处理尺寸变化
由于色带元素的大小取决于包装元素的字体大小,因此定义不同的尺寸非常简单。
/* Small ribbons */
.ribbon--small {
font-size: 10px;
}
/* Big ribbons */
.ribbon--big {
font-size: 20px;
}
这里我们使用了一组较小的色带
查看 Pen
CSS 网格色带 - 小尺寸 by Silvestar Bistrović (@CiTA)
在 CodePen 上。
这里是一组不错的粗色带
查看 Pen
CSS 网格色带 - 大尺寸 by Silvestar Bistrović (@CiTA)
在 CodePen 上。
将所有东西结合起来!
我们还可以组合不同的修饰符类来实现更丰富的样式。例如,让我们将渐变和阴影修饰符一起使用。
查看 Pen
CSS 网格色带 - 阴影渐变 by Silvestar Bistrović (@CiTA)
在 CodePen 上。
还有其他角度需要考虑吗?
使用不同的 CSS 技术创建自定义元素是每个人都能改进或刷新自己知识的好方法。在开始之前,值得花些时间思考所构建组件的可维护性和模块化。一致的命名约定(例如 BEM)当然很有帮助。可访问性也是一个大问题,因此从一开始就考虑到它并一路记录可访问性功能将对你大有裨益。
我们查看了四种不同的方法来绘制色带三角形。您是否使用过其他方法或知道我们没有考虑到的方法?请在评论中告诉我!
很棒的方法!我个人会选择边界方法,因为即使是较旧的浏览器(如 IE8 lol)也可以渲染它。
关于可访问性说明,据我所知,nav 元素不需要
role="navigation"
。nav 元素本身已经表明它是用于导航的。敲敲!
谁在那里?
IE8!!!
IE8是谁?
那个让很多开发者在过去的日子里秃头的人。
Whew,我以为是 IE7…
有趣的文章。但在我的电脑上(我使用的是 Windows 7 上的 Chrome 73),我在彩色箭头和色带本身之间看到一个小间隙。除了使用 SVG 时,这在所有示例中都会发生。我尝试找出问题所在,据我所知,如果您设置
right: 1px;
或减少transform: translateX(0.64em);
在.ribbon--alpha .ribbon__element:after
上,它会修复这个问题。感谢您的赞赏!
我不是可访问性专家,但我认为您是正确的。但是,在这种情况下,
role
属性并不会造成伤害。我使用了一种技术,涉及伪元素和
transform: skewX()
(顶部为30deg
,底部为-30deg
),放置两个半高伪元素,覆盖面包屑元素的右侧。通过在左侧添加填充以供下一个元素使用,您可以获得不错的重叠效果。效果很好,但正如大家可能想到的那样,z-index
必须按递减顺序定义(例如,对于:nth-child(n)
,z-index: X-n
,其中 X 是您的顶层值)。这是我项目中的一个示例(删除了许多内容,因此它在这里没有响应),但由于它应该集成到导航栏中,因此第一个和最后一个项目不应该在开头和结尾分别具有三角形。
不错的文章,
几年前我用“transform: skew”制作了一个:https://codepen.io/johanmouchet/pen/WxpqBa
我曾经用两个
transform: skew()
的伪元素来实现。啊。我看到其他人已经抢先一步了。:) 管理员,如果您愿意,可以删除这些评论。
感谢您发表这篇文章,它很有趣!请问您为什么决定将最后一个网格元素的宽度设置为 1em?我找不到原因。
Raúl,最后一个网格元素设置为 1em 是因为伪元素
after
被放置在色带之外。它的尺寸也是 1em。请告诉我这是否对您有所帮助。
感谢您发表这篇文章。有一个错别字。
0.333em + 0.667em + 0.667em = 2.667em
应该是
1.333em + 0.667em + 0.667em = 2.667em
感谢您,quannt,发现了问题!
已经修复了!
您好 Silvestar!我理解最后一个网格元素的作用,但是,三角形的尺寸不是 0.667em 吗?
Raúl,请记住
em
是相对于父元素的。.ribbon
元素设置为15px
,最后一个网格元素设置为1em
,所以它的宽度是15px。但是,.ribbon__element
设置为1.5em
,也就是22.5px
。由于三角形是相对于.ribbon__element
元素的,我们应该通过将15px
除以22.5px
来重新计算宽度,在本例中为0.667
。希望这有帮助。带边框的变体怎么样?我喜欢让非活动项目成为只有边框的幽灵元素。
问题 - 将悬停引入到 ribbon_element 背景颜色选择器中,代码会变得多么复杂?我正在考虑使用一些 JS 来更改悬停/活动元素右侧的 svg 元素的填充颜色...