# CSS 技巧

评判一个网页的美观程度，首要的当然是看它的 CSS 样式写的如何。

日本某宅写了一个非常厨二的页面——[臆病な魔女](https://yui540.graphics/)，可以说是把 CSS 动画玩得非常 6 了，看得笔者顿时燃起了学好 CSS 的欲望。

如果想 CSS 玩的溜，除了熟悉基本的知识以外，掌握大量技巧也是必不可少的。

## 形状

### 扩张式矩形

目的：创建一个矩形，鼠标悬浮时扩张一定的大小

[预览](https://codepen.io/alphardex/pen/KKPZObw)

```markup
<div class="expanding-box"></div>
```

```css
body {
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
}

.expanding-box {
  position: relative;
  width: 15em;
  height: 10em;

  &::after {
    position: absolute;
    content: "";
    top: -1em;
    right: -1em;
    bottom: -3em;
    left: -1em;
    background: black;
    --path: inset(2rem 0 2rem 2rem round 10px);
    clip-path: var(--path);
    transition: clip-path 0.3s;
  }

  &:hover::after {
    --path: inset(0 round 10px);
  }
}
```

首先利用伪元素`::after`生成一个矩形

利用`clip-path`的`inset`来裁切矩形（顺序是上右下左圆角）

鼠标悬浮时去除裁切效果即可

## 字体排印

### 分割符

目的：实现分隔符效果

[预览](https://codepen.io/alphardex/pen/MWgeoKz)

```markup
<ul>
  <li><a href="#">Home</a></li>
  <li><a href="#">Archive</a></li>
  <li><a href="#">About</a></li>
</ul>
```

```css
ul {
  display: flex;
  font-weight: bold;
  list-style-type: none;

  li {
    &:not(:last-child) {
      margin-right: 5px;

      &::after {
        content: "|";
        margin-left: 5px;
      }
    }
  }
}
```

添加伪元素`::after`，内容`content: "|"`，设置间距

再用`:not(:last-child)`去除伪元素的最后一个子元素即可

## 用户体验

### 提示用角标

目的：在按钮右上角创建一个提示用的角标

[预览](https://codepen.io/alphardex/pen/KKPWMRq)

```markup
<button class="badge" count="7">消息</button>
```

```css
body {
  display: flex;
  justify-content: center;
}

button {
  position: relative;
  padding: 20px;
  border-radius: 20px;
  font-size: 24px;
}

.badge::before {
  content: attr(count);
  position: absolute;
  right: 10px;
  top: -8px;
  background: tan;
  padding: 0 8px;
  border-radius: 12px;
  transform: translate(50%, 0);
}
```

伪元素的`content`可通过`attr()`来获取标签的某一属性值

类似的提示框也可以配合过渡动画来实现：[预览](https://codepen.io/alphardex/pen/ExYvYzP)

### 伸缩式按钮

目的：实现伸缩式按钮（鼠标悬浮按钮则展开，移开按钮则收缩）

[预览](https://codepen.io/alphardex/pen/eYOegOx)

```markup
<a href="#" class="sliding-button">
  <span>下载</span>
  <span>720p</span>
</a>
<a href="#" class="sliding-button">
  <span>下载</span>
  <span>1080p</span>
</a>
```

```css
$tianyi-blue: #66ccff;

body {
  display: flex;
  height: 100vh;
  align-items: center;
  justify-content: center;
}

.sliding-button {
  background: $tianyi-blue url(https://i.loli.net/2019/09/04/JULm1Agr5Gzy7iK.png)
    no-repeat -30px center;
  overflow: hidden;
  transition: padding 0.2s ease, background-position 0.2s ease,
    transform 0.5s ease;

  border: 1px solid $tianyi-blue;
  border-radius: 4px;
  color: #fff;
  text-decoration: none;
  text-transform: uppercase;
  padding: 6px 16px 6px 16px;
  font-size: 18px;
  font-weight: bold;
  line-height: 27px;

  &:nth-child(1) {
    margin-right: 1em;
  }

  span:nth-child(1) {
    position: absolute;
    left: -80px;
  }

  &:hover {
    padding-left: 69px;
    background-position: 5px center;
    transform: scale(1, 1);

    span:nth-child(1) {
      left: 30px;
    }
  }
}
```

给“下载”设置负的`left`用于将其移除视界范围

鼠标悬浮时设置`padding-left`使按钮给“下载”留出空间

`left: 30px;`和`transform: scale(1, 1);`把“下载”拉回按钮中（这个很 hack）

最后设定过渡动画即可看到流畅的过渡效果了

## 结构与布局

### 圣杯布局

目的：实现圣杯布局（左中右三列，左右两列定宽，中间列自适应宽度）

[预览](https://codepen.io/alphardex/pen/ewVLJZ)

```markup
<div class="g-container">
  <div class="g-left">left</div>
  <div class="g-middle">middle</div>
  <div class="g-right">right</div>
</div>
```

```css
.g-container {
  display: flex;
  height: 100vh;
  min-width: 400px;

  & > div {
    text-align: center;
    color: #fff;
    line-height: 100vh;
    font-size: 3vw;
  }

  .g-left {
    order: 1;
    flex: 200px 0 0;
    background: cyan;
  }

  .g-middle {
    order: 2;
    flex: auto 1 0;
    background: purple;
  }

  .g-right {
    order: 3;
    flex: 200px 0 0;
    background: red;
  }
}
```

左右列的`flex-grow`设为`200px`，表示它们各占`200px`空间

中间列的`flex-grow`设为`auto`，`flex-shrink` 设为 1，表明是自适应宽度

## 过渡与动画

### 动态侧边栏

目的：创建一个导航用的侧边栏，里面包含一个列表，用户鼠标浮动到链接上时会显示过渡动画

[预览](https://codepen.io/alphardex/pen/QXvgaq)

```markup
<body>
  <nav>
    <ul>
      <li><a href="#">home</a></li>
      <li><a href="#">resume</a></li>
      <li><a href="#">tags</a></li>
      <li><a href="#">categories</a></li>
      <li><a href="#">archives</a></li>
      <li><a href="#">search</a></li>
    </ul>
  </nav>
</body>
```

```css
nav {
  position: relative;
  background: hsl(60, 100%, 65%);
  width: 218px;
  padding-top: 5px;
}

ul {
  list-style: none;
  width: 197px;
  margin: 0 auto;
  padding: 0;
  vertical-align: baseline;
  li {
    margin: 2.5px 0;
    a {
      display: inline-block;
      position: relative;
      padding: 8px 0;
      padding-left: 20px;
      color: black;
      font-weight: 800;
      font-size: 22px;
      letter-spacing: 0.075em;
      text-decoration: none;
      text-transform: uppercase;
      &::before {
        content: "";
        position: absolute;
        opacity: 0;
        background: url("https://test.demo-1s.com/images/2019/06/24/d31DXx0KYav6cZkG.png")
          left center no-repeat;
        top: 0;
        left: 2px;
        width: 16px;
        height: 100%;
        transform: translateX(10px);
        transition: 0.3s ease-out;
      }
      &:hover::before {
        opacity: 1;
        transform: translateX(0);
      }
      &::after {
        content: "";
        position: absolute;
        height: 4px;
        width: 0;
        bottom: 2px;
        left: -3px;
        opacity: 0;
        background: white;
        border: 2px solid black;
        transition: all 0.3s cubic-bezier(0.445, 0.05, 0.55, 0.95);
      }
      &:hover::after {
        opacity: 1;
        width: calc(100% + 6px);
      }
    }
  }
}

body {
  margin: 0;
  padding: 0;
}
```

给 a 添加 2 个伪元素，并分别为其设置`transition`以及`hover`时的状态即可

### 图片幻灯片

目的：四幅图片（这里用颜色代替）按顺序依次向右滑动显示，最后被四道白色矩形依次遮住

[预览](https://codepen.io/alphardex/pen/WqaJbG)

```markup
<body>
  <main id="slider">
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
    <div class="paint">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>
  </main>
</body>
```

```css
$imgs: blue, cyan, yellow, tan;

html,
body {
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  min-height: 100vh;
}

#slider {
  position: relative;
  width: 500px;
  height: 300px;
  margin: auto;
}

.image {
  position: absolute;
  width: 0%;
  height: 100%;
  overflow: hidden;

  &::after {
    content: "";
    display: block;
    width: 500px;
    height: 300px;
    background-size: cover;
    background-position: center;
  }

  @for $i from 1 through length($imgs) {
    &:nth-child(#{$i})::after {
      background-color: nth($imgs, $i);
    }
  }
}

.paint {
  position: absolute;
  width: 100%;
  height: 100%;

  div {
    width: 100%;
    height: 25%;
    background-color: white;
    transform: translateX(-100%);
  }
}

// animation
@for $i from 1 to 5 {
  .image:nth-child(#{$i}) {
    animation: show-image 0.7s ease (0.5s + 0.2s * $i - 0.2s) forwards;
  }
}

@for $i from 1 to 5 {
  .paint div:nth-child(#{$i}) {
    animation: slide-right 0.5s ease (1.6s + 0.1s * $i - 0.1s) forwards;
  }
}

// keyframes
@keyframes show-image {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}

@keyframes slide-right {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0%);
  }
}
```

添加一个 slider，用`flex`使其垂直居中

在 slider 上添加 4 个 image，并使其大小和 slider 相同并重叠在一起，利用`overflow:hidden`隐藏它们

为这些 image 设置`::after`矩形伪元素，用作图片背景

创建`show-image`关键帧：元素的宽度从 0%变为 100%

为 image 创建动画：把`show-image`作为关键帧，每张图设置不同的延时`delay`，`fill-mode`设置`forwards`表示动画完成时保持最后一个关键帧的属性值，这样就完成了 4 张图依次向右滑动的效果

添加一个 paint 以及 4 个子 div，表示 4 道白色矩形，默认的位置是向 x 轴左侧平移 1 个元素单位

创建`slide-right`关键帧：向 x 轴左侧平移 1 个元素单位变为平移 0 个单位，也就是平移到原点

为 paint 的 4 个子 div 创建动画：把`slide-right`作为关键帧，同样给每个矩形设置不同的延时`delay`和`forwards`的`fill-mode`，就完成了四道白色矩形依次将图片遮住的效果


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://alphardex.gitbook.io/coder-operation-record/qian-duan/css.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
