哦,制作三角形面包屑丝带的多种方法!

Avatar of Silvestar Bistrović
Silvestar Bistrović

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费赠送额度!

哦,制作三角形面包屑丝带的多种方法

让我们看看如何创建一行链接,这些链接以类似于 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;
}

创建独特的三角形形状

在定义每个丝带末端的三角形时,我们有多种选择。我们可以

  1. 我们可以使用伪元素创建 使用边框的三角形
  2. 我们可以使用伪元素的 SVG 背景图像
  3. 我们可以使用内联 SVG 图像
  4. 我们可以使用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)当然很有帮助。可访问性也是一个大问题,因此从一开始就考虑到它并一路记录可访问性功能将对你大有裨益。

我们查看了四种不同的方法来绘制色带三角形。您是否使用过其他方法或知道我们没有考虑到的方法?请在评论中告诉我!