剖析CSS-Tricks,为我所用-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

剖析CSS-Tricks,为我所用

简介: [CSS-Tricks网站](//css-tricks.com/)是一个非常优秀的网站,特别对于CSSer而言,该网站不断的在更新一些优秀的教程和技巧,为前端社区做出了具大的贡献。她一直是我的偶像,也是我学习的榜样,因为我从该网站上学到了很多新技术,让我在CSS方面的眼界扩展的更大。今年1月份改版之后,整个社交媒体的响应非常的热烈,很多人都为新版赞赏。而我这次参加[中国第五届CSS Conf大会]

CSS-Tricks网站是一个非常优秀的网站,特别对于CSSer而言,该网站不断的在更新一些优秀的教程和技巧,为前端社区做出了具大的贡献。她一直是我的偶像,也是我学习的榜样,因为我从该网站上学到了很多新技术,让我在CSS方面的眼界扩展的更大。今年1月份改版之后,整个社交媒体的响应非常的热烈,很多人都为新版赞赏。而我这次参加中国第五届CSS Conf大会之前和 @裕波 聊过,该和社区的同学聊什么样的话题呢?经过讨论下来,要不就聊聊CSS-Tricks网站这次改版运用的一些新特性,这些新特性又为用户体验带来什么样的改变,甚至为前端开发者又带来什么样的改变?所以借此机会,以我自己的视角来聊聊相关的话题。

CSS-Trick 设计改版本的变更

undefined

时至今日,CSS-Tricks网站的改版已经经历了 17 个版本的变更,每个版本都有其独之处,特别是今年(2019年1月发布的版本)在整个前端社区得到热烈的反响,不管是在Twitter还是Facebook针对改版后的讨论都非常地多。先上一下最早发布版本的视觉截图:

css-conf-5-2.png

你可以现在访问的版本和所看到的最初版本略有不同,因为该设计(版本)一直在不断的更新。

当然从设计稿上看,只能看出来设计稿和以往版本有何不同的外观效果,但做为技术人员,还是喜欢看看新版的设计运用了什么样的新特性,这些新特性又给用户和我们带来什么样的变化。那么接下来,我们一起来聊了新版用了哪些CSS方面的特性。

V17. 版本技术亮点

前面也提到了,接下来要聊的技术亮点是有关于CSS的,在新版中运用的到技术亮点,主要总结起来有以下几点:

  • SVG的使用
  • 滚动特性
  • 自定义属性
  • Web Layout
  • 混合模式和滤镜
  • 其他(^_^)

这仅仅是从我自己的角度出发的,或许有很多遗漏的,如果您有更多的见解,欢迎在下面的评论中与我们一起讨论和分享。

SVG

对于SVG而言,她是一套独立而又成熟的体系,也有自己的相关规范(Scalable Vecgtor Graphics 2),即 SVG2。虽然该规范已经存在很久了,但很多有关于SVG相关的特性在不同的浏览器中得到的支持度也是有所不一致的。特别是SVG中的渐变滤镜相关的特性。不过,随着技术的革新,在Web的应用当中SVG的使用越来越多,特别是SVG 图标相关的方面的运用。

在CSS-Tricks新版中,好几个地方都使用了SVG的技术,特别是SVG的图标。比如:

css-conf-5-3.gif

可以说,站上有关于图标都是通过SVG来完成的,而且采用的是SVG Sprites相关的技术。而且该方面的技术也算不上是什么新技术,早久之前就有相关方面的介绍,比如:

Web自1989年到2019年也算是走过了30年的历程了,那么Web上面有关于图标的使用也经历了相应的发展:

undefined

  • 最早通过<img>标签来引用图标(每个图标一个文件)
  • 为了节省请求,提出了Sprites的概念,即 将多个图标合并在一起,使用一个图片文件 ,借助background相关的属性来实现图标
  • 图片毕竟是位图,面对多种设备终端,或者说更易于控制图标颜色和大小,开始在使用 Icon Font 来制作Web图标
  • 当然,字体图标是解决了不少问题,但每次针对不同的图标的使用,需要自定义字体,也要加载相应的字体文件,相应的也带了一定的问题,比如说跨域问题,字体加载问题
  • 随着SVG的支持力度越来越强,大家开始在思考SVG,使用SVG来制作图标。该技术能解决我们前面碰到的大部分问题,特别是在而对众多终端设备的时候,它的优势越发明显
  • SVG和img有点类似,我们也可以借助<symbol>标签和<use>标签,将所有的SVG图标拼接在一起,有点类似于Sprites的技术,只不过在此称为SVG Sprites

有关于这方面更详细的介绍,可以阅读《Web中的图标》一文。

既然SVG Sprites有这么的优势,那么在Web中使用也就理所当然。估计也正因为这些原因,CSS-Tricks网站也使用了该技术。使用该技术并不复杂,比如:

<!-- HTML -->
<svg width="0" height="0" display="none" xmlns="http://www.w3.org/2000/svg">
    <symbol id="half-circle" viewBox="0 0 106 57">...</symbol>
    <!-- .... -->
    <symbol id="icon-burger" viewBox="0 0 24 24">...</symbol>
</svg>

SVG Sprites和img Sprites有所不同,SVG Sprites就是一些代码(类似于HTML一样),估计没有接触过的同学会问,SVG Sprites对应的代码怎么来获取呢?其实很简单,可以借助一些设计软件来完成,比如Sketch。当然也可以使用一些构建工具,比如说svg-sprite。有了这个之后,在该使用的地方,使用<use>标签,指定<symbol>中相应的id值即可,比如:

<svg class="icon-nav-articles" width="26px" height="26px">
    <use xlink:href="#icon-nav-articles"></use>
</svg>

使用SVG的图标还有一优势,我们可以在CSS中直接通过代码来控制图标的颜色:

.site-header .main-nav .main-sections>li>a>svg {
    // ...
    fill: none;
    stroke-width: 2;
    stroke: #c2c2c2;
}
.site-header .main-nav:hover>ul>li:nth-child(1) svg {
    stroke: #ff8a00;
}

是不是很简单。如果你是使用类似像Vue或React之类的JavaScript框架之类的,还可以将该特性封装成一个组件,只需要将需要使用的图标存入到指定的位置,借助Webpack构建能力就可以自动生成相应的SVG Sprites代码,使用封装好的组件,在另何需要使用的地方调用即可。比如《如何在Vue项目中使用SVG Icon》一文所介绍的,在Vue中使用SVG图标就非常的容易。

在新版的CSS-Tricks中,除了使用了SVG的图标之外,还使用了SVG的渐变,制作一些渐变的效果。不过这里就不做过多的阐述,感兴趣的同学可以自己的探讨。

有关于SVG更多的内容,可以点击这里进行了解

滚动特性

有关于容器滚动方面的CSS新特性其实有很多个,比如:

  • 自定义滚动条的外观
  • scroll-behavior指容容器滚动行为,让滚动效果更丝滑
  • overscroll-behavior优化滚动边界,特别是可以帮助我们滚动的穿透
  • 滚动捕捉

早前在《改变用户体验的滚动新特性》一文针对滚动特性带来的用户体验改变做过详细的探讨。而在CSS-Tricks新版中,运用了自定义动条的外观和滚动捕捉的特性。

embed: cards.mp4

先来看一下自定义滚动条。

在不同的系统和平台下,滚动条的外观都是有所不同的:

undefined

虽然不同的平台上滚动条的外观有所不同,但它们对应的关键部分是一样的:

undefined

在CSS中,我们可以使用-webkit-scrollbar来自定义滚动条的外观。该属性提供了七个伪元素:

  • ::-webkit-scrollbar:整个滚动条
  • ::-webkit-scrollbar-button:滚动条上的按钮(下下箭头)
  • ::-webkit-scrollbar-thumb:滚动条上的滚动滑块
  • ::-webkit-scrollbar-track:滚动条轨道
  • ::-webkit-scrollbar-track-piece:滚动条没有滑块的轨道部分
  • ::-webkit-scrollbar-corner:当同时有垂直和水平滚动条时交汇的部分
  • ::-webkit-resizer:某些元素的交汇部分的部分样式(类似textarea的可拖动按钮)

比如上面的视频中,整个页面的滚动条外观就是自定义的,在html中使用-webkit-scrollbar相应的伪元素来完成的:

html {
    --maxWidth:1284px;scrollbar-color: linear-gradient(to bottom,#ff8a00,#da1b60);
    scrollbar-width: 30px;
    background: #100e17;
    color: #fff;
    overflow-x: hidden
}

html::-webkit-scrollbar {
    width: 30px;
    height: 30px
}

html::-webkit-scrollbar-thumb {
    background: -webkit-gradient(linear,left top,left bottom,from(#ff8a00),to(#da1b60));
    background: linear-gradient(to bottom,#ff8a00,#da1b60);
    border-radius: 30px;
    -webkit-box-shadow: inset 2px 2px 2px rgba(255,255,255,.25),inset -2px -2px 2px rgba(0,0,0,.25);
    box-shadow: inset 2px 2px 2px rgba(255,255,255,.25),inset -2px -2px 2px rgba(0,0,0,.25)
}

html::-webkit-scrollbar-track {
    background: linear-gradient(to right,#201c29,#201c29 1px,#100e17 1px,#100e17)
}

是不是很简单。通过这几个伪元素,可以实现你自己喜欢的滚动条外观效果,比如下面这个示例:

undefined

在线Demo可以点击这里查看

另外,上面的视频还展示了另一个滚动特特,即滚动捕捉。该特性可以为我们提供一个流式精确的滚动体验。比如Swiper的效果

scrolling-13.gif

对于这样的效果,我们都想为触控以及输入设备的用户提供一个流式、精确的滚动体验。而这样的效果,被称为Scroll Snap效果,而早前实现类似的效果需要借助JavaScript库来完成。如今可以使用CSS Scroll Snap Points来实现。

CSS Scroll Snap Points工作原理很简单:

通过在 x 以及 y 轴上定义“捕捉”来控制滚动容器的滚动行为。当用户在水平或者垂直方向滚动时,利用捕捉点,滚动容器会捕捉到内容区域的某一点。

Scroll Snap Points主要提供了以下几个属性:

  • scroll-snap-type:定义在滚动容器中的一个捕捉点如何被严格的执行
  • scroll-snap-type-x:定义在滚动容器中水平轴上捕捉点如何被严格的执行
  • scroll-snap-type-y:定义在滚动容器中垂直轴上捕捉点如何被严格的执行
  • scroll-snap-coordinate:结合元素的最近的祖先元素滚动容器的 scroll-snap-destination 定义的轴,定义了元素中 xy 坐标偏移的位置。如果元素已经变型,捕捉坐标也以相同的方式进行变型,为了使元素的捕捉点向元素一样被显示
  • scroll-snap-destination:定义滚动容器的可视化 viewport 中元素捕捉点的 xy 坐标位置
  • scroll-snap-points-x:定义滚动容器中内容的捕捉点的水平位置
  • scroll-snap-points-y:定义滚动容器中内容的捕捉点的垂直位置
  • scroll-snap-align:元素相对于其父元素滚动容器的对齐方式。它具有两个值 xy。如果你只使用其中的一个,另外一个值默认相同
  • scroll-snap-padding:与视觉窗口的滚动容器有关。工作原理也相近与正常的内边距,值设置一致。此属性具有动画效果,所以如果你想要对齐捕捉点进行滚动,这将是一个很好的而选择

随着技术的变更,该特性相关的属性也发生了相应的变化:

undefined

其中scroll-snap-alignscroll-snap-destinationscroll-snap-coordinatescroll-snap-type是其中较为重要的几个属性,简单的用下图来向大家描述:

undefined

比如CSS-Tricks上就使用了相关的特性:

undefined

该特性如果应用得好,效果会更有意思,比如下面这个示例:

css-tips-0216.gif

在线Demo可以点击这里查看

自定义属性

CSS自定义属性对于很多前端同学可能并没有接触过,甚至没有听说过,但如果说CSS的变量,估计就很多同学有听说过。但这里再次强调,我们应该纠正这样的说法:

CSS没有变量,没有变量,没有变量;只有自定义属性,只有自定义属性,只有自定义属性!

CSS自定义属性在W3C规范是独立的一个模块,目前是 Level 1版本(CSS Custom Properties for Cascading Variables Module Level),另外还有一个Level 2版本(目前处于ED阶段)。该特性虽然在CSS中的历史不长,但很多开发者都非常这个特性,在Youtuebe和Facebook页面上都可以看到它们的影子:

undefined

在CSS-Tricks这次改版后的代码中,同样也看到了CSS自定义属性:

undefined

而且在互联网上,有关于CSS 自定义属性的教程和案例特别的多。这里推荐大家阅读@Michael Riethmuller在SmashingMagazine上的一篇博文《A Strategy Guide To CSS Custom Properties》(注,中文可以点击这里阅读)。如果你从未接触过CSS自定义属性的话,我建议你先花点时间阅读《是时候开始使用自定义属性》一文。

回到我们的主题中来,CSS 自定义属性使用非常的简单,但就是这些简单的使用可以帮助我们构建出非常优秀的东东出来。当然,还能让我们更好的编写和维护CSS代码。接下来通过几个小示例来向大家简单的介绍一下。

为了让新接触的同学对CSS自定义属性有点概念或印象,简单的来看一下他的基本使用。当然,如果接触过CSS处理器的同学,过渡过来还是非常快的。比如你曾经使用过Sass或LESS这样的处理器,你编写代码的时候可能会像下面这样:

/* SCSS */
$color: red;
a {
    color: $color;
}

/* LESS */
@color: red;
a {
    color: @color;
}

而CSS自定义属性的声明也非常的类似,先用--properyName: value这样的形式来声明一个自定义属性,然后通过var()函数来调用:

:root {
    --color: red;
}

a {
    color: var(--color)
}

在CSS自定义属性中,除了可以在:root{}中声明任何你想要的自定义属性,还可以在CSS的任何选择器中声明,只不过使用的范围有所区别(后面会介绍到);另外var()函数调用已声明好的自定义属性时还可以通过其第二个参数,给自定义属性提供一个备用值:

a {
    color: var(--color, black)
}

是不是非常的简单,但这里我还是需要再次强调一下:

它是自定义属性,是属性,而不是变量

接下来,从另外几个方面来阐述一下CSS自定义属性和CSS处理器有何不同之处,另外它除了这些还具备一些什么样的优势。首先要说的是:

动态 vs. 静态

CSS处理器器运行机制整个过程用下图应该可以很清楚的说清楚:

undefined

也就是说,最终在页面上给大家呈现的依旧是CSS代码。也就是说,CSS处理器中的变量或其他功能仅仅是在编译的时候生效,它是一种静态的。而CSS自定义属性却不一样,他是一种动态的,你在客户端运行时就可以做出相应的改变。比如你想在不同的视窗中运用不同的padding值:

// SCSS
$gutter: 1em;

@media (min-width: 30em) {
    $gutter: 2em; /* 无效的 */
}

.container {
    padding: $gutter;
}

使用过SCSS的同学,或许一眼就看出来了,在@media中的$gutter并没有生效,最后编译出来的CSS代码中,你只能找到:

.container {
    paddidng: 1em;
}

你的视窗(浏览器宽度)变化,它始终都是1em。这就是所谓的 静态 (处理器无法在客户端动态编译)。当然,如果你添加了JavaScript来处理是另外一回事,这一块不做深入的探讨。接着来看CSS自定义属性,所起的作用是完全不一样的:

:root {
    --gutter: 1em;
}

@media (min-width: 30em) {
    :root {
        --gutter: 2em;
    }
}

.container {
    padding: var(--gutter)
}

如果你自己亲手撸了上面的代码,当你改变浏览器宽度的时候,你会发现.containerpadding会发生相应的变化。也就是说,CSS自定义属性是动态的,在客户端中可以动态响应。除此之外,CSS自定义属性还可以通过JavaScript来动态改变其值,比如:

:root {
    --mouse-x: 0px;
    --mouse-y: 0px;
}

.move {
    left: var(--mouse-x);
    top: var(--mouse-y);
}

let root = document.documentElement;

root.addEventListener('mouse', e => {
    root.style.setProperty('--mouse-x', e.clientX + 'px');
    root.style.setProperty('--mouse-y', e.clientY + 'px');
})

这样的话,你能做的事情就更多了。

级联 和 继承

级联和继承在CSS中一个很重要的概念。在CSS自定义属性中处理起来也相对容易一些,为了更好的有一个对比性,我们来看两个简单的示例。

// SCSS . 级联
$font-size: 1em;

.user-setting-larger-text {
    $font-size: 1.5em; // 无效
}

body {
    font-size: $font-size
}

/ * CSS 自定义属性 * /

:root {
    --font-size: 1em;
}

.user-setting-large-text {
    --font-size: 1.5em; /* 有效的 */
}

body {
    font-size: var(--font-size); 
}

上面的示例中,SCSS编译出来的CSS代码,bodyfont-size始终都是1em,哪怕是用户显式设置了.user-setting-large-text也是如此。但在CSS自定义属性中却不一样,默认bodyfont-size值是1em,一旦.user-setting-large-text生效(比如说显式在body中添加了这个类名或JavaScript给body添加了这个类名),那么bodyfont-size值就变成了1.5em

再来看一个继承的示例。警告框的UI在Web页面中也是常见的一种UI效果,很多时候我们希望某些元素的UI能继承父元素的值(或在其基础上做相应的计算),比如下面这样的一个示例:

// SCSS . 继承
$alert-color: red;
$alert-info-color: green;

.alert {
    background-color: $alert-color;

    &.info {
        background-color: $alert-info-color;
    }

    button {
        border-color: darken(background-color, 25%); // 不生效
    }
}

/ * CSS 自定义属性 * 

.alert {
    --background-color: red;
}

.alert.info {
    --background-color: green;
}

.alert button {
    border-color: color-mod(var(--background-color), darken(25%)); / *生效* /
}

在SCSS中那段darken(background-color, 25%)是无效的,因为它无法继承父元素的任何CSS属性,但在CSS自定义属性中却可以。有关于这个示例,@E0 大大针对换肤的一个答案中做过详细的介绍

**color-mod()** 函数是CSS Color Module Level 4 中的一部分,到目前还没有浏览器实现。有关于这方面更详细的介绍,可以阅读《使用color-mod()函数修改颜色》和《CSS中的函数》。另外 @Claudia Romano 的博文《How to combine SASS color functions and CSS Variables》中介绍了如何通过Sass和CSS自定义属性的结合来实现类似color-mod()函数中部分功能。

全局 vs. 局部

这里所说的是全局作用域和局部作用域,其实不管是CSS的处理器,还是CSS的处理器,甚至到现在的CSS自定义属性中,都存有全局和局部的一说法。只不过使用的场景会有所不同,前几天@PPK在他的博文《Scope in CSS》中就有做过方面的讨论。这里我们来看看,CSS自定义属性中的全局和局部的一个对比,还是拿小示例来说吧:

<!-- HTML -->
<div> 我是什么颜色?</div>
<p> 那我是什么颜色? </p>
<div class="alert">
    我又是啥色?
    <p>那我呢?</p>
</div>

/ *CSS* /
:root {
    --color: red;
}
div {
    --color: yellow;
}
.alert {
    --color: blue;
}
* {
    color: var(--color);
}

先猜猜上面的每个元素对应的文本颜色是什么颜色?

从示例中,可以看出来,分别在:rootdiv元素和.alert中声明了一个相同的自定义属性--color,不同的是值,分别为redyellowblue。事实上他们影响的面是有所不同的:

  • :root表示是在根元素上声明的,--color: red,将会影响所有的元素
  • div是一个标签元素选择器,其声明的--color: yellow将会影响所有div元素以及他的后代元素
  • .alert是一个类选择器,其声明的--color: blue会影响类名为.alert的元素以及其后代码元素

所以在第一个div的文本颜色是一个yellow(它覆盖了:rootred颜色);第一p的文本颜色是red(运用了:rootred色),第二个div.alert以及它的子元素p的文本颜色是blue(运用了.alert中的blue)。

是不是和你预想的结果一样呢?

条件判断

众所周知,在CSS中到目前还没有条件判断相关的特性(或许将来会有)。不过我们可以借助CSS自定义属性相关的特性配合calc()函数来实现一个类似于if...else这样的条件判断功能。假设有一个自定义属性--i,当:

  • --i的值为1,表示真(即打开)
  • --i的值为0,表示假(即关闭)

比如,我们有一个box,根据条件来判断它是否旋转。当--i:0(假,不旋转),反之--i:1(真,旋转)。那么我们就可以像下面这样来完成:

:root {
    --i: 0;
}

.box {
    // 当 --i = 0 => calc(var(--i) * 30deg) = calc(0 * 30deg) = 0deg
    // 当 --i = 1 => calc(var(--i) * 30deg) = calc(1 * 30deg) = 30deg
    transform: rotate(calc(var(--i) * 30deg))
}

.box.rotate {
    --i: 1;
}

或者:

:root {
    --i: 1;
}

.box {
    // 当 --i = 0 => calc((1 - var(--i)) * 30deg) = calc((1 - 0) * 30deg) = calc(1 * 30deg) = 30deg
    // 当 --i = 0 => calc((1 - var(--i)) * 30deg) = calc((1 - 1) * 30deg) = calc(0 * 30deg) = 0deg
    transform: rotate(calc((1 - var(--i)) * 30deg))
}

.box.rotate {
    --i: 0;
}

整个效果如下图:

css-custom-property-5.gif

是不是非常有意思。

上面演示的是01之间的切换,其实还可以非零之间的切换,非零值之间的切换相对而言要更为复杂一些,这里就不做过多的阐述,如果感兴趣的话,可以阅读 @Ana 的两篇博文:

我把上面两篇文章整合到一起,不喜欢阅读英文的同学,可以阅读《如何通过CSS自定义属性给CSS属性切换提供开关》一文。

JS in CSS (CSS Houdini)

前面提到过,使用CSS自定义属性的时候,可以通过JavaScript来操作自定义属性的值。其实还可以更强大一点,如果你对CSS Houdini熟悉的话,可以借助其特特,直接在CSS的代码中来操作CSS自定义属性,比如下面这个@Una在CodePen上写的示例:

<!-- HTML -->
<div contenteditable class="el">JS in CSS  </div>

<script>
    CSS.paintWorklet.addModule('/una/pen/xByaoB.js')
</script>

/* CSS */
.el {
    --scallopRadius: 10;
    --scallopColor: #8266ff;
    --scallopWeight: 4;
    --background-canvas: (ctx, geom) => {
        const radius = var(--scallopRadius);
        const scallopWeight = var(--scallopWeight);
        const color = `var(--scallopColor)`;
        const height = geom.height;
        const width = geom.width;

        ctx.lineWidth = scallopWeight;
        ctx.strokeStyle = color;
        
        const getSteps = (sizeVal) => {
            return Math.floor(sizeVal / (radius * 2) - 2)
        };

        const getOwnRadius = (sizeVal, otherRad) => {
            const steps = getSteps(sizeVal) + 1;
            const totalSpace = sizeVal - (radius * 2);
            const spaceTaken = steps * (radius * 2);
            let pixelsRemaining = totalSpace - spaceTaken;

            if (otherRad) {
                const radDif = otherRad - radius;
                pixelsRemaining = totalSpace - spaceTaken - radDif;
            };

            const newRadius = radius + ((pixelsRemaining / 2) / (steps + 1));
            return (newRadius);
        };

        const horizRadius = getOwnRadius(width, getOwnRadius(height));
        const vertRadius = getOwnRadius(height, getOwnRadius(width));

        for (let i = 0; i <= getSteps(width); i++) {
            ctx.beginPath();
            ctx.arc(horizRadius + horizRadius + (horizRadius * i * 2), horizRadius + (scallopWeight * 1) , horizRadius, 0, Math.PI, true);
            ctx.stroke();
        }

        for (let i = 0; i <= getSteps(width); i++) {
            ctx.beginPath();
            ctx.arc(horizRadius + horizRadius + (horizRadius * i * 2), height - horizRadius - (scallopWeight * 1), horizRadius, 0, Math.PI, false);
            ctx.stroke();
        }

        for (let i = 0; i <= getSteps(height); i++) {
            ctx.beginPath();
            ctx.arc(vertRadius + (scallopWeight * 1), vertRadius + vertRadius + (vertRadius * i * 2), vertRadius, Math.PI * 0.5,  Math.PI * 1.5, false);
            ctx.stroke();
        }

        for (let i = 0; i <= getSteps(height); i++) {
            ctx.beginPath();
            ctx.arc(width - vertRadius - (scallopWeight * 1), vertRadius + vertRadius + (vertRadius * i * 2), vertRadius, Math.PI * 0.5,  Math.PI * 1.5, true);
            ctx.stroke();
        }
    };
    background: paint(background-canvas); 
}

.el:after {
    --offset: 3px;
    content: '';
    width: 100%;
    height: 100%;
    position: absolute;
    top: var(--offset); 
    left: var(--offset);
    z-index: -1;
    filter: hue-rotate(50deg) opacity(0.4);
    background: paint(background-canvas); 
}
/* 其他CSS代码略去 */

// JavaScript
if (typeof registerPaint !== 'undefined') {
    registerPaint('background-canvas', class {
        static get inputProperties() {
            return ['--background-canvas'];
        }
        paint(ctx, geom, properties) {
            eval(properties.get('--background-canvas').toString())(ctx, geom, properties);
        }
    })
}

整个效果如下:

undefined

在线Demo可以点击这里查看

有了CSS自定义属性之后,我们能做的事情会很多,也变得更为简单。在动画效果、图表、主题切换之中都能看到CSS自定义属性的身影。比如下面这个Loading的动效:

css-tips-02169.gif

在线Demo可以点击这里查看

比如@Keith Clark写的图表效果:

undefined

在线Demo可以点击这里查看

@Stephanie写的主题切换的效果:

undefined

点击这里查看Demo效果

更多的示例就不一一列出来了,在Codepne上搜索CSS custom property关键词,可以看到很多类似的Demo

Web Layout

聊完CSS自定义属性,我们来接着聊Web布局。对于Web布局而言,前端就一直在探讨这方面的最优方式。早期的table布局,接着的floatposition相关的布局,多列布局,Flexbox布局和Grid布局等。Flexbox和Grid的出现,Web布局的灵活性越来越高。

CSS-Tricks这次大胆的使用了CSS Grid布局:

undefined

这个是整个网站较为复杂的部分,这个部分就采用了CSS Grid来做的布局。

.monthly-mixup {
    display:grid;
    grid-template-columns: repeat(6,1fr);
}
.mixup-card:nth-of-type(1) {
    grid-column: 5/6;
    grid-row: 1/2;
}

上面只是CSS Grid的其中用法之一。其涉及到的知识点和相关术语还是很多的,如果你感兴趣的话,可以阅读以前整理一些笔记和教程

平时和一些同学聊CSS Grid的时候,都认哥其强大之处,但很多同学都觉得CSS Grid的调试和使用都非常的累,甚至网格线让大家感到困惑。事实上有这样的困惑存在,但我们可以借助一些工具来帮助我们更好的克服或者说使用CSS Grid。比如Firefox浏览器的网格调试器,就是一个很好的东东:

undefined

有关于怎么使用Firefox浏览器的网格调试器相关的使用介绍可以阅读《使用Firefox 网格检查器调试 CSS网格布局》一文。

上面的示例只是CSS Grid中最基础的功能,其实在CSS Grid中提供了很多强大的特性,比如:

  • fr单位,可以很好的帮助我们来计算容器可用空间
  • repeat()函数,允许我们给网格多个列指定相同的值。它也接受两个值:重复的次娄和重复的值
  • minmax()函数,能够让我们用最简单的CSS控制网格轨道的大小,其包括一个最小值和一个最大值
  • auto-fillauto-fit,配合repeat()函数使用,可以用来替代重复次数,可以根据每列的宽度灵活的改变网格的列数
  • max-contentmin-content,可以根据单元格的内容来确定列的宽度
  • grid-suto-flow可以更好的让CSS Grid布局时能自动排列

结合这些功能点,布局会变得更轻松。比如我们要实现一个响应式的布局,很多时候都会依赖于媒体查询(@media)来处理,事实上,有了CSS Grid Layout之后,这一切变得更为简单,不需要依赖任何媒体查询就可以很好的实现响应式的布局。特别是当今这个时代,要面对的终端设备只会增加不会减少,那么希望布局更容易的适配这些终端的布局,那么CSS Grid Layout将会起到很大的作用,比如下面这个示例:

点击这里查看Demo效果

在你的浏览器中打开上面这个Demo,改变你的浏览器视窗的大小,你会看到布局的变化,这些变化都是客户端自动在计算的,而且没有依赖于任何媒体查询。感兴趣的同学,不仿亲自试试。

CSS Grid 的出现,对于很多同学而言存在很多的困惑,比如前面提到的网格线的编号,甚至对于CSS Grid的理解也存在一定的误区。@Jen Simmons整了九个视频,针对CSS Grid 的九大误区做过详细的阐述:

  • 认为CSS Grid就是神,⽆无所不不能
  • 只使⽤用百分⽐比设置尺⼨寸⼤大⼩小
  • Grid实现响应式布局依旧需要断点
  • 被⽹网格线编写搞糊涂
  • 总是使⽤用12列列⽹网格
  • 忽略略⾏行行的幂值
  • CSS Grid Layout != CSS Grid System (Framework) 8 等待IE11 的消亡,在开始使⽤用
  • 犹豫不不决

有关于这方面的详细介绍,你感兴趣的话可以点击这里直接获取相关的视频链接地址

随着CSS Grid的出现,社区就出现这样的争论,Flexbox和Grid谁更好。在今年年初的时候,几位大大在Twitter上就有相关的讨论:

@chriscoyier提了一个这样的问题:

For y'all that have an understand of both CSS grid and flexbox, what's your favorite way of explaining the difference? (@chriscoyier January 25, 2019)

我摘取了 CSS Grid方面的专家(也称得上是CSS Grid Layout的推动者或之父)分别是这样回答的。先来看@rachelandrew的回答:

Flexbox is for one dimensional layout. A row OR a column. Grid is for two dimensional layout. Rows AND columns. (@rachelandrew January 25, 2019)

而 @jensimmons 是这样回答的:

Grid makes actual columns and rows. Content will line up from one to the other, as you ask it to. Flexbox doesn’t. Not only in the second dimension (which is easiest to talk about), but also in the first dimension. Flexbox isn’t for most of the things we’ve been using it for. (@jensimmons January 25, 2019)

为了保持原汁原味,我没有做任何的转译。当然,有关于这方面的讨论也可以阅读 《To Grid or to Flex?》一文。

随着Web布局的功能越来越强,我自认为未来的Web布局系统将会由Flexbox、Grid和Box Alignment几大模块组合在一起来构建。

undefined

如果再细分一下,未来的Web布局系统所包括的主要知识点将会是:

其中CSS-Tricks的布局中就用到了GridFlexbox书写模式对齐方式媒体查询TransformScroll Snapping等。

有关于这几个方面的详细介绍,这里就不做过多的阐述,如果感兴趣的话,可以在小站上获取相关的教程或查阅W3C上的功能模块。

混合模式和滤镜

CSS混合模式滤镜主要是用来处理图片的。这个有点类似于图像编辑软件一些功能:

undefined

对于imgpicture元素引入的图像,可以使用mix-blend-modefilter。如果是背景图片,可以使用background-blend-mode来处理。另外还有一个isolation属性用来配合mix-blend-modebackground-blend-mode使用。

其中mix-blend-modebackground-blend-mode不同的值得到的效果将会是不一样的,比如:

undefined

filter对应属性值的效果如下:

undefined

如果把图层混合模式和滤镜结合起来一起使用,还可以创作出一些带有艺术性的效果(以前类似的效果,只能依赖于图像处理软件来完成):

undefined

当然,他们不仅仅用于图片上,咱们还可以运用于任何的HTML元素上。

有关于CSS图层混合模式和滤镜更多的教程可以点击这里

其他(^_^)

CSS-Tricks网站运用的新特性除了上述之外,还有一些小特性,比如:

  • 渐变文本和边框
  • CSS片段模板
  • CSS计数器
  • clip-path
  • object-fit
  • 书写模式writing-mode
  • 自定义元素

简单地来过一下这些。

渐变文本

对于渐变文本的效果来说,可以说不是什么新特性了,通过background-clip: texttext-fill-color的配合就可以轻易的实现,比如下面这个示例:

undefined

上面截图省略了背景图片的代码,就上图而言,使用多背景特性,引用了多张图片。

渐变边框

针对于渐变边框,主要是依赖于渐变属性来和background-clip等属性来完成。其实除了这种方式,不可以使用border-image一起来使用。就我个人而言我更推荐使用渐变属性和背景相关属性配合一起来做。比如下面这个效果:

undefined

点击这里查看Demo效果

有关于这方面详细的使用不在这做过多的阐述,如果你感兴趣的话,可以阅读早前写的一篇博文《聊聊双11互动主动法中前端技术亮点》。

除了渐变边框,利用该方法你还可以实现一些更复杂的边框效果,比如说纹理边框。下面这个示例是@Ana Tudor在Codepen写的一个示例

undefined

点击这里查看Demo效果

CSS 片段模板

CSS片段模块(CSS Fragmentation Module Level 3)中有一个box-decoration-break属性。主要用来指定background、padding、border、border-image、box-shadow和clip在行内元素中如何使用。行内元素的盒模型是打断的,也就是内联盒子是多行的情况之下。比如下面这样的一个效果:

undefined

点击这里查看Demo效果

上面这个效果是 @Chokcoco 在他的博文《有趣的 box-decoration-break》中演示的一个效果。

box-decoration-break主要有两个值sliceclone。他们之间效果的差异,我用下图来展示,大家一眼就能看出其中的差异性:

undefined

该属性对于内联元素换行的场景写一些效果是非常有效的。具体的介绍可以阅读《初探box-decoration-break》和《CSS Tips:段落每行渐变色文本效果》。

CSS计数器

CSS计数器其实涉及到三个属性:counter-incrementcounter-resetcounter()。一般情况都是配合CSS的伪元素::before::aftercontent一起使用。可以用来计数(比如一个列表的序列号),还可以来做一些小游戏。如果配合CSS自定义属性,还可以实现动态修改生成的内容。比如下面这个示例:

undefined

点击这里查看Demo效果

clip-path

clip-path提供了一些函数功能,比如polygon()circle()ellipse()inset()可以帮助我们绘制一些基本的形状,如下图所示:

undefined

使用clip-path可以实现一些不规则图形的效果,比如你想你的用户头像的效果个性化一点,那么就可以使用polygon()函数:

.avatar { 
    clip-path: polygon(0% 5%, 100% 0%, 100% 85%, 65% 80%, 75% 100%, 40% 80%, 0% 75%); 
}

来看@Chris Coyier在Codepen上写的一个案例

undefined

有关于clip-path更多的介绍可以点击这里

object-fit

object-fit属性有点类似于background-size的功能,可以很好的帮助我们来处理imgvideo的适配效果。其还有另一个属性是object-position,该属性有点类似于background-position

object-fit一共有五个值,每个值所起的效果都不一样,比如下图所示的效果:

undefined

书写模式

书写模式writing-mode简单地来说是用来控制排版本的方式。比如下面这样的一个竖排效果:

undefined

事实上书写模式涉及以的知识点非常的多,@hj_chen 老师有一篇博文《Vertical typesetting with writing-mode revisited》,做过详细的阐述,如果你不喜欢阅读英文,可以点击这里阅读译文

自定义元素

在新版本的CSS-Tricks中,还有一个自定义元素circle-text,用来制作圆形的文本排版。比如:

undefined

有关于这部分,我并没有深入学习,如果你感兴趣,可以点击这里阅读源码

写在最后

上面所说的只是我自己阅读CSS-Tricks网站源码所获得的知识点以及CSS一些优秀的特性。当然,很多同学可能会说,这里所描述的特性,估计有很多浏览器都不支持。事实上呢?也是如此,在不同的浏览器中差异性是有所不同的。但这并不是阻碍我们去学习和探讨的原因所在。

就我个人而言,CSS虽然简单,但其也并不容易。很多东西他就像是宇宙里中未知的领域,我们需要不断的去探索和挖掘:

undefined

我们应该始终要有一颗好奇的心去探索,同时应该有颗勇敢的心去不断的尝试。只有我们整个社区一起努力,才能推动其更进步,更上一层楼。

自从Web在1989年出现到今年(2019年),刚好有30年,借此机会,我要感谢Web,因为Web带给我很多乐趣,也让我认识很多朋友。

undefined

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章