248

我正在尝试在媒体查询中使用 CSS 变量,但它不起作用。

:root {
  --mobile-breakpoint: 642px;
}

@media (max-width: var(--mobile-breakpoint)) {

}
4

9 回答 9

209

规格

var()函数可用于代替元素上任何属性中值的任何部分。该var()函数不能用作属性名称、选择器或除属性值之外的任何其他内容。(这样做通常会产生无效的语法,或者其含义与变量无关的值。)

所以不,你不能在媒体查询中使用它。

这是有道理的。因为可以设置--mobile-breakpointeg 为:root,即<html>元素,并从那里继承到其他元素。但是媒体查询不是一个元素,它不继承自<html>,所以它不能工作。

这不是 CSS 变量试图完成的任务。您可以改用 CSS 预处理器。

于 2016-11-21T15:07:07.070 回答
109

正如Oriol 所回答的,CSS 变量级别 1var()目前不能用于媒体查询。然而,最近的发展将解决这个问题。一旦CSS 环境变量模块级别 1标准化并实现,我们将能够env()在所有现代浏览器的媒体查询中使用变量。

CSS 工作组 (CSSWG) 编纂env()了一个新标准(目前处于草案阶段):CSS 环境变量模块级别 1(有关更多信息,请参阅此 GitHub 评论此评论)。该草案将媒体查询中的变量作为一个明确的用例:

因为环境变量不依赖于从特定元素中提取的任何内容的值,所以它们可以用于没有明显元素可供提取的地方,例如在函数无效的@media规则中。var()

如果您阅读了规范并有疑虑,或者您想表达您对媒体查询用例的支持,您可以在issue #2627issue #3578任何标有“css”的 CSS GitHub 问题中这样做-env-1”</a>。

GitHub 问题 #2627GitHub 问题 #3578专门用于媒体查询中的自定义环境变量。


来自 2017-11-09 的原始答案:最近,CSS 工作组决定CSS变量级别 2 将支持用户定义的环境变量使用env(),他们将尝试使它们在媒体查询中有效。在 Apple于 2017 年 9 月正式发布 iPhone X 之前不久,Apple 首次提出标准用户代理属性后,该小组解决了这个问题(另见WebKit:Timothy Horton 的“Designing Websites for iPhone X”)。其他浏览器代表随后同意它们在许多设备上普遍有用,例如电视显示器和带有出血边缘的墨水打印。(env()以前叫constant(),但现在已被弃用。您可能仍然会看到引用旧名称的文章,例如Peter-Paul Koch 的这篇文章。)几周后,Mozilla 的 Cameron McCormack 意识到这些环境变量可以在媒体查询中使用,并且Tab Atkins, Jr.随后,Google 意识到用户定义的环境变量作为可用于媒体查询的全局、不可覆盖的根变量特别有用。现在,Apple 的院长“Dino”Jackson 将与 Atkins一起编辑 Level 2。

w3c/csswg-drafts您可以在GitHub 问题 #1693中订阅有关此问题的更新(有关特别相关的历史详细信息,请展开嵌入在CSSWG 会议机器人的决议中的会议日志并搜索“MQ”,它代表“媒体查询”)。

于 2017-11-09T22:51:40.747 回答
90

但是,您可以做的是@media 查询您的:root声明!

:root {
     /* desktop vars */
}
@media screen and (max-width: 479px) {
    :root {
        /* mobile vars */
    }
}

完全可以在 Chrome、Firefox 和 Edge 中运行,至少是本文发布时的最新生产版本。

一个限制:如果您需要将值作为变量访问——例如在其他地方的计算中使用——您将需要一个变量,并且需要在两个地方定义变量:媒体查询和变量声明。

于 2019-01-26T04:42:47.730 回答
24

显然不可能使用这样的原生 CSS 变量。这是限制之一。

使用它的一个聪明方法是更改​​媒体查询中的变量,以影响您的所有风格。我推荐这篇文章

:root {
  --gutter: 4px;
}

section {
  margin: var(--gutter);
}

@media (min-width: 600px) {
  :root {
    --gutter: 16px;
  }
}
于 2016-11-21T14:57:43.517 回答
13

实现您想要的一种方法是使用 npm package postcss-media-variables

如果你对使用 npm 包没问题,那么你可以在这里查看相同的文档:

例子

/* input */
:root {
  --min-width: 1000px;
  --smallscreen: 480px;
}
@media (min-width: var(--min-width)) {}
@media (max-width: calc(var(--min-width) - 1px)) {}

@custom-media --small-device (max-width: var(--smallscreen));
@media (--small-device) {}
于 2016-11-21T15:09:43.260 回答
7

媒体查询的第 5 级规范定义了几乎可以满足您的需求的自定义媒体查询。它允许您定义断点,类似于使用 CSS 变量的方式,然后在不同的地方使用它们。

规范中的示例:

@custom-media --narrow-window (max-width: 30em);

@media (--narrow-window) {
  /* narrow window styles */
}
@media (--narrow-window) and (script) {
  /* special styles for when script is allowed */
}

实际上仍然不支持此功能,因此我们必须等待才能使用此功能。

于 2021-08-13T22:25:22.903 回答
6

简答

您可以使用 JavaScript 更改媒体查询的值并将其设置为 css 变量的值。

// get value of css variable
getComputedStyle(document.documentElement).getPropertyValue('--mobile-breakpoint'); // '642px'

// search for media rule
var mediaRule = document.styleSheets[i].cssRules[j];

// update media rule
mediaRule.media.mediaText = '..'


长答案

我写了一个小脚本,您可以将其包含在您的页面中。它将每个媒体规则的值替换1px为 css 变量的值--replace-media-1px,规则的值2px替换为 with--replace-media-2px等等。这适用于媒体查询with, min-width, max-width, heightmin-height甚至max-height当它们使用and.

JavaScript:

function* visitCssRule(cssRule) {
    // visit imported stylesheet
    if (cssRule.type == cssRule.IMPORT_RULE)
        yield* visitStyleSheet(cssRule.styleSheet);

    // yield media rule
    if (cssRule.type == cssRule.MEDIA_RULE)
        yield cssRule;
}

function* visitStyleSheet(styleSheet) {
    try {
        // visit every rule in the stylesheet
        var cssRules = styleSheet.cssRules;
        for (var i = 0, cssRule; cssRule = cssRules[i]; i++)
            yield* visitCssRule(cssRule);
    } catch (ignored) {}
}

function* findAllMediaRules() {
    // visit all stylesheets
    var styleSheets = document.styleSheets;
    for (var i = 0, styleSheet; styleSheet = styleSheets[i]; i++)
        yield* visitStyleSheet(styleSheet);
}

// collect all media rules
const mediaRules = Array.from(findAllMediaRules());

// read replacement values
var style = getComputedStyle(document.documentElement);
var replacements = [];
for (var k = 1, value; value = style.getPropertyValue('--replace-media-' + k + 'px'); k++)
    replacements.push(value);

// update media rules
for (var i = 0, mediaRule; mediaRule = mediaRules[i]; i++) {
    for (var k = 0; k < replacements.length; k++) {
        var regex = RegExp('\\((width|min-width|max-width|height|min-height|max-height): ' + (k+1) + 'px\\)', 'g');
        var replacement = '($1: ' + replacements[k] + ')';
        mediaRule.media.mediaText = mediaRule.media.mediaText.replace(regex, replacement);
    }
}

CSS:

:root {
  --mobile-breakpoint: 642px;

  --replace-media-1px: var(--mobile-breakpoint);
  --replace-media-2px: ...;
}

@media (max-width: 1px) { /* replaced by 642px */
  ...
}

@media (max-width: 2px) {
  ...
}
于 2020-05-10T16:43:14.497 回答
2

正如您可以阅读其他答案,仍然不可能这样做。

有人提到自定义环境变量(类似于自定义css变量,env()而不是var()),原理是合理的,但仍然存在2个主要问题:

  • 浏览器支持弱
  • 到目前为止还没有办法定义它们(但可能会在将来,因为到目前为止这只是一个非官方的草案)
于 2018-12-15T14:19:38.857 回答
1

您可以使用matchMedia以编程方式构建媒体查询:

const mobile_breakpoint = "642px";
const media_query = window.matchMedia(`(max-width: ${mobile_breakpoint})`);
function toggle_mobile (e) {
  if (e.matches) {
    document.body.classList.add("mobile");
  } else {
    document.body.classList.remove("mobile");
  }
}
// call the function immediately to set the initial value:
toggle_mobile(media_query);
// watch for changes to update the value:
media_query.addEventListener("change", toggle_mobile);

然后,不要在 CSS 文件中使用媒体查询,而是在bodymobile类时应用所需的规则:

.my-div {
  /* large screen rules */
}

.mobile .my-div {
  /* mobile screen rules */
}
于 2021-08-15T04:38:37.490 回答