if()

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

if() CSS 函数允许根据条件测试的结果为属性设置不同的值。该测试可以基于样式查询媒体查询特性查询

语法

css
/* Single <if-test> */
if(style(--scheme: dark): #eeeeee;)
if(media(print): black;)
if(media(width > 700px): 0 auto;)
if(supports(color: lch(7.1% 60.23 300.16)): lch(7.1% 60.23 300.16);)

/* <if-test> with else */
if(style(--size: "2xl"): 1em; else: 0.25em;)
if(media(print): white; else: black;)
if(media(width < 700px): 0 auto; else: 20px auto)
if(
  supports(color: lch(7.1% 60.23 300.16)): lch(7.1% 60.23 300.16);
  else: #03045e;
)
if(
  supports(color: lch(75% 0 0)): 3px solid lch(75% 0 0);
  else: 3px solid silver;
)

/* Multiple <if-test>s */
if(
  style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
  style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971);
  else: none;
)

/* <if-test> within a shorthand */
3px yellow if(
  style(--color: green): dashed;
  style(--color: yellow): inset;
  else: solid;
)

参数

参数是一个以分号分隔的 <if-branch> 列表。每个 <if-branch> 是一个 <if-condition>,后面跟着一个冒号和一个 <value>

<if-branch> = <if-condition> : <value>;
<if-condition>

一个 <if-test>else 关键字。

<if-test>

一个样式查询媒体查询特性查询

else

一个关键字,代表一个总是评估为 true 的 <if-condition>

<value>

一个属性值。

返回值

一个值或保证无效值

描述

CSS if() 函数为 CSS 属性值提供了条件逻辑,其工作方式类似于 JavaScript 的 if...else 语句。

if() 函数可以在任何属性的值中使用,并且可以包含零个或多个以分号分隔的 <if-condition>。每个 <if-condition> 要么是一个 <if-test> : <value> 对,要么是一个 else : <value> 对。最后一个 <if-condition> 后面的分号是可选的。

返回值按如下方式计算

  1. <if-condition> 表达式按照它们在函数中出现的顺序进行评估。
  2. 第一个评估为 true<if-condition> 将返回其关联的 <value>
  3. 如果没有 <if-condition> 评估为 true,则函数返回一个 <guaranteed-invalid>。如果在具有回退值的值语句中使用 if() 函数(例如自定义属性anchor() 函数),这会表现为无效或 false

例如

css
div {
  background-image: if(
    style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
    style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971);
    else: none;
  );
}

在这个例子中,我们根据 --scheme 自定义属性是否设置为 icefire,为 <div> 元素的 background-image 设置不同的 linear-gradient()。如果 --scheme 不存在,或者存在但设置为任何其他值,则 else 值生效,background-image 属性将被设置为 none

备注: 每个条件必须用冒号与其关联的值分隔开,并且每个 <if-condition> : <value> 对必须用分号分隔开。最后一个 <if-condition> : <value> 对的分号是可选的。

警告: if 和左括号(()之间不能有空格。否则,整个声明将无效。

如果单个 <if-condition><value> 无效,它不会使整个 if() 函数无效;相反,解析器会继续处理下一个 <if-condition> : <value> 对。如果没有一个 <if-condition><value> 是有效的,函数将返回保证无效值

else : <value> 对的频率和位置

你可以在一个 if() 函数中包含多个 else : <value> 对,位置不限。然而,在大多数情况下,在分号分隔列表的末尾使用单个 else : <value> 对来提供默认值,如果没有任何一个 <if-test> 评估为 true,则始终返回该默认值。

如果你在任何 <if-test> : <value> 对之前包含一个 else : <value> 对,其后的条件将不会被评估,因为 else 总是评估为 true。因此,下面的 if() 总是返回 none,而两个 <if-test> : <value> 对永远不会被评估。

css
div {
  background-image: if(
    else: none;
    style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
    style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971)
  );
}

调试一个行为不符合预期的值时,你可能希望将 else : <value> 放在值列表末尾以外的位置。在下面的例子中,我们试图确定第一个 <if-test> : <value> 对是否正常工作。如果不是,else : <value> 对会返回一个 url("debug.png") 值,以显示一张图片,指示第一个 <if-test> : <value> 对需要修复。最后两个 <if-test> : <value> 对同样永远不会被评估。

css
div {
  background-image: if(
    style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
    else: url("debug.png");
    style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971);
    else: none;
  );
}

请注意,如果 if() 函数只包含一个 else : <value> 对,或者什么都不包含,它仍然是有效的。以下属性值都是有效的。

css
background-color: if(else: yellow);
background-image: if();

这些函数并没有什么用。包含它们是为了证明其有效性。在这种情况下,background-color 值将始终设置为 yellow,而 background-image 将被设置为其初始值。你最好直接将 background-color 设置为 yellow,将 background-image 设置为 initialnone

if-test 的类型

<if-test> 接受三种查询类型之一。本节将详细介绍每一种。

样式查询

一个样式查询 <if-test> 允许你测试某个元素上是否设置了特定的属性值,并因此为另一个属性应用一个值。我们之前已经看过几个样式查询的例子;让我们再看一个例子。

css
background-image: if(
  style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8);
  else: none;
);

如果在同一个元素上 --scheme 自定义属性被设置为 ice,则返回提供的 linear-gradient() 值。如果不是,则返回 none

if() 语句中使用样式查询比 @container 查询有一个优势——你可以根据元素上是否设置了自定义属性来直接为该元素设置样式,而无需检查容器父元素上设置的样式。

你还可以在样式查询中使用 andornot 逻辑。例如:

css
background-color: if(
  style((--scheme: dark) or (--scheme: very-dark)): black;
);

background-color: if(
  style((--scheme: dark) and (--contrast: hi)): black;
);

background-color: if(
  not style(--scheme: light): black;
);

@container 查询确实有一些优势——使用 if() 样式查询一次只能设置单个属性值,而 @container 查询可以用于有条件地应用整套规则。这两种方法是互补的,各有不同的用途。

请注意,容器样式查询目前不支持常规的 CSS 属性,只支持 CSS 自定义属性。例如,以下代码将无法工作:

css
if(
  background-color: if(style(color: white): black;);
)

媒体查询

一个媒体查询 <if-test> 可以用来根据媒体查询测试是否返回 true 来设置属性的值。

你可以使用媒体类型。例如,下面的 <if-test> : <value> 对在打印媒体上返回 white,而 else 子句使得在非打印媒体上返回 #eeeeee

css
background-color: if(
  media(print): white;
  else: #eeeeee;
)

你也可以使用媒体特性——如果当前视口宽度小于 700px,以下代码返回 0 auto;否则返回 20px auto

css
margin: if(
  media(width < 700px): 0 auto;
  else: 20px auto;
)

当你需要根据媒体查询结果改变单个属性值时,这非常有用。

你还可以在媒体查询中使用 andornot 逻辑。例如:

css
border-color: if(
  media((width > 700px) and (width < 1000px)): blue;
);

border-color: if(
  media((width < 500px) or (orientation: landscape)): blue;
);

background-color: if(
  not media(width < 500px): blue;
  else: red
);

当你想根据一个媒体查询设置多个声明或规则时,需要使用常规的 @media 结构。这两种方法是互补的,各有不同的用途。

特性查询

一个特性查询 <if-test> 可以用来根据浏览器是否支持某个特定的属性值来设置属性的值。

例如,如果支持 lch() 颜色,以下代码将返回一个 lch() 颜色;否则返回一个 rgb() 颜色。

css
color: if(
  supports(color: lch(75% 0 0)): lch(75% 0 0);
  else: rgb(185 185 185);
)

选择器支持查询也同样有效。如果浏览器支持 :buffering 伪类,以下代码将返回 1em;否则返回 initial

css
margin-top: if(
  supports(selector(:buffering)): 1em;
  else: initial;
)

你还可以在特性查询中使用 andornot 逻辑。例如:

css
margin-top: if(
  supports((selector(:buffering)) and (color: blue)): 1em;
);

margin-top: if(
  supports((selector(:buffering)) or (color: not-a-color)): 1em;
);

margin-top: if(
  supports(not selector(:buffering)): 1em;
);

当你需要根据对某个特定值或独立属性的支持来改变单个属性值时,在 if() 语句中使用特性查询非常有用。当你想根据一个特性查询设置多个声明或规则时,常规的 @supports 结构更好。这两种方法是互补的,各有不同的用途。

提供回退值

if() 语句不会优雅降级;需要为不支持的浏览器提供明确的回退值。

例如,在这种情况下,我们为不支持 if() 的浏览器提供了一个静态的 padding 值。支持 if() 的浏览器会用第二个声明覆盖第一个声明,根据 --size: "2xl" 自定义属性是否设置来设置不同的内边距值。

css
padding: 1em;
padding: if(style(--size: "2xl"): 1em; else: 0.25em);

备注: 记住要包含 else 条件。在支持 if() 的浏览器中,如果没有包含 else 值且 --size 不等于 "2xl",padding 将被设置为 initial

完整值和部分值

if() 函数可以作为任何 CSS 属性的值,但它也可以用来决定属性值的一部分。例如,下面的代码根据是否支持 lch() 颜色,在 border 简写属性中设置了不同的 border-color

css
border: if(
  supports(color: lch(75% 0 0)): 3px solid lch(75% 0 0);
  else: 3px solid silver;
);

然而,我们也可以只用 if() 函数来决定 border-color 组件。

css
border: 3px solid
  if(
    supports(color: lch(75% 0 0)): lch(75% 0 0); else: silver;
  );

嵌套 if() 函数

因为 if() 函数可以替代整个属性值或单个组件,所以可以在其他 if() 函数内部嵌套 if() 函数,也可以在 calc() 等其他函数内部嵌套。

例如,在这个声明中,我们使用 if() 根据各种条件来设置 color 属性值。我们有一个外部的 if() 函数,根据 --scheme 自定义属性是否设置为 icefire 来返回一个特定的值(如果两个条件都不满足,则返回 elseblack)。

然而,这两个 <value> 本身也是 if() 函数。这些内部的 if() 函数会在用户偏好深色配色方案时(通过 prefers-color-scheme 媒体查询确定)返回一个浅色值,否则返回一个深色值。

css
color: if(
  style(--scheme: ice):
    if(
      media(prefers-color-scheme: dark): #caf0f8;
      else: #03045e;
    );
  style(--scheme: fire):
    if(
      media(prefers-color-scheme: dark): #ffc971;
      else: #621708;
    );
  else: black
);

在下一个例子中,我们将 width 属性设置为一个 calc() 函数,该函数从父元素宽度的百分比中减去 50px。这个百分比由一个 if() 函数表示,该函数测试 --scheme: wide 自定义属性是否被设置。如果是,百分比为 70%,所以外部函数解析为 calc(70% - 50px)。如果不是,百分比为 50%,所以外部函数解析为 calc(50% - 50px)

css
width: calc(if(
    style(--scheme: wide): 70%;
    else: 50%;
  ) - 50px);

正式语法

解析错误:意外的输入

示例

if() 的基本用法

在这个例子中,我们将展示三种 <if-test> 类型的基本用法。

HTML

我们的 HTML 中有一个 <section> 元素,里面有两个 <article> 元素,包含 <h2> 标题。这个 <section> 在其 style 属性中设置了一个自定义属性——--show-apple:true——我们稍后会用它来有条件地设置一个属性值。

html
<section style="--show-apple:true">
  <article><h2>First article</h2></article>
  <article><h2>Second article</h2></article>
</section>

CSS

在我们的 CSS 中,我们首先选择 <section> 元素,使用 flexbox 对其进行布局,并设置两个子 <article> 元素之间的 gap。然后,我们使用一个带有 orientation 媒体查询 <if-test>if() 函数来设置 flex-direction 属性的值,如果文档处于横向方向,则为 row,如果处于纵向方向,则为 column。这使得 article 元素在宽屏上并排布局,在窄屏上从上到下布局。

css
section {
  display: flex;
  gap: 16px;
  flex-direction: if(
    media(orientation: landscape): row;
    else: column;
  )
}

接下来,我们选择 <h2> 元素的 ::before 伪元素,将其 content 属性设置为一个苹果表情符号,但仅当 --show-apple: true 被设置时(我们之前在 HTML 中用内联 <style> 实现了这一点)。我们通过一个带有样式查询 <if-test>if() 函数来实现这一点。

css
h2::before {
  content: if(
    style(--show-apple: true): "🍎 ";
  );
}

最后,我们选择 <h2> 元素本身。我们使用一个特性查询 <if-test> 来测试浏览器是否支持 lch() 颜色,如果支持,则将 color 属性设置为一个 lch() 颜色,否则设置为一个等效的十六进制颜色。

css
h2 {
  color: if(
    supports(color: lch(29.57% 43.25 344.44)): lch(29.57% 43.25 344.44);
    else: #792359;
  )
}

结果

注意样式是如何应用的。通过使用浏览器的开发者工具修改渲染的演示,来测试前两个 if() 查询的条件样式。

  • 移除 <section> 元素的 style 属性,注意苹果表情符号是如何不再被渲染的。
  • 将嵌入的 <iframe>height 属性改为 1200px。这将使方向从横向变为纵向。注意布局因此发生的变化。

if() 控制配色方案

这个演示展示了如何用 CSS if() 函数玩出真正的乐趣。其中,我们使用 if() 函数有条件地设置一些自定义属性的值,从而使我们能够控制整个配色方案!

HTML

我们的 HTML 包含一个 <article> 元素,里面有一些内容——一个顶级标题、几个 <p> 元素和一个 <aside>。我们还包含了一个 <form>,其中有一个 <select> 下拉菜单,用于选择配色方案。

html
<article>
  <h1>Main heading</h1>
  <p>
    Lorem ipsum dolor sit amet consectetur adipiscing elit.
    Quisque faucibus ex sapien vitae pellentesque sem placerat.
    In id cursus mi pretium tellus duis convallis.
  </p>
  <aside>
    <h2>An aside</h2>
    <p>
      Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus
      fringilla lacus nec metus bibendum egestas.
    </p>
  </aside>
  <p>
    Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut
    hendrerit semper vel class aptent taciti sociosqu. Ad litora
    torquent per conubia nostra inceptos himenaeos.
  </p>
</article>
<form>
  <label for="scheme">Choose color scheme:</label>
  <select id="scheme">
    <option value="">Default</option>
    <option value="ice">Ice</option>
    <option value="fire">Fire</option>
  </select>
</form>

JavaScript

我们的 JavaScript 为 <select> 元素添加了一个 change 事件监听器。当选择了新值时,我们的脚本会将 <article> 元素的 class 属性设置为该值。

js
const articleElem = document.querySelector("article");
const selectElem = document.querySelector("select");

selectElem.addEventListener("change", () => {
  articleElem.className = selectElem.value;
});

CSS

在我们的 CSS 中,我们给 <body> 元素设置了一个 700pxmax-width,并使用 automargin 值使其居中。然而,我们使用一个带有媒体查询 <if-test>if() 函数,在 margin 简写属性中设置 margin-top 组件,如果视口宽度小于 700px,则为 0,如果更宽,则为 20px。这意味着在宽屏上,我们在内容顶部会有一点外边距,但在窄屏上这会被移除,因为它看起来有点奇怪。

css
body {
  max-width: 700px;
  margin: if(
    media(width < 700px): 0;
    else: 20px;
  ) auto 0;
}

然后,我们将 --scheme 自定义属性设置为与 <article> 元素的 class 名称匹配。当在我们的 <select> 元素中选择新值时,我们的 JavaScript 会设置该 class。你将在下一个 CSS 块中看到该自定义元素值的重要性。

css
.ice {
  --scheme: ice;
}

.fire {
  --scheme: fire;
}

当我们将 CSS if() 函数与自定义属性结合使用时,我们可以看到它们的真正威力。在这里,我们使用 if() 函数,根据 --scheme 自定义属性的值,将我们的 --color1--color2 自定义属性设置为不同的颜色值。然后,我们在 <article> 元素的 colorborderbackground-image 属性,以及我们的 <aside> 元素的 colorbackground-color 属性中使用 --color1--color2 的值。

我们通过自定义属性来控制整个配色方案,通过 if() 函数设置不同的值。

css
article {
  padding: 20px;
  --color1: if(
    style(--scheme: ice): #03045e;
    style(--scheme: fire): #621708;
    else: black;
  );
  --color2: if(
    style(--scheme: ice): #caf0f8;
    style(--scheme: fire): #ffc971;
    else: white;
  );

  color: var(--color1);
  border: 3px solid var(--color1);
  background-image: linear-gradient(
    to left,
    var(--color2),
    white,
    var(--color2)
  );
}

aside {
  color: var(--color2);
  background-color: var(--color1);
  padding: 20px;
}

最后,我们在另外几个地方使用了 if() 函数。

  • 如果视口宽度大于 700px,我们将 <h1> 元素的 font-size 设置为 calc(3rem + 2vw),否则设置为 3rem。这意味着在宽屏上,字体大小会随着视口宽度的变化而动态更新,但在窄屏上保持不变。
  • 我们根据 --scheme 自定义属性的值,为 <h1> 元素的 ::before 伪类的 content 设置了一个合适的表情符号。
css
h1 {
  margin: 0;
  font-size: if(
    media(width > 700px): calc(3rem + 2vw);
    else: 3rem;
  );
}

h1::before {
  content: if(
    style(--scheme: ice): "❄️ ";
    style(--scheme: fire): "🔥 ";
    else: "";
  );
}

结果

此演示渲染如下:

尝试选择不同的配色方案值,看看对外观和感觉的影响。

规范

规范
CSS 值和单位模块 Level 5
# if-notation

浏览器兼容性

另见