尽管名字听起来超级有趣,但魔法数字是一件坏事。 它是一个 老式的编程术语,指的是“未命名的数值常量”。 就像代码中插入的某个数字,它可能对程序正常运行至关重要,但对于不熟悉代码的人来说,很难理解它究竟是用来干什么的。 CSS 中充满了未命名的数值常量,但它们通常与属性和选择器一起出现,因此没什么神秘感。 不过,CSS 中确实存在魔法数字,而且它们依然很糟糕。
CSS 中的魔法数字指的是在某些情况下“有效”,但在这些情况发生变化时却很容易出错的数值。 它们通常都与字体 相关。 它们是由作者创建的,作者可能只在自己的浏览器中进行测试,并且是在理想条件下。 让我们来看一些例子,这样我们大家就能明白它们是什么,并希望在将来避免使用它们。
看看这个简单的选项卡集

每个选项卡都设置为 width: 100px;
。 在这个例子中,100px 是我们的“魔法数字”。 有很多事情可能会出错。 只要添加另一个文本更长的选项卡就可以证明这一点

有点尴尬,而且可能不理想。 我们可以使用 white-space: nowrap;
来防止换行,但这可能更糟

如果我们使用 min-width 而不是宽度,我们的选项卡就不会那么容易出错

或者根本不设置宽度

如果你一定要让所有选项卡的大小都相同,你可以在 overflow: hidden;
和 text-overflow: ellipsis;
上使用

在这种情况下,你可能需要一个 title
属性,这样就可以用某种方法显示整个选项卡名称。 你可能会认为可以通过内容管理系统来解决这个问题,只允许选项卡名称的字符数在一定范围内。 但是,那些为了可访问性而提高字体大小的用户怎么办? 这种固定大小可能对他们有害。
在最近的一篇文章 Line-On-Sides Headers 中,我使用了一个行高值,它是一个魔法数字。 假设你在带有精美 @font-face 字体的文本周围使用了这种技术。 假设该字体没有加载,或者用户覆盖了它,或者页面正在不支持 @font-face 的浏览器中查看。 备用字体将加载,而该备用字体的大小可能与自定义字体有很大不同。 备用字体外部的线条现在位置很尴尬,不像我们想要的那样居中。 魔法数字失败。

这个特定的例子 有点牵强,但我相信你们都见过 x-height 很疯狂的自定义字体。
假设你有许多内容不同的盒子。 你想把它们排列成网格,所以你把它们向左浮动。 有点乱

好吧,如果它们的高度都一样,那就没问题了!

也就是说,如果查看网站的用户与你具有完全相同的字体大小设置。 但是用户可以改变这一点。

现在出现一个大大的悲惨小号声

min-height 可以防止重叠的怪异现象,但这样一来,盒子的大小就不一样了,浮动问题又出现了。 我不会深入探讨解决方案,因为这已经很抽象了,但也许你可以使用盒子的滚动功能,或者使用一些 JavaScript 来调整大小,或者使用其他类型的布局。
Harry Roberts 在他的文章 Code smells in CSS 中指出了一个经典的魔法数字示例。
.site-nav > li:hover .dropdown{
position: absolute;
top: 37px;
left: 0;
}
这将用于 CSS 驱动的下拉菜单。 菜单隐藏在屏幕外,直到父列表项被悬停,然后下拉菜单移动到视图中。 它应该放置在父菜单项的底部。 对于编写这段代码的开发人员来说,在他们当前的浏览器中,该菜单项的高度为 37px。 我相信你可以想象,情况并非总是如此。 37px 是一个魔法数字。 Harry 建议使用 top: 100% 而不是 top: 37px,这意味着“从顶部到底部”,这更不容易出错。
在文章 Fighting the Space Between Inline-Block Elements 中,-4px 是一个用于边距的数字,它可以关闭这些间隙。 这绝对是一个魔法数字。 4px 恰好是许多字体在默认 16px 的 font-size
下的空间宽度。
将 font-family
更改为 Monaco 之类的字体? 坏了。 将字体大小更改为更大的或更小的任何值? 坏了。

查看 那篇文章 以了解其他修复方法。
定义混淆
因为我们要说“不要使用这些”,所以我们必须正确地定义与 CSS 相关的术语。 我过去看到过许多帖子,其中并非所有人都站在同一条战线上。 (1) (2) (3)。
以下是一些例子
-webkit-transform: translateZ(0);
魔法数字? 不。 只是我们过去为了提高性能而使用的一个小技巧。
.parent {
padding: 22px;
}
.child {
bottom: 22px;
left: 22px;
}
魔法数字? 不。 它是一个奇怪的数字,但它不是魔法。 子元素被定位到元素的左下角,不带填充。 因为这些数字匹配,所以可以理解发生了什么。 如果它们都不一样,就意味着可能存在一些与 font-size
相关的微调部分,而这将是一个魔法数字。
top: 37px;
假设这是一个魔法数字,就像上面的下拉菜单示例一样。 我们能用 Sass 解决它吗?
$topDistance: 37px;
.dropdown {
top: $topDistance;
}
它不再是一个“未命名的”数值常量,对吧,因为我们给它起了名字? 它仍然是 CSS 中的魔法数字。 它和以前一样脆弱。
letter-spacing: -.05em;
魔法数字? 不。 如果它是像素,它可能就是,因为像素值保持不变,所以它的效果会根据当前的字体大小而改变。 字体大小很大,几乎没有变化,字体大小很小,变化很大。 事实上,它是以相对单位表示的,这使得它不太脆弱。
最后总结
天哪,我讨厌博客文章中的这一部分,但有一个地方可以用来倾倒一些没有被整理好的小东西,感觉真好。
- WordPress CSS 编码标准 指出 不应该使用它们。 还有其他标准吗?
- 如果你使用的是图标字体,请记住它们是字体,所以它们会改变大小。 你不能在像素值中为它们“预留空间”,因为字体大小可以改变,但像素值不会改变。
- 我希望这篇博客文章能不断更新,以提供魔法数字失败的良好示例,所以如果你有任何经典的例子,请在下面留言。
- 如果你想将图像和文本居中。 vertical-align 的效果很好,但我发现它经常会偏离 1px 左右,所以我使用
postion: relative; top: 1px;
来进行调整。 这算是一个魔法数字吗? 不确定。
Biznarf。
很棒的文章,Chris!
最后一个 SCSS 演示中有一个小错误。 它应该是
$topDistance: 37px
而不是$topDistance = 37px
。 :)没错。 谢谢。
我认为这些也是魔法数字,我一直这么称呼它们,当你获得一个 Photoshop 配色稿,并且主列和侧边栏的宽度像这样的列只有一点点偏差时。 比如,侧边栏的宽度可能是 273px,而你的主列宽度是 667px。
当我获得这样的配色稿时,我通常会将它们四舍五入为 275px 和 670px,因为它们看起来不那么“神奇”,而且与网格更一致。
我发现真正优秀的设计师会使用 Photoshop 指南并在网格上进行操作。 这使得代码布局非常容易,没有魔法数字。
我认为这更多是网页开发人员的观点 :) 在设计中,如果数字相关且存在整体比例,任何数字都可以。 975 在设计中可能比 970 更合理,而使用 970 仅仅因为它是一个偶数,这将是一种妥协。
侧边栏的宽度为 273px 不是“魔法数字”,它只是一个布局。
有一个绝对定位的兄弟元素,它的“left: 273px”,以便它与该侧边栏的右侧对齐,现在它就是一个“魔法数字”。 只要侧边栏的大小发生改变,无论是直接改变还是通过 padding/margin/border 改变(取决于你的盒子模型),绝对定位项目的布局就无法正常工作了。
我同意。 网格将数字抽象为除法。 基本上,像素在今天没有意义,但设计师也不会考虑侧边栏中的实际像素。 他会把侧边栏看作内容的一部分(大约是内容宽度的一半,三分之一等等)。
在我看来,流体布局最好的地方之一是,数字通常不太容易成为“魔法”数字。
这些数字通常是简单的分数,很容易理解。一旦我们更多地使用 vw 单位(视窗宽度),情况会变得更加清晰。
我犯了 z-index 魔法数字的错误。从技术上讲,用名字命名我的图层并使用这些名字(使用 sass 或 less)是有道理的,但我只是尽量把它限制在几个图层内。
我最喜欢的(也是我经常使用的)是
我有时也会这样做,但我认为这不是魔法数字,而是我懒得弄清楚 z-index 优先级。它是一个始终应该位于所有内容顶部的元素,无论其他内容如何,所以只要没有人使用相同“技巧”/“懒惰编码”模式和更大的数字,就不会出现问题。
我个人使用灵活的宽度。也就是说,我仍然有一些魔法数字。例如;
可能被认为是一个魔法数字。曾经是多么好的东西,现在却成了多么糟糕的东西。我个人慢慢地将所有项目转换为使用 % 表示宽度,如果你像我一样是面向未来的开发者,你会非常欣赏
-webkit-calc
。我以前也犯过这种错误。不过,我认为这不能算作魔法数字。如果用户增大文本大小,包裹中的所有内容都应该仍然可以正常工作。如果确实导致了问题,那么问题可能出在内部元素而不是包裹上。
我现在也尽可能多地使用
%
来构建所有内容。流体布局当然更灵活,更适合响应式设计。用%
或ems
替换px
似乎是使用魔法数字的通用修复方法。所有这些都很好的观点。想出如何在 CSS 中避免使用魔法数字确实是一件麻烦事。
顺便说一下
#pedant-hat { status: on }
魔法数字实际上不是“未命名的数值常量”,这是一个错误的说法。它们是“未命名的数值字面量。” 仅此而已… :)
#pedant-hat { status: off }
嘿,Chris,我只是想知道你在什么情况下使用“ -webkit-transform: translateZ(0);”,谢谢!
谷歌上的第一个结果
看看硬件加速部分。
感谢您的信息,Will。
一篇非常优秀的帖子,Chris。正如您所知,编程中的魔法数字通常通过常量来解决。这也是熟悉 ems 的一个非常好的理由。即使您将所有字体大小都写成像素,ems 也是 Web 设计师工具箱中的一个重要工具。
我经常发现,那些依赖像素的人来自打印背景,打印背景是绝对的媒介。了解良好的编码标准可以帮助设计师不依赖于魔法数字。如果你要画画,你必须了解你的画布!
我最喜欢的魔法数字是颜色,虽然许多设计师想要选择精确的颜色,但他们应该始终在 Sass 或 Less 中对它们进行命名。#F3D210 不是一个好的命名选择!
这样好多了
我知道有些人说你应该使用 $brand-color 或者类似的东西,但我完全赞成使用命名的颜色。
通常情况下,我发现一个好的开发人员/设计师应用 DRY(不要重复自己)原则更容易进入成功的深渊!
虽然我做了同样的事情($red, $light-red),但我总是为此感到内疚。如果在某个时候决定所有红色的物品都应该变成绿色,因为红色会吓跑访问者,让他们不点击呢?你会得到
我偶尔会感到很内疚,我会将变量名更改为类似 $accent, $accent-light 之类的东西...
题外话,我喜欢将两种风格混合在一起,将 sass/less 颜色变量分组如下
我可以更改按钮边框颜色,而无需重新定义粘液绿。
无法避免的魔法数字的最佳解决方案是将它们注释起来。解释为什么这个数字出现了,如果它改变了会有什么问题等等,这样当您回到它时,您就会知道它的用途,了解其影响,并会谨慎行事
对于不同的高度框问题,您可以使用
display: inline-block
和vertical-align: top
来解决。诚然,您接下来会遇到 inline-block 元素之间存在空格的问题,但这是一种权衡!请查看此示例:http://codepen.io/WickyNilliams/pen/EFvds如果交错的外观是可以的,您也可以使用带有多列的 inline-block。这是您作品的分支:http://codepen.io/chriscoyier/pen/eFsrb
这很酷,我完全没有想到使用列!您今天刚满足了我的“学习新东西”配额 :)
奇怪的是,容器并没有完全塌缩到列的高度?
非常有趣,我也学到了新东西
text-overflow: ellipsis
感谢,Chris! ;)
用 ems 表示的字母间距非常脆弱,因为它误解了大多数如果不是所有 webkit 浏览器都只处理以像素为单位的字母间距,而其他浏览器则允许使用小数字母间距的概念。
每次看到人们这样做,我都会把它解释为 Web 开发新手犯的错误。
我已经使用基于 EM 的字母间距多年了,我还没有遇到任何问题。我已经在这个行业工作了 10 年。谁在乎它是否在某些浏览器中被舍入到最接近的像素……至少字母间距会随着文本大小而增大和缩小。
魔法数字的最大用途之一是当开发人员/设计师试图将 EM 等同于其他值时。“嗯,一个 em……哦,它只是 16px!?!”这可能是神秘数字中最糟糕的情况,因为它在表面上看起来是一种良好的编码实践,至少在表面上,因为尺寸和长度都是用每个人都喜欢的 EM 表示的。通常情况下,这将是一件好事,但当您这样做时,它与背景图像大小或类似的东西匹配时,就不是一件好事了。
我喜欢 Chris 关于关闭内联元素间隙的示例。我使用我称之为半魔法数字的东西,也就是说我知道它是在特定情况下计算出来的……但实际上没有更好的解决方案。例如,我发现 -.3em 的边距会关闭这些间隙,无论字体大小如何……只要它是衬线字体……但由于我的堆栈由衬线字体组成,并且默认为衬线字体……那么我知道我的布局将在 96% 的时间内保持……但我也知道如果我使用无衬线字体,我将需要重新计算。事实上,这就是我的 CSS 技巧……:) 我有时会将根设置为等宽字体,因为它使这种操作更容易。
我想我将“魔法数字”归结为开发人员使用常量来将苹果和橙子进行比较时的情况。
非常有趣。但正如 Kevin Attfield 所说,我认为有时 width:273px 不是魔法数字,它只是一种布局。但我对 -webkit-calc 很感兴趣,真可惜只有 Safari 6+ 和 Chrome 19+ 才正确使用它们。我最近在一个液体布局中遇到了一个问题,我真的很希望可以使用一些计算:它可以解决我所有的问题。
我实际上对所有这些 px 示例感到非常惊讶。
我们一直使用百分比和 ems 来构建网站,这使得网站更加灵活(并且更易于访问)。
我只在测量像素宽度时使用像素,例如,如果我有一个基于像素的背景图标,那么我将以 px 为单位测量元素的左填充。
DR
在我看来,魔法数字是一件好事。然而,缺少的是与之相关的文档。
由于我们倾向于通过反复试验来获得魔法数字,因此对每次尝试进行记录是有意义的。但这可能很糟糕,因为它可能导致根本没有文档。
解决方案:将您的魔法数字从主代码中移出,放到一个单独的样式表中。
CSSWizardry 刚刚发布了一篇与之相关的文章(http://csswizardry.com/2013/04/shame-css/)。魔法数字似乎是 shame.css 材料的绝佳选择。不过,这篇文章很好,和往常一样,Chris。
就像之前的一些评论者所说的那样,大多数魔法数字问题都可以通过使用 ems 百分比(或自动浏览器值)来避免。在我看来,这些应该几乎在所有地方使用……包括 max-width 媒体查询。
感谢你的帖子。我已经做了 3 年网页设计师了。我对 css 很了解,但关于魔数的这篇文章对我来说是陌生的。学到了新东西。非常感谢你的分享。希望将来能在这里学到很多东西。勾选“通过电子邮件通知我新帖子”以接收来自此处的最新帖子。
此致
保罗
我认为“魔数”是 CSS 中的另一种情况,你可以用它来处理小型网站,但当你尝试扩展时,它们会成为问题。这是构建小型营销网站和大型应用程序之间的区别之一。
显然,这里没有万能的答案。你做任何你需要做的事情来完成工作,每个情况都有不同的解决方案。就像 OOCSS 有好有坏一样。
我想补充一点,虽然 ems 和百分比是安全的选择,但在两者开始重叠时要小心,尤其是在响应式设计中。它们是灵活的值,但它们彼此之间没有关系,因此很难计算正方形、圆形以及此类元素的垂直/水平对齐方式。
虽然不是完全相关,但为什么用数值来代替 vertical-align 的 middle 值呢?因为 middle 值并不是你想要的准确值吗?
啊,对了!我经常忘记
vertical-align
可以接受长度值。很棒的文章,我曾经在很多场景中掉进了这些陷阱,但我一直在努力避免再次陷入。