用 CSS 编写逻辑 控制结构 逻辑门技术 总结

2025-05-24

用 CSS 编写逻辑

控制结构

逻辑门

技术

总结

CSS 是一种高度专业化的编程语言,专注于样式系统。由于其独特的用例及其声明式的特性,它有时难以理解。有些人甚至完全否认它是一门编程语言。让我们通过编写一个智能、灵活的样式系统来证明他们的观点是错误的。

控制结构

更传统的通用语言(例如 JavaScript)为我们提供了诸如条件(if/then)、循环(forwhile)、逻辑门(===&&等)和变量之类的工具。这些结构在 CSS 中的名称不同,它们的语法也大相径庭,以便更好地适应文档样式的特定用例,而且其中一些功能直到几年前才在 CSS 中可用。

变量

变量是最简单的。在 CSS 中,它们被称为自定义属性(尽管每个人都这么叫它们,甚至在他们自己的语法中也是如此)。

:root {
    --color: red;
}
span {
    color: var(--color, blue);
}
Enter fullscreen mode Exit fullscreen mode

双破折号声明一个变量并赋值。这必须在作用域内进行,因为在选择器之外这样做会破坏 CSS 语法。请注意:root,选择器的作用域是全局的。

状况

条件语句的编写方式多种多样,具体取决于具体使用场景。选择器的作用域限定于其元素,而媒体查询的作用域是全局的,因此需要使用各自的选择器。

属性选择器:

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

伪类:

:checked {
    /* if */
}
:not(:checked) {
    /* else */
}
Enter fullscreen mode Exit fullscreen mode

媒体查询:

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

循环

计数器是 CSS 中最直接的循环形式,也是使用场景最狭窄的循环形式。您只能在content属性中使用计数器,并将其显示为文本。您可以调整其增量、起始点以及任意给定点的值,但输出始终仅限于文本。

main {
    counter-reset: section;
}

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

section > h2::before {
    content: 'Headline ' counter(section) ': ';
}
Enter fullscreen mode Exit fullscreen mode

但是如果你想用循环来定义一个重复的布局模式怎么办?这种循环有点晦涩难懂:它是 Grid 的auto-fill属性。

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
Enter fullscreen mode Exit fullscreen mode

这会用尽可能多的元素填充网格,同时缩放它们以填满可用空间,并在需要时将它们分成多行。它会重复此操作,直到找到合适的元素,并将这些元素的最小宽度限制为 300px,最大宽度限制为其自身大小的一小部分。这看起来可能比解释起来更容易:

最后,还有循环选择器。它们接受一个参数,该参数可以是一个公式,用于非常精确地选择。

section:nth-child(2n) {
    /* selects every even element */
}

section:nth-child(4n + 2) {
    /* selects every fourth, starting from the 2nd */
}
Enter fullscreen mode Exit fullscreen mode

对于真正特殊的边缘情况,您可以结合:nth-child()使用:not(),例如:

section:nth-child(3n):not(:nth-child(6)) {
    /* selects every 3rd element, but not the 6th */
}
Enter fullscreen mode Exit fullscreen mode

您可以:nth-child():nth-of-type():nth-last-of-type()来替换以改变最后几个示例的范围。

逻辑门

Ana Tudor 写了一篇关于CSS 逻辑门的文章。这篇文章探讨了如何将变量与 组合calc。之后,她又运用 实现了 3D 建模和动画效果。这篇文章读起来就像神秘的魔法,随着文章的深入,越来越令人着迷,总的来说,它很好地解释了为什么 CSS 是一种编程语言。

技术

猫头鹰选择器

* + * {
    margin-top: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

Owl 选择器会选中一个项目之后的所有项目。应用margin-top可以有效地在项目之间添加间隙,就像grid-gap一样,但没有网格系统。这也意味着它更加可定制。您可以覆盖您的设置margin-top并适应任何类型的内容。想要1rem在每个项目之间,但在标题之前留出 的间距3rem?使用 Owl 选择器比使用网格更容易实现。

Kevin Pennekamp 有一篇关于它的深入文章,甚至用伪代码解释了它的算法。

条件样式

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

.box {
    padding: 1rem 1rem 1rem calc(1rem + var(--s) * 4rem);
    color: hsl(0, calc(var(--s, 0) * 100%), 80%);
    background-color: hsl(0, calc(var(--s, 0) * 100%), 15%);
    border: calc(var(--s, 0) * 1px) solid hsl(0, calc(var(--s, 0) * 100%), 80%);
}

.icon {
    opacity: calc(var(--s) * 100%);
    transform: scale(calc(var(--s) * 100%));
}
Enter fullscreen mode Exit fullscreen mode

根据的值--s.box它将启用或禁用其警报样式。

自动对比色

让我们进一步采用相同的逻辑,并创建一个依赖于与背景颜色对比度的颜色变量:

:root {
    --theme-hue: 210deg;
    --theme-sat: 30%;
    --theme-lit: 20%;
    --theme-font-threshold: 51%;

    --background-color: hsl(var(--theme-hue), var(--theme-sat), var(--theme-lit));

    --font-color: hsl(
        var(--theme-hue),
        var(--theme-sat),
        clamp(10%, calc(100% - (var(--theme-lit) - var(theme-font-threshold)) * 1000), 95%)
    );
}
Enter fullscreen mode Exit fullscreen mode

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

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

清理样式表

让我们整合一下目前为止的内容来清理样式表。按视口排序看起来有点杂乱,但按组件排序也同样感觉不错。使用变量,我们可以兼顾两者的优点:

/* define variales */
:root {
    --paragraph-width: 90ch;
    --sidebar-width: 30ch;
    --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));
    --layout: var(--layout-s);
    --template: var(--template-s);
    --gap-width: 1rem;
}

/* manipulate variables by viewport */
@media (min-width: 48rem) {
    :root {
        --layout: var(--layout-l);
        --template: var(--template-l);
    }
}

/* bind to DOM */
body {
    display: grid;
    grid-template: var(--template);
    grid-template-areas: var(--layout);
    grid-gap: var(--gap-width);
    justify-content: center;
    min-height: 100vh;
    max-width: calc(
        var(--paragraph-width) + var(--sidebar-width) + var(--gap-width)
    );
    padding: 0 var(--gap-width);
}
Enter fullscreen mode Exit fullscreen mode

所有全局变量都定义在最顶部,并按视口排序。该部分实际上成为了“行为定义”,从而解决了以下问题:

  • 样式表的全局方面有哪些?我正在考虑诸如font-size颜色、重复测量等等。
  • 我们有哪些经常变化的方面?我想到的是容器宽度、网格布局等等。
  • 不同视口之间的值应该如何变化?哪些全局样式适用于哪个视口?

以下是按组件排序的规则定义。这里不再需要媒体查询,因为它们已经在顶部定义并放入变量中。现在我们可以直接在样式表中进行代码编写,无需任何中断。

读取哈希参数

伪类的一个特例是:target选择器,它可以读取 URL 的哈希值。这里有一个使用这种机制来模拟类似 SPA 体验的演示:

我已经写了一篇关于此的文章。请注意,这会对可访问性产生严重影响,并且需要一些 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');
Enter fullscreen mode Exit fullscreen mode

上面的codepen 示例就是这样工作的。

总结

CSS 非常擅长定义智能且响应式的布局系统。与其他语言相比,它的控制结构和算法可能有些奇怪,但它们确实存在,并且能够胜任这项任务。让我们停止仅仅描述一些样式,开始让它们发挥作用吧。

文章来源:https://dev.to/iamschulz/writing-logic-in-css-3ig0
PREV
React 搜索栏 让我们在 React 中构建一个搜索栏!
NEXT
使用 Typescript 编写游戏