我正在尝试在媒体查询中使用CSS变量,但它无法正常工作。
:root {
--mobile-breakpoint: 642px;
}
@media (max-width: var(--mobile-breakpoint)) {
}
我正在尝试在媒体查询中使用CSS变量,但它无法正常工作。
:root {
--mobile-breakpoint: 642px;
}
@media (max-width: var(--mobile-breakpoint)) {
}
根据 规范,
var()
函数可以用于元素上任何属性的值中的任何部分。但是,var()
函数不能用作属性名称、选择器或除了属性值之外的任何其他内容。 (这样做通常会产生无效的语法,或者意义与变量无关的值)
所以,你不能在媒体查询中使用它。
这很合理。因为你可以将--mobile-breakpoint
设置为例如:root
,也就是<html>
元素,并从那里继承到其他元素。但是,媒体查询不是一个元素,它不会从<html>
继承,因此无法工作。
CSS变量并非旨在实现此目的。你可以使用CSS预处理器代替。
max-width
的属性值!完全应该可以工作! - user9645max-width
属性。CSS 属性声明充当“setter”,它为元素设置一个值,而媒体查询中的 max-width
则是一个“getter”:它请求某物的当前宽度(然后将其与提供的值进行比较)。作为媒体查询的文本 max-width: 1000px
(以一些非有效伪代码的形式)就像是 viewport.getWidth() <= 1000px
,而作为属性声明,则是 element.setMaxWidth(1000px)
。 - Taederias正如Oriol所回答的, CSS变量Level 1中的var()
当前无法在媒体查询中使用。然而,最近有了一些发展将解决这个问题。一旦CSS环境变量模块Level 1被标准化和实现,我们就能够在所有现代浏览器中在媒体查询中使用env()
变量。
CSS Working Group (CSSWG) 在一个新标准中(目前处于草案阶段)对env()
进行了编码:CSS Environment Variables Module Level 1(请参见此GitHub评论和此评论以获取更多信息)。该草案明确说明了变量在媒体查询中的使用情况:
如果你阅读了规范并有疑虑,或者想要表达对媒体查询用例的支持,你可以在问题#2627、问题#3578或任何带有“css-env-1”标签的CSS GitHub问题中发表意见。 GitHub问题#2627和GitHub问题#3578专注于媒体查询中的自定义环境变量。因为环境变量不依赖于从特定元素中提取的任何值,所以它们可以在没有明显元素可供提取的地方使用,例如
@media
规则,在那里var()
函数将无效。
env()
的用户定义环境变量,并尝试使它们在媒体查询中有效。该小组在2017年9月iPhone X的正式公告之前,Apple首次提出标准用户代理属性后不久就解决了这个问题(另请参见WebKit:Timothy Horton撰写的“为iPhone X设计网站”)。其他浏览器代表随后同意它们将在许多设备上普遍适用,例如具有出血边缘的电视显示器和油墨打印。 (env()
曾经被称为constant()
,但现已弃用。您可能仍会看到引用旧名称的文章,例如Peter-Paul Koch的这篇文章。)过了几周,Mozilla的Cameron McCormack意识到这些环境变量可以在媒体查询中使用,而Google的Tab Atkins Jr.意识到用户定义的环境变量将特别有用作为全局、不可覆盖的根变量,可以在媒体查询中使用。现在,Apple的Dean“Dino”Jackson将与Atkins一起编辑第2级。
你可以订阅 w3c/csswg-drafts
GitHub issue #1693 上的更新(如果需要查看相关历史细节,请展开CSSWG Meeting Bot’s resolutions中嵌入的会议记录并搜索“MQ”,表示“媒体查询”)。
:root {
/* desktop vars */
}
@media screen and (max-width: 479px) {
:root {
/* mobile vars */
}
}
截至本文发布时,该功能在最新版本的Chrome、Firefox和Edge中完全可用。
限制之一:如果您需要将值作为变量访问 - 例如在其他地方进行计算 - 您将需要一个变量,并且需要在媒体查询和变量声明中定义该变量两次。
:root
中声明不同的--text-size
值,然后稍后像这样做:h1 { font-size: var(--text-size) }
,它将使用适当的值。 - undefined显然,不能像那样使用本机CSS变量。这是其中一种限制。
一个聪明的方法是在媒体查询中更改您的变量,以影响所有样式。我建议阅读这篇文章。
:root {
--gutter: 4px;
}
section {
margin: var(--gutter);
}
@media (min-width: 600px) {
:root {
--gutter: 16px;
}
}
@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 */
}
目前还没有对此提供支持,因此我们必须等待才能使用此功能。
实现您想要的一种方式是使用npm包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) {}
您可以使用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
的规则替换为--replace-media-2px
,以此类推。这适用于使用and
连接的媒体查询with
、min-width
、max-width
、height
、min-height
和max-height
。
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) {
...
}
您可以使用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文件中使用媒体查询,而是在body
具有mobile
类时应用所需的规则:
.my-div {
/* large screen rules */
}
.mobile .my-div {
/* mobile screen rules */
}
正如您可以阅读其他答案,目前还不可能实现。
有人提到自定义环境变量(类似于自定义 CSS 变量使用的 env()
而不是 var()
),这个原则是可行的,但仍存在两个主要问题:
@media (min-width: 399px) {
body {
container-type: inline-size;
container-name: xsmall;
}
}
@media (min-width: 1079px) {
body {
container-type: inline-size;
container-name: xsmall small;
}
}
@media (min-width: 1439px) {
body {
container-type: inline-size;
container-name: xsmall small medium;
}
}
@media (min-width: 1999px) {
body {
container-type: inline-size;
container-name: xsmall small medium desktop;
}
}
在这里,我们可以使用多个容器名称来定义元素。使用移动优先的方法,在屏幕尺寸增大时添加样式。
现在,如果我们想要对小屏幕尺寸及更大的元素进行一些更改,可以添加以下样式定义。
@container small (min-width: 0) {
... some css
}
我个人认为,这个看起来有点粗糙的原因只是容器查询需要一个谓词,所以(min-width:0)是必需的才能使其正常工作。
通过这种方法,我们可以在一个地方指定屏幕断点,并且从组件中删除所有绝对屏幕尺寸查询。
:root
中修改了--mobile-breakpoint
,那将会是一个递归故障。 - Seangle