自定义属性最常被认为是 CSS 中的 **变量**。
.card {
--spacing: 1.2rem;
padding: var(--spacing);
margin-bottom: var(--spacing);
}
上面,--spacing
是自定义属性,值为 1.2rem
,而 var(--spacing)
是正在使用的变量。
也许使用自定义属性最宝贵的理由是:避免重复自己 (DRY 代码)。在上面的示例中,我可以在 一个 地方更改 **值** 1.2rem
,并让它影响 两个 地方。这将编程语言的功能引入 CSS。
关于自定义属性,还有很多东西需要了解,所以让我们深入了解一下。
为什么要关心 CSS 自定义属性?
- 它们有助于使您的 CSS 代码更加简洁。这就是“不要重复自己”。自定义属性可以使代码更易于维护,因为您可以更新一个值,并在多个地方反映它。不过要小心,过度抽象可能会产生相反的效果,使代码难以理解。
- 它们对于创建网站上的 **颜色主题** 特别有用。
- 它们在 CSS 中打开了有趣的功能。部分原因是它们具有级联性。
- 它们可以在 JavaScript 中更新的事实,打开了更多有趣的功能。
自定义属性命名
自定义属性 必须 在选择器内,并以两个连字符 (--
) 开头
/* Nope, not within a selector */
--foo: 1;
body {
/* No, 0 or 1 dash won't work */
foo: 1;
-foo: 1;
/* Yep! */
--foo: 1;
/* OK, but they're different properties */
--FOO: 1;
--Foo: 1;
/* Totally fine */
--mainColor: red;
--main-color: red;
/* Special characters are a no */
--color@home: red;
--black&blue: black;
--black^2: black;
}
最好使用字母、数字和连字符,并确保自定义属性在有效的选择器内定义。
属性作为属性
您可以使用另一个自定义属性设置自定义属性的值
html {
--red: #a24e34;
--green: #01f3e6;
--yellow: #f0e765;
--error: var(--red);
--errorBorder: 1px dashed var(--red);
--ok: var(--green);
--warning: var(--yellow);
}
有些人喜欢这样做,因为它允许自定义属性的名称具有描述性,然后在另一个具有更功能性名称的属性中使用,再次有助于保持 DRY。它甚至可以帮助提高功能性名称的可读性和易理解性。
使用其他自定义属性的自定义属性有一个大问题,您应该注意。
自定义属性的有效值
在接受的值方面,自定义属性令人惊讶地宽容。
以下是一些您期望能正常工作并确实能正常工作的基本示例。
body {
--brand-color: #990000;
--transparent-black: rgba(0, 0, 0, 0.5);
--spacing: 0.66rem;
--max-reading-length: 70ch;
--brandAngle: 22deg;
--visibility: hidden;
--my-name: "Chris Coyier";
}
看到了吗?它们可以是十六进制值、颜色函数、各种单位,甚至文本字符串。
但是,自定义属性不必像这样成为完整的值。让我们看看如何将有效的 CSS 值分解成部分,然后将其放入自定义属性中是多么有用。
分解值
您可以使用自定义属性来分解多部分值。
假设您正在使用颜色函数,例如 rgba()
。其中的每个颜色通道值都可以是独立的自定义属性。这开辟了许多可能性,例如更改特定用例的 alpha 值,或者创建颜色主题。
拆分颜色
例如,以 HSL 颜色为例。我们可以将其分解成各个部分,然后可以轻松调整我们想要调整的部分。也许我们正在处理按钮的背景颜色。我们可以更新其 HSL 结构的特定部分,以响应按钮的悬停、聚焦或禁用状态,而无需在任何这些状态上声明 background
。
button {
--h: 100;
--s: 50%;
--l: 50%;
--a: 1;
background: hsl(var(--h) var(--s) var(--l) / var(--a));
}
button:hover { /* Change the lightness on hover */
--l: 75%;
}
button:focus { /* Change the saturation on focus */
--s: 75%;
}
button[disabled] { /* Make look disabled */
--s: 0%;
--a: 0.5;
}
通过将这样的值分解,我们可以 控制它们的部分,这在以前是无法做到的。看看我们不需要声明所有的 HSL 参数来为按钮的悬停、焦点和禁用状态设置样式。我们只需要在需要时覆盖特定的 HSL 值。非常酷的东西!
阴影
box-shadow
没有一个简写属性来单独控制阴影的扩展。但是我们可以拆分 box-shadow
的扩展值,并将其作为自定义属性进行控制(演示)。
button {
--spread: 5px;
box-shadow: 0 0 20px var(--spread) black;
}
button:hover {
--spread: 10px;
}
渐变
没有像 background-gradient-angle
(或类似的)这样的渐变简写。有了自定义属性,我们可以像存在这样的简写一样,只改变那部分。
body {
--angle: 180deg;
background: linear-gradient(var(--angle), red, blue);
}
body.sideways {
--angle: 90deg;
}
逗号分隔值(如背景)
任何支持多个逗号分隔值的属性都可能是一个好的候选者,用于拆分值,因为没有办法只定位逗号分隔列表中的一个值并单独更改它。
/* Lots of backgrounds! */
background-image:
url(./img/angles-top-left.svg),
url(./img/angles-top-right.svg),
url(./img/angles-bottom-right.svg),
url(./img/angles-bottom-left.svg),
url(./img/bonus-background.svg);
假设你想在媒体查询中 删除多个背景中的一个。你可以使用自定义属性来实现,使交换或覆盖背景成为一项简单的任务。
body {
--bg1: url(./img/angles-top-left.svg);
--bg2: url(./img/angles-top-right.svg);
--bg3: url(./img/angles-bottom-right.svg);
--bg4: url(./img/angles-bottom-left.svg);
--bg5: url(./img/bonus-background.svg);
background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4);
}
@media (min-width: 1500px) {
body {
background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4), var(--bg5);
}
}
网格
我们正在进行中,所以我们可以再举几个例子。比如,我们可以提取 grid-template-columns
属性的值,并将它们抽象为自定义属性,以创建一个超级灵活的网格系统。
.grid {
display: grid;
--edge: 10px;
grid-template-columns: var(--edge) 1fr var(--edge);
}
@media (min-width: 1000px) {
.grid {
--edge: 15%;
}
}
变换
CSS 很快就会得到 单个变换,但我们可以使用自定义属性更快地实现它。我们的想法是在一开始就应用元素可能获得的所有变换,然后根据需要单独控制它们。
button {
transform: var(--scale, scale(1)) var(--translate, translate(0));
}
button:active {
--translate: translate(0, 2px);
}
button:hover {
--scale: scale(0.9);
}
单位类型的串联
有时,将值的不同部分组合起来的效果并不像你希望的那样。例如,你不能通过将 24
和 px
拼接在一起来创建 24px
。但是可以通过将原始数字乘以带有单位的数字值来实现。
body {
--value: 24;
--unit: px;
/* Nope */
font-size: var(--value) + var(--unit);
/* Yep */
font-size: calc(var(--value) * 1px);
/* Yep */
--pixel_converter: 1px;
font-size: calc(var(--value) * var(--pixel_converter));
}
使用自定义属性的上下文实用程序颜色类
让自定义属性(CSS 变量)更具动态性
更多 CSS 图表,使用网格和自定义属性
响应式设计和 CSS 自定义属性:定义变量和断点
使用 CSS 过渡和动画更改渐变的状态
使用 CSS 自定义属性在暗黑模式下调整可变字体粗细
使用层叠样式表
自定义属性使用层叠样式表这一事实是它们最有用之处之一。
您已经在我们涵盖的许多示例中看到了它在实际应用,但让我们重点说明一下。假设我们在一个比较“高”的位置(在 `body` 标签上)设置了一个自定义属性,然后又在某个特定类上再次设置它。我们将它用于一个特定的组件。
body {
--background: white;
}
.sidebar {
--background: gray;
}
.module {
background: var(--background);
}
然后假设我们有类似这样的实际 HTML 代码
<body> <!-- --background: white -->
<main>
<div class="module">
I will have a white background.
</div>
<main>
<aside class="sidebar"> <!-- --background: gray -->
<div class="module">
I will have a gray background.
</div>
</aside>
</body>
侧边栏中的“模块”具有灰色背景,因为自定义属性(像许多其他 CSS 属性一样)会通过 HTML 结构继承。每个模块都从其在 CSS 中定义的最近“祖先”获取 `--background` 值。
因此,我们只有一个 CSS 声明,但由于层叠样式表,它在不同的上下文中做着不同的事情。这太酷了。
这在其他方面也会发生作用
button {
--foo: Default;
}
button:hover {
--foo: I win, when hovered;
/* This is a more specific selector, so re-setting
custom properties here will override those in `button` */
}
媒体查询不会改变特异性,但它们通常出现在 CSS 文件中后面(或更低)的位置,而不是原始选择器设置值的初始位置,这也意味着自定义属性将在媒体查询中被覆盖。
body {
--size: 16px;
font-size: var(--size);
}
@media (max-width: 600px) {
body {
--size: 14px;
}
}
媒体查询 不仅用于屏幕尺寸。它们可以用于诸如辅助功能偏好之类的事情。例如,暗黑模式
body {
--bg-color: white;
--text-color: black;
background-color: var(--bg-color);
color: var(--text-color);
}
/* If the user's preferred color scheme is dark */
@media screen and (prefers-color-scheme: dark) {
body {
--bg-color: black;
--text-color: white;
}
}
:root
相关
您通常会看到自定义属性在“根”上设置。这就是它的含义
:root {
--color: red;
}
/* ...is largely the same as writing: */
html {
--color: red;
}
/* ...except :root has higher specificity, so remember that! */
没有特别令人信服的理由来那样定义自定义属性。这仅仅是将自定义属性设置到尽可能高的位置的一种方式。如果你喜欢那样,那完全没问题。我发现将它们应用于 `html` 或 `body` 选择器时,感觉更正常一些,因为这样可以设置我希望全局使用或在任何地方使用的属性。
您也没有必要在这么广的范围内设置变量。将它们直接设置在您要使用它们的位置(或在 DOM 树中非常接近的位置)可能同样有用,并且可能更易读、易懂。
.module {
--module-spacing: 1rem;
--module-border-width: 2px;
border: var(--module-border-width) solid black;
}
.module + .module {
margin-top: var(--module-spacing);
}
请注意,在模块本身设置自定义属性意味着该属性将不再从祖先继承(除非我们将值设置为 `inherit`)。与其他继承属性一样,有时需要在全局级别指定它们,而其他时候我们希望从上下文中继承它们(在组件级别)。两者都有用。自定义属性的妙处在于,我们可以定义它们,在幕后继承它们,并将其应用到完全不同的位置。我们可以掌控层叠样式表!
!important
结合使用
与 您可以在变量内或变量外使用 `!important` 修饰符。
.override-red {
/* this works */
--color: red !important;
color: var(--color);
/* this works, too */
--border: red;
border: 1px solid var(--border) !important;
}
将 `!important` 应用于 `--color` 变量,会使覆盖 `--color` 变量的值变得困难,但我们仍然可以通过更改 `color` 属性来忽略它。
在自定义属性的值中使用 `!important` 的行为非常不寻常。 Stefan Judis 对此有很好的说明,但要点是
- 最终,`!important` 会从自定义属性的值中剥离。
- 但在确定哪个值获胜时,它会用于多个位置设置。
div {
--color: red !important;
}
#id {
--color: yellow;
}
如果这两个选择器都应用于一个元素,您可能会认为 `#id` 值会获胜,因为它的特异性更高,但实际上 `red` 会获胜,因为有 `!important`,但最终会应用,但没有 `!important`。这有点难理解。
如果在自定义属性外部应用 `!important`,就像上面两个代码块中的第二个示例一样,我们的 `--border` 变量会保留较低的特异性(容易覆盖),但很难更改该值如何应用于 `border` 本身,因为整个声明都保留了 `!important`。
自定义属性回退
var()
函数是使自定义属性能够回退到其他值的原因。
这里,我们将 `scale()` 变换函数设置为自定义属性,但有一个用逗号分隔的第二个值 `1.2`。如果未设置 `--scale`,则将使用该 `1.2` 值。
.bigger {
transform: scale(var(--scale, 1.2));
}
在第一个逗号之后,任何其他逗号都是回退值的一部分。这允许我们创建包含逗号分隔值的回退。例如,我们可以让一个变量回退到一整堆字体
html {
font-family: var(--fonts, Helvetica, Arial, sans-serif);
}
我们也可以提供一系列变量回退(只要我们想要),但为了使其正常工作,我们必须嵌套它们
.bigger {
transform: scale(var(--scale, var(--second-fallback, 1.2));
}
如果 `--scale` 未定义,我们会尝试使用 `--second-fallback`。如果这也未定义,我们最终会回退到 `1.2`。
calc()
和自定义属性
使用 当我们将自定义属性与数学运算结合起来时,它们的功能更加强大!
这种事情很常见
main {
--spacing: 2rem;
}
.module {
padding: var(--spacing);
}
.module.tight {
/* divide the amount of spacing in half */
padding: calc(var(--spacing) / 2));
}
我们也可以用它来计算互补色的色调
html {
--brand-hue: 320deg;
--brand-color: hsl(var(--brand-hue), 50%, 50%);
--complement: hsl(calc(var(--brand-hue) + 180deg), 50%, 50%);
}
calc()
甚至可以与多个自定义属性一起使用
.slider {
width: calc(var(--number-of-boxes) * var(--width-of-box));
}
calc()
推迟 看到类似计算的数学运算,但没有 calc()
,可能会让人觉得奇怪
body {
/* Valid, but the math isn't actually performed just yet ... */
--font-size: var(--base-font-size) * var(--modifier);
/* ... so this isn't going to work */
font-size: var(--font-size);
}
诀窍是,只要您最终将其放入 calc()
函数中,它就可以正常工作
body {
--base-font-size: 16px;
--modifier: 2;
--font-size: var(--base-font-size) * var(--modifier);
/* The calc() is "deferred" down to here, which works */
font-size: calc(var(--font-size));
}
如果您要对变量进行大量的数学运算,并且 calc()
包装器在代码中变得令人分心或喧闹,这可能会有用。
@property
CSS 中的 `@property` “at-规则”允许您声明自定义属性的类型,以及它的初始值和它是否继承。
这有点像您正在创建一个实际的 CSS 属性,并且能够定义它的名称、语法、它与层叠样式表的交互方式以及它的初始值。
@property --x {
syntax: '<number>';
inherits: false;
initial-value: 42;
}
有效类型
length
number
percentage
length-percentage
color
image
url
integer
angle
time
resolution
transform-list
transform-function
custom-ident
(自定义标识符字符串)
这意味着浏览器知道它正在处理的是什么类型的值,而不是假设所有内容都是字符串。这意味着您可以以以前无法实现的方式进行动画。
例如,假设您有一个星形图标,您希望用 `@keyframes` 使其旋转,并用 `transform` 使其旋转。因此,您这样操作
.star {
--r: 0deg;
transform: rotate(var(--r));
animation: spin 1s linear infinite;
}
@keyframes spin {
100% {
--r: 360deg;
}
}
但这实际上无法正常工作,因为浏览器不知道 `0deg` 和 `360deg` 是有效角度值。您必须使用 `@property` 将它们定义为 `<angle>` 类型,才能使其正常工作。
@property --r {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.star {
--r: 0deg;
transform: rotate(var(--r));
animation: spin 1s linear infinite;
}
@keyframes spin {
100% {
--r: 360deg;
}
}
演示
值中的逗号
这可能有点令人困惑。也许不是这样
html {
--list: 1, 2, 3;
}
但下面,你需要仔细观察才能意识到回退值实际上是 1.2, 2
。第一个逗号分隔回退值,但其余部分都是值的一部分。
html {
transform: scale(var(--scale, 1.2, 2));
}
高级用法
Raven 技术 是一种使用数学和自定义属性来模拟容器查询的技术。准备好,这将从 0 到 100,复杂度直线上升!
演示
调整此演示的大小,查看内联块元素网格的列数从 4 变为 3,再变为 1。
以下是一些其他展示自定义属性高级用法的示例
initial
和空格技巧
想想 @media
查询,以及当 **一个** 东西改变时(例如页面宽度)你可以控制 **多个** 东西。这就是这个技巧的思路。你改变 **一个** 自定义属性,就可以控制 **多个** 东西。
这个技巧是,自定义属性的 initial
值会触发回退,而空空格值则不会。为了解释,我们定义两个全局作用域的自定义属性,ON
和 OFF
:root {
--ON: initial;
--OFF: ;
}
假设我们有一个“暗色”变体类,它设置了许多不同的属性。默认值为 --OFF
,但可以随时翻转为 --ON
.module {
--dark: var(--OFF);
}
.dark { /* could be a media query or whatever */
--dark: var(--ON);
}
现在你可以使用 --dark
来有条件地设置值,这些值只在将 --dark
翻转为 --ON
时才生效。演示
Lea Verou 写了一篇很棒的文章,涵盖了所有这些内容。
内联样式
在 HTML 中使用内联样式设置自定义属性是完全合法的。
<div style="--color: red;"></div>
就像任何内联样式一样,它将具有非常高的特异性。
这对于 HTML 可能会访问一些有用的样式信息,而这些信息放在静态 CSS 文件中太奇怪或太难时非常有用。一个很好的例子是保持元素的纵横比
<div style="--aspect-ratio: 16 / 9;"></div>
现在我可以设置一些 CSS 来创建具有该精确大小的框,无论我在哪里需要它。 有关该方法的完整说明在这里,但以下 CSS 使用类似于 ol’ padded box 的技巧应用于伪元素,它将框推到所需的大小
[style*="--aspect-ratio"] > :first-child {
width: 100%;
}
[style*="--aspect-ratio"] > img {
height: auto;
}
@supports (--custom: property) {
[style*="--aspect-ratio"] {
position: relative;
}
[style*="--aspect-ratio"]::before {
content: "";
display: block;
padding-bottom: calc(100% / (var(--aspect-ratio)));
}
[style*="--aspect-ratio"] > :first-child {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
}
但是,现在,我们在 CSS 中有一个原生 aspect-ratio
属性,因此在内联样式中设置它在未来可能更有意义。
<div style="aspect-ratio: 16 / 9;"></div>
悬停和伪类
无法通过内联样式应用:hover
样式(或其他伪类/伪元素)。也就是说,除非我们使用自定义属性来玩点小花招。假设我们想要在一些盒子中自定义悬停颜色,我们可以将此信息作为自定义属性传递。
<div style="--hover-color: red;"><div>
<div style="--hover-color: blue;"><div>
<div style="--hover-color: yellow;"><div>
然后在 CSS 中使用它,当然可以对链接的悬停状态进行样式设置。
div:hover {
background-color: var(--hover-color);
}
/* And use in other pseudos! */
div:hover::after {
content: "I am " attr(style);
border-color: var(--hover-color);
}
自定义属性和 JavaScript
JavaScript 可以设置自定义属性的值。
element.style.setProperty('--x', value);
以下是一个使用自定义属性定位的红色正方形的示例,JavaScript 使用鼠标位置更新这些自定义属性值。
通常你认为 JavaScript 将值传递给 CSS 以供使用,这可能是这里 99% 的用途,但请注意,你也可以将内容从 CSS 传递到 JavaScript。 正如我们所见,自定义属性的值可以相当宽松。这意味着你可以传递一个逻辑语句。例如
html {
--logic: if (x > 5) document.body.style.background = "blue";
}
然后在 JavaScript 中获取该值并执行它。
const x = 10;
const logic = getComputedStyle(document.documentElement).getPropertyValue(
"--logic"
);
eval(logic);
自定义属性不同于预处理器变量
假设你已经使用 Sass、Less 或 Stylus。所有这些 CSS 预处理器都提供变量,这是它们成为构建过程一部分的主要原因之一。
// Variable usage in Sass (SCSS)
$brandColor: red;
.marketing {
color: $brandColor;
}
那么,**你真的需要使用原生 CSS 自定义属性吗?** 是的,你应该使用。以下是一些简要原因。
- **原生 CSS 自定义属性比预处理器变量更强大。** 它们与 DOM 中**级联**的集成是预处理器变量永远无法做到的。
- **原生 CSS 自定义属性是动态的。** 当它们发生变化时(可能是通过 JavaScript 或媒体查询),浏览器会重新绘制需要重新绘制的部分。预处理器变量在编译时解析为一个值,并保持该值。
- **使用原生功能有利于代码的持久性。** 你不需要预处理原生 CSS。
我在文章“CSS 变量和预处理器变量有什么区别?”中更详细地介绍了这一点。
说句公道话,预处理器变量有一些自定义属性难以或不可能实现的小功能。例如,如果你想要去掉一个值的单位。 你可以用 Sass 来做,但是如果你只用 CSS 自定义属性,你会发现很难做到。
你能预处理自定义属性吗?
可以。你可以这样做,以 Sass 为例,它是一个流行的预处理器。
$brandColor: red;
body {
--brandColor: #{$brandColor};
}
这样做只是将 Sass 变量移动到自定义属性。这在某些情况下可能很有用,但并非十分必要。Sass 只是在那里生成 --brandColor: red;
,不会处理自定义属性。
如果浏览器不支持自定义属性,那就这样吧。你无法通过 CSS 语法转换来强迫浏览器执行自定义属性的功能。可能存在某种 JavaScript polyfill 来解析你的 CSS 并复制它,但我真的不建议这样做。
然而,PostCSS 自定义属性插件确实执行了 CSS 语法转换以提供帮助。它所做的是尽其所能地找出值,并输出该值以及自定义属性。例如
:root {
--brandColor: red;
}
body {
color: var(--brandColor);
}
将输出如下内容
:root {
--brandColor: red;
}
body {
color: red;
color: var(--brandColor);
}
这意味着你将获得一个值,希望它在缺乏自定义属性支持的浏览器中不会出现损坏,但它不支持你可以使用自定义属性执行的任何花哨的功能,甚至不会尝试执行。我有点怀疑这有多大用处,但我认为这是你能做的最好的事情,我喜欢它试图在较旧的浏览器或较新的浏览器中不破坏任何东西的精神。
可用性
值得注意的另一个区别是,对于 CSS 预处理器,变量仅在处理过程中可用。类似 $brandColor
在你的 HTML 或 JavaScript 中没有任何意义。但是,当你使用自定义属性时,你可以设置使用这些自定义属性的内联样式,它们将生效。或者,如果需要,你可以使用 JavaScript 来找出它们当前的值(在上下文中)。
除了预处理器变量的一些比较深奥的功能(例如一些数学可能性),自定义属性更强大且有用。
自定义属性和 Web 组件(Shadow DOM)
对 Web 组件进行样式设置的一种最常见且实用的方法(例如具有 Shadow DOM 的 <custom-component>
)是使用自定义属性作为样式挂钩。
Shadow DOM 的主要目的是它不会将样式泄漏到其中或从中泄漏出去,以一种其他方式无法实现的样式隔离方式提供样式隔离,除了 <iframe>
。样式仍然会按级联的方式传递到内部,我只是无法选择进入内部。这意味着自定义属性可以直接滑入其中。
以下是一个示例
Shadow DOM 另一个常见情况是使用 SVG 和 <use>
元素。
浏览器支持
此浏览器支持数据来自Caniuse,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
49 | 31 | 不支持 | 16 | 10 |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 127 | 10.0-10.2 |
你可以预处理以获得更深层的浏览器支持,但存在重大限制。
@supports
如果你想为浏览器是否支持自定义属性编写条件 CSS
@supports (--custom: property) {
/* Isolated CSS for browsers that DOES support custom properties, assuming it DOES support @supports */
}
@supports not (--custom: property) {
/* Isolated CSS for browsers that DON'T support custom properties, assuming it DOES support @supports */
}
相关文章
致谢
感谢 Miriam Suzanne 与我一起撰写这篇文章!
我为 IE11 制作了一个 polyfill,发现它非常方便,即使考虑到 polyfill 的影响,也可以简单地加载一个脚本。在大多数情况下,它运行良好。
https://github.com/nuxodin/ie11CustomProperties
感谢 Chris 和 Miriam 撰写了这份非常详细的指南。
我要补充一点,现在 DevTools 中有一些有用的功能可以帮助处理自定义属性。
例如,在 Edge 和 Chrome 中,你可以点击 var() 函数,直接跳转到定义该函数中使用的属性的位置。
所有 DevTools 都会在悬停时提供计算后的值作为工具提示。
很棒的总结,很多技巧!
一个小建议:在 JavaScript 示例中将“mousemove”替换为“pointermove”,以包含触摸设备。
我一直将 CSS 变量视为 HTML 和 Web 组件的“API”。易于使用,易于通过 JS 修改,甚至可以公开组件中的内容。它们在我的 Angular 项目中非常有用,在 $JOB 中也是如此。
不过,网格技巧非常有趣。
使用预处理器变量和自定义属性可以做的一件有用的事情是自动将颜色转换为一堆用于其红色/绿色/蓝色/色调/饱和度/亮度/不透明度的自定义属性。
(另外,根据我的经验,当你将 Sass 内容用在自定义属性中时,必须显式地回显它们,否则它只会将它们视为字符串)
然后,如果你愿意,可以将一些简单的颜色定义为 Sass 变量,但也可以轻松地创建变体颜色。就像你的互补色示例:
--complement: hsla(calc(var(--background-color-hue) + 180deg), var(--background-color-saturation), var(--background-color-lightness), var(--background-color-opacity));
。我在项目中有一个混合器,它接受一个颜色/属性名称映射并将其分解成这样。好文章!
我可能会添加一些关于在 :root 中使用 CSS 变量以及常见的错误,例如在那里评估变量。
例如
然后我们尝试通过调整其他变量来改变
--color
(例如:.container {--r:255;}
这是行不通的。相关 SO 问题:https://stackoverflow.com/a/52015817/8620333(类似的问题经常出现)
TL;DR:如果我们想要在不同的元素中改变变量,我们应该始终避免在 :root 中进行评估。
关于 “循环依赖” 的另一条说明也很有用,因为它也是一个常见的错误,试图执行以下操作:
--p:calc(var(--p) + 1px)
,认为我们可以增加变量,这是无效的。最后一条说明(不是很重要):即使我们应该正确命名它们,但了解它们可能有一些不常见的语法还是很好的。例如,我们可以只使用两个连字符
--:red
。当然,这不是最佳实践,但值得了解。(相关:CSS 变量可能具有奇怪的语法)好文章,但缺少了一件非常重要的事情:你不能在媒体查询中使用自定义属性。所以这行不通
这不是 bug,但可能是为了防止无限循环,例如当你想要在媒体查询中更改 –tablet 的值时。
但是,你可以使用 Sass 变量作为媒体查询断点,而且由于这仍然是一件非常普遍且有用的事情,因此 Sass 和 CSS 变量之间的比较可能应该提到这一点。
所以,这可以正常工作,因为 $tablet 在编译成 css 时被替换为 40em
感谢这个很棒的指南,你是我的救星 :)
很棒的帖子!
只有一点小错误。给出的 Sass 变量和自定义属性示例不再有效。以前是正确的,但现在应该是
$brandColor: red;
body {
--brandColor: #{$brandColor};
}
更多详细信息请参见此处 https://github.com/sass/libsass/issues/2621
不错,感谢提醒!
您好,
有一些转换器帮助我从 CSS 迁移到 LESS 再到 SCSS。它们主要提供扫描常见属性并从中创建变量的功能。
现在我想回到使用自定义属性的纯 CSS。我正在寻找一个脚本或其他解决方案来输入已编译的 CSS(或原始 SCSS/LESS 文件),它将 CSS 转换为使用自定义属性的版本。
这样 input.css
或 input.scss
将被转换为 output.css
我发现了很多执行相反操作的转换器(例如,PostCSS 用于将自定义属性转换为静态“CSS3”样式 CSS),但没有反过来。
我只找到一篇指向这个方向的文章:https://codepen.io/jakealbaugh/post/css4-variables-and-sass
这场讨论侧重于 Sass 解决方案:从 SCSS 变量创建 CSS 自定义属性。
所以现在我求助于我的最后手段,CSS tricks。有没有人知道自动或半自动转换脚本?
有点晚了,但可能仍然有用,Chatgpt 可以做到我认为你想要做的事情。
我输入了我目前正在使用的网站的一些 css,虽然只有 100 行,但它几乎完美地修改了 css,为所有条目添加了自定义属性的使用。我确实需要做一些调整,但大部分工作都自动完成了。