vlambda博客
学习文章列表

CSS是编程语言吗?

喜欢就 关注我们吧


CSS是一门专业性很强的编程语言,它专注于网页样式系统的实现。由于其独特的用例以及声明性本质,有时很难理解。有些人甚至完全否认它是编程语言。今天,我们就通过编写一个智能、灵活的样式系统来证明这样的观点是错误的。

控制结构

较为传统的通用编程语言(如JavaScript)为我们提供了条件(if/then)、循环(forwhile)、逻辑运算(===&&等)和变量等工具。这些结构在CSS中不但命名不同,而且为了更好地适应样式化文档的特定用例,语法也是大相径庭,甚至其中一些直到几年前才在CSS中可用。

变量

变量就是其中之一。它们在CSS中被称为自定义属性(尽管我们都称为变量)。

:root {
    --color: red;
}
span {
    colorvar(--color, blue);
}

双破折号声明变量并赋值。这必须发生在作用域内,因为在选择器之外这样做会破坏CSS语法。注意:root选择器作用于全局范围。

条件

条件可以用多种方式编写,具体取决于你打算在何处使用。选择器的作用域在于其元素,而媒体查询是全局范围的,且需要各自的选择器。

属性选择器:

[data-attr='true'] {
    /* if */
}
[data-attr='false'] {
    /* elseif */
}
:not([data-attr]) {
    /* else */
}

伪类:

:checked {
    /* if */
}
:not(:checked) {
    /* else */
}

媒体查询:

:root {
    color: red; /* else */
}
@media (min-width > 600px) {
    :root {
        color: blue; /* if */
    }
}

循环

计数器既是CSS中最直接的循环形式,也是用例范围最窄的一种。你只能在content属性中使用计数器,并将其显示为文本。你可以在任何给定点调整其步长增量、起始点以及它的值,但输出始终限制为文本。

main {
    counter-reset: section;
}

section {
    counter-increment: section;
    counter-reset: section;
}

section > h2::before {
    content'Headline ' counter(section) ': ';
}

那么,如果我们想使用循环来定义重复的布局模式,应该怎么做?这种循环有点晦涩:通过Gridauto-fill 属性。

.grid {
    display: grid;
    grid-template-columnsrepeat(auto-fill, minmax(300px1fr));
}

这将会使尽可能多的元素填充网格,同时缩放以填充可用空间,但在需要时又可以分成多行。只要找到元素并将其限制为最小宽度300px和最大宽度为其自身大小的一小部分,它就会重复。直接看代码可能更清楚:

@for $i from 1 through 20 {
 .random-#{$i} {
  backgroundrgb(random(255), random(255), random(255));
 }
}

@keyframes size {
 0%, 2% {
  max-width100vw;
 }
 80%, 100% {
  max-width300px;
 }
}

body {
 animation: size 5s infinite alternate;
 animation-delay3s;
 background: black;
 font-family: sans-serif;
}

.grid {
 display: grid;
 grid-template-columnsrepeat(auto-fill, minmax(300px1fr));
}

.box {
 display: grid;
 place-items: center;
 color: white;
 font-size3rem;
 text-shadow1px 1px 2px black; 
 padding2rem;
}

CSS是编程语言吗?

最后,还有循环选择器。它可以接受参数,并且参数可以是公式。

section:nth-child(2n) {
    /* 选中第偶数个元素 */
}

section:nth-child(4n + 2) {
    /* 选中从第二个开始,第4n个元素 */
}

对于非常特殊的情况,你可以将:nth-child():not()结合使用,例如:

section:nth-child(3n):not(:nth-child(6)) {
    /* 选中第3的倍数的元素,但不是第6的倍数的元素 */
}

也可以将:nth-child()替换为:nth-of-type():nth-last-of-type()以更改最后几个示例的作用域。

逻辑运算

CSS逻辑运算是说明CSS实际上是编程语言的最佳解释之一。

猫头鹰选择器

* + * {
    margin-top1rem;
}

猫头鹰选择器选择项目后面的每个项目。应用margin-top有效地增加了项目之间的间隙,就像是grid-gap,但没有网格系统。这也意味着更具可定制性。你可以覆盖margin-top并适应任何类型的内容。想要每个项目之间有1rem的空间,而在标题之前的空间是3rem?使用猫头鹰选择器比在网格中更容易实现。

CSS是编程语言吗?

使用猫头鹰选择之后只会调整第二三行间距,而不会设置第一行的margin-top值。

条件样式

我们可以在css代码中创建开关,使用变量和calc来打开和关闭某些规则。这为我们提供了非常灵活的条件。

.box {
    padding1rem 1rem 1rem calc(1rem + var(--s) * 4rem);
    colorhsl(0, calc(var(--s, 0) * 100%), 80%);
    background-colorhsl(0, calc(var(--s, 0) * 100%), 15%);
    bordercalc(var(--s, 0) * 1px) solid hsl(0, calc(var(--s, 0) * 100%), 80%);
}

.icon {
    opacitycalc(var(--s) * 100%);
    transformscale(calc(var(--s) * 100%));
}

CSS是编程语言吗?

根据--s.box的值启用或禁用警告样式。

自动对比色

让我们采用相同的逻辑创建颜色变量,该变量取决于与背景颜色的对比度:

:root {
    --theme-hue210deg;
    --theme-sat30%;
    --theme-lit20%;
    --theme-font-threshold51%;

    --background-colorhsl(var(--theme-hue), var(--theme-sat), var(--theme-lit));

    --font-colorhsl(
        var(--theme-hue),
        var(--theme-sat),
        clamp(10%, calc(100% - (var(--theme-lit) - var(theme-font-threshold)) * 1000), 95%)
    );
}

此代码段通过反转背景的亮度值,根据HSL值和黑色或白色字体颜色计算背景颜色。仅此一项就可能导致颜色对比度低(例如60%灰色背景上的40%灰色字体几乎难以辨认),所以我将减去一个阈值(颜色从白色切换到黑色的点),乘以像1000这样非常高的值并将其限制在10%到95%之间,以最终获得有效的亮度百分比。这一切都可以通过编辑代码片段开头的四个变量来控制。

CSS是编程语言吗?

此方法还可用于编写复杂的颜色逻辑和自动主题,仅基于HSL值。

清理样式表

让我们结合当前内容来清理样式表。按视口对所有内容进行排序似乎有点乱糟糟,但按组件排序也没有变得更好。不过有了变量,则可以两全其美:

/* 定义变量 */
:root {
    --paragraph-width90ch;
    --sidebar-width30ch;
    --layout-s"header header" "sidebar sidebar" "main main" "footer footer";
    --layout-l"header header" "main sidebar" "footer footer";
    --template-s: auto auto minmax(100%1fr) auto /
        minmax(70%, var(--paragraph-width)) minmax(30%, var(--sidebar-width));
    --template-l: auto minmax(100%1fr) auto /
        minmax(70%, var(--paragraph-width)) minmax(30%, var(--sidebar-width));
    --layoutvar(--layout-s);
    --templatevar(--template-s);
    --gap-width1rem;
}

/* 根据视图窗口操作变量 */
@media (min-width: 48rem) {
    :root {
        --layoutvar(--layout-l);
        --templatevar(--template-l);
    }
}

/* 绑定到DOM中 */
body {
    display: grid;
    grid-templatevar(--template);
    grid-template-areasvar(--layout);
    grid-gapvar(--gap-width);
    justify-content: center;
    min-height100vh;
    max-widthcalc(
        var(--paragraph-width) + var(--sidebar-width) + var(--gap-width)
    );
    padding0 var(--gap-width);
}

所有全局变量都在最顶部定义并按视图窗口排序。该部分有效地解决以下问题:

  • 样式表有哪些全局范围?考虑 font-size、颜色、重复测量等。
  • 有哪些经常变化的方面?考虑容器宽度、网格布局等。
  • 视口之间的值如何变化?什么样的全局样式适用于什么样的视口?

以下是按组件排序的规则定义。这里不再需要媒体查询,因为它们已经在顶部定义并放入变量中。此时,我们可以不间断地编写样式表。

读取哈希参数

伪类的一个特例是:target选择器,它可以读取URL的哈希片段。下面是使用此机制来模拟类似SPA体验的演示:

CSS是编程语言吗?

注意,上面有一些严重的可访问性影响,并且需要一些JavaScript机制才能真正无障碍。所以不要在现场环境中这样做。

在JavaScript中设置变量

操作CSS变量现在已经成为一个非常强大的工具。也可以用于JavaScript中:

// set --s on :root
document.documentElement.style.setProperty('--s', e.target.value);

// set --s scoped to #myID
const el = document.querySelector('#myID');
el.style.setProperty('--s', e.target.value);

// read variables from an alement
const switch = getComputedStyle(el).getPropertyValue('--s');

总结

CSS非常擅于定义智能和响应式布局系统。与其他语言相比,它的控制结构和算法可能有点奇怪,但就是这样的CSS完成任务全不在话下。

感谢大家的阅读。

每日分享前端插件干货,欢迎关注!

点赞和在看就是最大的支持❤️