[ 基础系列 ] - CSS 小测 12

244 阅读5分钟

系列文章

说在前面

本篇是张鑫旭老师的 CSS 基础测试13 的阅后笔记。

题目

先来看看题目:

img-00

需求:

  • 尺寸自适应
  • 视觉还原
  • 语义化
  • 无障碍访问

分析

题目看起来似曾相识,仿佛在哪里见过......emmmm,是哪里呢?

img-01

!!!

原来是这里,github。既然如此,我们可以直接参考源码:

<ul class="btn-group-list">
  <!-- watch -->
  <li class="btn-group">
    <details class="details-wrap">
      <summary class="btn" btn-type="summary">
        <i class="icon" icon-type="eye"></i> Unwatch
      </summary>
      <div class="details-menu">
        watch menu
      </div>
    </details>
    <a class="btn" href btn-type="link">63</a>
  </li>

  <!-- star -->
  <li class="btn-group">
    <button class="btn" btn-type="default">
      <i class="icon" icon-type="star"></i> Star
    </button>
    <a class="btn" href btn-type="link">445</a>
  </li>

  <!-- fork -->
  <li class="btn-group">
    <button class="btn" btn-type="default">
      <i class="icon" icon-type="fork"></i> Fork
    </button>
    <a class="btn" href btn-type="link">25</a>
  </li>
</ul>

接下来我们先重置 css 样式:

/* reset css */
body,
ul,
button,
a {
  margin: 0;
  padding: 0;
  list-style: none;
  text-decoration: none;
}

details {
  display: inline-block;
}

summary,
button {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
    Arial, sans-serif;
}

summary::-webkit-details-marker {
  display: none;
}

效果如下:

img-02

然后设定图标:

/* set icon */
.icon {
  display: inline-block;
  width: 1.2em;
  height: 1.2em;
  vertical-align: -2px;
}

[icon-type="eye"] {
  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' width='16' height='16' xmlns='http://www.w3.org/2000/svg' fill='%2324292e' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z'/%3E%3C/svg%3E")
    no-repeat center/100%;
}

[icon-type="star"] {
  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' width='16' height='16' xmlns='http://www.w3.org/2000/svg' fill='%2324292e' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z'/%3E%3C/svg%3E")
    no-repeat center/100%;
}

[icon-type="fork"] {
  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' width='16' height='16' xmlns='http://www.w3.org/2000/svg' fill='%2324292e' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M8 1a1.993 1.993 0 00-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 002 1a1.993 1.993 0 00-1 3.72V6.5l3 3v1.78A1.993 1.993 0 005 15a1.993 1.993 0 001-3.72V9.5l3-3V4.72A1.993 1.993 0 008 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z'%3E%3C/path%3E%3C/svg%3E")
    no-repeat center/100%;
}

效果如下:

img-03

再接着设置布局,这里有以下几种方案:

  • flex
  • BFC
  • table

由于三种布局方案都在前面的小测笔记里介绍过,所以就不分别实现了,这里我们选择最为方便的 flex 来实现布局。

/* layout */
.btn-group-list {
  display: flex;
  flex-wrap: wrap;
}

效果如下:

img-04

接下来就是按钮样式的设置了。

/* btn style */
.btn-group {
  display: inline-flex;
  margin: 10px;
}

.btn {
  display: inline-block;
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 600;
  line-height: 20px;
  vertical-align: middle;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  border: 1px solid #ccced0;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  outline: none;
}

[btn-type="summary"]::after {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 3px;
  transform: translateY(25%);
  border: 4px solid transparent;
  border-top-color: currentColor;
}

.details-menu {
  position: absolute;
  margin-top: 4px;
  overflow: hidden;
  padding: 3px 10px;
  font-size: 14px;
  color: #586069;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid rgba(27, 31, 35, 0.15);
  border-radius: 3px;
  box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15);
}

[btn-type="summary"],
[btn-type="default"] {
  border-radius: 3px 0 0 3px;
}

[btn-type="link"] {
  border-radius: 0 3px 3px 0;
  border-left: none;
}

[btn-type="summary"],
[btn-type="default"] {
  color: #24292e;
  background-color: #eff3f6;
  background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
}

[btn-type="link"] {
  color: #24292e;
  background-color: #fff;
}

[btn-type="summary"]:hover,
[btn-type="default"]:hover,
[btn-type="summary"]:focus,
[btn-type="default"]:focus {
  background-color: #e9ecef;
  background-image: none;
  border-color: rgba(27, 31, 35, 0.35);
  box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
}

[btn-type="link"]:hover,
[btn-type="link"]:focus {
  color: #0366d6;
}

效果如下:

img-05

这里值得一提的是,上面的代码有一定的不足:比如

  • 按钮组有多个左侧样式的按钮,或者右侧链接是的按钮
  • 按钮做右侧反过来放置

那么将会出现如下情况:

img-06

通常在实现按钮组,标签组等组件的时候,使用 :first-child,:last-child 伪类来进行样式是比较巧妙的方法,比如用在这里:

.btn-group > .btn:first-child,
.btn-group > .details-wrap:first-child > .btn {
  border-radius: 3px 0 0 3px;
}

.btn-group > .btn:last-child {
  border-radius: 0 3px 3px 0;
}

效果如下:

img-07

而中间重叠 border 的解决方案,可以利用 :not 伪类:

.btn-group > .btn:not(:first-child),
.btn-group > .details-wrap:not(:first-child) {
  margin-left: -1px;
}

效果如下:

img-05

这里是在线 demo

结束语

本题要点张老师已经总结得很详细了,所以这里就不深入展开了:

  • 按钮就用 <button> 实现即可,而别使用 <a>
  • 按钮组的实现方法,通常有3种 inline-block,float,inline-flex
  • 每个按钮控制不要使用标签选择器,而是树结构伪类,例如:first-child,:last-child
  • 中间的线要走合并,每一个元素都要有完整的边框,(如margin负值合并),而不是根据标签类型进行缺省