cssmagic / css-secrets Goto Github PK
View Code? Open in Web Editor NEW📖 《CSS 揭秘》这本书的大本营。
📖 《CSS 揭秘》这本书的大本营。
这本书的全名叫作《CSS Secrets:Better solutions to everyday web design problems》。
现向热心网友征集副标题的中文翻译。
本文是早期译版,未经校审。仅供参考。
图 1.1
“Standards are like sausages: it’s better not to see them being made” -- Anonymous W3C WG member
“标准就像香肠:最好别去看它们是怎么做出来的。”——某位匿名的 W3C 工作组成员
Contrary to popular belief, the W3C does not “make” standards. Instead, it acts as a forum for interested parties to get together and do so, in its W3C Working Groups. Of course, the W3C is not a mere observer: it sets the ground rules and it oversees the process. But it’s not (primarily) W3C staff that actually write the specifications.
与大众的理解大相径庭的是,W3C 并不 “生产” 标准。实际上,它扮演的是一个论坛的角色——W3C 以工作组的方式,把某项技术的相关各方聚集起来,最终由他们来产出标准。当然,W3C 并不只是一个观察者:它设定了整个平台的规则,监督整个进程。但这些技术规范(基本上)并不是由 W3C 的工作人员编写完成的。
CSS specifications, in particular, are written by the members of the CSS Working Group, often abbreviated as CSS WG. At the time of writing, the CSS WG includes 98 members, and its composition is as follows:
CSS 规范通常是由 CSS 工作组的成员来编写的。在编写本书时,CSS 工作组共有 98 名成员,人员结构如下:
图 1.2
The composition of the CSS WG:
- Member companies
- Invited Experts
- W3C staff members
CSS 工作组的人员结构:
- 会员公司
- 特邀专家
- W3C 工作人员
As you might notice, the vast majority of WG members (88%) come from W3C member companies. These are companies--such as browser vendors, popular websites, research institutes, general technology companies, etc.--that have a vested interest in seeing web standards flourish. Their yearly membership dues represent the majority of the W3C’s funding, enabling the Consortium to distribute its specifications freely and openly, unlike other standards bodies that have to charge for them.
你可能注意到了,工作组的绝大多数成员(88%)来自于 W3C 会员公司。这些公司(比如浏览器厂商、主流网站、研究机构、常规技术公司等)都是 Web 标准兴旺发展的直接受益者。它们每年的会费也是 W3C 的主要资金来源,使得 W3C 能够免费、开放地发布所有技术规范,而不像其他标准制定组织那样不得不采取收费政策来维持运作。
Invited Experts are web developers who have been asked to participate in the standards process, after demonstrating a continuous commitment to helping out, and a sufficient technical background to participate in the discussions.
特邀专家是指那些被邀请参与标准制定的 Web 开发者。在真正获得这样的殊荣之前,他们需要证明自己在解决难题时能够持续不断地投入、在参与讨论时能够体现出深厚的技术背景。
Last, but not least, W3C staff members are people who actually work at the Consortium and facilitate communication between the WG and the W3C.
最后,同样不可忽视的是W3C 工作人员。他们才是真正在 W3C 内工作的人,他们为工作组和 W3C 之间的交流提供便利。
A widespread misconception among web developers is that the W3C creates standards from up high that the poor browsers then have to follow, whether they like them or not. However, this couldn’t be further from the truth: browser vendors have much more of a say than the W3C in what goes into standards, as evidenced by the numbers listed before.
Web 开发者们普遍存在一个误解,以为 W3C 手握标准、号令天下,而可怜的浏览器厂商们则唯唯诺诺、莫敢不从。但这显然不是真相——对于哪些东西该进入标准,浏览器厂商们比 W3C 有多得多的发言权,因为上面列出的人员结构已经证明了这一点。
Also contrary to popular belief, standards are not created in a vacuum, behind closed doors. The CSS WG is committed to transparency and all its communications are open to the public, inviting review and participation:
同样跟大众观念截然相反的是,标准并不是闭门造车造出来的。CSS 工作组坚持透明原则,它内部所有的交流都是公开的,并邀请公众的关注和参与:
All this is part of the W3C process and has to do with decision making. However, the ones that are actually responsible for putting these decisions to writing (i.e., authoring the specifications) are the Spec Editors. Spec Editors might be W3C staff members, browser developers, interested Invited Experts, or member company employees who are doing it as a full-time job, paid by their companies to advance standards for the common good.
所有这些都是 W3C 进程的一部分,任何决定都是通过这样的方式来产生的。此外,那些真正负责把这些决定写成文字(即编撰规范)的人叫作规范编辑。规范编辑可能是 W3C 的工作人员、浏览器开发者、相关专业的特邀专家,也可能是会员公司的雇员,他们全职受薪从事此工作、为了共同利益去推进标准。
{原书注释!}
Interested in learning more? Elika Etemad (fantasai) has written a series of amazing articles on how the CSS WG operates. Very highly recommended.
想了解更多这方面的信息?Elika Etemad(网名 fantasai)写了一系列非常精彩的关于 CSS 工作组如何运作的文章,强烈推荐。
Each specification goes through multiple stages as it evolves from initial inception to maturity:
每项规范从最初启动到最终成熟,都会经过以下这些阶段:
One or two WG members have the role of being chairs. Chairs are responsible for organizing meetings, coordinating calls, timekeeping, and generally moderating the whole thing. Being chair is a very time-consuming and energy-draining role, and is frequently compared to herding cats. Of course, everyone involved in standards knows that such a comparison is moot: herding cats is actually considerably easier.
工作组中会有一到两位成员将会担任主席的角色。主席负责组织会议、协调讨论、控制时间,而且要从大局上去斡旋整件事情。担任主席是一件耗时费神的工作,经常被比作放养一大群猫。当然,所有接触过这项工作的人都知道这个比喻并不恰当——养猫比这要容易多了。
图 1.3
Chairing a W3C Working Group is frequently compared to herding cats
主持一个 W3C 工作组往往被比作养一大群猫。
CSS 1 was a very short and relatively simple specification, published in 1996 by Håkon Wium Lie and Bert Bos. It was so small that it was all included in a single HTML page, which required around 68 sheets of A4 paper to print.
CSS 1 的规范非常短,而且相对比较简单,由 Håkon Wium Lie 和 Bert Bos 发布于 1996 年。它的内容少到用一个 HTML 页面就足以呈现了,即使用 A4 纸打印出来也只需要 68 页。
CSS 2, published in 1998, was more strictly defined, and included much more power and two more spec editors: Chris Lilley and Ian Jacobs. At this point, the length of the specification had grown to 480 (!) printed pages and was already getting too big to be held in human memory in its entirety.
CSS 2 发表于 1998 年,它的定义更加严格,囊括了更多的功能,而且增加了两名编辑:Chris Lilley 和 Ian Jacobs。此时,规范的篇幅暴增到了 480 页打印纸,以致于正常人类已经无法把它完整地记下来了。
After CSS Level 2, the CSS WG realized that the language was getting too big to be contained in a single specification. Not only was it extremely unwieldy to read and edit, but it was also holding CSS back. Remember that for a specification to advance to the final stages, every single feature in it needs at least two independent implementations and exhaustive tests. This was no longer practical. Therefore, it was decided that going forward, CSS was going to be broken into multiple specifications (modules), each with its own versioning. Those that expand on features that were already present in CSS 2.1 would have a level number of 3. For example, some of these modules are:
在 CSS 2 之后,CSS 工作组意识到这门语言已经变得非常庞大了,再也无法把它塞进单个规范中了。这样不仅阅读和编辑极其困难,而且也限制了 CSS 本身的快速发展。别忘了,一项规范如果要推进到最终阶段,其中的每项特性都必需具备两个独立的实现和全面的测试。原先的那种方式已经玩不转了。因此,我们决定跨出一步,将 CSS 打散到多个不同的规范(模块)中,每个模块都可以独立更新版本。这其中,那些延续 CSS 2.1 已有特性的模块会升级到 3 这个版本号。比如以下这些模块:
However, modules that introduce entirely new concepts start from Level 1. Here are a few examples:
此外,如果某个模块是前所未有的新概念,那它的版本号将从 1 开始。比如下面这些:
Despite the popularity of the “CSS3” buzzword, there is actually no specification defining such a thing, like there was for CSS 2.1 or its predecessors. Instead, what most authors are referring to is an arbitrary set of Level 3 specs, plus some Level 1 specs. Although there is some good degree of consensus among authors on which specs are included in “CSS3,” as CSS modules evolve at different rates over the years, it will become more and more difficult to refer to things like CSS3, CSS4, and so on and be universally understood.
尽管 “CSS3” 这个名词非常流行,但它实际上并没有在任何规范中定义过——这一点跟 CSS 2.1 或更早的 CSS 1 不一样。真正的情况是,绝大多数编辑在提到这个词时,指的是一个非正式的集合,它包括 CSS 规范第三版(Level 3)再加上一些版本号还是 1 的新规范。尽管在哪些规范应该归作 “CSS3” 的问题上,编辑们基本上是有共识的,但我们也不得不面对现实——由于 CSS 的各个模块在近些年里是以不同速度在推进的,我们已经越来越难以把这些规范以 CSS3、CSS4 这样的方式来划分了,而且这样也难以被大众理解和接受。
In standards development, there is always a big catch-22: standards groups need input from developers to create specifications that address real development needs. However, developers are generally not interested in trying out things they can’t use in production. When experimental technologies get widely used in production, the WG is forced to stick with the early, experimental version of the technology, to avoid breaking several existing websites if they change it. Obviously, this completely negates the benefits of getting developers to try out early standards.
在标准的开发过程中,总是有一条大大的 “第 22 条军规” 挡在面前{1[译注:《第 22 条军规》是美国作家 Joseph Heller 的作表作,这部讽刺小说被誉为 20 世纪最伟大的文学作品之一。书中提到的 “第 22 条军规” 是一条自相矛盾的、永远不可能执行的悖论。]}:标准的工作组需要网页开发者这一端的输入,以确保各项规范可以处理真实的开发需求。但是,开发者们往往没有兴趣尝试那些在生产环境中还不能使用的东西。当实验性的技术被广泛应用到生产时,工作组就被这些技术早期的、实验性的版本捆住手脚了,因为一旦这些技术有变动,那些已经在用这些技术的网站就挂了。显然,这完全否定了让开发者尝试早期标准的好处。
Over the years, many solutions have been proposed to address this conundrum, none of them perfect. The universally despised vendor prefixes were one of them. The idea was that every browser would be able to implement experimental (or even proprietary) features with their own prefix prepended to its name. The most common prefixes are -moz-
for Firefox, -ms-
for IE, -o-
for Opera, and -webkit-
for Safari and Chrome. Developers would be able to freely experiment with these prefixed features and provide feedback to the WG, which would then incorporate this feedback into the specs and slowly perfect the design of the feature. Because the final, standardized version would have a different name (no prefix), it wouldn’t collide with the existing uses of its prefixed counterparts.
这些年来,为了解决这个难题,许多方案被提了出来,但都不够完美。饱受诟病的浏览器前缀就是其中之一。这个方案是指每个浏览器都可以实现这些实验性的(或甚至是私有的、非标准的)特性,但要在名称前面加上自己特有的前缀。最常见的前缀分别是 Firefox 所用的 -moz-
、IE 所用的 -ms-
、Opera 的 -o-
以及 Safari 和 Chrome 所用的 -webkit-
。网页开发者们可以自由地尝试这些加了前缀的特性,并把试用结果反馈给工作组,而工作组随后会将这些反馈吸收到规范之中,并且逐渐完善该项特性的设计。由于最终标准化的版本会有一个不同的名称(没有前缀),它在实际应用中就不会跟加前缀版本相冲突了。
Sounds great, right? Of course, as you probably know, the reality was quite different from the vision. When developers realized that these experimental, vendor-prefixed properties could make it so much easier to create effects that previously required messy workarounds, they started using them everywhere. Vendor-prefixed properties quickly became the CSS trend of the time. Tutorials were written, StackOverflow replies were given, and soon almost every self-respecting CSS developer was using them all over the place.
听起来不错,对吧?但是,你可能也猜到了,现实距离我们的期望往往有着很大的落差。当开发者们发现这些实验性的、加了前缀的属性可以轻而易举地实现以前大费周章才能达到效果时,他们就开始撒开了用了。这些加了浏览器前缀的属性迅速成为 CSS 领域的一大潮流。网上的教程会写出它们、StackOverflow 上的问答会提到它们,很快,几乎每个有上进心的 CSS 开发者都开始争先恐后地使用它们。
Eventually, authors realized that using only existing vendor prefixes meant they would have to go back to previous work and add new declarations every time another browser implemented their favorite cool new CSS feature. Not to mention how hard it became to keep up with which prefixes were needed for what feature. The solution? Add all possible vendor prefixes preemptively, including the unprefixed version at the end, to future-proof it. We ended up with code like the following:
不久,网页开发者们就发现,在使用这些神奇的新特性时,如果只写出当下有效的浏览器前缀,就意味着以后要经常回来打补丁——每当多了一个浏览器实现了这个新特性时,他们都需要多加一行。去跟进各个特性在各个浏览器下是不是要加前缀,光是想想就让人头皮发麻。那开发者们会怎样应对?就是先发制人地加上所有可能的浏览器前缀,再把无前缀的版本放在最后,以图一劳永逸。我们最终写出的代码可能就是这样的:
-moz-border-radius: 10px;
-ms-border-radius: 10px;
-o-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
Two of the declarations here are completely redundant: -ms-border-radius
and -o-border-radius
never existed in any browser, as IE and Opera implemented border-radius
unprefixed from the get-go.
这里面有两条声明是完全多余的:-ms-border-radius
和 -o-border-radius
这两个属性从来没有在任何浏览器中出现过,因为 IE 和 Opera 从一开始就是直接实现 border-radius
这个无前缀版本的。
Obviously, repeating every declaration up to five times was tedious and unmaintainable. It was only a matter of time until tools were built to automate this:
显然,把每个声明都重复五遍是相当枯燥的,而且很难维护。因此出现某个工具来把这项工作自动化只是个时间问题:
Because authors were using the unprefixed version of features as a means to future-proof their code, it became impossible to change them. We were basically stuck with half-baked early specs that we could change in very limited ways. It didn’t take long for everyone involved to realize that vendor prefixes were an epic failure.
由于网页开发者们使用无前缀的属性是想确保代码的向前兼容,那么工作组想要修改这些无前缀语法就变得不可能了。我们基本上就跟这些半生不熟的早期规范绑在一起了,只能通过极其有限的途径来修改它们。用不了多久,这个坑里的每个人就会意识到,浏览器前缀已是一场史诗般的失败。
These days, vendor prefixes are rarely used for new experimental implementations. Instead, experimental features require config flags to be turned on, effectively preventing developers from using them in production, as you can’t really tell users to change their settings in order to view your website properly. Of course, this has the consequence that fewer authors get to play with experimental features, but we still get enough feedback, and arguably, better quality feedback, without the drawbacks of vendor prefixes. However, it will be a long time before the ripple effects of vendor prefixes stop haunting us all.
最近以来,浏览器厂商已经很少以前缀的方式来实验性地实现新特性了。取而代之的是,这些实验性特性需要通过配置开关来启用,这可以有效防止开发者们在生产环境使用它们,因为你不能要求你的用户为了正确地浏览你的网站而去修改他们的浏览器设置。当然,这会导致一个后果,尝试这些实验性特性的开发者会减少,但我们仍然会得到足够多的反馈,甚至是(你可能会质疑)更高质量的反馈,同时还避免了浏览器前缀的所有缺点。不过我们还需要很长的时间,才能从浏览器前缀所引发的涟漪效应中解脱出来。
现有的主要译法如下:
由于目前在书中还没有遇到这个词,我也没有做最终决定,暂时先采用了我印象中最流行的 “优先级” 这个译法写入表中。
本讨论源自: #1 (comment)
本文是早期译版,未经校审。仅供参考。
Keeping code DRY and maintainable is one of the biggest challenges in software development, and that applies to CSS as well. In practice, one big component of maintainable code is minimizing the amount of edits necessary to make a change. For example, if to enlarge a button you need to make 10 edits in many different rules, chances are you will miss a few of them, especially if you are not the one who wrote the original code. Even if the edits are obvious, or you eventually find them, you have just wasted time that could be put to better use.
在软件开发中,保持代码的 DRY 和可维护性是最大的挑战之一,而这句话对 CSS 也是适用的。在实践中,代码可维护性的最大要素是尽量减少做改动时要编辑的地方。举例来说,如果在放大一个按钮时你需要在一堆规则中做出 10 处修改,那你很可能会漏改其中某处,尤其当你是在给别人擦屁股时更是如此。即使这些要修改的地方很明显,或者你最终可以找齐它们,但你还是浪费了时间,而你原本可以利用这些时间来做点更有意义的事情。
Furthermore, this is not just about future changes. Flexible CSS makes it easier to write CSS once, and then create variations with very little code, as there are only a few values you need to override. Let’s look at an example.
而且,这还不仅仅是后期修改的问题。灵活的 CSS 通常更容易扩展——在写出基础样式之后,只用极少的代码就可以扩展出不同的变体,因为你只需要去覆盖一些变量就可以了。让我们来看个例子。
图 1.4
The button we are going to use in our example
在我们的示例中会一直用到这个按钮。
Take a look at the following CSS, which styles the button shown in Figure 1.4:
让我们来看一眼下面这段 CSS,它给按钮添加了一些效果(参 图 1.4):
padding: 6px 16px;
border: 1px solid #446d88;
background: #58a linear-gradient(#77a0bb, #58a);
border-radius: 4px;
box-shadow: 0 1px 5px gray;
color: white;
text-shadow: 0 -1px 1px #335166;
font-size: 20px;
line-height: 30px;
There are several issues with the maintainability of this code that we can fix. The low-hanging fruit is the font metrics. If we decide to change the font size (perhaps to create a variation that will be used for important, bigger buttons), we also need to adjust the line spacing, as they are both absolute values. Furthermore, the line spacing doesn’t reflect what its relationship is to the font size, so we would even need to perform calculations to figure out what it should be for a different font size. When values depend on each other, try to reflect their relationship in the code. In this case, the line spacing is 150% the line height. Therefore, it would be much more maintainable to show this in the code:
这段代码在可维护性方面存在一些问题,我们来一一修复。最软的杮子应该是跟字体尺寸相关的部分了。如果我们决定改变字号{1[译注:在本书中,“字号” 是对字体尺寸(font-size
)的俗称。]}(可能是为了生成一个更大、更重要的按钮),那就得同时调整行高,因为这两个属性都写成了绝对值。更麻烦的是,行高并没有反应出它跟字号的关系,因此我们还得做些算术,算出字号改变之后的行高该是多少。当某些值相互依赖时,应该把它们的相互关系用代码表达出来。在这个例子中,行高是字号的 1.5 倍。因此,把代码改成下面这样会明显更易维护:
font-size: 20px;
line-height: 1.5;
图 1.5
Enlarging the font size breaks other effects in our button (corner rounding being the most noticeable), as they are specified using absolute lengths
只放大字体会破坏按钮的其他效果(最突兀的就是圆角了),因为它们都被指定了一些绝对的长度值。
While we’re at it, why did we specify the font size as an absolute length? Sure, absolute lengths are easy to work with, but they come back to bite you every single time you make changes. Now, if we decide to make the parent font size bigger, we would have to change every single rule in the stylesheet that uses absolute font measurements. It’s much better to use percentages or em
s:
既然跨出了这一步,我们为什么还把字号定为绝对长度值呢?没错,绝对值很容易掌控,但每当你想要修改它们的时候,它们都会回头反咬你一口。比如说,如果我们决定把父级的字号加大时,我们将不得不修改每一处使用绝对值作为字体尺寸的样式。如果改用百分比或 em
单位就好多了:
/* Assuming a 16px parent font size */
font-size: 125%; /* 假设父级的字号是 16px */
line-height: 1.5;
Now if I change the parent font size, the button will instantly become bigger. However, it will look quite different (Figure 1.5), because all other effects were designed for a smaller button and did not scale. We can make all the other effects scalable as well, by specifying any lengths in em
s, so that they all depend on the font size. This way, we can control the size of the button in one place:
现在,如果我们改变父级的字号,按钮的尺寸就会随之变化。但是,它看起来很不协调(图 1.5),因为所有其他效果都是为一个小按钮设计的,并没有跟着缩放。如果我们把这些长度值都改成 em
单位,那这些效果的值就都变成可缩放的了,而且是依赖字号进行缩放。按照这种方法,我们就可以在一处控制按钮的所有尺寸样式了:
padding: .3em .8em;
border: 1px solid #446d88;
background: #58a linear-gradient(#77a0bb, #58a);
border-radius: .2em;
box-shadow: 0 .05em .25em gray;
color: white;
text-shadow: 0 -.05em .05em #335166;
font-size: 125%;
line-height: 1.5;
{原书注释!}
Here we wanted our font size and measurements to be relative to the parent font size, so we used
em
s. In some cases, you want them to be relative to the root font size (i.e., the font size of<html>
), andem
s result in complex calculations. In that case, you can use therem
unit. Relativity is an important feature in CSS, but you do have to think about what things should be relative to.这里我们希望字号和其他尺寸能够跟父级的字号建立关联,因此我们采用了
em
单位。但在某些情况下,你可能希望这些尺寸是和根级字号(即<html>
元素的字号)相关联的,此时使用em
可能会导致复杂的计算。在这种情况下,你可以使用rem
单位。在 CSS 中,相关性是一个很重要的特性,但你得想清楚到底哪些东西是真正相关的。
Now our larger button looks much more like a scaled version of the original (Figure 1.6). Notice that we still left some lengths as absolute values. It’s a judgment call which effects should scale with the button and which ones should stay the same. In this case, we wanted our border thickness to stay 1px
regardless of the button dimensions.
现在我们的大号按钮看起来更像是一个原按钮的等比例放大版本了(图 1.6)。请注意仍然还有一些长度值是绝对值。此时就需要重新审视到底哪些效果应该跟着按钮一起放大,而哪些效果的尺寸是保持不变的。比如在这个例子中,我们希望按钮的边框粗细保持在 1px
,不受按钮尺寸的影响。
图 1.6
Now we can make our button larger, and all its effects scale too
现在我们可以把按钮放大,而且它的所有效果也都跟着放大了。
However, making the button smaller or larger is not the only thing we might want to change. Colors are another big one. For example, what if we want to create a red Cancel button, or a green OK button? Currently, we would need to override four declarations (border-color
, background
, box-shadow
, text-shadow
), not to mention the hassle of recalculating all the different darker/lighter variants of our main color, #58a
, and figuring out how much lighter or darker each color is. Also, what if we want to place our button on a non-white background? Using gray
for its shadow will only look as intended on a white background.
不过,让按钮变大或变小并不是我们唯一想要改动的地方。颜色是另一个重要的变数。比如说,假设我们要创建一个红色的取消按钮,或者一个绿色的确定按钮,该怎么做呢?眼下,我们可能需要覆盖四条声明(border-color
、background
、box-shadow
和 text-shadow
),更别提另一大难题了——我们还得根据按钮的亮面和暗面相对于主色调 #58a
变亮和变暗的程度来分别推导出其他颜色各自的亮色和暗色版本。此外,假设我们想把按钮放在一个非白色的背景之上呢?显然使用灰色(gray
)作投影只适用于纯白背景的情况。
We could easily eliminate this hassle by using semi-transparent white and black for lighter/darker variants, respectively, overlaid on our main color:
其实只要把半透明的黑色或白色叠加在主色调上,即可产生出主色调的亮色和暗色变体,这样就能简单地化解这个难题了:
padding: .3em .8em;
border: 1px solid rgba(0,0,0,.1);
background: #58a linear-gradient(hsla(0,0%,100%,.2),
transparent);
border-radius: .2em;
box-shadow: 0 .05em .25em rgba(0,0,0,.5);
color: white;
text-shadow: 0 -.05em .05em rgba(0,0,0,.5);
font-size: 125%;
line-height: 1.5;
{小提示}
[TIP] Use
HSLA
instead ofRGBA
for semi-transparent white, as it has slightly fewer characters and is quicker to type, due to the lack of repetition.推荐使用
HSLA
而不是RGBA
来产生半透明的白色,因为它的字符长度更短,打起来也更快。这归功于它的重复度更低。
Now all it takes to create variations with different colors is to override background-color
(Figure 1.7):
现在我们只要覆盖 background-color
属性,就可以得到不同颜色版本的按钮了(参见 图 1.7):
button.cancel {
background-color: #c00;
}
button.ok {
background-color: #6b0;
}
图 1.7
All it took to create these color variations was changing the background color
只要改变背景色,就可以得到其他颜色版本的按钮了。
Our button is already much more flexible. However, this example doesn’t demonstrate every opportunity to make your code more DRY. You will find a few more tips in the following sections.
我们的按钮现在已经非常灵活了。不过,这个例子并没有涵盖所有能让代码变得更 DRY 的方法。你会在下面几节中发现更多的技巧。
Sometimes, maintainability and brevity can be mutually exclusive. Even in the previous example, our final code is a bit longer than our original. Consider the following snippet to create a 10px
thick border on every side of an element, except the left one:
有时候,“代码易维护” 和 “代码量少” 不可兼得。比如在上面的例子中,我们最终采用的代码甚至比一开始的版本还要略长一些。来看看下面的代码片断,我们要为一个元素添加一道 10px
宽的边框,但左侧不加边框。
border-width: 10px 10px 10px 0;
It’s only one declaration, but to change the border thickness we would need to make three edits. It would be much easier to edit as two declarations, and it’s arguably easier to read that way too:
只要这一条声明就可以搞定了,但如果日后要改动边框的宽度,你需要同时改三下。但如果把你它拆成两条声明的话,改起来就容易多了,而且它的可读性或许也会更好一些:
border-width: 10px;
border-left-width: 0;
In CSS Color Level 3, we got many new color keywords like lightgoldenrodyellow
, which aren’t that useful. However, we also got a special new color keyword, borrowed from SVG: currentColor
. This does not correspond to a static color value. Instead, it always resolves to the value of the color
property, effectively making it the first ever variable in CSS. A very limited variable, but a variable nevertheless.
在 CSS 颜色(第三版)规范中,增加了很多新的颜色关键字,比如 lightgoldenrodyellow
等,其实并不是很常用。但是,我们还得到了一个特殊的颜色关键字 currentColor
,它是从 SVG 那里借鉴来的。这个关键字并没有绑定到一个固定的颜色值,而是总是解析为 color
属性的值——实际上,这的特性让它成为 CSS 中有史以来的第一个变量。虽然功能很有限,但它真的是个变量。
{原书注释!}
Some would argue that the
em
unit was actually the first variable in CSS, as it referred to the value offont-size
. Most percentages play a similar role, though in less exciting ways.可能有人会争论说
em
单位才是 CSS 中的第一个变量,因为它引用了font-size
的值。其实大多数百分比数值也扮演了类似的角色,只不过它们的工作方式不是很起眼。
For example, let's assume we want all of the horizontal separators (all <hr>
elements) to automatically have the same color as the text. With currentColor
, we could do this:
举个例子,假设我们想让所有的水平分割线(所有 <hr>
元素)自动跟文本的颜色保持一致。有了 currentColor
之后,我们只需要这样写:
hr {
height: .5em;
background: currentColor;
}
You might have noticed similar behavior with many existing properties. For example, if you specify a border with no color, it automatically gets the text color. This is because currentColor
is also the initial value of many CSS color properties: border-color
, the text-shadow
and box-shadow
colors, outline-color
, and others.
你可能已经注意到了,很多已有的属性也具有类似的行为。举例来说,如果你没有给边框指定颜色,它就会自动地从文本颜色那里得到颜色。这是因为 currentColor
本身就是很多 CSS 颜色属性的初始值,比如 border-color
和 outline-color
,以及 text-shadow
和 box-shadow
的颜色值等等。
In the future, when we get functions to manipulate colors in native CSS, currentColor
will become even more useful, as we will be able to use variations of it.
在未来,当我们在原生 CSS 中拥有处理颜色的函数后,currentColor
就会变得更加有用了,因为我们可以用这些函数来产生它的各种深浅明暗的变体。
While most authors are aware of the inherit
keyword, it is often forgotten. The inherit
keyword can be used in any CSS property and it always corresponds to the computed value of the parent element (in pseudo-elements that is the element they are generated on). For example, to give form elements the same font as the rest of the page, you don’t need to re-specify it, just use inherit
:
尽管绝大多数开发者都知道有 inherit
这个关键字,但还是很容易遗忘它。inherit
可以用在任何 CSS 属性中,而且它总是绑定到父元素的计算值(对伪元素来说,则会去取生成该伪元素的宿主元素)。举例来说,要把表单元素的字体设定为跟页面的其他部分相同,你并不需要重复指定字体属性,只需利用 inherit
的特性即可:
input, select, button { font: inherit; }
Similarly, to give hyperlinks the same color as the rest of the text, use inherit
:
与此类似,要把超链接的颜色设定为跟页面中其他文本相同,还是要用 inherit
:
a { color: inherit; }
The inherit
keyword can often be useful for backgrounds as well. For example, to create speech bubbles where the pointer automatically inherits the background and border (Figure 1.8):
这个 inherit
关键字对于背景色同样是非常有用的。举个例子,在创建提示框的时候,你可能希望它的小箭头能够自动继承背景和边框的样式(图 1.8):
.callout { position: relative; }
.callout::before {
content: "";
position: absolute;
top: -.4em; left: 1em;
padding: .35em;
background: inherit;
border: inherit;
border-right: 0;
border-bottom: 0;
transform: rotate(45deg);
}
图 1.8
A speech bubble where the pointer gets the background color and border from the parent
提示框的小箭头从父元素那里获取了背景色和边框样式。
The human eye is far from being a perfect input device. Sometimes accurate measurements result in looking inaccurate and designs need to account for that. For example, it’s well known in visual design literature that our eyes don’t perceive something as being vertically centered when it is. Instead, it needs to be slightly above the geometrical middle to be perceived as such. See that phenomenon for yourself, in Figure 1.9.
人类眼睛并不是一台完美的输入设备。有时候精准的尺度看起来并不精准,而我们的设计需要顺应这种偏差。举一个在视觉设计领域广为人知的例子吧,我们的眼睛在看到一个完美垂直居中的物体时,感觉它并不居中。实际上,我们应该把这个物体从几何学的中心点再稍微向上挪一点儿,才能取得理想的视觉效果。来亲身体验一下这件怪事儿吧(请看 图 1.9)。
图 1.9
In the first rectangle, the brown square is mathematically vertically centered, but doesn’t look so; in the second one, it is actually placed slightly above the geometrical center, but it looks more centered to the human eye
在第一个矩形中,棕色方块在数学层面上是完美垂直居中的,但看起来并不是这样;在第二个矩形中,方块从几何中心向上轻微移动了一点儿,但它在人类的眼睛看来却是恰好居中的。
Similarly, in type design, it is well known that round glyphs such as “O” need to be slightly larger than more rectangular glyphs, as we tend to perceive round shapes as smaller than they actually are. Check that out for yourself in Figure 1.10.
与此类似,在字体设计领域广为人知的是,圆形的字形(比如 “0”)跟矩形字形相比,需要稍微放大一些,因为我们倾向于把圆形感知得比它实际的尺寸更小一些。在 图 1.10 中你也可以体验一下。
图 1.10
The circle looks smaller, but its bounding box is exactly the same as the square
圆形看起来要小一些,但实际上它占据的宽高和方形是完全一样的。
Such optical illusions are very common in any form of visual design, and need to be accounted for. An extremely common example is padding in containers with text. The issue is present regardless of the amount of text--it could be a word or several paragraphs. If we specify the same amount of padding on all four sides of a box, it actually ends up looking uneven, as Figure 1.11 demonstrates. The reason is that letterforms are much more straight on the sides than their top and bottom, so our eyes perceive that extra space as extra padding. Therefore, we need to specify less padding for the top and bottom sides if we want it to be perceived as being the same. You can see the difference this makes in Figure 1.12.
这些视觉上的错觉在任何形式的视觉设计中都普遍存在,需要我们有针对性的作为调整。一个非常常见的例子是给一个文本容器设置内边距。不论内容文本有多长,是一个单词还是几个段落,这个问题都是会出现。假如我们给容器的四边指定相同的内边距,则实际效果看起来并不相等,就像 图 1.11 显示的那样。原因在于,字母的形状在两端都比较整齐,而顶部和底部则往往参差不齐,从而导致你的眼睛把这些参差不齐的空缺部分感知为多出来的内边距。因此,如果我们希望四边的内边距看起来是基本一致的,就需要减少顶部和底部的内边距。你可以在 图 1.12 中看出这种差异。
图 1.11
Specifying the same padding (
.5em
here) on all four sides of a container with text makes it look larger on the top and bottom sides为容器的四边指定了相同的内边距(这里用了
.5em
),但实际看起来上下空得多,而左右空得少。
图 1.12
Specifying larger padding (here:
.3em .7em
) on the left and right side makes it look much more uniform如果把左右内边距增大一些(这里把
padding
属性写成.3em .7em
),则看起来就明显更加统一了。
RWD has been all the rage over the past few years. However, the emphasis is often placed on how important it is for websites to be “responsive,” leaving a lot unsaid about what good RWD entails.
响应式网页设计(Responsive Web Design,简称 “RWD”)在最近这几年已经火得不行了。但是,人们大多只是在不停念叨网页的 “响应式” 是多么重要,而极少有人去深入探讨怎样才能做好响应式设计。
The common practice is testing a website in multiple resolutions and adding more and more media queries to fix the issues that arise. However, every media query adds overhead to future CSS changes, and they should not be added lightly. Every future edit to the CSS code requires checking whether any media queries apply, and potentially editing those too. This is often forgotten, resulting in breakage. The more media queries you add, the more fragile your CSS code becomes.
比较常见的实践是用多种不同的分辨率来测试一个网站,然后添加越来越多的媒体查询(Media Query)规则来修补网站在这些分辨率下出现的问题。但是,对于今后的 CSS 改动来说,每个媒体查询都会增加成本,而这种成本是不应轻易上升的。未来每次对 CSS 代码的修改都要求我们逐一核对这些媒体查询是不是都需要配合修改,甚至可能要求我们反过来修改这些媒体查询的设置。这一点常常被我们忽略,但后患无穷。你添加的媒体查询越多,你的 CSS 代码就会变得越来越经不起折腾。
That is not to say that media queries are a bad practice. Used right, they can be indispensable. However, they should be a last resort, after every other attempt to make a website design flexible has failed, or when we want to completely change an aspect of the design in smaller/larger viewports (e.g., making the sidebar horizontal). The reason is that media queries do not fix issues in a continuous manner. They are all about specific thresholds (a.k.a. “breakpoints”), and unless the rest of the code is written to be flexible, media queries will only fix specific resolutions, essentially sweeping issues under the rug.
这并不是说媒体查询是一种不良实践。只要用对了,它们就是利器。但是,你只应该把它作为最后的手段——比如你想把网站做得弹性灵活,但其他尝试全都失败了;或者我们希望在较大或较小的视口下完全改变网站的设计形态(譬如,把侧栏改成水平布局)。我这么说的原因在于,媒体查询不能以一种连续的方式来修复问题。它们的工作原理是基于某几个特定的阶梯(亦称 “断点”)的,如果大部分样式代码并不是以弹性的方式来编写的,那媒体查询能做的只是修补某个特定分辨率下的特定问题——这本质上只是把灰尘扫到地毯下面而已。
{小提示}
Consider using
em
s in your media queries instead of pixels. This allows text zoom to trigger layout changes as necessary.不妨考虑在你的媒体查询中使用
em
单位取代像素单位。这允许文本缩放在必要时可以触发布局的变化。
Of course, it goes without saying that media query thresholds should not be dictated by specific devices, but by the design itself. Not only because there are so many different devices (especially if we take future devices into account) that a website should look good at any possible resolution, but also because a website on the desktop might be viewed in a window of any size. If you are confident that your design works well in every possible viewport size, who cares about what resolution specific devices have?
当然,有一点上面并没有提到,媒体查询的断点不应该由具体的设备来决定,而应该根据设计自身来决定。不仅是因为我们的网站需要面向的设备太多了(尤其是当我们考虑到未来的设备时),还因为一个网站在桌面端可能会以任意尺寸的窗口来显示。如果你有信心做到让你的设计在任何可能出现的视口尺寸下都能工作良好,谁关心这些设备的分辨率具体是多少呢?
Following the principles described in the XXXX will also help with this, as you won’t have to override as many declarations in your media queries, essentially minimizing the overhead they cause.
遵从 **“{$section$}” 段落(第 {$page$} 页)**所描述的原则对此也是有帮助的,因为你不需要去覆盖媒体查询里的同样数量的声明,这在本质上减轻了它们所产生的维护成本。
Here are a few more tips to avoid needless media queries:
这里还有一些建议,可能会帮你避免不必要的媒体查询:
vw
, vh
, vmin
, vmax
), which resolve to a fraction of the viewport width or height.vw
、vh
、vmin
和 vmax
),它们的值解析为视口宽度或高度的百分比。max-width
, not width
, so it can still adapt to smaller ones without media queries.max-width
而不是 width
,因为它可以适应较小的分辨率,而无需使用媒体查询。max-width
of 100%
for replaced elements such as img
, object
, video
, and iframe
.img
、object
、video
、iframe
等)设置一个 max-width
,值为 100%
。background-size: cover
can help maintain that regardless of said container’s size. However, bear in mind that bandwidth is not unlimited, and it’s not always wise to include large images that are going to be scaled down via CSS in mobile designs.background-size: cover
这个属性可以做到这一点,不管容器的尺寸如何变化。但是,我们也要绷紧一根弦儿——带宽并不是无限的,因此,在移动网页中通过 CSS 来把一张大图缩小显示往往是不太明智的。display: inline-block
and regular text wrapping can help with that.display: inline-block
加上常规的文本折行行为,都可以实现这一点。column-width
instead of column-count
, so that you get one column only in small resolutions.column-width
(列宽)而不是指定 column-count
(列数),这样它就可以在较小的屏幕上自动显示为单列布局。In general, the idea is to strive for liquid layouts and relative sizing between media query breakpoints. When a design is sufficiently flexible, making it responsive shouldn’t take more than a few short media queries. The designers of Basecamp wrote about this very matter in late 2010:
总的来说,我们的思路是尽最大努力实现弹性可伸缩的布局,并在媒体查询的各个断点区间内指定相应的尺寸。当网页本身的设计足够灵活时,让它变成响应式应该只需要用到一些简短的媒体查询代码。Basecamp 的设计师在 2010 年曾写过这种非常规情况:
“As it turned out, making the layout work on a variety of devices was just a matter of adding a few CSS media queries to the finished product. The key to making it easy was that the layout was already liquid, so optimizing it for small screens meant collapsing a few margins to maximize space and tweaking the sidebar layout in the cases where the screen is too narrow to show two columns.”
——Experimenting with responsive design in Iterations
“结果我们发现,想让网页在一堆不同的设备上合理展示,只需要在最终产品上添加一丁点儿 CSS 媒体查询就可以了。这件事情之所以这么简单,关键在于我们的布局原本就是弹性可伸缩的。因此,优化网页在小屏幕上的表现,其实只意味着把一些外边距收拢到最小程度,然后再把因为屏幕太窄而无法显示成双列的侧栏调整为单列布局而已。”
{--:}——在 Iterations 中实践响应式设计
If you find yourself needing a boatload of media queries to make your design adapt to smaller (or larger) screens, take a step back and reexamine your code structure, because in all likelihood, responsiveness is not the only issue there.
如果你发现你自己需要一大堆的媒体查询才能让你的设计适应大大小小不同的屏幕,那不妨往后退一步,重新审视你的代码结构。因为在所有的情况下,响应式都不是唯一需要考虑的因素。
As you probably know, the following two lines of CSS are not equivalent:
你可能知道,以下两行 CSS 代码并不是等价的:
background: rebeccapurple;
background-color: rebeccapurple;
The former is a shorthand and will always give you a rebeccapurple
background, whereas the element with the longhand (background-color
) could end up with a pink gradient, a picture of a cat, or anything really, as there might also be a background-image
declaration in effect. This is the problem when you mainly use longhands: you are not resetting all the other properties that could be affecting what you’re trying to accomplish.
前者是简写,它可以确保让你得到 rebeccapurple
纯色背景;但如果你用的是展开式的单个属性(background-color
),那这个元素的背景最终有可能会显示为一个粉色的渐变图案、一张猫的图片、或任何东西,因为同时可能会有一条 background-image
声明在起作用。通常在使用展开式属性的写法时,会遇到这样的问题:展开式写法并不会帮助你清空所有相关的其他属性,从而可能会干扰你想要达到的效果。
You could of course try to set all the longhands and call it a day, but then you might forget some. Or the CSS WG might introduce more longhands in the future, and your code will have failed to reset those. Don’t be afraid of shorthands. It is good defensive coding and future-proofing to use them, unless we intentionally want to use cascaded properties for everything else, like we did for the colored button variants in the XXXXXXXXXXXX.
当然你可以把所有的展开式属性全都设置一遍,然后收工,但你可能会漏掉几个没写全。又或者,CSS 工作组可能会在未来引入更多的展开式属性,那时你的代码就无法完全覆盖它们了。不要不敢用简写属性。合理使用简写是一种良好的防卫性的编码方式,可以抵御未来的风险。当然,如果我们很明确地要去覆盖某个具体的展开式属性并保留其他相关样式,那就用展开式属性,就像我们在 **“{$section$}” 段落(第 {$page$} 页)**中为了得到按钮的其他颜色版本所做的那样。
Longhands are also very useful in combination with shorthands, to make code DRY-er in properties whose values are a comma-separated list, such as the background
properties. This is best explained with an example:
展开式属性与简写属性的配合使用也是非常有用的,可以让代码更加 DRY。对那些接受一个用逗号分隔的列表的属性(比如 background
)来说,尤其如此。下面的例子可以很好地解释这一点:
background: url(tr.png) no-repeat top right / 2em 2em,
url(br.png) no-repeat bottom right / 2em 2em,
url(bl.png) no-repeat bottom left / 2em 2em;
Notice how the background-size
and background-repeat
values are repeated three times, despite being the same for every image. We can take advantage of CSS list expansion rules which say that if only one value is provided, it is expanded to apply to every item in the list, and move these repeated values to longhands:
请注意 background-size
和 background-repeat
的值被重复了三遍,尽管每层背景的这两个值确实是相同的,但这仍然是一种冗余。其实我们可以从 CSS 的 “列表扩散规则” 那里得到好处——它的意思是说,如果只为某个属性提供了一个值,那它就会扩散并应用到列表中的每一项。因此,我们可以把这些重复的值从简写属性中抽出来写成一个展开式属性:
background: url(tr.png) top right,
url(br.png) bottom right,
url(bl.png) bottom left;
background-size: 2em 2em;
background-repeat: no-repeat;
Now we can change the background-size
and background-repeat
with only one edit instead of three. You will see this technique used throughout the book.
现在,我们只需要在一处修改,就可以改变所有的 background-size
和 background-repeat
了。你会发现这个技巧在本书中的使用非常普遍。
{小花絮}
Weird shorthand syntax
怪异的简写语法
You might have noticed in the shorthand and longhand example that specifying
background-size
in thebackground
shorthand requires also providing abackground-position
(even if it’s the same as the initial one) and using a slash (/
) to separate them. Why do some shorthands have such weird rules?你可能已经注意到前面那个背景属性简写的例子了:在
background
简写属性中指定background-size
时,需要同时提供一个background-position
值(哪怕它的值就是其初始值也需要写出来),而且还要使用一个斜杠(/
)作为分隔。为什么有些简写的语法如此怪异?This is almost always done for disambiguation purposes. Sure, in the example here, it’s obvious that
top right
is abackground-position
and2em 2em
abackground-size
regardless of their ordering. However, think of values like50% 50%
. Is it abackground-size
or abackground-position
? When you are using the longhands, the CSS parser knows what you mean. However, in the shorthand, the parser needs to figure out what that50% 50%
refers to without any help from the property name. This is why the slash is needed.这通常都是为了消除歧义。是的,在这个例子中,很显然
top right
是background-position
,而2em 2em
是background-size
,不管它们的顺序如何。但是,请设想一下50% 50%
这样的值。它到底是background-size
还是background-position
呢?当你在使用展开式属性时,CSS 解析器是知道你的意图的。但当你使用简写属性时,解析器需要在没有属性名提示的情况下弄清楚50% 50%
到底是指什么。这就是为什么需要引入这个斜杠。For most shorthands, there is no such disambiguation issue and their values can be specified in any order. However, it’s always good practice to look up the exact syntax, to avoid nasty surprises. If you are familiar with regexes and grammars, you could also check the grammar for the property in the relevant specification, which is probably the quickest way to see if there is a specific ordering.
对绝大多数的简写属性来说,并没有这样的歧义问题,因而简写属性的多个值往往可以随意排列。不过,我还是建议你养成随手查阅语法的好习惯,以免踩坑。如果你对正则表达式以及规范的语法描述方式(grammar)很熟悉的话,不妨直接在相关规范中查询语法描述——如果要确定某个属性的值是不是有明确的顺序要求,这可能是最快方式。
You've probably heard of CSS preprocessors such as LESS, Sass, or Stylus. They offer several conveniences for authoring CSS, such as variables, mixins, functions, rule nesting, color manipulation, and more.
你很可能听说过像 Stylus、Sass 或 LESS 这样的 CSS 预处理器。它们为 CSS 的编写提供提供了一些便利,比如变量、mixin、函数、规则嵌套、颜色处理等等。
Used properly, they can help keep code more flexible in a large project, when CSS itself proves too limited to let us do so. As much as we strive to code robust, flexible, DRY CSS, sometimes we just stumble on the limitations of the language. However, preprocessors also come with a few issues of their own:
如果使用得当,它们在大型项目中可以让代码更加灵活,而 CSS 自身在这方面确实有很大局限。只要我们在代码健壮性、灵活性、DRY 方面有追求,我们就会感受到 CSS 在这方面的局限。不过,预处理器也不是完美无缺的:
In addition to the issues listed here, preprocessors also pose the risk of making authors dependent on them, perpetuating their use even when unnecessary, such as in smaller projects or in the future, after their most popular features have been added to native CSS. Surprised? Yes, many preprocessor-inspired features have been making their way into pure CSS:
除了上面列出的这些问题,预处理器还可能导致这种风险——网站开发者们可能会不自觉地 “依赖” 和 “滥用”。因为在某些时候,预处理器并不必要,比如在小型项目中,或者在未来,说不定预处理器最受欢迎的那些特性都被加入到原生 CSS 中了。很惊讶吗?没错,很多受预处理器启发的特性都已经以各种方式融入到原生 CSS 中了:
calc()
from CSS Values & Units Level 3 not only is very powerful for performing calculations, but also very well supported, even today.calc()
这个函数,不仅在处理运算时非常强大,而且已经得到了广泛的支持,当下可用。color()
function in CSS Color Level 4 will provide means to manipulate colors.color()
函数将会提供颜色运算方法。图 1.13
Myth is an experimental preprocessor that emulates these native CSS features, instead of introducing proprietary syntax, essentially acting like a CSS polyfill
Myth 是一款实验性质的预处理器,它只去模拟上述这些原生的 CSS 新特性,而不是引入私有语法。它本质上扮演了 CSS polyfill 的角色。
Note that native features like these are generally much more powerful than the ones provided by preprocessors, as they are dynamic. For example, a preprocessor has no clue how to perform a calculation like 100% - 50px
, because the value percentages resolve to is not known until the page is actually rendered. However, native CSS calc()
has no trouble evaluating such expressions. Similarly, variable use like the following is not possible with preprocessor variables:
请注意这些原生特性通常会比预处理器提供的版本要强大得多,因为它们是动态的。举个例子,预处理器完全不知道如何完成 100% - 50px
这样的计算,因为在页面真正被渲染之间,百分比值是无法解析的。但是,原生 CSS 的 calc()
在计算这样的表达式时没有任何压力。与此类似,下面这样的变量玩法在预处理器中是不可能做到的:
{原书注释!}
Don’t forget that native CSS features like these can be manipulated through scripting too. For example, you could use JS to change the value of a variable.
不要忘了这样的原生 CSS 特性也可以通过脚本来操纵。比如说,你可以用 JS 来改变一个变量的值。
ul { --accent-color: purple; }
ol { --accent-color: rebeccapurple; }
li { background: var(--accent-color); }
Can you see what we did there? The background of list items in ordered lists will be rebeccapurple
, whereas the background of list items in unordered lists will be purple
. Try doing that with a preprocessor! Of course, in this case, we could have just used descendant selectors, but the point of the example was to show how dynamic these variables will be.
你看清楚这段代码的意图了吗?有序列表的列表项的背景色将是 rebeccapurple
,但在无序列表中,列表项的背景色将是 purple
。你试试用预处理器能不能做到吧!当然,在这个例子中,我们可以直接使用后代选择符,只不过这个例子的重点在于——向你展示 CSS 的原生变量所具备的动态性。
Because most of the aforementioned native CSS features are not well supported today, in many cases using preprocessors is unavoidable if maintainability matters (and it should). My advice would be to start off every project with pure CSS, and when it starts being impossible to keep it DRY, switch to using a preprocessor then. To avoid becoming completely dependent on preprocessors or using them when they are not actually needed, their use needs to be a conscious decision, not a mindless first step performed by default in every new project.
上面提到的原生 CSS 特性绝大多数在目前还没有得到很好的支持,因此,在很多情况下,如果可维护性很重要(它确实很重要),使用预处理器是不可避免的。我的建议是,在每个项目开始时使用纯 CSS,只有当代码开始变得无法保持 DRY 时,才切换到预处理器的方案。为了避免可能发生的 “依赖” 或 “滥用”,在引入预处理器的问题上需要冷静决策,不应该在每个项目一开始时就不动脑筋顺着惯性来。
In case you were wondering (and haven’t read the first chapter, tsk-tsk), the style of this book was authored in SCSS, although it started as pure CSS and only switched when the code grew too complex to be maintainable. Who said CSS and its preprocessors are only for the Web?
可能你还不知道(居然直接跳过前言了,啧啧),这里再说一次,这本书的样式是用 SCSS 写的。但这些样式代码是以纯 CSS 起步的,而且只在代码增长得太过复杂以致无法维护时才切到 SCSS 的。谁说 CSS 和预处理器只能用在网页上?
贴一个09年时我在图灵内部论坛的讨论。此文基于六年前情况,现在没有重新检讨过状况,所以谨供参考。
层叠和级联本身并没有什么特别区别,只是对cascade的不同译法。我偏好级联主要有几个原因:
- 层叠这个词对于初学者有点不知所云。最初翻译的人可能是不知道还有级联这种在其他领域已经有的译法可直接拿来用,所以才译为了层叠。
我的经验是,很长一段时间以来,我很熟悉CSS,但是就是一直搞不懂层叠这个词到底什么意思。对于这点,可能有人会认为有主观之嫌,最好的方式是去问问那些对网页有些概念,但是并不很了解CSS的人看到层叠这个词做何理解。至于为什么不理解层叠的情况下,层叠的用法甚广,实际上是反正大家很多时候不求甚解,知道这个指代什么就得了。然而进入翻译领域,则不得不刨根问底。- 层叠这个词主要被用作形容词(许多人更愿意加一个 式 字,以 层叠式 来翻译cascading),而cascade是名词或动词。虽然你总可以强制将层叠作为一个名词或动词看待,但是总归不太自然。而级联则名词、动词、形容词均适用。
可对比以下这些说法:
A和B层叠 vs A和B级联(cascade A and B)
A和B的层叠 vs A和B的级联(The cascade of A and B)
层叠顺序 vs 级联顺序(cascading order)
- 所谓cascade,归根到底是动词性的,即将一些样式表合并起来的过程。所以cascading style sheet之cascading并不是单纯描述若干sheet层叠的状态,或者如何进行叠放,而是强调cascading乃是一种机制,是对各个冲突的css declarations进行选择的机制。
例句:
The key feature is the ability to cascade style information specified in the default (user agent) style sheet, user style sheets, linked style sheets, the document head, and in attributes for the elements forming the document body.
核心意思是对样式信息进行cascade
对比:
对样式信息进行层叠
对样式信息进行级联这样可以看到层叠这个词的缺陷:
层叠给人的感觉是仍旧是一层一层的、分离的。
而级联是将这些东西串接到一起。
这是最重要的差别。
最后是 级联 这个词在其他领域的用法。
主要是两种:
一个是物理设备的层次式的连接,比如USB hub就是一个级联的例子。
另一个是数据库中的层次关联性操作,比如级联删除。严格的说,在CSS中,基本上是一个串联,而不是一分二、二分四的级联,所以按理说,串接样式表或者串联样式表的译法最为靠谱。
但是级联这个词本身,其实只包含 分级 和 联接 的意思,并没有规定一级到二级就要一分n,因此拿来用也并不能算错。
而且我们要考虑约定俗成。就这点而言,串联、串接一律淘汰。只剩下级联和层叠是在一个数量级上的。
所以最后我建议选择 级联。
本文是早期译版,未经校审。仅供参考。
Prerequisites
- CSS gradients
background-size
- the {#secret-xxx}
背景知识
- CSS 渐变
background-size
- “{$secret$}” 攻略(第 {$page$} 页)
Cutting corners is not just a way to save money, but also a rather popular style in both print and web design. It usually involves cutting out one or more of an element’s corners in a 45° angle (also known as beveled corners). Especially lately, with flat design winning over skeuomorphism, there has been an increase in the popularity of this effect. When the cutout corners are only on one side and occupy 50% of the element’s height each, it creates an arrow shape that is very popular for buttons and breadcrumb navigation -- see Figure XX.XX.
切角不仅是为了省钱,它还是一种非常流行的设计风格,不论是在印刷媒介还是在网页设计中都是如此。它最常见的形态是把元素的一个或多个角切成 45° 的缺口(也称作 斜面切角)。尤其是在最近,当扁平化设计的风头完全盖过拟物化之后,这种效果就越发流行了。当切角效果只应用在元素的某一侧,且切角的尺寸刚好达到元素高度的 50% 时,就会得到一个箭头形状,这在按钮和面包屑导航中的应用非常普遍——参见 图 3.23。
图 3.23
A button with cutout corners, creating an arrow shape that emphasizes its meaning
一个使用了切角效果的按钮,箭头形状很好地强调了它自身的含义。
However, CSS is still not well equipped for creating this effect in an easy, straightforward one-liner. This leads most authors toward using background images to achieve it, either by obscuring the cutout corners with triangles (when the backdrop is a solid color), or by using one or more images for the entire background, with the corner(s) already cut.
但是,目前的 CSS 仍然无法做到只用一行简单直观的代码就生成这样的效果。这导致绝大多数网页开发者倾向于使用背景图片来达到目的,比如使用三角形盖住元素的顶角来模拟切角效果(假设此时网页背景是纯色),或者使用一张或多张已经切过角的图片来作为整个元素的背景。
图 3.24
An example of a website where a cutout corner (bottom-left of the semi-transparent “Find & Book” box) really adds to the design
以这个网站为例,它就在设计中运用到了切角效果(“Find & Book” 半透明区块的左下角)。
Such methods are clearly inflexible, difficult to maintain, and add latency, both by increasing HTTP requests and the total filesize of the website. Is there a better way?
这些方案显然都不够灵活,难以维护,而且增加了网页的加载时间——不仅增加了额外的 HTTP 请求,而且网页所需的文件体积也增加了。我们还有更好的方法吗?
One solution comes in the form of the omnipotent CSS gradients. Let’s assume we only want one cutout corner, say the bottom-right one. The main trick is to take advantage of the fact that gradients can accept an angle direction (e.g., 45deg
) and color stop positions in absolute lengths, both of which are not affected by changes in the dimensions of the element the background is on.
第一种方案来自于无所不能的 CSS 渐变。假设我们暂时只需要一个角被切掉的效果,就以右下角为例吧。这其中最大的窍门在于充分利用渐变的一大特性——渐变可以接受一个角度(比如 45deg
)作为方向,而且色标的位置信息也可以是绝对的长度值,这一点丝毫不受容器尺寸的影响。
Putting it all together, all we need is one linear gradient. It would need a transparent color stop for the cutout corner and another color stop in the same position, with the color we want for our background. The CSS looks like this (for a 15px
size corner):
综合以上这些想法,我们只需要一个线性渐变就可以达到目标。这个渐变需要把一个透明色标放在切角处,然后在相同位置设置另一个色标,并且把它的颜色设置为我们想要的背景色。CSS 代码看起来是这样的(假设切角的深度是 15px
):
background: #58a;
background:
linear-gradient(-45deg, transparent 15px, #58a 0);
Simple, wasn’t it? You can see the result in Figure XX.XX. Technically, we don’t even need the first declaration. We only included it as a fallback: if CSS gradients are not supported, the second declaration will be dropped, so we still want to get at least a solid color background.
很简单,对吧?你可以在 图 3.25 中看到结果。事实上,第一行声明并不是必需的。我们加上它是把它作为回退机制:如果某些浏览器不支持 CSS 渐变,那第二行声明会被丢弃,而此时我们至少还能得到一个简单的实色背景。
图 3.25
An element with the bottom right corner cut off, through a simple CSS gradient
元素右下角的切角效果是由一幅简单的 CSS 渐变实现的。(图中文字为:嘿,别走神儿!你应该把注意力放在边角上,而不是放在文字上。这段文字只是用来充充场面而已。)
Now, let’s assume we want two cutout corners, say the two bottom ones. We can’t achieve this with only one gradient, so we will need two. Our first thought might be something like this:
现在,假设我们想要两个角被切掉的效果,比如以底部的两个角为例吧。我们只用一层渐变是无法做到这一点的,因此,我们得再加一层。我们最初的想法可能是这样的:
background: #58a;
background:
linear-gradient(-45deg, transparent 15px, #58a 0),
linear-gradient(45deg, transparent 15px, #655 0);
{小提示}
We are using separate colors (
#58a
and#655
) for easier debugging. In practice, both gradients would be the same color.我们为两层渐变使用了不同的颜色(
#58a
和#655
)以便更好地调试。在实际开发中,它们的颜色应该是相同的。
图 3.26
Failed attempt to apply the cutout effect to both bottom corners
尝试给底部的两个角设置切角样式,但失败了。
However, as you can see in Figure XX.XX, this doesn’t work. By default, both gradients occupy the entire element, so they obscure each other. We need to make them smaller, by using background-size
to make each gradient occupy only half the element:
可是,我们在 图 3.26 中可以发现,这样写是行不通的。默认情况下,这两层渐变都会填满整个元素,因此它们会相互覆盖。需要让它们都缩小一些,于是我们使用 background-size
让每层渐变分别只占据整个元素一半的面积。
background: #58a;
background:
linear-gradient(-45deg, transparent 15px, #58a 0)
right,
linear-gradient(45deg, transparent 15px, #655 0)
left;
background-size: 50% 100%;
图 3.27
background-size
is not enough光用
background-size
还是不够的。
You can see what happens in Figure XX.XX. As you can see, although background-size
was applied, the gradients are still covering each other. The reason for this is that we forgot to turn background-repeat
off, so each of our backgrounds is repeated twice. Therefore, our backgrounds are still obscuring each other -- by repetition this time. The new code would look like this:
我们可以在 图 3.27 中看到结果。如你所见,尽管我们已经用了 background-size
,但这两层渐变仍然是相互覆盖的。原因在于,我们忘记把 background-repeat
给关掉了,因而每层渐变图案都各自平铺了两次。这导致我们的两层渐变背景仍然是相互覆盖的——只不过这次是因为背景平铺的关系。改进后的代码会是这样的:
background: #58a;
background:
linear-gradient(-45deg, transparent 15px, #58a 0)
right,
linear-gradient(45deg, transparent 15px, #655 0)
left;
background-size: 50% 100%;
background-repeat: no-repeat;
图 3.28
Our bottom-left and bottom-right cutout corners work now
左下角和右下角的切角效果终于实现了。
You can see the result in Figure XX.XX and verify that--finally--it works! At this point, you are probably able to figure out how to apply this effect to all four corners. You will need four gradients, and the code looks like this:
你可以在 图 3.28 中看最终效果——我们终于成功了!看到这里,你应该已经猜到怎样把四个角都做出切角效果了。你需要四层渐变图案,而代码如下所示:
background: #58a;
background:
linear-gradient(135deg, transparent 15px, #58a 0)
top left,
linear-gradient(-135deg, transparent 15px, #655 0)
top right,
linear-gradient(-45deg, transparent 15px, #58a 0)
bottom right,
linear-gradient(45deg, transparent 15px, #655 0)
bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
图 3.29
The effect applied to all four corners, with four gradients
通过四层渐变图案,就可以给四个角都加上切角效果。
You can see the result in Figure XX.XX. One issue with the preceding code is that it’s not particularly maintainable. It requires five edits to change the background color and four to change the corner size. A preprocessor mixin could help reduce the repetition. Here’s how the code could look with SCSS:
你可以在 图 3.29 中看到结果。上面这段代码有一个问题,它的可维护性并不理想。我们在改变背景色时需要修改五处;而在改变切角尺寸时需要修改四处。使用预处理器的 mixin 可以帮助我们减少代码的重复度。如果用 SCSS 来写,代码会是这样的:
@mixin beveled-corners($bg,
$tl:0, $tr:$tl, $br:$tl, $bl:$tr) {
background: $bg;
background:
linear-gradient(135deg, transparent $tl, $bg 0)
top left,
linear-gradient(225deg, transparent $tr, $bg 0)
top right,
linear-gradient(-45deg, transparent $br, $bg 0)
bottom right,
linear-gradient(45deg, transparent $bl, $bg 0)
bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
}
Then, where needed, it would be used like this, with 2–5 arguments:
然后,在需要的时候,我们就可以直接调用它,并传入 2~5 个参数:
@include beveled-corners(#58a, 15px, 5px);
In this example, the element we will get a 15px
top-left and bottom-right cutout corner and a 5px
top-right and bottom-left one, similar to how border-radius
works when we provide fewer than four lengths. This is due to the fact that we provided default values for the arguments in our SCSS mixin, and yes, these default values can refer to other arguments as well.
在上面这行代码中中,元素的左上角和右下角会得到 15px
的切角效果,而右上角和左下角会得到 5px
的切角效果——如果我们提供的值少于四个时,它的行为跟 border-radius
属性是类似的。这归功于我们在 SCSS 的 mixin 中为各个参数指定了默认值,而且没错,这些默认值也可以引用其他参数的值。
图 3.30
An excellent use of curved cutout corners in g2geogeske.com; the designer has made them the central design element, as they are present in the navigation, the content, and even the footer
g2geogeske.com 网站很好地驾驭了内凹圆角设计风格;设计师把它作为一种核心设计元素,因为内凹圆角不仅出现在导航中,还出现在内容中,甚至是页脚中。
A variation of the gradient method works to create curved cutout corners, an effect many people refer to as “inner border radius,” as it looks like an inverse version of rounded corners. The only difference is using radial gradients instead of linear ones:
上述渐变技巧还有一个变种,可以用来创建弧形切角(很多人也把这种效果称为 “内凹圆角”,因为它看起来就像是圆角的反向版本)。唯一的区别在于,我们会用径向渐变来替代上述线性渐变:
background: #58a;
background:
radial-gradient(circle at top left,
transparent 15px, #58a 0) top left,
radial-gradient(circle at top right,
transparent 15px, #58a 0) top right,
radial-gradient(circle at bottom right,
transparent 15px, #58a 0) bottom right,
radial-gradient(circle at bottom left,
transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
图 3.31
Curved cutout corners, with radial gradients
使用径向渐变生成的内凹圆角效果。
You can see the result in Figure XX.XX. Just like in the previous technique, the corner size can be controlled through the color stop positions and a mixin would make the code more maintainable here as well.
你可以在 图 3.31 中看到效果。跟前一段所述的技巧类似,切角的大小可以通过色标的位置信息来控制,而且我们同样可以用一个 mixin 来改善这段代码的可维护性。
While the gradient-based solution works, it has quite a few issues:
虽然基于渐变的方案是行之有效的,但也不是完全没有问题:
Thankfully, there are a couple different methods we could use, depending on our needs. One of them is to use border-image
with an inline SVG that generates the corners. Given how border-image
works (if you don’t remember, take a look at the quick primer in Figure XX.XX), can you imagine how our SVG would look?
谢天谢地,我们还有其他一些方法可供选择,具体采用哪种方法取决于实际需求。其中之一就是使用 border-image
,并通过一个内联的 SVG 图像来产生切角效果。基于 border-image
的工作原理(如果对此有些淡忘了,不妨回头看看 图 3.32 所作的科普),你能想像出这个 SVG 图像是什么样子吗?
Because dimensions don’t matter (border-image
takes care of scaling and SVGs scale perfectly regardless of dimensons -- ah, the joy of vector graphics!), every measurement could be 1, for easier, shorter, numbers. The corners would be of length 1, and the straight edges would also be 1. The result (zoomed) would look like Figure XX.XX. The code would look like this:
由于尺寸完全不是一个问题(border-image
会解决缩放问题,而 SVG 可以做到与尺寸完全无关的完美缩放——这就是矢量图的好处啦!),每个切片的尺寸都可以设置为 1,以便理解和书写。切角的尺寸也是 1,直线边缘也都是 1。它的(
缩放大后的)样子会是 图 3.32 这样的。而相应的代码可能是这样的:
border: 15px solid transparent;
border-image: 1 url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg"
width="3" height="3" fill="%2358a">\
<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\
</svg>');
图 3.32
Our SVG-based border image, with its slicing
边框图像是基于 SVG 的,切割线也标示出来了。
Note that we used a slice size of 1
. This does not mean 1 pixel; it is referring to the coordinate system of the SVG file (hence the lack of units). If we had specified it in percentages, we would need to approximate of the image with something like
33.34%
. Approximating numbers is always risky, because not all browsers use the same level of precision. However, by using units of the coordinate system of the SVG file, we’re saved from precision headaches.
请注意,我们使用的切片尺寸是 1
。这并不表示 1 个像素;它所对应的是 SVG 文件的坐标系统(因此不需要单位)。如果我们用百分比来指定这些长度,就只能采用 33.34%
这样的值来近似地获得图像尺寸的三分之一。近似值总是有风险的,因为不是所有的浏览器都使用同等级别的计算精度。但如果使用 SVG 文件的坐标系统作为度量单位,我们就不用掉这个坑了。
The result is shown in Figure XX.XX. As you can see, our cutout corners are there, but there is no background. We can solve that in two ways: either by specifying a background, or by adding the keyword fill
to our border-image
declaration, so that it doesn’t discard the middle slice. In this case, we are going to go with specifying a background, because it will also act as a fallback.
它的效果展示在 图 3.33 中。如你所见,我们的切角效果出来了,但还缺少整片背景。我们有两种办法可以解决这个问题:要么提供一个背景色,要么给 border-image
属性值加上 fill
关键字——这样它就不会丢掉 SVG **的那个切片了。在这个例子中,我们决定指定一个背景色,因为它还可以发挥回退的作用。
图 3.33
Applying our SVG on the
border-image
property把我们的 SVG 应用到
border-image
属性中。
In addition, you may have noticed that our corners are smaller than with the previous technique, which can be baffling. But we specified a 15px
border width! The reason is that with the gradient, the 15px
was along the gradient line, which is perpendicular to the direction of the gradient. The border width, however, is not measured diagonally, but horizontally/vertically. Can you see where this is going? Yup, it’s the ubiquitous Pythagorean theorem again, that we also saw in the {#secret-xxx}. Figure XX.XX should help make things clearer. Long story short, to achieve the same size, we need to use a border width that is times larger than the size we would use with the gradient method. In this case, that would be
pixels, which is sensible to approximate to
20px
, unless we really, absolutely need the diagonal size to be as close to 15px
as possible:
这里插一句,你可能已经注意到了,我们的切角跟前面的技巧相比要小一些,这令人有些困惑。我们明明已经指定 15px
作为边框宽度了呀!其实原因在于,在渐变中,这个 15px
是沿着渐变轴来度量的,它的方向是跟渐变推进的方向是一致的。而边框宽度,是以斜边的方式来度量的,而不是水平或垂直方向。你能看出它们的差别吗?对,我们又要请出万能的勾股定理了,我们在 “{$secret$}” 攻略(第 {$page$} 页)中就已经见过它了。图 3.34 应该可以很好的解释这个问题。简而言之,为了得到相同的尺寸,我们需要把渐变中的尺寸拿来,再乘以 倍,然后才能用在边框宽度属性中。在这个例子中,它实际上就是
像素,我们取个近似值
20px
就可以了,除非我们真的、绝对要求斜边尺寸尽最大可能严格接近 15px
:
border: 20px solid transparent;
border-image: 1 url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg"
width="3" height="3" fill="%2358a">\
<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\
</svg>');
background: #58a;
图 3.34
Specifying a
border-width
of15px
, results in a (diagonally measured) corner size of, which is why our corners looked smaller
把
border-width
指定为15px
,所产生的切角尺寸(斜向度量结果)为,这就是为什么它比前面的切角看起来稍小一些。
However, as you can see in Figure XX.XX, this doesn’t exactly have the expected result. Where did our laboriously created cutout corners go? Fear not, young padawan, for our corners are still there. You can understand what’s happening if you set the background to a different color, such as #655
.
但是,我们在 图 3.35 中会发现,这实际上并没有得到我们期望的效果。我费尽周折创建的切角效果去哪儿了啊?别担心,年轻人,我们的切角其实就在那儿。如果你把背景设置为另一种颜色,比如 #655
,就会比较容易理解事情的真相了。
图 3.35
Where did our nice corners go?!
我们美美的切角效果跑到哪儿去了?!
As you can see in Figure XX.XX, the reason our corners disappeared was that the background we specified was obscuring them. All we need to do to fix this is to use background-clip
to prevent the background from extending to the border area:
如 图 3.36 所示,原来这是因为背景色和切角边框混成一团了。我们接下来做的就是请出 background-clip
来修复这个问题,避免背景色蔓延到边框区域:
border: 20px solid transparent;
border-image: 1 url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg"\
width="3" height="3" fill="%2358a">\
<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\
</svg>');
background: #58a;
background-clip: padding-box;
图 3.36
Changing our
background
to another color solves the ...disappearing corners mystery把背景色设置为另一种颜色,终于解开了 “切角消失” 的谜团。
The issue is now fixed and our box now looks exactly like Figure XX.XX. However, we can easily change the corner size in only one place: we just modify the border width. We can even animate it, because border-width
is animatable! We can also change the background with only two edits instead of five. In addition, because our background is now independent of the corner effect, we can even specify a gradient on it, or any other pattern, as long as it’s still #58a
toward the edges. For example, check out Figure XX.XX for an example using a radial gradient from hsla(0,0%,100%,.2)
to transparent
.
这样一来,问题就解决了,我们的容器现在看起来完全就是 图 3.29 中的效果。终于,我们做到了在改变切角尺寸时只改一处:我们只需要修改边框宽度就可以了。我们甚至可以给它加上动画,因为 border-width
属性是支持动画的!我们还做到了在改变背景时只改两处,而不是五处。此外,由于背景效果跟切角效果是相互独立的,我们甚至可以把背景设置为一层渐变图案,或者其他图案——来看看 图 3.37 这个例子吧,它就使用了一幅由 hsla(0,0%,100%,.2)
过渡到 transparent
的径向渐变图案。
图 3.37
Our cutout corners with a radial gradient background
切角与径向渐变背景组合之后的效果。
There is only one small issue remaining. If border-image
is not supported, the fallback is not only the absence of corners. Due to background clipping, it also looks like there is less spacing between the box edge and its content. To fix that, we could just give our border a color that is identical to the background:
我们只剩下最后一个小问题了。在不支持 border-image
的环境下,回退的结果就不仅是没有切角效果了。由于背景裁切的关系,它看起来好像在容器的边缘和内容之间缺了一圈空隙。为了修复这个问题,我们可以给边框指定跟背景一致的颜色:
border: 20px solid #58a;
border-image: 1 url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg"\
width="3" height="3" fill="%2358a">\
<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\
</svg>');
background: #58a;
background-clip: padding-box;
This color is ignored when border-image
applies, but will provide a more graceful fallback when it doesn’t, which will look like Figure XX.XX. As a drawback, this increases the number of edits we need to make to change the background color to three.
当 border-image
属性生效时,这个边框色就会被忽略;但当 border-image
不支持时,边框色就可以提供一个更加平稳的回退措施,此时的结果就是 图 3.35 的样子。不过随之来而的一个缺点就是,当我们改变背景色时所要修改的地方会增加到三处。
{致敬}
Hat tip to Martijn Saly for coming up with the initial idea of using
border-image
and inline SVG as a solution for beveled corners, in a tweet of his from January 5, 2015.向 Martijn Saly 脱帽致敬,感谢他在 2015 年 1 月 5 日的一条推文 中首次提出用
border-image
和内联 SVG 实现斜面切角的创意。
{警告!!} 不完全支持
While the border-image
solution is very compact and relatively DRY, it still has its limitations. For example, we still need to have either a solid color background, or a background that is a solid color toward the edges. What if we want a different kind of background, such as a texture, a pattern, or a linear gradient?
前面所述的 border-image
方案确实非常紧凑,也比较 DRY,但它还是存在一些局限。举个例子,我们要么得指定某个实色的背景,要么得指定一个边缘接近某个实色的背景图案。假如我们想设置其他类型的背景(比如纹理、平铺图案或一道线性渐变),又该如何?
There is another way that doesn’t have these limitations, though it of course has other limitations of its own. Remember the clip-path
property from the {#secret-xxx}? The amazing thing about CSS clipping paths is that we can mix percentages (which refer to the element dimensions) with absolute lengths, offering us tremendous flexibility.
还有另外一种方法不存在这种局限,但也有着它自已独有的局限。还记得我们在 **“{$secret$}” 攻略(第 {$page$} 页)**中用到的 clip-path
属性吗?CSS 裁切路径最神奇的地方在于我们可以同时使用百分比数值(它会以元素自身的宽高作为基数度进行换算)和绝对长度值,从而提供巨大的灵活性。
For example, the code for the clipping path to clip an element in a rectangle with beveled corners of 20px
size (measured horizontally) would look like this:
举个例子,如果用裁切路径来给一个元素切出 20px
大小(以水平方向度量)的斜面切角,它的代码可能就是这个样子的:
background: #58a;
clip-path: polygon(
20px 0, calc(100% - 20px) 0, 100% 20px,
100% calc(100% - 20px), calc(100% - 20px) 100%,
20px 100%, 0 calc(100% - 20px), 0 20px
);
Despite the code being short, this doesn’t mean it’s DRY, which is one of its biggest issues if you’re not using a preprocessor. In fact, it’s the most WET of the pure CSS solutions we presented, with eight (!) edits required to change the corner size. On the other hand, we can change the background in only one place, so there’s that.
尽管这种方法的代码确实短了很多,但这并不意味着它是 DRY 的,如果你不用预处理器,这就是它最大的缺陷。实际上,它是本节所述的所有纯 CSS 方案中最不 DRY 的,因为如果要改动切角的尺寸,我们需要修改八处!不过另一方面,改变背景倒是变得比较方便,只需修改一处即可。
Among its benefits is that we can have any background we want, or even clip replaced elements such as images. Check out Figure XX.XX for an image styled with beveled corners. None of the previous methods can do this. In addition, it is also animatable, not only to different corner sizes, but different shapes altogether. All we need to do is use a different clipping path.
这个方法最大的好处在于,我们可以使用任意类型的背景,甚至还可以对替换元素(比如图片)进行裁切。来看看 图 3.38 这个例子,它给一张图片设置了斜面切角样式。
图 3.38
An image styled with beveled corners, via
clip-path
运用
clip-path
属性给一张图片设置斜面切角样式。
Beyond its WETness and its limited browser support, it also has the drawback that it will clip text, if there is no sufficient padding, as it just clips the element without distinguishing between its parts. In contrast, the gradient method will just let the text overflow beyond the corners (because they’re just a background) and the border-image
method will act just like a border and make the text wrap.
暂且不提代码不够 DRY 以及浏览器支持程度上的不足,它还有一个更大的缺点,就是当内边距的不够宽时,它会裁切掉文本,因为它只能对元素做统一的裁切,并不能区分元素的各个部分。与此不同的是,渐变方案允许文字溢出并超出切角区域(因为它只是背景图案);而 border-image
方案则会起到普通边框的作用,令文字折行。
{关于未来}
切角效果
In the future, we won’t have to resort to CSS gradients, clipping, or SVG for this effect. A new property,
corner-shape
, is coming in CSS Backgrounds & Borders Level 4 to save us from these pains. It will be used in conjunction withborder-radius
to produce cutout corners of different shapes, with their sizes defined inborder-radius
. For example, specifying15px
cutout corners on all sides would be as simple as:在未来,我们再也不需要费尽心机地动用 CSS 渐变、裁切或 SVG 来实现这个效果了。CSS 背景与边框(第四版)将引入一个全新的属性
corner-shape
,可以彻底解决这个痛点。这个属性需要跟border-radius
配合使用,从而产生各种不同形状的切角效果,而切角的尺寸正是border-radius
的值。举例来说,为容器的四个角指定15px
的斜面切角就是如此简单:border-radius: 15px; corner-shape: bevel;
Related specs
- CSS Backgrounds & Borders
- CSS Image Values
- CSS Transforms
- CSS Masking
- CSS Transitions
- CSS Backgrounds & Borders Level 4
相关规范
p9 中currentColor小节的第二行
增加了很多新的颜色关键字,比如lightgoldernrodyellow等。
此处英文关键字的排版似乎加空格更合适一些。
slider.insertBefore(img, div);
正确的写法应该是 slider.insertBefore(div, img);
。
这个错误已存在于英文原书的待确认的勘误表,不过这确实是个错误。
第50页—图 2-64显示在正文
我在微博上发起了一个小调查,结果也证实了我的猜测——大约有 30% 的读者是几乎不看译者序的。于是我就脑洞大开了:我在纸质书中把译者序的篇幅压缩到最短 → 节省了一页纸 → 凑巧减少了一个印张 → 成本下降 → 售价降低 → 销量大增,这岂不是功德无量的大好事?蝴蝶效应谁知道呢!
因此,这篇译者序将只包含致谢环节。(当然我知道还有 50% 的朋友是必看译者序的,请放心,我会在网上为你们准备一篇超长完整版。)
最后,我还为所有看完译者序的朋友准备了一件礼物:我正在为这本书编写注解,尽我所能解答关于这本书的所有疑问。所有注解都将以开源的方式发布到 book.cssmagic.net 网站,在那里你还可以与我以及万千读者交流探讨、携手共进。祝阅读愉快!
{--:}CSS魔法
{--:}2015 年 11 月 16 日于百姓网
伪元素方案中对伪元素设置z-index:-1后,会使得伪元素的层叠层次不仅被推到宿主元素之后,甚至可能推到宿主元素的父级元素的背景之后,导致无效。 demo
解决方法:为.button添加z-index:0。
本文是早期译版,未经校审。仅供参考。
Prerequisites
- Viewport-relative units (see the {#secret-xxx})
calc()
背景知识
- 视口相关的长度单位(参见第 {$page$} 页的 “{$secret$}” 攻略)
calc()
This is one of the oldest and most common problems in web design, so common that most of us have experienced it at one point or another. It can be summarized as follows: a footer with any block-level styling, such as a background or shadow, works fine when the content is sufficiently long, but breaks on shorter pages (such as error messages). The breakage in this case being that the footer does not “stick” at the bottom of the viewport like we would want it to, but at the bottom of the content.
在网页设计中,存在一个相当古老但又相当常见的问题,它是前端工程师绕不开的坎,躲得了初一逃不过十五。这个问题可以简单地概括如下:有一个具有块级样式的页脚(比如它设置了背景或阴影),当页面内容足够长时,这个页脚一切正常;而当页面较短时(比如错误信息页面)就会出现问题。此时的问题在于,页脚不能像我们所期望的那样 “紧贴” 在视口的最底部,而是紧跟在内容的下方。
{原书注释!}
Specifically, the issue appears on pages whose content is shorter than the viewport height minus the footer height.
具体来说,当页面内容的长度小于视口高度减去页脚高度时,这个问题就会出现。
It is not only its ubiquity that made it popular, but also how deceptively easy it looks at first. It’s a textbook case of the type of problem that requires significantly more time to solve than expected. In addition, this is still not a solved problem in CSS 2.1: almost all classic solutions require a fixed height for the footer, which is flimsy and rarely feasible. Furthermore, all of them are overly complicated, hacky, and have specific markup requirements. Back then, this was the best we could do, given the limitations of CSS 2.1. But can we do better with modern CSS, and if so, how?
这个问题不仅普遍存在,而且乍看起来确实相当简单。因此,在所有 “隐蔽大坑” 式的难题中,它简直就是教科书般的典型案例。不仅如此,CSS 2.1 基本上拿它没有办法:几乎所有的经典解决方案都需要给页脚设置固定的高度,而这显然是不健壮的,也是不实际的。此外,所有这些解决方案都太过复杂了、太像 hack 了,而且往往还要求网页按照特定的结构来写。在那个年代,受制于 CSS 2.1 有限的能力范围,这已是我们所能达到的最好结果了。不过,在现代 CSS 协助下,我们可以做得更好吗?如果答案是肯定的,又该如何去做?
{原书注释!}
If you’ve never had the pleasure of pulling your hair out and diving in the existing literature for this problem, here are a few popular links with existing, widely used solutions that have served many a web developer before CSS Level 3 specs were conceived:
如果你从来没有在这个大坑里体验过疯魔的快感,那不妨来感受一下前人针对这个问题所积累下来的宝贵财富。在 CSS 第三版规范推出之前,这些解决方案曾经挽救过一个又一个的网页开发者:
- cssstickyfooter.com
- ryanfait.com/sticky-footer
- css-tricks.com/snippets/css/sticky-footer
- pixelsvsbytes.com/blog/2011/09/sticky-css-footers-the-flexible-way
- mystrd.at/modern-clean-css-sticky-footer
The last two are the most minimal in the lot, but still have their own limitations.
最后两个解决方案是这众多链接中使用最少的,但仍然有其独到之处。
We will work with an extremely bare-bones page with the following markup inside the <body>
element:
我们手头的这个页面极其简单,<body>
元素内的结构代码如下所示:
<header>
<h1>Site name</h1>
</header>
<main>
<p>Bacon Ipsum dolor sit amet...
<!-- Filler text from baconipsum.com -->
<!-- 从 baconipsum.com 那里复制了一些示意文字过来 --></p>
</main>
<footer>
<p>© 2015 No rights reserved.</p>
<p>Made with ♥ by an anonymous pastafarian.</p>
</footer>
图 7.23
How our simple page looks when its content is sufficiently long
这是一个简单的页面,当它的内容足够长时看起来是这样的。
We have also applied some basic styling to it, including a background on the footer. You can see how it looks in Figure XX.XX. Now, let’s reduce the content a bit. You can see what happens then, in Figure XX.XX. This is the sticky footer problem in all its glory! Great, we have recreated the problem, but how do we solve it?
然后我们给页面加上一些基本样式,再给页脚加上一层背景。你可以在 图 7.23 中看到它的样子。现在,让我们把页面内容缩短一点儿,然后可以在 图 7.24 中看到结果。此时页脚沉不到底的问题就完全暴露出来了!好吧,我们重现了这个问题,但我们该如何解决这个问题呢?
图 7.24
The sticky footer problem in all its glory
页脚沉不到底的问题相当刺眼啊。
If we assume that our footer text will never wrap, we can deduce a CSS length for its height:
假设这个页脚的文本永远不可能折行,那我们就可以推算出它实际所占的高度:
2 lines × line height + 3 × paragraph margin + vertical padding =
{-:-}2 行 × 行高 + 3 × 段落的垂直外边距 + 页脚的垂直内边距 =
{-:-}2 × 1.5em
+ 3 × 1em
+ 1em
= 7em
Similarly, the header height is 2.5em
. Therefore, by using viewport-relative units and calc()
, we can “stick” our footer to the bottom with essentially one line of CSS:
同样,我们可以得出页头的高度是 2.5em
。接下来,借助视口相关的长度单位以及 calc()
函数,我们只需一行 CSS 代码就可以表达出这种尺寸关系,从而把页脚 “固定” 到底部:
main {
min-height: calc(100vh - 2.5em - 7em);
/* Avoid padding/borders screwing up our height: */
/* 避免内边距或边框搞乱高度的计算: */
box-sizing: border-box;
}
{警告!!}
Be careful when using
calc()
with subtraction or addition: the+
and-
operators require spaces around them. This very odd decision was made for future compatibility. If at some point keywords are allowed incalc()
, the CSS parser needs to be able to distinguish between a hyphen in a keyword and a minus operator.在
calc()
中使用加减法时要特别当心:记得在+
和-
运算符的左右各加一个空格。这条怪异的规则是为了向前兼容。在未来的某个时候,calc()
内部可能会允许使用关键字,那么 CSS 解析器就需要有依据来区分关键字中的连字符和减号运算符。
Alternatively, we could apply a wrapper around our <header>
and <main>
elements so that we only need to calculate the footer height:
或者换个方式,我们可以把 <header>
和 <main>
元素包在一个容器里,然后在算式中就只需要考虑页脚的高度了:
#wrapper {
min-height: calc(100vh - 7em);
}
图 7.25
The footer after we’ve applied CSS to make it stick
我们运用 CSS 的计算功能将页脚固定到了底部。
This works (Figure XX.XX) and it seems to be slightly better than the existing fixed height solutions, mainly due to its minimalism. However, except for very simple layouts, this is not practical at all. It requires us to assume that the footer text will never wrap, we need to edit the min-height
every time we change the footer metrics (i.e., it is not DRY), and unless we’re willing to add a wrapper HTML element around our header and content, we need to do the same calculations and modifications for the header as well. Surely, in this day and age we can do better, right?
这个方法是有效的(图 7.25),而且它似乎比已有的那些需要固定高度的方案还要稍稍好一些,主要好在它的代码极其精简。不过,如果页面布局不是这么简单的话,那这个方法就完全不实用了。它不仅要求我们确保页脚内的文本是永远不会折行的,而且每当我们改变了页脚的尺寸时,都需要跟着调整 min-height
值(也就是说,这不够 DRY);此外,除非我们愿意给页头和内容主体套一层额外的 HTML 元素,不然我们还得跟着页头的尺寸改。想必在这个美好的新时代,我们还有更好的办法,对吗?
Flexbox is perfect for these kinds of problems. We can achieve perfect flexibility with only a few lines of CSS and there is no need for weird calculations or extra HTML elements. First, we need to apply display: flex
to the <body>
element, as it’s the parent of all three of our main blocks, to toggle Flexible Box Layout (Flexbox) for all three of them. We also need to set flex-flow
to column
, otherwise they will be all laid out horizontally on a single row (Figure XX.XX):
Flexbox 对于此类问题同样是完美的选择。我们只需寥寥几行 CSS 代码就可以完美达成十分灵活的布局效果,而且完全不需要复杂的计算,或是添加多余的 HTML 元素等等。首先,我们需要对 <body>
元素设置 display: flex
,因为它是这三个主要区块的父元素,当父元素获得这个属性之后,就可以对其子元素触发 “伸缩盒布局模型”。我们还需要把 flex-flow
设置为 column
,否则子元素会被水平排放在一行上(图 7.26):
body {
display: flex;
flex-flow: column;
}
图 7.26
Applying
flex
without applying anything else arranges the children of our element horizontally只应用
flex
而没有应用其他属性时,会让所有子元素水平排列。
At this point, our page looks about the same as it did before all the Flexbox stuff, as every element occupies the entire width of the viewport and its size is determined by its contents. Ergo, we haven’t really taken advantage of Flexbox yet.
此时,页面看起来跟没有启用 Flexbox 的情况似乎是一样的,因为所有元素都占据了整个视口的宽度,而它们的高度也都是由它们自身的内容来决定的。也就是说,我们还没有真正发挥 Flexbox 的威力呢。
To make the magic happen, we need to specify a min-height
of 100vh
on <body>
, so that it occupies at least the entire height of the viewport. At this point, the layout still looks exactly like Figure XX.XX, because even though we have specified a minimum height for the entire body element, the heights of each box are still determined by their contents (i.e., they are intrinsically determined, in CSS spec parlance).
为了完全释放它的魔力,我们首先要把 <body>
的 min-height
属性指定为 100vh
,这样它就至少会占据整个视口的高度。不过,现在整个页面的布局仍然跟 图 7.24 无异,原因在于,虽然我们已经为整个 body
元素指定了最小高度,但各个子元素的高度仍然是以各自的内容为准的(按照 CSS 规范的说法,它们的高度仍然是由内部因素来决定的)。
What we need here is for the height of the header and footer to be intrinsically determined, but the height of the content should flexibly stretch to all the leftover space. We can do that by applying a flex
value that is larger than 0
(1
will work) to the <main>
container:
此时我们所期望的是,页头和页脚的高度由其内部因素来决定,而内容区块的高度应该可以自动伸展并占满所有的可用空间。我们只要给 <main>
这个容器的 flex
属性指定一个大于 0
的值(比如 1
即可),就可以实现这个效果了:
body {
display: flex;
flex-flow: column;
min-height: 100vh;
}
main { flex: 1; }
{小提示}
The
flex
property is actually a shorthand offlex-grow
,flex-shrink
, andflex-basis
. Any element with aflex
value greater than0
becomes flexible andflex
controls the ratio between the dimensions of different flexible elements. For example, in our case, if<main>
hadflex: 2
and<footer>
hadflex: 1
, the height of the footer would be twice the height of the content. Same if the values were4
and2
instead of2
and1
, because it’s their relationship that matters.这个
flex
属性实际上是flex-grow
、flex-shrink
和flex-basis
的简写语法。任何元素只要设置了一个大于0
的flex
值,就将获得可伸缩的特性;而flex
的值就负责控制多个可伸缩元素之间的尺寸分配比例。举例来说,在我们眼前的这个例子中,如果<main>
是flex: 2
而<footer>
是flex: 1
的话,那么内容区块的高度将是页脚高度的两倍。如果把它们的值从2
和1
改为4
和2
,结果也是一样的,因为真正起作用的是它们的数值比例。
That’s it, no more code required! The perfect sticky footer (same visual result as in Figure XX.XX), with only four simple lines of code. Isn’t Flexbox beautiful?
这样就可以了!我们只需要四行简单的代码,就完美实现了紧贴底部的页脚(跟 图 7.25 中的效果一致)。Flexbox 是不是相当霸气?
{致敬}
Hat tip to Philip Walton for coming up with this technique.
向 Philip Walton 脱帽致敬,感谢他提出 这个技巧。
Related specs
相关规范
章节编号 | 章节标题 | 试读样章 [1] | 官方电子版 [2] | 注解 [3] |
---|---|---|---|---|
000 | 译者序 | 试读 | 免费阅读 ↗ | - |
003 | 本书收到的赞誉 | 试读 | 免费阅读 ↗ | - |
030 | 原书推荐序 | 试读 | 免费阅读 ↗ | - |
040 | 前言 | 试读 | 免费阅读 ↗ | 免费阅读 |
041 | 致谢 | 试读 | 免费阅读 ↗ | - |
042 | 这本书是怎样炼成的 | - | 免费阅读 ↗ | - |
043 | 关于本书 | 试读 | 免费阅读 ↗ | 免费阅读 |
第一章 | 引言 | 免费阅读 ↗ | ||
101 | Web 标准:是敌还是友 | 试读 | 〃 | - |
102 | CSS 编码技巧 | 试读 | 〃 | 免费阅读 |
第二章 | 背景与边框 | 购买后阅读 ↗ | ||
201 | [1] 半透明边框 | - | 〃 | 免费阅读 |
202 | [2] 多重边框 | - | 〃 | - |
203 | [3] 灵活的背景定位 | - | 〃 | - |
204 | [4] 边框内圆角 | - | 〃 | 免费阅读 |
205 | [5] 条纹背景 | - | 〃 | 免费阅读 |
206 | [6] 复杂的背景图案 | 试读 | 〃 | 免费阅读 |
207 | [7] 伪随机背景 | - | 〃 | - |
208 | [8] 连续的图像边框 | - | 〃 | 免费阅读 |
第三章 | 形状 | 购买后阅读 ↗ | ||
301 | [9] 自适应的椭圆 | - | 〃 | - |
302 | [10] 平行四边形 | - | 〃 | - |
303 | [11] 菱形图片 | - | 〃 | - |
304 | [12] 切角效果 | 试读 | 〃 | 免费阅读 |
305 | [13] 梯形标签页 | - | 〃 | - |
306 | [14] 简单的饼图 | - | 〃 | 免费阅读 |
第四章 | 视觉效果 | 购买后阅读 ↗ | ||
401 | [15] 单侧投影 | - | 〃 | - |
402 | [16] 不规则投影 | - | 〃 | - |
403 | [17] 染色效果 | - | 〃 | 免费阅读 |
404 | [18] 毛玻璃效果 | 试读 | 〃 | 免费阅读 |
405 | [19] 折角效果 | - | 〃 | 免费阅读 |
第五章 | 字体排印 | 购买后阅读 ↗ | ||
501 | [20] 连字符断行 | - | 〃 | 免费阅读 |
502 | [21] 插入换行 | - | 〃 | 免费阅读 |
503 | [22] 文本行的斑马条纹 | - | 〃 | 免费阅读 |
504 | [23] 调整 Tab 的宽度 | - | 〃 | - |
505 | [24] 连字 | - | 〃 | 免费阅读 |
506 | [25] 华丽的 & 符号 | 试读 | 〃 | 免费阅读 |
507 | [26] 自定义下划线 | - | 〃 | - |
508 | [27] 现实中的文字效果 | - | 〃 | 免费阅读 |
509 | [28] 环形文字 | - | 〃 | 免费阅读 |
第六章 | 用户体验 | 购买后阅读 ↗ | ||
601 | [29] 选用合适的鼠标光标 | - | 〃 | - |
602 | [30] 扩大可点击区域 | - | 〃 | - |
603 | [31] 自定义复选框 | 试读 | 〃 | 免费阅读 |
604 | [32] 通过阴影来弱化背景 | - | 〃 | - |
605 | [33] 通过模糊来弱化背景 | - | 〃 | - |
606 | [34] 滚动提示 | - | 〃 | - |
607 | [35] 交互式的图片对比控件 | - | 〃 | 免费阅读 |
第七章 | 结构与布局 | 购买后阅读 ↗ | ||
701 | [36] 自适应内部元素 | - | 〃 | - |
702 | [37] 精确控制表格列宽 | - | 〃 | - |
703 | [38] 根据兄弟元素的数量来设置样式 | - | 〃 | - |
704 | [39] 满幅的背景、定宽的内容 | - | 〃 | - |
705 | [40] 垂直居中 | - | 〃 | 免费阅读 |
706 | [41] 紧贴底部的页脚 | 试读 | 〃 | 免费阅读 |
第八章 | 过渡与动画 | 购买后阅读 ↗ | ||
801 | [42] 缓动效果 | - | 〃 | 免费阅读 |
802 | [43] 逐帧动画 | 试读 | 〃 | 免费阅读 |
803 | [44] 闪烁效果 | - | 〃 | - |
804 | [45] 打字动画 | - | 〃 | - |
805 | [46] 状态平滑的动画 | - | 〃 | - |
806 | [47] 沿环形路径平移的动画 | - | 〃 | 免费阅读 |
在增强网页设计效果时,你会使用生成性内容,而不是依赖冗余的标签和图片。
“生成性内容” 是指在文档结构中不存在的、由 CSS 生成的内容。比如 ::after
等伪元素就是最典型的生成性内容。
我们只会用到一个工具函数——
$$()
。它可以让我们更容易地获取和遍历所有匹配特定 CSS 选择符的 DOM 元素。
这个函数的作用类似于 jQuery 的选择器函数 $()
,但它返回的元素集合是数组。这样我们就可以直接使用原生的数组方法来操作这个集合。
考虑到这些特性的草案可能还很不稳定,浏览器兼容性查询网站可能还没有包含它们……
这里的 “浏览器兼容性查询网站” 指的就是 CanIUse.com 这样的网站。
如果要检测选择符和
@
规则的支持情况,则会稍稍复杂一些。
@
规则” 指的是 @support
和 @media
等这些以 @
开头的 CSS 指令。
(暂无)
本文是早期译版,未经校审。仅供参考。
This is a new generation of CSS books, for a new generation of CSS. No longer a simple language tied to complicated browser hacks and workarounds, CSS is now a richly powerful and deeply complex ecosystem of over 80 W3C specifications. Nobody is better at making sense of this new CSS, and of providing design principles that help you solve problems with it, than Lea Verou—among the handful of truly amazing coders I've known.
这本书是为新一代 CSS 所写的新一代 CSS 书籍。也许从前的 CSS 只会让你联想到浏览器里的各种小把戏,但如今 CSS 已经成为一门功能强大、具备完整生态、涉及 80 多项 W3C 规范的复杂语言。而在我所知的技术专家中,没人比 Lea Verou 更能领会新一代 CSS 的精髓,没人能像她那样透彻地给出问题解决之道。
Jeffrey Zeldman,
author, Designing with Web Standards
{--:}——Jeffrey Zeldman
《网站重构》作者
Lea Verou's encyclopaedic mind is one of a kind, but thanks to this generous book, you too can get an insight into what it's like to wield CSS to do just about anything you can think of. Even if you think you know CSS inside-out, I guarantee that there are still secrets in this book waiting to be revealed.
Lea Verou 那渊博的学识是无法复制的,但这本书的倾囊相授令我等凡人也有机会一窥 CSS 强大威力与神奇魔力。即使你认为自己对 CSS 已经了如指掌,我保证这本书仍然会让你大开眼界。
Jeremy Keith,
Shepherd of Unknown Futures, Clearleft
{--:}——Jeremy Keith
《JavaScript DOM 编程艺术》作者
If you want the inside scoop on fascinating CSS techniques, smart best practices, and some flat-out brilliance, don't hesitate—read this book. I loved it!
如果你想深入探寻 CSS 的美妙之处、创意之处与智慧之处,那就别犹豫了——赶快翻开这本书吧。它让我爱不释手!
Eric A. Meyer
{--:}——Eric A. Meyer
《CSS 权威指南》作者
Lea is an exceedingly clever coder. This book is absolutely packed with clever and useful ideas, even for people who know CSS well. Even better, you'll feel more clever in your work as this book encourages pushing beyond the obvious.
Lea 是一位极其智慧的编码人。这本书蕴含了无穷的智慧与创意,即使你已是 CSS 高手,也仍将从中获益。更重要的是,这本书还将长久激发你的智慧,因为它已传授给你突破平庸的信念。
Chris Coyier,
CodePen
{--:}——Chris Coyier
CodePen 创始人
CSS Secrets is an instant classic—so many wonderful tips and tricks you can use right away to enhance your UX designs!
《{$BOOK_NAME$}》就是一部现成的经典——它汇集了众多精彩绝伦的技巧,能够立即改善你的网页体验!
Christopher Schmitt,
author of CSS Cookbook
{--:}——Christopher Schmitt
《CSS Cookbook》作者
There aren't many books that provide as many practical techniques as Lea Verou's CSS Secrets. Filled with dozens of solutions to common design problems, the book is a truly valuable collection of smart tips and tricks for getting things done well, and fast. Worth reading, even if you think that you know the ins and outs of CSS!
极少有哪本书能像 Lea Verou 的这本《{$BOOK_NAME$}》一样囊括如此之多的实用技巧。它针对常见的网页设计难题提出了数不胜数的解决方案,手到擒来,高效可靠,堪称 CSS 智慧与技巧的集大成之作。哪怕你自认是 CSS 领域的专家,这本书仍然值得一读!
Vitaly Friedman,
cofounder and editor-in-chief of Smashing Magazine
{--:}——Vitaly Friedman
《Smashing Magazine》联合创始人兼总编
Without fail, whenever I read something written by Lea Verou, I manage to learn something new. CSS Secrets is no different. The book is broken down into easy-to-digest chunks filled with lots of juicy bits of knowledge. While some of the book is very forward looking, there is plenty that I’ve been able to take away and apply to my own projects right away.
每当我读到 Lea Verou 的文章时,总能收获新技能。这本《{$BOOK_NAME$}》也不例外。这本书经过了精心编排,它把丰富的经验和技巧分解为一篇篇的短文,易读易懂。尽管书中的部分内容颇具前瞻性,但大多数知识还是极为实用的,我一直在自己的项目中现学现用。
Jonathan Snook,
web designer and developer
{--:}——Jonathan Snook
网页设计师兼开发者
Lea's book is fantastic. She bends and contorts CSS to do things I'm pretty sure even the spec authors never imagined! You will learn multiple ways of accomplishing each graphic effect by trying out the techniques she walks through in each chapter. Later, in your work, you'll find yourself saying, "hmm, that thing Lea did will work perfectly here!" Before you know it, your site is almost image free because your graphics are all in easy to maintain CSS components. What's more, her techniques are fun, walking the line between practical and improbable!
Lea 的这本书棒极了。她对 CSS 运用已臻化境,她创造的各种神奇效果我敢说即便是规范作者也无法想像!这本书的每个章节都各具特色,呈现了各式各样的技巧,它将教会你用多种思路来达成各式各样的设计效果。不久之后,你在工作中就会不由自主地念念有词:“嗯,这个效果可以用 Lea 的方法来完美实现!” 甚至在不知不觉之中,你的网站都不怎么需要用到图片了,因为你已经把所有图形效果都写成了易于维护的 CSS 组件。最重要的是,她的表达方式生动活泼,注重实效但又不失奇趣!
Nicole Sullivan,
Principal Software Engineer, creator of OOCSS
{--:}——Nicole Sullivan
资深软件工程师、OOCSS 项目创始人
Lea Verou's CSS Secrets is useful not so much as a collection of CSS tips, but as a textbook on how to solve problems with CSS. Her in-depth explanation of the thought process behind each secret will teach you how to create your own solutions to CSS problems. And don't miss the Introduction, which contains some must-read CSS best practices.
Lea Verou 的这本《{$BOOK_NAME$}》不只是一套 CSS 技巧的合集,而更是一本注重实效的 CSS 经典教科书。在每篇攻略中,她不仅讲授解决问题的方法,更会深入解析背后的思路,从而教会你如何自行探索更多的 CSS 奥秘。千万不要错过第一章引言,那里有你不可不知的 CSS 最佳实践。
Elika J. Etemad (aka fantasai),
W3C CSS Working Group Invited Expert
{--:}——Elika J. Etemad(网名 fantasai)
W3C CSS 工作组特邀专家
Lea’s presentations have long been must-see events at web development conferences around the world. A distillation of her years of experience, CSS Secrets provides elegant solutions for thorny web design issues, while also—and more importantly—showing how to solve problems in CSS. It's an absolute must-read for every frontend designer and developer.
Lea 在全球各地的技术会议上所作的演讲早已成为不可错过的学习素材。作为她多年经验的提练与升华,这本《{$BOOK_NAME$}》为各种网页设计顽症提供了优雅的解决方案,而且更重要的是,这本书展示了 CSS 解决问题的方式和思路。这绝对是每位前端设计师和开发者必读的一本书。
Dudley Storey,
designer, developer, writer, web education specialist
{--:}——Dudley Storey
设计师、开发者、作家、Web 教育专家
I thought I had a pretty advanced understanding of CSS, then I read Lea Verou's book. If you want to take your CSS knowledge to the next level, this is a must-own.
我一直以为我对 CSS 的理解已经达到相当高的层次了,直到我读到了 Lea Verou 的这本书。如果你希望把自己的 CSS 技能提升到一个新的段位,那这本书正是不二之选。
Ryan Seddon,
Team Lead, Zendesk
{--:}——Ryan Seddon
Zendesk 团队负责人
CSS Secrets is by far the most technical book that I have ever read on the topic. Lea has managed to push the boundaries of a language as simple as CSS so far that you will not be able to distinguish this from magic. Definitely not a beginner's read; it's heavily recommended to anyone thinking they know CSS all too well.
《{$BOOK_NAME$}》是迄今为止我在这个领域里读到的最具专业性的书籍。或许你曾以为 CSS 这门语言简单无奇,但 Lea 已将它的疆域拓展为如此广袤的一片天地,令人叹为观止、惊为幻术。这本书显然不适合初学者;但我会强烈推荐给每一位自信精通 CSS 的开发者。
Hugo Giraudel,
frontend developer, Edenspiekermann
{--:}——Hugo Giraudel
前端开发者、Edenspiekermann 公司工程师
I often think that CSS can seem a bit like magic: a few rules can transform your web pages from blah to beautiful. In CSS Secrets, Lea takes the magic to a whole new level. She is a master magician of CSS, and we get to explore that magical world along with her. I can’t count how many times I said out loud while reading this book, “That’s so cool!” The only trouble with CSS Secrets is that after reading it, I want to stop everything else I’m doing and play with CSS all day.
我一直觉得 CSS 就像是一种魔法:寥寥数行的代码就可以让你的网页脱胎换骨、焕然一新。而在《{$BOOK_NAME$}》这本书中,Lea 将这种魔法提升到了一个全新的境界。她就像是一位 CSS 魔法大师,带领我们在这个充满魔力的世界里自由翱翔。在阅读这本书的过程中,我曾无数次地脱口而出——“这简直帅呆了!” 这本书唯一的问题在于,在读完它之后,我已对其它事情兴趣全无,终日沉溺在 CSS 的魔幻之中无法自拔。
Elisabeth Robson,
cofounder of WickedlySmart.com and coauthor of Head First JavaScript Programming
{--:}——Elisabeth Robson
WickedlySmart.com 联合创始人、《Head First JavaScript Programming》联合作者
CSS Secrets is a book that all web developers should have in their library. Using the information it contains you'll learn numerous hints and tips to make CSS perform tasks you never thought possible. I was astonished at how often the author came up with simple and elegant lateral thinking solutions to problems that had bugged me for years.
《{$BOOK_NAME$}》值得被每一位网页开发者收入囊中。这本书将传授给你丰富的经验和技巧,帮助你实现那些你原以为不可能用 CSS 完成的任务。最令我惊讶不已的是,对于每一个困扰我多年的网页样式难题,作者居然都可以从不同的角度提出不止一种简洁优雅的解决方案。
Robin Nixon,
web developer, online instructor, and author of several books on CSS
{--:}——Robin Nixon
网页开发者、在线讲师、多本 CSS 书籍的作者
As a master designer and programmer, Lea Verou's book is as beautiful and as well thought out as her code. Whether you're fairly new to CSS, or well versed in the intricacies of CSS3, this book has something for everyone.
Lea Verou 是一位网页设计和开发领域的大师,而她的书跟她的代码一样富有美感、饱含智慧。不论你对 CSS 是否精通,也不论你对 CSS3 的种种细节是否了解,这本书总有适合你的地方。
Estelle Weyl,
Open Web Evangelist and coauthor of CSS: The Definitive Guide
{--:}——Estelle Weyl
开放 Web 布道师、《CSS 权威指南》联合作者
原因是
background-origin
的默认值是padding-box
,因此,图片的显示尺寸不仅取决于 padding box 的尺寸,而且被放置在了 padding box 的原点(左上角)。
此时背景图片的显示尺寸取决于 padding box 的尺寸,原因在于元素设置了 background-size: cover
样式。这个属性的行为是在保持图片的宽高比不变的前提下,先把图片缩小至无穷小,再尝试逐渐拉伸图片,当图片刚刚好可以覆盖住元素时定形。
说到 “覆盖住元素”,还是那句话,实际上需要覆盖住的是元素的 “背景定位区域”。而元素的背景定位区域是由 background-origin
属性来决定的,默认是 padding-box
。
(暂无)
本文是早期译版,未经校审。仅供参考。
The primary target audience for this book is intermediate to advanced CSS developers. By getting the introductory stuff out of the way, we can explore more advanced use cases of modern CSS features and combinations thereof. This, however, means that quite a few assumptions have been made about you, dear reader:
这本书的主要目标受众是正在由中级向高级进阶的 CSS 开发者。我们将跳过基础入门部分,直接探索现代 CSS 特性所针对的更高级的应用场景,并将它们融汇贯通。不过在此之前,亲爱的读者朋友,我假设你已经具备以下条件:
!important
all over your code because you actually understand specificity, inheritance, and the cascade. You know what the different parts of the box model are, and you are not fazed by margin collapsing. You are familiar with the different length units and know when it’s best to use each one.!important
来打补丁,因为你已经深入理解选择符优先级、继承和层叠机制。你知道盒模型中的各个部分都是什么,而且不会为外边距重叠头疼不已。你对各种长度单位了如指掌,而且清楚它们分别该用在什么地方。box-shadow
, or create a linear gradient. You’ve played with some basic 2D transforms, and have enhanced interactions with basic transitions and animations.However, to enable readers that don’t meet all these assumptions to enjoy this book, there is a “Prerequisites” box in the beginning of some secrets, briefly listing any CSS knowledge or previous secrets that need to be known for the secret to make sense (excluding CSS 2.1 features, otherwise the box would get really long). It looks like this:
尽管如此,为了让不完全符合上述条件的读者也可以愉快阅读这本书,在每篇攻略的开头,我都会准备一个 “背景知识” 提示框,简要地列出读懂当前攻略所必需的 CSS 知识以及前面相关的攻略(不过 CSS 2.1 的内容就不列出了,否则这个提示框会撑爆的)。这个提示框是这个样子的:
Prerequisites
box-shadow
- basic CSS gradients
- the “Flexible ellipses” secret on page 76
背景知识
box-shadow
- 基本的 CSS 渐变
- “{$secret$}” 攻略(第 {$page$} 页)
This way, even if certain things are not already known, one can read up about them and come back to the secret afterward. As long as their prerequisites are met, the secrets can actually be read in any order, though there is value in reading them in the book order, as a lot of thought has been put into what the optimal order is.
这样一来,哪怕你暂时还没有掌握这些基础知识,也可以在补好课之后再回来阅读。只要你具备了某篇攻略所要求的背景知识,就可以直接学习它了,不用在乎顺序。不过,还是建议你按照书中的顺序来阅读,因为我花了很多心思才把这些章节调整到最佳顺序。
Note that I mentioned “CSS developers” and that “design skills” are not in the list of assumptions above. It’s important to note that this is not a design book. While it unavoidably touches on certain design principles and describes a few UX improvements, CSS Secrets is first and foremost a book about solving problems with code. CSS might have a visual output, but it is still code, just like SVG, WebGL/OpenGL, or the JavaScript Canvas API is code, not design. Writing good, flexible CSS requires the same kind of analytical thinking that programming does. Nowadays, most people use preprocessors for their CSS, with variables, math, conditionals, and loops, so it’s almost starting to look like programming!
请注意,我在上面列出的条件中说的是 “CSS 开发者”,并没有要求任何 “设计能力”。一定要意识到这并不是一本关于设计的书。虽然我们会不可避免地涉及一定的设计原理、阐述一些用户体验的改进方式,但这本书的初衷和核心价值是帮助你用代码解决问题。CSS 会产生视觉上输出结果,但它仍然还是代码,就好比 SVG、WebGL/OpenGL 或 JavaScript 里的 Canvas API,它们都是代码,而不是设计。编程要求我们具备条理性的思维,而想要写出合理的、灵活的 CSS 代码同样如此。如今,绝大多数 CSS 开发者都在使用 CSS 预处理器,他们会用到变量、数学计算、条件判断和循环,因此写 CSS 看起来已经像是在编程了。
This is not to imply that designers are discouraged from reading this book. Anybody who has sufficient coding experience with CSS can benefit from it, and there are many talented designers who can also write excellent CSS code. However, it’s important to note that teaching you how to improve the visual design or usability of a website is not among the goals of this book, even if it happens as a side effect.
当然这并不是说不鼓励设计师们读这本书。任何人只要具备足够的 CSS 编写经验,都可以从本书中受益;而且有很多才华横溢的设计师可以写出非常出色的 CSS 代码。总之,希望大家可以明白,这本书的目标并不是教大家如何改进网站的视觉设计或可用性——即使它在这些方面会起到间接的帮助。
The book consists of 47 “secrets,” grouped by topic in seven chapters. These secrets are more or less independent and—as long as their prerequisites are met—can be read in any order. The demos in every secret are not complete websites, or even parts thereof. They are purposefully as small and simple as possible, in order to facilitate understanding. The assumption is that you already know what you want to implement. The purpose of this book is not to give design ideas, but implementation solutions.
这本书包含了 47 篇攻略,并根据主题的不同收入到 7 章中。这些攻略基本上是相互独立的,并且可以以任意顺序阅读——只要你掌握了各篇攻略所需的背景知识。在每篇攻略中出现的演示案例并不是完整的网站,甚至连网站的片断都算不上。这些案例有意设计得尽量简短,以便降低理解负担。这本书的目的并不是要给出设计创意,而是给出创意的实现方案。
Every secret is split into two or more sections. The first section, titled “The problem,” introduces a common CSS challenge that we are going to solve. Sometimes this introduction might describe widely popular solutions that are suboptimal (e.g., solutions that require a lot of markup, hardcoded values, etc.), and usually concludes with variations of the question “Is there a better way to achieve this?”
每篇攻略分为两个或多个大段。第一大段叫作 “难题”,会引入一项常见的 CSS 挑战,需要我们去解决。有时这个引言可能会列出一些广泛流行但不够完美的解决方案(比如,需要添加大量的结构标记、需要死写数值等等),而且往往会以类似 “还有更好的方法吗” 这样的设问作为结尾。
After introducing the problem, one or more solutions follow. This book was inspired by the CSS talks I have presented at various conferences so I tried to maintain the interactive presentation format as much as a book allows. Therefore, every solution is illustrated by a number of figures, demonstrating the visual output for every step of the solution that results in a visual change. Because figures are not always directly next to the text that describes what they demonstrate, they are numbered and referenced in the text. You can see an example of a figure in Figure P.1 and the current sentence was an example of a reference to it.
在引入问题之后,会给出一个或多个解决方案。这本书的灵感来源于我在各种技术会议上所作的 CSS 演讲,因此我尝试在书中尽可能保持那种互动式的风格。因此,每种解决方案都会配以多幅插图,把每个会产生视觉变化的步骤都用图片演示出来。由于所有插图不一定都能紧贴着对应的段落,我就给它们都编上号,这样就可以在正文中引用这些插图。你可以在 图 P.1 中看到一个插图的示例,而且这句话本身也是一个引用插图的示例。
图 P.1
This is an example sidebar figure, introducing the great Sir Adam Catlace
这是一个侧栏插图的示例。图中是伟大的 Adam Catlace 爵士。
{原书注释!}
Notes, such as this one, provide additional information or explain a term mentioned in the text.
这里是侧栏注解。它会提供额外信息,或解释正文中提到的某个术语。
{警告!!}
[!] This is a warning. Its purpose is to warn you (surprising, I know!) about possible false assumptions and certain things that could go wrong.
这是一个警告。它的作用是警告你(要做好心理准备哦),为你指出一些常见的误区,或提醒你哪些地方容易出错。
Inline code is denoted by monospace text
and colors often have a small preview next to them as well (e.g., #f06
). Block-level code looks like this:
行内的代码采用 等宽字体
来表示,颜色值也是如此。颜色值前面通常还会加一个小的预览色块(比如这样 #f06
)。而代码块则是这样的:
background: url("adamcatlace.jpg");
or this:
或这样的:
<figure>
<img src="adamcatlace.jpg" />
<figcaption>Sir Adam Catlace</figcaption>
</figure>
As you might have noticed, when the language of a code block is not CSS, it’s noted in the top-right corner. Also, when the example discussed only involves a single element, and no pseudo-classes or pseudo-elements are involved, there is usually no selector or braces ({}
) included in the code blocks, for brevity.
你可能已经看出来了,只有当代码块的语言不是 CSS 时,语言类型才会在右上角标记出来。同样,为简洁起见,当示例代码只涉及单个元素、不涉及伪类或伪元素时,通常就不再把选择符和花括号({}
)写出来了。
All JavaScript examples in the book are vanilla JavaScript, with no frameworks or libraries required. There is only one helper function used, $$()
, in order to make it easier to loop over a set of elements that match a certain CSS selector. The function’s definition is:
本书中的所有 JavaScript 示例都是原生的 JavaScript,不需要依赖任何类库或框架。我们只会用到一个工具函数——$$()
,它可以让我们更容易地获取和遍历所有匹配特定 CSS 选择符的 DOM 元素。这个函数的定义如下:
function $$(selector, context) {
context = context || document;
var elements = context.querySelectorAll(selector);
return Array.prototype.slice.call(elements);
}
{小花絮}
[TRIVIA] Side trivia
随便聊两句
Dark “Trivia” sections at the bottom of pages introduce tangentially related trivia, such as the historical or technical background behind a CSS feature. They are not necessary for using or understanding the main material, but readers might find them interesting nevertheless.
书页最底部深色调的 “小花絮” 段落会扯得稍微远一些,比如介绍一下某个 CSS 特性背后的历史性的或技术性的趣闻。它们对使用和理解正文内容没有直接作用,但读者或许会在这里发现他们感兴趣的东西。
Every secret includes one or more live examples that can be accessed with short, memorable URLs in play.csssecrets.io
. The references to them look like this:
每篇攻略至少会附上一个在线示例,URL 都在 play.csssecrets.io 域名下,而且都很简短易记。这些在线示例的链接是这样展示的:
{试一试} play.csssecrets.io/polka
It is strongly recommended that you check out the “Play!” examples, especially if you are confused by the techniques described or if you get stuck while following along.
强烈建议你打开这些 “试一试” 示例,尤其是当你对文中所述的技巧不那么清楚时,或者当你在读到某个地方卡住了的时候。
{致敬}
Credit where it’s due: When a technique described was first documented by someone else in the community, credit will be given in a “Hat Tip” paragraph like this one, referencing the URL of the source as well. We all know that having to find the “References” section at the end of a book is a hassle, so these essentially provide references in context.
该表扬就表扬:当文中提到的某个技巧是某人在社区中首次提出时,我们都会在类似本段这样的 “致敬” 环节向他发出谢意,同时也会给出相关链接。如果把这些链接都放在全书末尾的 “参考链接” 章节,查起来会很不方便,因此我们将采用就近注解的方式。
{关于未来}
[FUTURE] Future solutions
未来的解决方案
“Future” sections (positioned at the bottom of pages and set on a dark background) introduce techniques that are already in draft specifications, but at the time of writing have no implementations. Readers should always check if these techniques are supported, as they might have been implemented after the publication of this book. In cases where the feature is obscure enough that browser support websites might not include it, the section will include a test that the reader can load, in short memorable URLs, such as the one shown here in the “Test!” example. These tests are usually designed so that shades of green appear when the feature is supported and shades of red otherwise. The exact instructions are mentioned in the code, as a comment.
“关于未来” 段落(通常安排在书页的最底部、并配以深色背景)会介绍一些已经被列入规范草案的技术,但在编写本书时可能还没有浏览器实现。但你在阅读本书的时候,别忘了再次查证这些特性是否已经可用了,因为当本书出版后,浏览器可能已经实现了。考虑到这些特性的草案可能还很不稳定,浏览器兼容性查询网站可能还没有包含它们,因此,这个段落同样也会提供一个测试性的示例页面,以便读者自行查验。这些测试页面的 URL 同样也很简短易记,会以下面这样 “测一测” 的形式标注出来。这些测试通常是这样设计的——绿色阴影表示当前浏览器已经支持某个特性,而红色阴影反之。在测试代码的注释中,也提供了明确的说明。
At the end of almost every secret you’ll find a list of related specifications that looks like this:
在每篇攻略的末尾,你还会发现一份相关规范的清单,就像下面这样:
[RELATED SPECS]
相关规范
This includes references to all the specifications from which features were mentioned. However, just like the “Prerequisites” box, this does not apply to CSS 2.1, otherwise it would be listed in the “Related Specs” section of every single secret. This means that the few secrets that only discuss CSS 2.1 features have no “Related Specs” section at all.
这份清单列出了当前攻略所述技术所对应的各项技术规范。不过,跟前面提到的 “背景知识” 提示框一样,这份清单也不再列出 CSS 2.1 的内容。这意味着,少数几篇只讨论 CSS 2.1 的攻略就根本没有 “相关规范” 这个段落了。
Possibly the biggest peculiarity of this book is the complete lack of browser compatibility tables. This was a conscious decision, as with today’s browser release cycles, such information is bound to get out of date before this book even hits the shelves. I believe that inaccurate browser support information is misleading, and is actually worse than no information.
本书的最大创举可能就是完全不提供浏览器兼容性表格。这是一个经过深思熟虑的决定,因为以当前浏览器的更新频繁来看,这些信息必定在书还没有上架时就已经过时了。我认为不准确的浏览器支持信息极具误导性,还不如干脆没有。
However, most secrets described either currently have decent browser support and/or degrade gracefully. In cases where a technique described presently has particularly poor browser support, there is a “Limited Support” warning icon next to the relevant solution, like the one next to this paragraph. This should be enough to hint that you should not use the solution without looking up browser support for it and taking extra care for providing good fallbacks.
尽管如此,书中大多数攻略要么已经在浏览器中获得了良好支持,要么可以做到平稳退化。万一某项技术在眼下的支持程度特别不理想时,我会在紧邻相关段落的侧栏处设置一个 “不完全支持” 的警告图标,比如本段旁边就有一个示例。它应该足以提醒你在正式使用这些技术之前不要忘了查证一下浏览器的支持情况,并且要特别注意做好回退机制。
{警告!!} 不完全支持
[LIMITED SUPPORT]
There are plenty of excellent websites containing up-to-date browser support information. Here are some suggestions:
有很多优秀的网站提供了及时有效的浏览器兼容性信息。推荐如下:
Sometimes you might find that a certain feature is supported, but slightly differently across browsers. For example, it might need a vendor prefix, or slightly different syntax. Only the standards-compliant, unprefixed syntax will be included in the examples. However, you can almost always use different syntaxes alongside and let the cascade take care of which one wins. For this reason, always place the standard version last. For example, to get a vertical linear gradient from yellow
to red
, the book would only list the standard version:
有时候你可能会发现某个特性已经被浏览器支持了,但不同浏览器的表现可能还有着细微的差异。比如说,它可能需要一个浏览器前缀,或者在语法上存在细微的差别。我们的示例代码中只会包含符合标准的、无前缀的语法。不过在绝大多数情况下,你都可以同时使用各种不同的语法,并且通过层叠机制来确保哪条声明最终生效。出于这个原因,你应该把标准语法排在最后。举例来说,要得到一条从黄色到红色的垂直渐变色,本书只会列出标准语法:
background: linear-gradient(90deg, yellow, red);
However, if you want to support very old browsers, you might end up having to write something like the following:
但是如果你想要支持那些较早些的浏览器,你可能得把代码写成这样才能奏效:
background: -moz-linear-gradient(0deg, yellow, red);
background: -o-linear-gradient(0deg, yellow, red);
background: -webkit-linear-gradient(0deg, yellow, red);
background: linear-gradient(90deg, yellow, red);
{原书注释!}
You can read more on vendor prefixes, why they exist, and how to abstract them away from your code in the “A story of ice, fire, and vendor prefixes” section on page {$page$}.
关于浏览器前缀的更多信息,比如它们为什么会存在,以及如何在代码中把它们抽象出来,你可以在 **“{$section$}” 段落(第 {$page$} 页)**中进一步了解。
Because the landscape of these differences is just as fluid as browser support, it is expected that things like this are part of your standard research before using a CSS feature and are not discussed further in the solutions presented.
这种差异的局面跟浏览器兼容性的局面一样,时刻处在变化之中,因此,当你在使用某项 CSS 特性之前,不要忘记这方面也是你要做好的功课,而且本书就不再为此花费过多篇幅了。
Similarly, most of the time it’s good practice to provide fallbacks, so that your website doesn’t break in older browsers, even if it doesn’t look as fancy in them. These are not discussed extensively when they are obvious, as the assumption is that you know how the cascade works. For example, when specifying a gradient, such as the one just shown, you should also add a solid color version before all of them. A good idea for the solid color might be the average of the two gradient colors (in this case, rgb(255, 128, 0)
):
还有一些内容我们也不再赘述了。提供回退机制通常是一种很好的做法,这样可以确保你的网站不会在低版本浏览器中挂掉,只是看起来没有那么炫而已。当这些后备机制很明显的时候,我们就不展开讨论了,因为你应该已经很清楚样式声明的层叠机制了。举例来说,当我们像上面那样指定一个渐变色作为背景的时候,你应该在前面添加一行实色背景的声明。如果这个实色是取自渐变色的平均色值(比如在这个例子中是 rgb(255, 128, 0)
),那便是极好的。
background: rgb(255, 128, 0);
background: -moz-linear-gradient(0deg, yellow, red);
background: -o-linear-gradient(0deg, yellow, red);
background: -webkit-linear-gradient(0deg, yellow, red);
background: linear-gradient(90deg, yellow, red);
However, sometimes it’s not possible to provide decent fallbacks through the cascade. As a last resort, you could use tools like Modernizr, which adds classes like textshadow
or no-textshadow
to the root element (<html>
), so you can use them to target elements only when certain features are (not) supported, like so:
不过,有些时候不太可能只通过样式的层叠就可以提供完善的回退样式。这时别忘了你还有一招,可以使用 Modernizr 这样的工具来给根元素(<html>
)添加一些辅助类,比如 textshadow
或 no-textshadow
等等,这样你就可以针对支持或不支持某些特性的浏览器来分别编写样式了,就像这样:
h1 { color: gray; }
.textshadow h1 {
color: transparent;
text-shadow: 0 0 .3em gray;
}
If the feature you are trying to create a fallback for is sufficiently new, you could use the @supports
rule, which is the “native” Modernizr. For example, the preceding code would become:
如果你想尝试使用的某个 CSS 特性非常新,还可以试试用 @supports
规则来实现回退,它可以视作浏览器 “原生的” Modernizr。比如说,上面的代码还可以这样写:
h1 { color: gray; }
@supports (text-shadow: 0 0 .3em gray) {
h1 {
color: transparent;
text-shadow: 0 0 .3em gray;
}
}
However, for now, be wary of using @supports
. By using it here we just limited our effect not only to browsers that support text shadows, but also to browsers that support the @supports
rule—a much more limited set.
但在眼下,还必须慎用 @supports
。在上面的例子中,我们想要的文本投影效果只会在那些支持 text-shadow
且同时支持 @supports
规则的浏览器中生效——这个范围明显比我们所期望的要窄。
Last, but not least, there is always the option of using a few lines of home-baked JavaScript to perform feature detection and add classes to the root element in the same fashion as Modernizr. The main way to determine whether a property is supported is to check its existence on the element.style
object of any element:
最后,同样值得一提的是,即使你不打算使用 Modernizr,你也可以自己写一小段 JavaScript 代码来实现相同的功能——做一些特性检测然后给根元素加一些辅助类。如果要检测某个样式属性是否被支持,核心思路就是在任一元素的 element.style
对象上检查该属性是否存在:
var root = document.documentElement; // <html>
if ('textShadow' in root.style) {
root.classList.add('textshadow');
}
else {
root.classList.add('no-textshadow');
}
If we need to test for multiple properties, we can easily turn this into a function:
如果我们需要检测多个属性,也可以很容易地把上述代码改造成一个函数:
function testProperty(property) {
var root = document.documentElement;
if (property in root.style) {
root.classList.add(property.toLowerCase());
return true;
}
root.classList.add('no-' + property.toLowerCase());
return false;
}
If we want to test a value, we need to assign it to the property and check if the browser retains it. Because we are modifying styles here and not just testing for their existence, it makes sense to use a dummy element:
如果我们想要检测某个具体的属性值是不是支持,那就需要把它赋给对应的属性,然后再检查浏览器是不是还保存着这个值。很显然,这个过程会改变元素的样式,因此我们需要用一个隐藏元素来做这件事情:
var dummy = document.createElement('p');
dummy.style.backgroundImage = 'linear-gradient(red,tan)';
if (dummy.style.backgroundImage) {
root.classList.add('lineargradients');
}
else {
root.classList.add('no-lineargradients');
}
This can easily be converted to a function as well:
这段代码同样也可以很容易地改造成一个函数:
function testValue(id, value, property) {
var dummy = document.createElement('p');
dummy.style[property] = value;
if (dummy.style[property]) {
root.classList.add(id);
return true;
}
root.classList.add('no-' + id);
return false;
}
Testing selectors and @rules
is a bit more complex, but follows the same principle: when it comes to CSS, browsers drop anything they don’t understand, so we can check if a feature is recognized by dynamically applying it and checking if it was retained. Of course, keep in mind that a browser being able to parse a CSS feature offers no guarantee that the feature is correctly implemented, or even that it’s implemented at all.
如果要检测选择符和 @
规则的支持情况,则会稍稍复杂一些。不过原理也很简单,在解析 CSS 代码时,浏览器总会丢弃它自己无法识别的部分,因此我们可以动态地应用样式并检查它是否生效,以此来判断浏览器是否可以识别某个特性。当然,我们也要清楚地认识到,浏览器可以解析某个 CSS 特性并不代表它已经实现(或正确实现)了这个特性。
前言
这本书图文并茂、全彩印刷,对排版工作也提出了极大的挑战。
本页面仅记录排版方面的失误或值得改进的地方。
图标含义
- ✅ - 已在后续印次中修正。
▶️ - 已被出版社确认,即将在后续印次中修正。- ⏸ - 已确认,但可能不会修正。
- 🕒 - 有待出版社确认。
最后一个字符 3
应与其它字符的颜色一致。
(贡献者: 图灵社区 Aline)
代码在浅粉色的代码块内没有垂直居中。
(贡献者: @HDMing)
这个代码块缺失了浅粉色背景。
(贡献者: @HDMing)
代码在浅粉色的代码块内没有垂直居中。
(贡献者: @HDMing)
width: 100px; height: 100px;
height
应设置为高亮色。
(贡献者: @zhuxinghan)
<dd>[email protected]</dd>
本行中的 <dd>
应设置为高亮色(绿色)。
(贡献者: @HDMing)
HTML <span class="amp">&</span> CSS
<span class="amp">
末尾的 >
应为绿色。
(贡献者: @HDMing)
left: 50%;
本行中的 left
应设置为高亮色(玫红色),并加粗。
(贡献者: @HDMing)
transform: translate(-50%, -50%);
本行中的 transform
应设置为高亮色(玫红色),并加粗。
(贡献者: @HDMing)
35 交互式的图片对比控件
数字 35
的花瓣形背景图形偏上,导致数字底部显示不完整。
(贡献者: @HDMing)
span
标签 >
颜色不对应该为绿色
left
颜色不对应该为玫红色
transform
颜色不对应该为玫红色
事实上,指定一个等于描边宽度的扩张值在某些浏览器中可能会得到渲染异常……
此时描边和投影在理论上所占的范围应该是完全一样的,这里所说的 “渲染异常” 指的是投影可能会溢出描边的范围。
出现这种渲染异常的底层原因在于,描边与投影的绘制原理不同:描边是完全规则的矩形,完全对齐像素;而具有扩张效果的投影需要由元素的原始形状扩散而成。
对浏览器来说,前者更像是一种基于光栅的绘图算法,而后者更像是一种矢量算法。因此,在对待非整数像素值时,两者的行为可能存在差别——前者在渲染前会对像素长度值取整,而后者可能会接受非整数的长度值(在计算出矢量路径之后光栅化),从而引发两者之间的渲染误差。
如果浏览器的显示比例处于缩放的情况下,或者显示器是视网膜屏幕(由多个物理像素显示一个逻辑像素的高精度显示设备),又或者元素处于 CSS 动画之中,通常比较容易出现这种渲染误差。
(暂无)
本文是早期译版,未经校审。仅供参考。
Ah, the good old days. Back in the previous millennium, we had just two CSS-capable browsers, and what they did was a fairly limited subset of a fairly limited specification, so you could fairly easily keep a complete map of what worked and what didn’t in your head. That map included the bugs in each implementation, as they had many errors and oversights, some of them verging on the comical. Heck, some bugs were so fundamental that they made the browsers’ layout behavior completely incompatible, forcing us to come up with a whole army of parser-bug-exploiting hacks just to work around the differences!
啊,过去的日子多美好啊!回想 2000 年之前,我们只有两款支持 CSS 的浏览器,而且它们所实现的也只是一套非常有限的规范的一个非常有限的子集,因此你可以很容易地在自己的头脑中建立一幅完整的图景,标记出什么好用、什么不好用。这幅图景需要包含各个浏览器实现中的各种 bug,因为这些实现其实存在着不少的错误和疏忽,有些地方甚至错得离谱。唉,有些 bug 还涉及非常基础的层面,以致于各浏览器的布局行为完全不兼容,这迫使我们不得不想出对策,反过来利用浏览器自身的解析器 bug 来变相地纠正这些不一致的行为!
Wait a minute. The old days were horrible. Glad we’re done with all that!
慢着。过去那些日子其实糟透了。所幸它们已经一去不复返了!
Things really have gotten so much better in the last several years, CSS-wise. Browsers have, for the most part, converged on compatibility, and where they are incompatible, it’s nearly always because one browser doesn’t support a feature that another does, as opposed to both of them trying to support the same thing differently, and usually badly. The specifications have pushed capabilities forward even as they’ve added features that recreate the convoluted tricks of old in much simpler, more compact ways. CSS has far more features and far more power than ever before--but, as we all know, with great power comes great complexity. It’s not even a case of intentional complexity: when you combine enough working parts, no matter how simple each may be, interesting things can and do emerge. (For more on this topic, see The LEGO Movie.)
就在最近这几年,CSS 领域已经发生了极大的好转。浏览器(绝大多数)已经在兼容性上逐渐趋同,它们互不兼容的地方几乎都是因为某一家支持了某个特性,而另一家还没支持——这比两家都支持但效果不一样要强多了。规范不仅推动了兼容性的进步,与此同时,规范还增加了新的特性,用简洁得多的新方法取代了以前繁琐的技巧。CSS 拥有了比以多得多的特性、强得多的能力——不过,我们都知道,功能越强大,复杂度也会越高。这甚至已经不是表象上的复杂度问题了:当你把足够多的工作部件组合到一起之后,不管单个部件看起来有多简单,这个组合体一定会产生有趣的结果。(关于这个话题的更多内容,请看《乐高大电影》。)
But it’s exactly that unintended complexity that gives CSS the ability to surprise us with emergent features we never expected, or even planned. There are secrets to be found in the intersections of properties and the bending of values. You can carve corners with gradients, animate elements, increase clickable areas, even create pie charts...and so much more. CSS has capabilities that we only dreamed of back when I was but a lad, possibilities beyond anything we imagined. It’s added abilities that I once thought could never be expressed in a compact, human-readable manner--animations, to pick one example. It’s advanced far enough that I’m confident there are many, many secrets yet to be discovered. Perhaps you’ll discover some of them.
这其实是无意之中产生的复杂度,在不经意之间,CSS 已经获得了种种我们从未期望或设计过的神奇特性。在属性与属性的交错之间、在值与值的混合之下,有很多秘密有待发现。你可以通过渐变图案来挖出凹角、让元素产生动画、扩大可点击区域、甚至创建饼图……不一而足。如今,CSS 已经拥有了我多年前梦寐以求的强大功能,它带来的可能性已经远远超越了我当初的想像。很多我原以为绝不可能以简洁易懂的方式表达出来的功能,现在也已成为 CSS 的一部分——比如动画。CSS 已经进化得如此强大,令我坚信它仍然还有很多很多的秘密有待我们去发现——或许某天你也会有所斩获。
Until that day arrives, there are plenty of fascinating techniques that have already been unearthed, and few have done more than Lea Verou to find and share them with the world. From her blog posts to her open source contributions to her dynamic, interactive talks all over the world, Lea has amassed a formidable reserve of CSS knowledge. This book is a beautiful distillation of that knowledge. You now possess a guide to some of the most interesting, surprising, and useful techniques that CSS has yielded, a guide compiled by one of the brightest minds in the field. What Lea has prepared for you in these pages will enrich, delight, and--yes--even astonish.
眼下,很多炫酷的技巧已经被世人所发挖,但极少有人能像 Lea Verou 那样善于探索、乐于分享。从她的博客文章到她的开源贡献,再到她在全球各地所做的生动演讲,Lea 在 CSS 领域已经建立了令人钦佩的知识储备。而这本书正是这些知识储备的完美升华。你手里的这本书由这个领域内最顶尖的一位智者精心打造,她将带你领略 CSS 所能达成的最有趣、最神奇、最实用的技巧。Lea 在这本书里为你准备的内容将令你充实、愉悦、甚至惊喜。
Go forth, learn well, and let these discoveries be secrets no more.
狂奔吧,成长吧,别再让这些精湛的技艺沦为失传的秘密!
Eric A. Meyer
{--:}——Eric A. Meyer,《CSS 权威指南》作者
坐标图 右边的公式(写反了):
错误:sinα = x/r cosα = y/r
正确:sinα = y/r cosα = x/r
本文是早期译版,未经校审。仅供参考。
Prerequisites
- Basic CSS animations
- the {#secret-xxx}
背景知识
- 基本的 CSS 动画
- “{$secret$}” 攻略(第 {$page$} 页)
Quite often, we need an animation that is difficult or impossible to achieve by transitioning CSS properties on elements. For example, a cartoon moving or a complex progress indicator. In this case, image-based frame-by-frame animations are perfect, but surprisingly challenging to accomplish on the Web in a flexible manner.
在很多时候,我们需要一个动画,但这个动画又很难(或不可能)只通过某些 CSS 属性的过渡来实现。比如说,一段卡通影片,或是一个复杂的进度指示框。在这种场景下,基于图片的逐帧动画才是完美选择,但是,想在网页中以一种灵活的方式来实现这种动画,可谓是一项惊人的挑战啊。
At this point, you might be wondering, “Can’t we just use animated GIFs?” The answer is yes, for many cases, animated GIFs are perfect. However, they have a few shortcomings that might be a deal-breaker for certain use cases:
看到这里,你可能会产生这样一种疑问:“难道我们用 GIF 动画不行吗?” 对大多数情况来说,答案是 “行”,GIF 动画可以完美胜任。但是,GIF 动画也有一些短板,在某些场景下可能会让整体效果大打折扣:
图 8.13
A semi-transparent progress indicator (on dabblet.com); this is impossible to achieve with animated GIFs
半透明的加载提示(来自 dabblet.com 网站);这个效果用 GIF 动画是无法实现的。
Back in 2004, there was an effort by Mozilla to address the first two issues by allowing frame-by-frame animation in PNG files, akin to the way we can have both static and animated GIF files. It was called APNG and was designed to be backward compatible with non-supporting PNG viewers, by encoding the first frame in the same way as traditional PNG files, so old viewers would at least display that. Promising as it was, APNG never got enough traction and to this day, has very limited browser and image editor support.
在 2004 年的时候,Mozilla 发起了一个建议:在 PNG 格式中增加对逐帧动画的支持,就像 GIF 格式同时支持静态图像和动画一样。这种格式被称作 “APNG”,它在设计时就考虑到了如何向后兼容——它会把动画的第一帧画面以传统 PNG 文件的方式进行编码,因此,对于那些不支持 APNG 特性的旧版看图软件来说,至少可以把第一帧正常显示出来。尽管它看起来很有前途,但 APNG 并没有获得足够的推广,时至今日,只有极少数的浏览器和图像处理软件支持这种格式。
{原书注释!}
For more information about APNG, see wikipedia.org/wiki/APNG.
关于 APNG 的更多信息,请参阅 wikipedia.org/wiki/APNG。
Developers have even used JavaScript to achieve flexible frame-by-frame animations in the browser, by using an image sprite and animating its background-position
with JS. You can even find small libraries to facilitate this! Is there a straightforward way to achieve this with only nice, readable CSS code?
后来,网页开发者们甚至会动用 JavaScript 来在浏览器中实现灵活的逐帧动画,比如用一张拼合图片(image sprite)作为背景,然后用 JS 去动态地控制它的 background-position
。你甚至还可以找到一些专门为此设计的小型类库!我们有没有可能只借助清爽易读的 CSS 代码就把这个需求给搞定呢?
Let’s assume we have all frames of our animation in a PNG sprite like the one shown in Figure XX.XX.
假设我们已经把动画中的所有帧全部拼合到一张 PNG 图片中了,如 图 8.14 所示。
图 8.14
Our spinner’s eight frames (dimensions: 800×100)
旋转菊花所需要的八帧画面已经合并到一起了(图片尺寸为 800×100)。
We also have an element that will hold the loader (don’t forget to include some text, for accessibility!), to which we have already applied the dimensions of a single frame:
然后我们用一个元素来容纳这个加载提示(别忘了在里面写一些文字,来确保可访问性!),并把它的宽高设置为单帧的尺寸:
<div class="loader">Loading...</div>
.loader {
width: 100px; height: 100px;
background: url(img/loader.png) 0 0;
/* Hide text */
/* 把文本隐藏起来 */
text-indent: 200%;
white-space: nowrap;
overflow: hidden;
}
图 8.15
The first frame of our loader shows, but there is no animation yet
加载提示的第一帧显示出来了,但还没有动画效果。
Currently, the result looks like Figure XX.XX: the first frame is displayed, but there is no animation. However, if we play with different background-position
values, we will notice that -100px 0
gives us the second frame, -200px 0
gives us the third frame, and so on. Our first thought could be to apply an animation like this:
目前它的效果就是 图 8.15 中的这个样子了:第一帧显示出来了,但还没有动画效果。但是,如果我们尝试对它应用各种不同的 background-position
值,就会发现 -100px 0
会让它显示出第二帧,-200px 0
会得到第三帧,以此类推。于是我们的第一反应就是用下面的方法来让它动起来:
@keyframes loader {
to { background-position: -800px 0; }
}
.loader {
width: 100px; height: 100px;
background: url(img/loader.png) 0 0;
animation: loader 1s infinite linear;
/* Hide text */
/* 把文本隐藏起来 */
text-indent: 200%;
white-space: nowrap;
overflow: hidden;
}
However, as you can see in the following stills (taken every 167ms), this doesn’t really work:
但是,在下面这几幅静态截图(动画每经历 167ms 时的情形)中,你会发现这样其实是行不通的:
图 8.16
Our initial attempt for a frame-by-frame animation failed, as we did not need interpolation between keyframes
我们为了实现逐帧动画所做出的首次尝试失败了,因为我们并不需要帧与帧之间的过渡状态。
It might seem like we’re going nowhere, but we are actually very close to the solution. The secret here is to use the steps()
timing function, instead of a Bézier-based one.
看起来似乎我们走错了路,但其实我们离真正的答案已经相当接近了。这里的秘诀在于,采用 steps()
这个调速函数,而不是基于贝塞尔曲线的调速函数。
“The what timing function?!” you might ask. As we saw in the previous chapter, all Bézier-based timing functions interpolate between keyframes to give us smooth transitions. This is great; usually, smooth transitions are exactly the reason we are using CSS transitions or animations. However, in this case, this smoothness is destroying our sprite animation.
“采用哪个调速函数?!” 你可能会这样问。就像我们在前一节所看到的,所有基于贝塞尔曲线的调速函数都会在关键帧之间进行插值运算,从而产生平滑的过渡效果。这个特性很棒,因为在通常情况下,平滑的过渡确实是我们使用 CSS 过渡和动画的原因。但在眼前的这个场景下,这种平滑特性恰恰毁掉了我们想实现的逐帧动画效果。
Very unlike Bézier timing functions, steps()
divides the whole animation in frames by the number of steps you specify and abruptly switches between them with no interpolation. Usually this kind of abruptness is undesirable, so steps()
is not talked about much. As far as CSS timing functions go, Bézier-based ones are the popular kids that get invited to all the parties and steps()
is the ugly duckling that nobody wants to have lunch with, sadly. However, in this case, it’s exactly what we need. Once we convert our animation to the following, our loader suddenly starts working the way we wanted it to:
跟贝塞尔曲线调速函数迥然不同的是,steps()
会根据你指定的步进数量,把整个动画切分为多帧,而且整个动画会在帧与帧之间硬切,而不会做任何插值处理。通常这种硬切效果是我们极力避免的,因此我们很少听到关于 steps()
的讨论。在 CSS 调速函数的世界里,贝塞尔曲线家的孩子就像是处处受众人追捧的校花校草,而 steps()
则是旁人避之唯恐不及的丑小鸭。不过,在我们这个案例中,后者却是我们通向成功的关键。一旦我们把整个动画的代码修改为下面的形式,这个加载提示就瞬间变成我们想要的样子了:
animation: loader 1s infinite steps(8);
图 8.17
A comparison of
steps(8)
,linear
and the default timing function,ease
对比
steps(8)
、linear
以及默认的ease
这三种调整函数的差异。
Keep in mind that steps()
also accepts an optional second parameter, start
or end
(default) that specifies when the switch happens on every interval (see Figure XX.XX for the default behavior of end
), but that is rarely needed. If we only need a single step, there are also shortcuts: step-start
and step-end
, which are equivalent to steps(1, start)
and steps(1, end)
, respectively.
别忘了 steps()
还接受可选的第二个参数,其值可以是 start
或 end
(默认值)。这个参数用于指定动画在每个循环周期的什么位置发生帧的切换动作(关于默认值 end
的行为,参见 图 8.17),但实际上这个参数用得并不多。如果我们只需要一个单步切换效果,还可以使用 step-start
和 step-end
这样的简写属性,它们分别等同于 steps(1, start)
和 steps(1, end)
。
{致敬}
Hat tip to Simurai for coming up with this useful technique in Sprite sheet animation with steps().
向 Simurai 脱帽致敬,感谢他在《用
steps()
实现拼合图片的动画效果》这篇文章中提出了这个实用的技巧。
Related specs
相关规范
(本文完)
这个词通常表示在纯粹的代码层面就可以改动某个效果。
正如 JavaScript 在 2004 年前后所经历的那场革命
在 2004 至 2005 年间,Google 通过其 Gmail 和 Google Maps 等 Web 富应用产品提出了 Ajax 概念,掀起了 Web App 的热潮。
(暂无)
左右两侧的背景颜色反了,左边应为#655,右边应为#58a
由国内顶尖的计算机图书公司 “图灵文化” 正式引进。
本书的中文版已由 “魔法哥” 翻译完成。
联系方式:
大家都很关注这个问题,为此我制作了一张 进度表,所有环节的进展一目了然。大家想起这本书的时候,回来刷刷页面就可以了。
这里简单预测一下:在最理想的情况下,纸质书在年内就可以在各大书店上架。当然,电子版会更快,图灵官网最快可能在十一月份逐章发售电子版,可在线阅读。
实际进度比预期要慢一些,主要是因为全彩图文的排版与印刷比较花时间。目前预计图灵官网的在线电子版会在一月内发售;纸质书将在二月份上架。 目前预计纸质书将在 2016 年 5 月上旬发售。
(迫不及待的同学请阅读本站发布的 试读样章,试读量超过全书的 10%。)
作为一本图文并茂的经典著作,中文版将与英文原版一样,采用全彩印刷。同时,中文版的版式也将与英文原版基本保持一致。
出于成本考虑,纸张采用 80 克胶版纸(并非英文原版的铜版纸);开本为大 16 开,比英文原版略大一些。
格式:除了图灵官网标配的 “在线阅读版电子版” 以外,还将提供 PDF、Mobi 等格式,兼容 Kindle、iBooks 等电子书阅读器。(一次购买,即可获得所有格式电子书的正版授权。)
排版:
发售时间:排版确定后就可以制作并发售,预计将和纸质书同期上架。 已于 2016 年 4 月中旬上架。
购买纸质书之后如何兑换电子版?
纸质书的封底有一张 “图灵刮刮卡” 贴纸,刮开后请到 图灵社区 兑换 “银子”(银子是图灵社区的虚拟货币,可用于购买电子书)。兑换后会收到一张优惠券,凭此优惠券可以在 这本书的详情页面 以 9.99 元的低价购买电子书。
如何推送到 Kindle 阅读器或 Kindle App?
注意:不推荐阅读 Mobi 格式的电子书。
图灵社区的官方文档:《如何推送电子书》
需要注意的是:
如何下载 PDF 格式?
购买电子版之后,请在 这本书的详情页面 点击 “随书下载” 标签页,即可下载。
原书并没有以打包的方式提供代码下载,只在各章节附上了在线演示(“试一试” 链接)。在这些在线演示页面中,我们可以查看 HTML、CSS、JS 源码。
在线演示页面所在的服务器(dabblet.com)位于境外,可能会遇到网络线路问题。
如果你愿意整理一份按章节组织的在线演示的索引表,请提 PR,谢谢!
你可以到各大网上书店选购,也可以预订译者签名版,详见 购买方式。
如题,作为dom元素,伪元素都是在容器内进行渲染的,,input无法容纳元素,第十页图1-8上面的提示框气泡,一看就是input标签所弹出的,而css中写到有::before,input不会支持,不是特殊的HTML5类型的输入框是不会弹出提示气泡的,而这里有,我感觉不对劲,是我理解错了还是怎么?解决方案,在input上套一个div,给div那个加before,才能实现!然后最后一行的 transform我看不懂,提示气泡为何要让它旋转?,那么这样又与图1-8的显示对不上号了!如果我的评论提交是错误的恳请原谅!如果正确请修正~谢谢
本文是早期译版,未经校审。仅供参考。
{原书注释!}
For readability, we will refer to “checkboxes” throughout this secret, but everything discussed applies to both checkboxes and radio buttons unless otherwise noted.
为易于理解,我们在本篇攻略中统一使用 “复选框” 这个词。但实际上,除非特别注明,本节讨论的所有内容是同时适用于复选框和单选框的。
Designers always wanted more control over every element in a web page. When a graphic designer with limited CSS experience is tasked to create a website mockup, they almost always produce one with customized form elements, making the developer tasked to convert it to CSS want to pull their hair out.
设计师对网页中各种元素的控制欲是永无止境的。当一个 CSS 经验不足的平面设计师接到一个网页设计设计任务时,他几乎一定会为各种表单元素设计一套自己的样式,然后接下来写 CSS 的工程师就精神崩溃了。
When CSS was first introduced, form styling was extremely limited and is still not clearly defined in any of the various CSS specifications. However, browsers got more and more permissive over the years about what CSS properties they allow on form controls, enabling us to style most of them quite extensively.
当 CSS 最初出现时,它对表单元素的样式控制力是极为有限的,而且现在仍然没有哪个 CSS 规范明确定义了这方面的行为。不过这些年来,浏览器们已经在逐步放开 CSS 属性对表单控件的作用范围,从而允许我们设置大多数表单控件的样式。
Unfortunately, checkboxes and radio buttons are not among those form controls. To this day, most browsers allow little to no styling when it comes to them. As a result, authors end up either coming to terms with their default look or employing awful, inaccessible hacks, such as recreating them with divs and JS.
然而不幸的是,复选框和单选框不在此列。直到今天,这两种控件在绝大多数浏览器中仍然是几乎或完全无法设置样式的。这最终导致网页开发者们要么接受默认样式,要么求助于一些糟糕透顶、可访问性极差的 hack 方案,比如用 div
和 JS 来模拟这两种控件。
Is there a way to get around these restrictions and customize the look of our checkboxes, without bloat and without giving up on semantics and accessibility?
有没有一种方法,既可以克服这些限制、自由定制复选框的外观,同时又可以摆脱臃肿的代码、保全结构层的语义和可访问性呢?
Until a few years ago, this task was impossible without scripting. However, in Selectors Level 3, we got a new pseudo-class: :checked
. This pseudo-class only matches when the checkbox is checked, whether that is done through user interaction, or through script.
在多年以前,这个任务还无法在脱离脚本的情况下完成。不过,在 选择符(第三版)中,我们得到了一个新的伪类 :checked
。这个伪类只会在复选框被勾选时才会匹配,不论这个勾选状态是由用户交互所触发的,还是由脚本触发的。
{小提示}
Wondering what the difference is between
:checked
and the attribute selector[checked]
? The latter doesn’t update based on user interaction, as user interaction doesn’t affect the HTML attribute.你是不是还在疑惑伪类选择符
:checked
和属性选择符[checked]
之间的区别?后者是不会根据用户的交互行为进行更新的,因为用户的交互并不会影响到 HTML 标签上的属性。
It’s not very useful when applied directly to checkboxes, as -- like we previously mentioned -- there aren’t many properties we can successfully apply to them. However, we can always use combinators to style other elements based on a checkbox state.
如果直接对复选框设置样式,那这个伪类并不实用,因为——前面已经交待过了——没有多少样式是能够对复选框起作用的。不过,我们倒是可以基于复选框的勾选状态借助组合选择符来给其他元素设置样式。
You might be wondering what other elements we may want to style based on whether a checkbox is checked or not. Well, there is one kind of element that has special behavior around checkboxes: <label>
s. A <label>
that is associated with a checkbox also acts as a toggle for it.
你可能还没搞清楚,我们要根据复选框的勾选状态来给哪个元素设置样式?嗯,其实有一个元素总是跟复选框形影不离、息息相关,它就是 <label>
(标签元素)。当 <label>
元素跟复选框关联之后,也可以起到触发开关的作用。
Because labels--unlike checkboxes--are not replaced elements, [_] we can _add generated content to them and style that based on checkbox state*. Then, we could hide the real checkbox in a way that doesn’t remove it from the tabbing order, and have the generated content act as a styled checkbox instead!
由于 label
不是复选框那样的替换元素 [],我们可以__为它添加生成性内容(伪元素),并基于复选框的状态来为其设置样式__。然后,我们就可以__把真正的复选框隐藏起来__(但不能把它从 tab 键切换焦点的队列中完全删除掉),再把生成性内容美化一番,用它来__顶替原来的复选框*!
{原书脚注!}
[] From the CSS 2.1 specification: *“[A replaced element is] an element whose content is outside the scope of the CSS formatting model, such as an image, embedded document, or applet.” Replaced elements cannot have generated content applied to them, though some browsers allow it.
据 CSS 2.1 规范所述:“替换元素的特征在于,其内容超出了 CSS 格式化模型的范畴,比如图片、嵌入的文档或小应用程序等。” 原则上我们无法为替换元素添加生成性内容,尽管某些浏览器可能会允许这样做。
Let’s see this in action. We will start from the following simple markup:
让我们来亲手试一试。我们先从下面这段简单的结构代码开始:
<input type="checkbox" id="awesome" />
<label for="awesome">Awesome!</label>
{原书注释!}
Nesting the checkbox in the label would free us from using ids, but then we wouldn’t be able to target the label based on the checkbox status, because we do not yet have parent selectors.
把复选框嵌套进
label
中同样可以把这两者建立关联,还可以省掉 ID 属性。但这样一来,我们就无法基于复选框的状态来设置label
的样式了,因为现在还不存在父元素选择符。
The next step is to generate a pseudo-element that will be used as our styled checkbox, and apply some basic styling to it:
接下来我们需要生成一个伪元素,用它作为美化版的复选框。我们先给这个伪元素加上一些基本的样式:
input[type="checkbox"] + label::before {
/* non-break space */
content: '\a0'; /* 不换行空格 */
display: inline-block;
vertical-align: .2em;
width: .8em;
height: .8em;
margin-right: .2em;
border-radius: .2em;
background: silver;
text-indent: .15em;
line-height: .65;
}
{原书注释!}
The style we will apply to our checkboxes in these examples is pretty basic, but the possibilities are endless. You could even skip CSS styling altogether and use images for all different checkbox states!
在这些例子中,我们给复选框添加的样式是相当简单的,但实际的可能性是无穷无尽的。你甚至可以完全跳过 CSS 美化这一招,直接把复选框的各种状态显示为不同的图片!
图 6.9
Our rudimentary custom checkbox alongside the original checkbox
左边是原生复选框;而右边是我们经过初步自定义的复选框。(图中文字:帅呆了!)
You can see how our checkbox and label currently look in Figure XX.XX. The original checkbox is still visible, but we will hide it later. Now we need to apply a different style to our checkbox when it’s checked. This could be as simple as applying a different color and adding a checkmark as content:
你可以在 图 6.9 中看到复选框和 label
现在看起来是什么样的。原来的复选框仍然是可见的,但待会儿我们会把它隐藏掉。现在我们需要给复选框的勾选状态加点儿不一样的样式。我们就简单意思一下,比如换种颜色,再加个勾选标记进去:
input[type="checkbox"]:checked + label::before {
content: '\2713';
background: yellowgreen;
}
图 6.10
Styling our pseudo-element as a customized checked checkbox
在复选框的勾选状态下,伪元素也需要美化一番。
As you can see in Figure XX.XX, this is already functioning as a rudimentary styled checkbox. Now, we need to hide the original checkbox in an accessible way, which means we can’t use display: none
, as that would remove it from the keyboard tabbing order entirely. Instead, we could use something like this:
你在 图 6.10 中可以看到,这个伪元素俨然已经是一个简单美化过的复选框了,而且功能完备。现在,我们需要把原来的复选框以一种不损失可访问性的方式给隐藏起来,这意味着我们不能使用 display: none
,因为那样会把它从键盘 tab 键切换焦点的队列中完全删除掉。我们会改用另一种方法来达到目的:
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
}
{警告!!}
Be careful when using such permissive selectors. Using
input[type="checkbox"]
will also hide checkboxes without a label after them (e.g., those nested in a label), essentially making them unusable.在使用宽松的选择符时一定要小心。对于那些没有
label
跟在后面的复选框来说(比如它是被嵌套进一个label
的),使用input[type="checkbox"]
选择符也会把它们隐藏掉,从而损害可用性。
That’s it, we’ve made a very basic custom checkbox! We could of course improve it further--for example, by changing its style when it’s focused or disabled, which you can see in Figure XX.XX:
这就成了,我们已经得到了一个简单定制化的复选框!我们还可以进一步优化它——比如说,在它聚焦或禁用时改变它的样式(效果如 图 6.11 所示):
input[type="checkbox"]:focus + label::before {
box-shadow: 0 0 .1em .1em #58a;
}
input[type="checkbox"]:disabled + label::before {
background: gray;
box-shadow: none;
color: #555;
}
图 6.11
Top to bottom: customized focused checkbox, customized disabled checkbox, and checked checkbox
- 上图:自定义复选框的聚焦状态。
- 中图:自定义复选框被禁用的状态。
- 下图:自定义复选框被勾选且被禁用的状态。
You could even make these effects smoother by applying transitions or animations or go nuts and create things like skeuomorphic switches. The possibilities really are endless!
你甚至还可以用过渡或动画来让各个状态之间的切换更加平滑,或者脑子进水搞一个拟物化的开关出来也是可以的。这方面的可能性真的是无穷无尽的!
{原书注释!}
Although the possibilities are endless, avoid styling checkboxes as circles: most users associate round toggles with radio buttons. Same applies to square radio buttons.
尽管可能性是无穷无尽的,但仍然要避免把复选框设置为圆形:绝大多数用户会把圆形的开关理解为单选框。这个道理也适用于方形的单选框。
{致敬}
Hat tip to Ryan Seddon for coming up with the first version of this effect, now known as “the checkbox hack”. Ryan has since used this idea to implement all sorts of widgets that require state persistence, such as modal dialogs, dropdown menus, tabs, and carousels, though abusing checkboxes this much results in accessibility problems.
向 Ryan Seddon 脱帽致敬,感谢他最先提出这个效果——这个技巧现在已被大家称作 “复选框 hack” 了。Ryan 曾用这个创意 实现了各种需要保持状态的 UI 组件,比如模态对话框、下拉菜单、标签页、跑马灯等,不过像这样滥用复选框很容易导致可访问性上的问题。
You could use a variation of “the checkbox hack” to emulate toggle buttons, as HTML does not provide a native way to create them. Toggle buttons are push buttons that act like checkboxes: they are used to toggle a setting on or off, and look pressed when checked and unpressed when unchecked. Semantically, there is no real difference between toggle buttons and checkboxes, so you can both use this trick and maintain semantic purity.
说到开关式按钮,HTML 并没有提供一种原生的方式来生成它,但我们可以利用这个 “复选框 hack” 的思路来把它模拟出来。开关式按钮是一种跟复选框的行为十分相似的按钮:它们可以用来切换某个选项的开关状态——当启用时,它是被按下的状态;而停用时,它就是浮起的状态。在语义上,开关式按钮和复选框并没有本质上的差别,因此,你可以放心地使用这个技巧,而不用担心语义上会有什么问题。
图 6.12
A toggle button in both its states
开关式按钮的两种状态。
To create toggle buttons with this trick, you would just style the labels as buttons, instead of using pseudo-elements. For example, to create the toggle buttons shown in Figure XX.XX, the code would look like this:
如果我们想用这个技巧来生成开关式按钮,其实只需要把 label
设置为按钮的样式即可,并不需要用到伪元素。具体来说,要生成 图 6.12 中的这个开关式按钮,代码可以这样写:
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
}
input[type="checkbox"] + label {
display: inline-block;
padding: .3em .5em;
background: #ccc;
background-image: linear-gradient(#ddd, #bbb);
border: 1px solid rgba(0,0,0,.2);
border-radius: .3em;
box-shadow: 0 1px white inset;
text-align: center;
text-shadow: 0 1px 1px white;
}
input[type="checkbox"]:checked + label,
input[type="checkbox"]:active + label {
box-shadow: .05em .1em .2em rgba(0,0,0,.6) inset;
border-color: rgba(0,0,0,.3);
background: #bbb;
}
However, be wary about using toggle buttons. In most cases, toggle buttons hinder usability as they can easily be confused with regular buttons that perform an action when pressed.
不过,在使用开关式按钮时还需慎重。在绝大多数场景下,开关式按钮对可用性有负面作用,因为它们很容易跟普通按钮混淆起来,让人误以为按下它会触发某个动作。
Related specs
相关规范
本文是早期译版,未经校审。仅供参考。
This book would not have been possible without the help and support of a number of fantastic people, to whom I’m deeply grateful. A big, heartfelt thank you goes to:
如果没有以下这些贵人的帮助和支持,这本书不可能成形。我心存感激。热烈而衷心地感谢你们:
A big thanks to the lovely people who publish their photos with permissive Creative Commons licenses; otherwise, every example in this book would feature pictures of my cat (and many examples do, regardless). Here is a list of the CC photos I used and where you can find them:
非常感谢那些可爱的摄影师们,慷慨地以 CC 协议(Creative Commons)发布了他们的照片;如果没有他们,恐怕本书中每个示例的图片都得请出我家的猫咪了(不过有些地方还是会有它的身影)。下面列出了我使用的 CC 照片及其来源:
“House Made Sausage from Prairie Grass Cafe, Northbrook,” Kurman Communications, Inc.
“Cats that Webchick Is Herding,” Kathleen Murtagh
“A Field of Tulips,” Roman Boed
“Resting in the Sunshine,” Steve Wilson
“Naxos Island, Greece,” Chris Hutchison
(所有预览图片由 碁峰资讯股份有限公司 提供。)
(本文完)
或者是 30°?又或者是 3.1415926535°?
咦?这个值不就是 π 嘛!当然这个值在这里并没有什么特殊意义,作者只是在卖萌。
这里的
background-size
是初始值,对渐变来说就是以整个元素的范围进行填充。
background-size
属性的初始值是 auto auto
。此时,如果背景图像是普通位图,则 background-size
取图片本身的宽高;如果背景图像是渐变图案,则 background-size
取该元素的尺寸。
准确地说,这里所谓的 “该元素的尺寸” 实际上是指元素的 “背景定位区域” 的尺寸。而元素的背景定位区域是由 background-origin
属性来决定的,默认是 padding-box
。
(暂无)
<dd>
印刷颜色不对<dl> <dt>Name: </dt> <dd>Lea Verou</dd> <dt>Email: </dt> <dd>[email protected]</dd> <dt>Location: </dt> <dd>Earth</dd> </dl>
[email protected]
前面的 <dd>
应该为印刷为绿色,而实际是黑色
`
(贡献者: @HDMing)
本文是早期译版,未经校审。仅供参考。
Prerequisites
- CSS gradients
- the {#secret-xxx}
背景知识
- CSS 渐变
- “{$secret$}” 攻略(第 {$page$} 页)
In the previous section, we learned how to use CSS gradients to create all sorts of stripes. However, stripes are not the be-all and end-all of background patterns or even just geometric patterns. We quite often need many other different types, such as grids, polka dots, checkerboards, and many others.
在上一节中,我们学会了如何用 CSS 渐变来创建各种条纹图案。但是,条纹并不是我们要实现的唯一的背景图案,它甚至只是几何图案中最简单的一种。我们还需要很多其他不同类型的图案,比如网格、波点、棋盘等等。
Thankfully, CSS gradients can help with many of these too. It’s possible to create almost any kind of geometric pattern with CSS gradients, although it’s not always practical. If we’re not careful, we might end up with an insane amount of unmaintainable code. CSS patterns are also one case where it really pays off to use a CSS preprocessor, such as Sass to reduce repetition, as the more complex they get, the less DRY they become.
幸运的是,CSS 渐变在实现这些图案时也能大展拳脚。用 CSS 渐变来创建任何种类的几何图案几乎都是可能的,只不过有时这种方法不太实际。我们可能稍不留神就会弄出一大坨无法维护的代码。CSS 图案可以算是一个值得使用 CSS 预处理器(比如 Sass)来减少代码冗余的案例,因为最终图案越复杂,相应的代码就会变得越来越不 DRY。
图 2.38
My CSS3 Patterns Gallery (found at lea.verou.me/css3patterns) showed what is possible with CSS gradients as early as 2011. It was included in almost every article, book, and conference talk that mentioned CSS gradients between 2011 and 2012 and was used by several browser vendors to fine-tune their CSS gradients implementations. However, not every pattern showcased in it would be a good use case for a production website. Some of them are included only to show what is possible, but their code is extremely long and repetitive. For those cases, SVG is a better choice. For some examples of SVG patterns, visit philbit.com/svgpatterns, which was created as the SVG answer to the CSS Patterns Gallery.
我的 CSS3 图案库(位于 lea.verou.me/css3patterns)展示了 CSS 渐变早在 2011 年就能够实现的效果。在 2011 到 2012 年间,几乎每篇文章、每本书、每场技术会议在提到 CSS 渐变时都会提到这个图案库;而且几大浏览器厂商也曾用它来调校自己在 CSS 渐变上的实现。但是,并不是这里面的每个例子都适用于生产环境。其中的某些例子只是用来展示可能性,而且它们所用到的代码极度冗长繁琐。对于这些例子来说,SVG 可能是更好的选择。关于 SVG 图案的演示,请访问 philbit.com/svgpatterns,这个网站可看作是我的 CSS 图案库的 SVG 版的实现。
In this secret, we will focus on creating some of the easiest and commonly needed patterns.
在本篇攻略中,我们将深入讨论如何创建那些简单而常用的图案。
When using only one gradient, there aren’t that many patterns we can create. The magic starts to unfold when we combine multiple gradients, having them show through each other’s transparent regions. Perhaps the easiest such pattern is overlaying horizontal and vertical stripes to create various types of grids. For example, the following code creates the tablecloth-reminiscent (gingham) pattern shown in Figure XX.XX:
当只使用一个渐变时,我们所能创建的图案并不多。当我们开始把多个渐变图案组合起来时,让它们透过彼此的透明区域显现,神奇的事情就开始了。按照这个思路,我们首先想到的可能就是把水平和垂直的条纹叠加起来,从而得到各种样式的网格。举例来说,下面的代码会创建如图 图 2.39 所示的桌布(方格纹)图案。
background: white;
background-image: linear-gradient(90deg,
rgba(200,0,0,.5) 50%, transparent 0),
linear-gradient(
rgba(200,0,0,.5) 50%, transparent 0);
background-size: 30px 30px;
图 2.39
Our tablecloth (gingham) pattern, as well as the two gradients that comprise it (transparency shown here as the conventional gray checkerboard)
我们的桌布(方格纹)图案,是由两层渐变图案所组成的(按照惯例,灰色的棋盘图案表示透明)。
In some cases, we want to be able to adjust the cell size of the grid, and have the width of its lines remain constant--for example, to create grid lines that serve as guides. This is a great use case for using lengths instead of percentages as gradient color stops:
在某些情况下,我们希望网格中每个格子的大小可以调整,同时又希望网格线条的粗细保持固定——举例来说,类似图纸辅助线的网格就是这种情况。这是一个非常好的例子,展示了使用长度而不是百分比作为色标的场景:
background: #58a;
background-image:
linear-gradient(white 1px, transparent 0),
linear-gradient(90deg, white 1px, transparent 0);
background-size: 30px 30px;
The result (seen on Figure XX.XX) is a grid of 1px
white lines with a grid cell size of 30px
. Just like in the {#section}, the base color is also functioning as a fallback color.
我们得到的结果就是一幅用 1px
白线画出来的 30px
大小的网格图案(参见 图 2.40)。与 **“{$section$}” 段落(第 {$page$} 页)**中的例子类似,主色调这里也起到了回退颜色的作用。
图 2.40
A basic blueprint grid CSS pattern whose lines remain
1px
regardless of the size of the grid一幅简单的蓝图网格图案。不管每个格子有多大,它的线条始终是
1px
这么细。
This grid is a good example of a pattern that can be made with reasonably maintainable (though not completely DRY) CSS code:
这个网格一个很好的例子,说明图案可以用合理的、可维护的(尽管还不是完全 DRY 的)CSS 代码生成出来:
{原书注释!}
To calculate the file size of your CSS pattern, paste the code in bytesizematters.com.
如果要统计你的 CSS 代码的文件体积,可以把代码贴到 bytesizematters.com。
We can even overlay two grids with different line widths and colors to create a more realistic blueprint grid (Figure XX.XX):
我们甚至可以把两幅不同线宽、不同颜色的网格图案叠加起来,来得到一个更加仿真的蓝图网格(参见 图 2.41):
图 2.41
A more complex blueprint grid, comprised of two grids with different parameters
一个更加复杂的蓝图网格,由两幅不同参数的网格图案组成。
background: #58a;
background-image:
linear-gradient(white 2px, transparent 0),
linear-gradient(90deg, white 2px, transparent 0),
linear-gradient(hsla(0,0%,100%,.3) 1px,
transparent 0),
linear-gradient(90deg, hsla(0,0%,100%,.3) 1px,
transparent 0);
background-size: 75px 75px, 75px 75px,
15px 15px, 15px 15px;
So far, we have only used linear gradients to make patterns. However, radial gradients can be very useful as well, as they allow us to create circles, ellipses, or parts thereof. The simplest pattern we can create with a radial gradient is an array of dots (Figure XX.XX):
目前为止,我们一直都是在用线性渐变来生成图案。但是,径向渐变同样也是非常实用的,因为它们允许我们创建圆形、椭圆,或是它们的一部分。我们可以用径向渐变创建的最简单的图案是圆点的阵列(参见 图 2.42):
图 2.42
An array of dots; the repeating tile is shown with dashed lines
圆点阵列;单幅贴片已经用虚线框标示出来了。
background: #655;
background-image: radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;
Admittedly, this is not very useful on its own. However, we can combine two of those gradients and give them different background positions, to create a polka dot pattern (Figure XX.XX):
不过坦白地说,目前的这个样子还不是很实用。别着急,我们可以生成两层圆点阵列图案,并把它们的背景定位错开,这样就可以得到真正的波点图案了(参见 图 2.43):
图 2.43
Polka dot pattern; both repeating tiles are shown with dashed lines
波点图案;两层背景图案各自的贴片已经分别用虚线框标示出来了。
background: #655;
background-image: radial-gradient(tan 30%, transparent 0),
radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;
background-position: 0 0, 15px 15px;
{试一试} play.csssecrets.io/polka
Note that for the effect to work, the second background position must be half of the tile size. Unfortunately, this means that to change the tile size, we need to make four edits. This is on the brink of being unmaintainable, although whether it has crossed the line is debatable. If you are using a preprocessor, you may want to convert it into a mixin:
请注意,为了达到效果,第二层背景的偏移定位值必须是贴片宽高的一半。但不幸的是,这意味着我们如果要改动贴片的尺寸,需要修改四处。虽然可能还没到不可收拾的地步,但这样的代码就快要跌入不可维护的深渊。如果你在使用预处理器,就赶紧把它转换成这个 mixin 吧:
@mixin polka($size, $dot, $base, $accent) {
background: $base;
background-image:
radial-gradient($accent $dot, transparent 0),
radial-gradient($accent $dot, transparent 0);
background-size: $size $size;
background-position: 0 0, $size/2 $size/2;
}
Then, to create the polka dot pattern, we would call it like this:
以后在创建波点图案时,我们就可以像这样调用它:
@include polka(30px, 30%, #655, tan);
Checkerboard patterns are used in a number of cases. For instance, subtle checkerboards can be an interesting alternative to a bland solid color background. Also, a gray checkerboard pattern is the de facto standard way to depict transparency, which is required in a number of different UIs. Making a checkerboard pattern in CSS is possible, but considerably trickier than one might expect.
棋盘图案在很多场景下都会用到。比如说,相对于单调的纯色背景来说,具有细微对比度的棋盘图案可能就是一个有趣的替代品。同样,在各种应用程序的界面中,灰色的棋盘图案已经是用于表示透明色的事实标准。在 CSS 中创建棋盘图案是可能的,只不过实现过程比我们想像的可能要 “绕” 一些。
图 2.44
A gray checkerboard pattern to indicate transparency; if this was created by repeating an image, the tile would be the one denoted by the dashed line
一个灰色的棋盘图案通常用来表示透明色;如果我们通过图片的重复平铺来实现它,那么单个贴片应该就是虚线框所标示的范围。
The typical tile that generates a checkerboard when repeated consists of two squares from each color, like the one indicated in Figure XX.XX. It looks like it should be easy to recreate with CSS: we would just create two squares with different background positions, right? Not exactly. Yes, we can technically create squares with CSS gradients, but with no spacing around them, the result will look like a solid color. However, there is no way to create squares with space around them with one CSS gradient. If you’re having doubts, try to find a gradient that, when repeated, produces the image in Figure XX.XX.
棋盘图案是可以通过平铺生成的,平铺成这个图案的最典型的贴片包含两种不同颜色的方块,且相互间隔,就像 图 2.44 中所标示出的那样。它貌似可以在 CSS 中很容易地重现出来:我们只需要创建两个不同背景定位的方块就可以了,没错吧?然而并非如此。是的,在技术上,我们可以用 CSS 渐变来创建平铺的方块,但每个方块的周围是不会有空隙的,因此最终的结果看起来就是一片实色。总的来说,只用一层 CSS 渐变是无法创建四周有空隙的方块的。如果你对此还怀有疑议,不妨找找看有没有一种渐变可以在重复平铺时产生 图 2.45 所示的图像。
图 2.45
Repeating a square with space around it; the tile is shown with dashed lines
四周有空隙的方块在平铺之后得到的结果;单个贴片用虚线标出来了。
The trick is to compose the square from two right triangles. We already know how to create right triangles (remember our failed attempt at diagonal stripes in Figure XX.XX?). To refresh your memory, the code looked like this (here with different colors and transparency):
这里的窍门在于用两个直角三角形来拼合出我们想要方块。我们已经知道了如何创建直角三角形了(还记得我们在 图 2.29 中尝试实现斜向条纹时所遭遇的失败吗?)。为了唤起你的记忆,我们再来看看当时的代码(这里换用了另一种颜色和透明色):
background: #eee;
background-image:
linear-gradient(45deg, #bbb 50%, transparent 0);
background-size: 30px 30px;
You might be wondering how this helps with anything. Sure, if we tried to compose squares from two triangles like the ones in Figure XX.XX, we would end up with a solid color. However, what if we reduce the legs of these triangles to half their original size, so that they occupy of the tile, instead of the current
? We can easily do that by changing the color stop position to 25% instead of 50%. Then we would end up with something like Figure XX.XX.
你可能还没有搞明白这个方法是怎么发挥作用的。没错,如果我们尝试直接用两个三角形组合出 图 2.29 中那样的正方形,我们可能只会得到一片实色。但是,如果我们把这些三角形的直角边缩短到原来的一半,从而只占据贴片面积的 ,而不是
时,会怎么样?要满足这一点,我们只需要简单地**把色标的位置值从 50% 改为 25%**就可以了。然后我们得到的效果会变成 图 2.46 所示的那样。
图 2.46
Right triangles with a lot of spacing around them
这些直角三角形的四周有很多空隙。
Similarly, we can create triangles of the opposite direction if we flip the color stops (Figure XX.XX):
与此类似,如果我们把色标的顺序反转,就可以创建相反方向的三角形了(图 2.47):
图 2.47
If we flip the color stops, we get triangles in the opposite direction
如果我们完全颠倒色标的顺序,就能得到相反方向的三角形了。
background: #eee;
background-image:
linear-gradient(45deg, transparent 75%, #bbb 0);
background-size: 30px 30px;
Can you guess what happens if we combine the two? The code would look like this:
你能猜到把它们组合到一起时会发生什么吗?代码是这样的:
background: #eee;
background-image:
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0);
background-size: 30px 30px;
图 2.48
Combining the two triangles
两层三角形图案组合之后的效果。
At first, the result in Figure XX.XX doesn’t look like we’re getting anywhere. However, we just need to move the second gradient by half the tile size, in order to combine them into a square:
乍看起来,图 2.48 中所示的效果似乎并不我们想要的。但是,我们只需要把第二层渐变在水平和垂直方向均移动贴片长度的一半,就可以它们拼合成一个完整的方块:
background: #eee;
background-image:
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0);
background-position: 0 0, 15px 15px;
background-size: 30px 30px;
图 2.49
Our combined triangles now form squares with space around them; the two tiles are shown with dashed lines and the second gradient is shown slightly darker
这两组三角形终于拼合成了一组四周留有空隙的正方形图案了;我们用虚线标出了两个贴片的位置,其中第二层渐变还特意用深色凸显出来。
Can you guess what the result looks like? It’s exactly what we were trying to achieve earlier, and looks like Figure XX.XX. Notice that this is essentially half a checkerboard. All we need to turn this into a full checkerboard is to repeat the two gradients to create another set of squares and offset their positions again, a bit like applying the polka dot technique twice:
你能猜到它的结果是什么样子吗?就是我们一直想要实现的效果,如 图 2.49 所示。请注意这本质上只是棋盘的一半。要把它转变为一幅完整的棋盘,我们只需要把现有的这一组渐变再重复一份,从而创建出另一组正方形,并且偏移它们的定位值——有点像我们在波点图案中所用到的技巧:
background: #eee;
background-image:
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0),
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0);
background-position: 0 0, 15px 15px,
15px 15px, 30px 30px;
background-size: 30px 30px;
The result is a checkerboard, identical to the one in Figure XX.XX. We can improve the code a bit by combining the opposite facing triangles (i.e., the first with the second and the third with the fourth) and making the darker gray semi-transparent black, so that we can change the base color without always having to adjust the top color accordingly:
最终结果就是一幅棋盘图案,正如 图 2.44 所示的那样。其实这段代码还可以稍稍优化一下,比如我们可以把这些处在贴片顶角的三角形两两组合起来(即把第一组和第二组合并为一层渐变,第三组和第四组合并为一层渐变);然后还可以把深灰色改成半透明的黑色——这样我们只需要改底色就可以改变整个棋盘的色调了,不需要单独调整各层渐变的色标了:
background: #eee;
background-image:
linear-gradient(45deg,
rgba(0,0,0,.25) 25%, transparent 0,
transparent 75%, rgba(0,0,0,.25) 0),
linear-gradient(45deg,
rgba(0,0,0,.25) 25%, transparent 0,
transparent 75%, rgba(0,0,0,.25) 0);
background-position: 0 0, 15px 15px;
background-size: 30px 30px;
图 2.50
This is a complex pattern and it’s often difficult to wrap one’s head around how it works, especially after reducing it to two gradients. It usually aids understanding of how a pattern works to give a random color to one of the gradients or color stops. For example, here the first gradient is shown with
rebeccapurple
instead of the semi-transparent black and the two tiles are outlined with dashed lines.这幅图案本身就比较复杂,而且它的实现原理也是相当复杂,比较烧脑,尤其是在把四层渐变精简为两层时。因此,我会用显眼的颜色来突出某个渐变或渐变中的某个色标,这通常有助于理解这个图案是如何构成的。举例来说,这里的第一层渐变是用
rebeccapurple
来展示的,而不是半透明的黑色,而两层贴片也都用虚线框标示出来了。
Now we have two gradients instead of four, but the code is almost as WET as before. To change the accent color or the cell size, we need to make four edits. At this point, it might be a good idea to use a preprocessor mixin to reduce duplication. For example, in Sass it would look like this:
现在我们把四层渐变消化成了两层渐变,但代码仍然跟以前一样 WET。为了改变棋盘的主色调或者是方格的尺寸,我们还是需要修改四个地方。在这一点上,使用预处理器的 mixin 来简化代码可能是一个好主意。比如说,用 Sass 写的代码可能就是这样的:
@mixin checkerboard($size, $base,
$accent: rgba(0,0,0,.25)) {
background: $base;
background-image:
linear-gradient(45deg,
$accent 25%, transparent 0,
transparent 75%, $accent 0),
linear-gradient(45deg,
$accent 25%, transparent 0,
transparent 75%, $accent 0);
background-position: 0 0, $size $size,
background-size: 2*$size 2*$size;
}
/* Used like... */
/* 调用时是这样的... */
@include checkerboard(15px, #58a, tan);
{原书注释!}
WET stands for “We Enjoy Typing” and is the opposite of DRY code (i.e., it refers to repetitive, unmaintainable code).
WET 的意思是 “We Enjoy Typing”(我们喜欢敲键盘),它是 DRY 的反义词,表示繁琐重复、无法维护的代码。
{关于未来}
Conical gradients
角向渐变
In the future, we won’t have to resort to meticulously overlaying triangles to create checkerboards. CSS Image Values Level 4 defines a new set of gradient functions to generate conical gradients (a.k.a. “angle gradients”). These gradients often look like a cone observed from above, hence the name “conical.” They are generated by a line that gradually changes color as it rotates around a fixed point. For example, the hue wheel shown here would be created with the following gradient:
在未来,在创建棋盘图案时,我们再也不需要小心翼翼地去拼合这些三角形了。CSS 图像(第四版)定义了一种新的渐变形式,可以生成角向渐变(也称作 “圆锥渐变”)。这种渐变的效果通常看起来像是一个俯视的圆锥体,因此得名为 “圆锥渐变”。它们的生成方式是这样的:所有色标的颜色变化是由一条射线绕着端点旋转来推进的。举个例子,以下渐变代码可以创建一个色轮:
background: conic-gradient(red, yellow, lime, aqua, blue, fuchsia, red);Conical gradients are useful for far more things than hue wheels: starbursts, brushed metal effects, and many other kinds of backgrounds, including (you guessed it!) checkerboards. They would enable us to create the repeating tile of Figure XX.XX in just one gradient:
除了色轮之外,角向渐变在很多地方都大有用武之地:放射状的光芒、金属拉丝效果,以及各种各样其他的背景,其中包括(猜对了!)棋盘图案。我们只需要一个角向渐变就可以实现 图 2.44 所示的贴片了:
background: repeating-conic-gradient(#bbb 0, #bbb 25%, #eee 0, #eee 50%); background-size: 30px 30px;Unfortunately, there is no browser support for conical gradients at the time of writing, but you can find a polyfill at leaverou.github.io/conic-gradient.
遗憾的是,截止到写书的此刻,还没有任何浏览器支持角向渐变;不过你可以在 leaverou.github.io/conic-gradient 找到一个 polyfill。
In any case, this is so much code that it might actually be better to go the SVG route. An SVG tile for Figure XX.XX would be as small and simple as:
在任何情况下,这样的代码量都不能算少,所以转到 SVG 方案可能是更好的选择。图 2.44 中贴片如果用 SVG 来实现,就像下面这样简短:
<svg xmlns="http://www.w3.org/2000/svg"
width="100" height="100" fill-opacity=".25" >
<rect x="50" width="50" height="50" />
<rect y="50" width="50" height="50" />
</svg>
One could reply, “But CSS gradients save us HTTP requests!” However, with modern browsers, we can embed the SVG file in our stylesheet as a data URI, and we don’t even need to base64 or URLencode most of it:
可能有人会说,“可是 CSS 渐变能省掉 HTTP 请求啊!” 其实,对于现代浏览器来说,我们可以把 SVG 文件以 data URI 的方式内嵌到样式表中,而且,我们甚至不需要用 base64 或 URLencode 来把它编码:
background: #eee url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg" \
width="100" height="100"
fill-opacity=".25">\
<rect x="50" width="50" height="50" /> \
<rect y="50" width="50" height="50" /> \
</svg>');
background-size: 30px 30px;
{小提示}
Note how you can break a CSS string into multiple lines for readability, by just escaping the line breaks with a backslash (
\
)!请注意,如果你出于可读性的考虑,需要把一句 CSS 代码打断为多行,只需要用反斜杠(
\
)来转义每行末尾的换行就可以了。
The SVG version is not only 40 characters shorter, but also considerably less repetitive. For example, we can change the colors in only one place and the size with two edits.
SVG 的版本不仅更短(只有 40 个字符),而且在代码冗余度方面也明显更低。举例来说,我们在改颜色时只需要改一处,而在改尺寸时只需要改两处。
图 2.51
Combining these techniques with blending modes, by using
background-blend-mode
with values other thannormal
for some (or even all) of the layers a background pattern is made of can yield very interesting results, as this pattern gallery by Bennett Feely demonstrates. Most of these patterns only use themultiply
blending mode, but other values such asoverlay
,screen
, ordifference
can be very useful too.我们还可以把本节的这些技巧和 混合模式(blending mode)结合起来。当我们对组成图案的某些(或所有)图层使用
background-blend-mode
属性并设置一个非normal
值时,可以产生非常有趣的结果——正如 Bennett Feely 的图案库 所演示的那样。这些图案大多只使用了multiply
混合模式,但其他值(比如overlay
、screen
或difference
)同样也是非常有用的。
Related specs
相关规范
未来,我们再也不需要费尽心机地动用 CSS 渐变、裁切或 SVG 来实现这个效果了。CSS 背景与边框(第四版)将引入一个全新的属性
corner-shape
,可以彻底解决这个痛点。
我在第二届 CSS Conf(CSS 开发者大会)上作了一个名为《重拾 CSS 的乐趣》的演讲。在这个演讲的下半部分,我提到了 “内凹圆角” 效果。
曾有开发者提议 “让 border-radius
接受负值以生成内凹圆角”(如上图所示),不过 CSS 工作组并没有采纳。尽管如此,这个需求并没有被遗忘。果然,在 “背景与边框(第四版)” 草案中,新增了一个叫作 corner-shape
属性,用于扩展 border-radius
的功能。
如果我们想要得到内凹圆角的效果,在未来很可能只需要这样写:
border-radius: 20px;
corner-shape: scoop;
这个新增的属性还可以让我们得到方形(notch
)或斜面(bevel
)的切角效果。虽然此时 border-radius
这个属性名稍显怪异,但好歹解决了一个大痛点,可谓喜大普奔!
但实际上 corner-shape
这个属性的诞生也并非一帆风顺。
这个想法最早由 CSS 工作组特邀专家 fantasai(国内开发者称她为 “CSS 女神”)提出,并加入了背景与边框(第四版)的编辑草案(WD)中。当时这个属性还叫作 border-corner-shape
。
本书的作者 Lea Verou 在听到这个想法后兴奋不已,开发了 一个预览页面,向大众演示这个新属性的神奇功能。
但此时 CSS 工作组内出现了一些质疑的声音,认为现实中并不存在这个属性的适用场景,要求从草案中删除。为了挽救这个襁褓中的婴儿,Lea 不得不向网友求助,征集实际案例。网友的反馈相当热烈,这个实用属性也得以保留。
如今,这个属性仍然处于 WD 阶段,还没有一款浏览器实现相关的功能。让我们共同祈盼它早日落地吧!
(暂无)
本文是早期译版,未经校审。仅供参考。
In the past few years, CSS has undergone a transformation, similar to the JavaScript revolution circa 2004. It went from being a dead-simple styling language with limited power, to a complex technology defined by over 80 W3C specifications (including drafts), with its own developer ecosystem, its own conferences, and its own frameworks and tooling. CSS has grown so much that it’s practically impossible for any single person to hold all of it in their brain. Even in the W3C CSS Working Group that defines the language, nobody is an expert on every single aspect of CSS--and few even come close. Instead, most WG members focus on certain CSS specifications and might know very little about others.
在过去的几年里,CSS 经历了一场巨变,正如 JavaScript 在 2004 年前后所经历的那场革命。它从一门极度简单、功能有限的样式语言,发展成为一项由 80 多项 W3C 规范(含草案)所定义的复杂技术,并建立起了独有的开发者生态圈、专属的技术会议、专用的框架和工具链。**CSS 已经如此壮大,以致于一个普通人已经无法把它完整地装进自己的头脑了。**甚至在 W3C 专门定义这门语言的工作组中,也没人敢说自己是精通 CSS 所有方面的专家——甚至连接近这个程度都非常困难。实际上,大多数工作组成员只专注在 CSS 的某个特定细节中,而可能对其他部分知之甚少。
Up until roughly 2009, CSS expertise was not defined by how well the language was known. This was more or less a given for any serious CSS work. Instead, CSS prowess was defined by the number of browser bugs and workarounds that had been committed to memory. Fast-forward to 2015, and browsers are now designed to support standards, and flimsy browser-specific hacks are frowned upon. There are still some unavoidable incompatibilities, but--especially because most browsers now auto-update--the pace of change is so fast, that attempting to document them in a book would be a waste of time and space.
大约在 2009 年以前,评判一个人的 CSS 专业程度并不是看他对这门语言的了解有多深。对当时的 CSS 行业来说,这或多或少就是现实。一个人是否能称得上 CSS 高手,往往要看他能记住多少个浏览器 bug 和相应的对策。时间一转眼就到了 2015 年,现在的浏览器都是以 Web 标准作为设计基准的,过去那些脆弱的针对特定浏览器的 hack 早已风光不再。当然,某些不兼容的情况仍然无法避免,但是——尤其是现在的浏览器几乎都已经实现自动更新了——迭代速度已经非常之快,把这些不兼容的情况记到纸上完全是在浪费时间和空间。
The challenge in modern CSS has little to do with working around transient browser bugs. The challenge now is using the CSS features we have in a creative way, in order to come up with DRY, maintainable, flexible, lightweight, and as much as possible, standards-compliant solutions. This is exactly what this book is all about.
我们在现代 CSS 中所面临的挑战已经不在于如何绕过这些转瞬即逝的浏览器 bug 了。如今的挑战是,如何在保证 DRY、可维护、可扩展、轻量级并且尽可能符合标准的前提下,把我们手中的这些 CSS 特性转化为网页中的各种创意。这正是这本书将要呈现的内容。
{原书注释}
DRY is an acronym that stands for “Don’t Repeat Yourself.” It’s a popular programming mantra to promote an aspect of maintainable code: being able to change its parameters with as few edits as possible, ideally one. Emphasis on DRY CSS code is a recurring theme in this book. The opposite of DRY is WET, which stands for “We Enjoy Typing” or “Write Everything Twice.”
DRY 是一个首字母缩写(原文 “Don’t Repeat Yourself”),意思是你不应该重复你已经做过的事。它是一种广为流传的编程理念,旨在提升代码某方面的可维护性:在改变某个参数时,尽量做到只需要改极少的几处地方,最好是一处。强调 CSS 代码的 DRY 原则将是一个贯穿本书的主题。DRY 的反面是 WET,它的意思是 “We Enjoy Typing”(我们喜欢敲键盘)或 “Write Everything Twice”(同样代码写两处)。
There are many books out there that document certain CSS features from A to Z. CSS Secrets, for better or for worse, is not one of them. Its purpose is to fill the knowledge gaps that are left after you’ve already familiarized yourself with the reference material--to open your mind to new ways to take advantage of the features you already know about, or to let you know about useful CSS features that aren’t as shiny and popular, and that deserve more love. However, above all, the main purpose of this book is to teach you how to solve problems with CSS.
市面上有很多书,其内容就是以字母顺序记载一些 CSS 特性。不管怎样,你手里的这本《{$BOOK_NAME$}》不是那种书。这本书的目的在于,当你已经熟悉了那些参考书的内容之后,帮你填补知识断档——它会让你的头脑接触各种全新的方法,充分发挥那些你已经熟悉的特性所具备的无穷威力;同时也让你明白,某些你不熟悉而且看似不起眼的 CSS 特性可能同样威力无比、不可小觑。不过,总的来说,这本书最核心的目的是教你如何用 CSS 解决难题。
CSS Secrets is not a cookbook either. Each “secret” is not a canned recipe, with rigid steps you must follow to achieve a specific effect. Instead, I’ve tried to describe the thinking behind every technique in detail, as I believe that understanding the process of finding a solution is far more valuable than the solution itself. Even if you don’t think that a certain technique is relevant to your work, learning how to reach a solution might still prove valuable for tackling even completely different problems. Long story short, you will hopefully get many proverbial fish from this book, but its main goal is to “feed you for a lifetime,” by teaching you how to catch them.
《{$BOOK_NAME$}》也不是一本 “菜谱书”(cookbook)。每篇 “攻略” 并不是即开即用的菜谱——死板地套用某些步骤就可以达成某个特定的效果。实际上我努力把每个技巧背后的思考都尽量细致地描述出来,因为我相信,发现解决方案的过程比这个解决方案本身要有用得多。即使你认为某项技巧跟你的工作没有直接关联,学会如何摸索并得出解决方案仍然是有价值的,哪怕你面对的是一个完全不同的问题。长话短说,这本书不仅授之以 “鱼”,而且授之以 “渔”,让你一辈子不会为 “没鱼吃” 发愁。
举例来说,要把表单元素的字体设定为与页面的其他部分相同,你并不需要重复指定字体属性,只需利用
inherit
的特性即可
“表单元素” 指的是输入框、按钮等元素。
为什么要把表单元素的字体设定为 “与页面的其他部分相同”?为什么表单元素不能自动从祖先元素那里继承字体样式?这是因为在浏览器的默认样式中,通常已经给表单元素指定了字体样式。当一个元素自身已经指定某个属性之后,就无法从祖先元素那里继承了。
——在 Iterations 中实践响应式设计
Iterations 是 Basecamp 公司的一个内部管理工具。
Basecamp 公司原来叫做 37signals,从 2014 年开始改为现在的名字。Basecamp 原来是这家公司的主打产品的名字,后来当产品的名气远远超过公司名气的时候,公司改名也就是顺理成章的事情了。
本文是早期译版,未经校审。仅供参考。
Prerequisites
- RGBA/HSLA colors
背景知识
- RGBA/HSLA 颜色
{原书注释!}
We are using the term “backdrop” here to mean the part of the page that is underneath an element, which shows through its semi-transparent background.
我们在这里用了 “背层”(backdrop)一词,它表示页面被某个上层元素遮住的部分,这部分区域透过该元素的半透明背景显现出来。
One of the first use cases of semi-transparent colors was using them as backgrounds, over photographic or otherwise busy backdrops, to decrease contrast and make the text possible to read. The result is quite impressive, but can still be hard to read, especially with very low opacity colors and/or busy backdrops. For example, take a look at Figure XX.XX, where the main element has a semi-transparent white background. The markup looks like this:
半透明颜色最初的使用场景之一就是把它们作为背景,当叠放在照片类或其他花哨的背层之上时,可以减少对比度,确保文本的可读性。这种效果确实很有视觉冲击力,但仍然可能导致文字很难阅读,特别是当背景色的不透明度较低或背层图案太过花哨时。举个例子,我们来看看 图 4.14,图中 main
元素的背景是半透明的白色。结构大致是这样的:
<main>
<blockquote>
“The only way to get rid of a temptation[...]”
<footer>--
<cite>
Oscar Wilde,
The Picture of Dorian Gray
</cite>
</footer>
</blockquote>
</main>
And the CSS looks like this (with all irrelevant bits omitted for brevity):
而 CSS 代码可能是这样的(简短起见,这里只列出了关键样式):
body {
background: url("tiger.jpg") 0 / cover fixed;
}
main {
background: hsla(0,0%,100%,.3);
}
图 4.14
Our semi-transparent white background makes the text hard to read
半透明白色背景使文字很难阅读。
As you can observe, the text is really hard to read, due to the image behind it being busy and the background color only being 25% opaque. We could of course improve readability by increasing the alpha parameter of the background color, but then the effect will not be as interesting (see Figure XX.XX).
相信你可以察觉出来,文字确实难以看清,因为它后面的图片太过花哨了,而它的背景色只有 25% 的不透明度。当然我们可以通过提升背景色的不透明度来增加文本的可读性,不过这样一来整个效果就没有那么生动了(参见 图 4.15)。
图 4.15
Increasing the alpha value of our background color does fix the readability issue, but also makes our design less interesting
增加背景色的 alpha 值(不透明度)可以改善文本可读性的问题,但同时也让整个设计变得无趣了。
In traditional print design, this issue is often addressed by blurring the part of the photo that is underneath our text container. Blurred backgrounds are not as busy, and thus, text on them is easier to read. Because blurring is computationally expensive, in the past its toll on resources was prohibitive for using this technique in websites and UI design. However, with GPUs improving and hardware acceleration becoming more commonplace for more and more things, these days it’s used quite frequently. In the past few years, we have seen this technique in newer versions of both Microsoft Windows, as well as Apple iOS and Mac OS X (Figure XX.XX).
在传统的平面设计中,这个问题的解决方案通常是把文本层所覆盖的那部分图片区域作模糊处理。模糊的背景看起来就不那么花哨了,因此,在它之上的文本就相对比较易读了。在过去,由于模糊运算的性能消耗极其巨大,以致于这个技巧在网页设计和 UI 设计中鲜有用武之地。不过,随着 GPU 的不断进化以及硬件加速的不断普及,眼下这个技巧已经逐渐流行起来。在过去这几年里,我们已经可以在较新的 Microsoft Windows 系统中看到这个技巧的身影,而苹果的 iOS 和 Mac OS X 操作系统也不例外(图 4.16)。
图 4.16
Translucent UIs with a blurred backdrop have been becoming increasingly common in the past few years, as the toll of blurring on resources has stopped being prohibitively expensive (Apple iOS 8.1 is shown on the left and Apple OS X Yosemite is shown on the right)
在过去这几年间,由于模糊处理的资源消耗已经不再像以前那么高不可攀了,由模糊背层所构成的半透明 UI 已经变得越来越常见了(左图是 Apple iOS 8.1,右图是 Apple OS X Yosemite)。
We also got the ability to blur elements in CSS, via the blur()
filter, which is essentially a hardware-accelerated version of the corresponding SVG blur filter primitive that we always had for SVG elements. However, if we directly apply a blur()
filter to our example, the entire element is blurred, which makes it even less readable. (Figure XX.XX). Is there any way to just apply it to the element’s backdrop (i.e., the part of the background that is behind our element)?
借助 blur()
滤镜,我们还在 CSS 中获得了对元素进行模糊处理的能力。我们在 SVG 中很早就可以使用模糊滤镜了,而这个 CSS 滤镜本质上就是它的硬件加速对应版本。不过,如果我们直接在这个例子中使用 blur()
滤镜,那整个元素都会被模糊,文本反而变得更加无法阅读了(图 4.17)。有没有某种方法可以只对元素的背层(即被该元素遮住的那部分背景)应用这个滤镜呢?
图 4.17
Applying a
blur()
filter to the element itself makes things worse对这个元素应用一个
blur()
滤镜反而会把事情搞砸。
{原书注释!}
It’s also possible even with non-fixed backgrounds, just messier.
对非固定的背景来说也是有办法实现的,只不过难搞一些。
Provided that our element has a background-attachment
of fixed
, this is possible, albeit a bit tricky. Because we cannot apply the blurring to our element itself, we will apply it to a pseudo-element that is positioned behind the element and whose background seamlessly matches the one on <body>
.
假设大背景的 background-attachment
值是 fixed
,这种情况是有可能的,只不过不太常见。由于我们不能直接对元素本身进行模糊处理,那我们就对一个伪元素进行处理,然后将其定位到元素的下层,而它的背景将会无缝匹配 <body>
的背景。
First, we add the pseudo-element and position it absolutely, with all offsets being 0
, so that it covers the entire <main>
element:
首先,我们添加一个伪元素,将其绝对定位,并把所有偏移量置为 0
,这样就可以将它完整地覆盖到 <main>
元素之上:
main {
position: relative;
/* [Rest of styling] */
/* [其余样式] */
}
main::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
/* for debugging */
background: rgba(255,0,0,.5); /* 仅用于调试 */
}
{警告!!}
Be careful when using a negative
z-index
to move a child underneath its parent: if said parent is nested within other elements with backgrounds, the child will go below those as well.在使用负的
z-index
来把一个子元素移动到它的父元素下层时,请务必小心:如果父元素的上级元素有背景,则这个子元素将出现在它们之后。
We also applied a semi-transparent red
background, so we can see what we’re doing, otherwise debugging becomes difficult when we’re dealing with a transparent (and therefore, invisible) element. As you can see in Figure XX.XX, our pseudo-element is currently above our content, thus obscuring it. We can fix this by adding z-index: -1;
(Figure XX.XX).
我们同时还给它设置了一层半透明的 red
背景,这样我们就可以看清楚我们做了什么,不然我们很难调试透明的(即不可见的)元素。你在 图 4.18 中可以看到,我们的伪元素现在就覆盖在内容元素之上。我们可以用 z-index: -1;
来修正这个问题(参见 图 4.20)。
图 4.18
The pseudo-element is currently obscuring the text
伪元素现在正覆盖在文本之上。
图 4.20
Moving the pseudo-element behind its parent, with
z-index: -1;
使用
z-index: -1;
来把伪元素移动到宿主元素的后面。
{原书注释!}
Why not just use
background: inherit
onmain::before
? Because then it will inherit frommain
, notbody
, so the pseudo-element will get a semi-transparent white background as well.为什么不对
main::before
使用background: inherit
呢?因为伪元素会从main
(而不是body
)那里继承样式,这样它只能得到一个半透明的白色背景。
Now it’s time to replace that semi-transparent red background, with one that actually matches our backdrop, either by copying over the <body>
background, or by splitting it into its own rule. Can we blur now? Let’s try it:
现在该把半透明红色背景换掉了,换成跟背层完全匹配的背景。要实现这一点,我们要么把 <body>
的背景复制过来,要么把伪元素的背景声明合并过去。我们现在可以进行模糊处理吗?来试试看:
body, main::before {
background: url("tiger.jpg") 0 / cover fixed;
}
main {
position: relative;
background: hsla(0,0%,100%,.3);
}
main::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
filter: blur(20px);
}
As you can see in Figure XX.XX, we’re pretty much there. The blurring effect looks perfect toward the middle, but is less blurred closer to the edges. This happens because blurring reduces the area that is covered by a solid color by the blur radius. Applying a red
background to our pseudo-element helps clarify what’s going on (Figure XX.XX).
你在 图 4.21 中可以看到,这跟我们的期望已经相当接近了。模糊效果在中心区域看起来非常完美,但在接近边缘处会逐渐消退。这是因为模糊效果会削减实色像素所能覆盖的范围,削减的幅度正是模糊半径的长度。对伪元素应用一个 red
背景将有助于我们看清事情的真相(图 4.22)。
图 4.21
Blurring our pseudo-element almost works, but its less blurry on the edges, diminishing the frosted glass illusion
伪元素模糊的方法基本上成功了,但模糊效果在边缘处会逐渐消退,尽重削弱了毛玻璃的视觉效果。
图 4.22
Adding a
red
background helps make sense of what’s happening添加一个
red
背景有助于解释事情的真相。
To circumvent this issue, we will make the pseudo-element at least 20px
(as much as our blur radius) larger than the dimensions of its container, by applying a margin of -20px
or less to be on the safe side, as different browsers might use different blurring algorithms. As Figure XX.XX demonstrates, this fixes the issue with the faded blurring at the edges, but now there is also some blurring outside our container, which makes it look like a smudge instead of frosted glass. Thankfully, this is also easy to fix: we will just apply overflow: hidden;
to main
, in order to clip that extraneous blurring. The final code looks as follows, and its result can be seen in Figure XX.XX:
为了补偿这种情况,我们需要让伪元素相比其宿主元素的尺寸再向外扩大至少 20px
(即它的模糊半径),我们可以通过 -20px
的外边距来达到目的,由于不同浏览器的模糊算法可能存在差异,用一个更大的绝对值(比如 -30px
)会更保险一些。如 图 4.19 所示,这个方法可以修复边缘模糊消退的问题,但现在的情况是有一圈模糊效果超出了容器,这让它看起来不像毛玻璃,而更像是玻璃脏了。不过幸运的是,这个问题也很容易修复:我们只要对 main
元素应用 overflow: hidden;
,就可以把多余的模糊区域裁切掉了。最终代码如下所示,而最终效果可以在 图 4.23 中看到:
图 4.19
We fixed the faded blurring at the edges, but now there is some blurring outside our element too
我们修正了边缘处的模糊消退情况,但现在又出现了模糊效果超出元素范围的情况。
body, main::before {
background: url("tiger.jpg") 0 / cover fixed;
}
main {
position: relative;
background: hsla(0,0%,100%,.3);
overflow: hidden;
}
main::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
filter: blur(20px);
margin: -30px;
}
图 4.23
Our final result
我们最终得到的效果。
Note how much more readable our page has now become, and how much more elegant it looks. It’s debatable whether the fallback for this effect constitutes graceful degradation. If filters are not supported, we will get the result we saw in the beginning (Figure XX.XX). We can make our fallback a bit more readable by increasing the opacity of the background color.
请注意现在页面文本的可读性相比以前要好多了,而整个设计也优雅多了。现在唯一有争议的问题就是这个效果的回退机制是否算得上平稳退化。如果浏览器不支持滤镜,我们将会得到最开始在 图 4.14 中所看到的结果。我们只能适当增加背景色的不透明度,以便让回退样式下的可读性得到少许提升。
Related specs
相关规范
相信你以前肯定尝试过 CSS 中的半透明颜色,比如
rgba()
和hsla()
。半透明颜色是 2009 年发生的一场重大变革。
RGBA 与 HSLA 颜色是在 “CSS Color Module Level 3” 这个规范模块中定义的。这个规范的最终确立主要经历了两个阶段。
在 2003 年及以前,这个规范模块叫作 “CSS3 Color Module”,已经推进到了 CR 阶段,但由于缺乏足够的实现,迟迟无法进入接下来的 PR 阶段。(注:关于 CSS 规范的推进流程,参见《Web 标准:是敌还是友》一节。)
这个规范一搁就是好几年。到了 2008 年,CSS 工作组终于坐不住了,决定重新推进这个规范。专家们砍掉了模块中的一些内容,把它倒回了 WD 的状态,重新征求各方意见。接下来的进展就顺利多了:
各浏览器对 RGBA 与 HSLA 颜色的最早支持时间是这样的:
浏览器 | 版本 | 发布时间 |
---|---|---|
Safari | 3.1 | 2008-03-18 |
Firefox | 3.0 | 2008-06-17 |
Chrome | 1.0 | 2008-12-11 |
Opera | 10.0 | 2009-09-01 |
IE | 9.0 | 2011-03-14 |
大家可以看到,除了 IE 之外,其它主流浏览器在 2008 年底都已经可以正常显示半透明颜色了。而此时 IE8 都还没有发布。在 IE9 出现之前,IE 浏览器只能借助其私有的滤镜特性实现极为有限的半透明背景色效果。
(暂无)
原文 | 本书译法 | 其它译法(未采用) |
---|---|---|
CSS | - | 层叠样式表、级联样式表、样式单 |
cascade | 层叠(机制) | 级联 |
fallback | 回退(机制、措施、方案) | 后备、回落、降级 |
selector | 选择符 | 选择器、选择式 |
specification | (技术)规范 | 规格说明书 |
spec | 〃 | 〃 |
specificity | (选择符的)优先级 | 特异性、权重 |
feature | 特性 | 功能 |
markup | (结构)标记 | - |
tag | 标签 | 标记 |
attribute | (标签)属性 | 特性 |
property | (对象、样式)属性 | - |
color | 颜色 | 色彩 |
transition | 过渡(动画) | 变换 |
transform | 变形 | 变换 |
translate | 位移 | 转移、转化 |
animation | 动画 | - |
gradient | 渐变(色、图案) | 过渡 |
linear-gradient | 线性渐变 | 直线~ |
radial-gradient | 径向渐变 | 辐射~ |
conic-gradient | 角向渐变 | 角度~、圆锥~ |
background | 背景 | - |
background color | 背景色 | 背景颜色 |
background image | 背景图像 | 背景图片 |
(background) repeat | (重复)平铺 | 重复 |
(repeating) tile | (平铺)贴片 | 瓦片、瓷砖 |
Box Model | 盒模型 | 盒子模型 |
padding | 内边距 | 补白 |
margin | 外边距 | 边距 |
margin collapsing | 外边距重叠 | ~叠加、~重合、~塌陷 |
border | 边框 | - |
outline | 描边 | 外框、轮廓 |
flexbox | 伸缩盒(布局) | 弹性盒、弹性盒子 |
block (level) element | 块级元素 | 块元素 |
inline element | 行内元素 | 内联~、行间~ |
container | 容器 | - |
wrapper | 容器 | - |
UI | - | - |
tab | 标签页 | 标签、页签、选项卡 |
preprocessor | 预处理器 | - |
bug | - | (程序)缺陷、漏洞 |
(CSS) hack | - | 招数、偏方、CSS 黑客程序 |
hacky | 有 hack 的味道(感觉) | - |
(未完)
-
表示不译,直接采用英文原文。-
表示暂无其它译法。~
)表示省略与前面重复的部分字词。为大致了解读者对中文书名的偏好,我在微博发起了一项投票。截至 11 月 15 日,各候选方案的得票情况如下:
备选书名 | 得票情况 |
---|---|
《鲜为人知的 CSS》 | 17 (3.5%) |
《CSS 秘密花园》 | 170 (34.6%) |
《CSS 探秘》 | 59 (12%) |
《CSS 的 47 个秘密》 | 19 (3.9%) |
《CSS 秘籍》 | 31 (6.3%) |
《你不知道的 CSS》 | 34 (6.9%) |
《CSS Secrets 中文版》 | 63 (12.8%) |
《CSS 一姐写的、魔法哥翻译的那本书》 | 98 (20%) |
同时,网友们也提出了一些不错的备选书名,比如:
本书的最终中文名将由出版社决定,但我相信网友的意见将是非常重要的参考因素。目前中文书名还未定稿,大家请在这里发表观点,谢谢!
最好说出你的观点,而不是简单地投票。
单纯的投票贴将在汇总后删除:
- @wileam @GuanyuChen 赞成《CSS 奥秘》
- @cforth @SuperKieran @iamcho 赞成《CSS 秘密花园》
- @MangGoDio 赞成《CSS 解密》
如何提交勘误?
如果你在阅读本书的过程中发现任何错误(包括编辑、排版、翻译、技术等方面的错误),请新开一个 issue 来描述这个错误,并请在标题中注明 “提交勘误”。当编辑或译者确认为错误后,将会收录到本页面中。
感谢你的严谨让这本书变得圆满,其他读者必将从中受益!
图标含义
- ✅ - 已在后续印次中修正。
⚠️ - 尝试在后续印次中修正,但仍有错误。▶️ - 已被出版社确认,即将在后续印次中修正。- 🕒 - 有待出版社确认。
标记含义
- 下划线 - 仅用于标注需要修改的部分,以便对照,并非排版格式。(但遗憾的是,GitHub 并不支持下划线样式的显示。我们需要使用其它 Markdown 编辑器打开本文的源码,才能看到效果。)
本页有两个代码块,均包含以下这行代码:
background: linear-gradient(90deg, yellow, red);
其中 90deg
应为 0deg
。
(贡献者:@yifon)
⚠️ 注意:在某些印次中,所有线性渐变的声明(包括加了浏览器前缀的属性)中的90deg
都被改成了0deg
,这是错误的。只有无前缀的声明需要改,加前缀的声明不需要改。
……它们为 CSS 的编写提供提供了一些便利……
“提供提供” 应为 “提供”。
(贡献者: 图灵社区 Skyline555)
(贡献者:@cssmagic)
……这样的结果显明更有随机的感觉……
“显明” 应为 “明显”。
(贡献者: 图灵社区 Taipa)
构成相对质数的这些数字没有公约数
“没有公约数” 应为 “没有公约数(除了 1 以外)”。
(贡献者:微信网友 “剑猫情缘”)
第一段:
为了让最小公倍数最大化,这些数字最好是 “相对质数”
应改为 “……这些数字最好是 ‘互质’ 的”
举例来说,3、4 和 5 是相对质数
应改为 “……3、4 和 5 是互质的”
要达成相对质数,……
应改为 “要达成互质关系,……”
因为质数跟其他任意数字都是相对质数。
应改为 “因为质数跟其他任意数字都是互质的”
脚注
相对质数是一种数字之间的关系
应改为 “互质是一种数字之间的关系”
构成相对质数的这些数字……
应改为 “构成互质关系的这些数字……”
比如说,10 和 27 是相对质数……
应改为 “比如说,10 和 27 是互质的……”
……一个质数跟其他所有数字都可以构成相对质数。
应改为 “……一个质数跟其他所有数字都可以构成互质关系。”
(贡献者:微信网友 “剑猫情缘”)
在上一条勘误的基础上,需要把关于 “互质” 的表述写得再严格一些:
第一段:
质数跟其他任意数字都是互质的
应表述为 “质数跟其他任意(不是自己倍数的)数字都是互质的”。
脚注
一个质数跟其他所有数字都可以构成互质关系
应改为 “一个质数跟其他所有(不是自己倍数的)数字都可以构成互质关系”
(贡献者:微信网友 “PiB”)
background: linear-gradient(white, white) padding-box, url(stone-art.jpg) border-box 0 / cover;
0
应为 0 0
。
(贡献者:@galliliu)
transform: skew(45deg);
45deg
应为 -45deg
。
(贡献者:@sanqianwdj)
算式右侧的 “414.421 356 2%” 应为 “141.421 356 2%”。
(贡献者:@ourfeel)
⚠️ 注意:在某些印次中,“141.421 356 2%” 被错误地改成了 “141.421 356 2 px”。
background: linear-gradient(-45deg, transparent 15px, #58a 0) right, linear-gradient(45deg, transparent 15px, #655 0) left;
此条声明中的 #58a
和 #655
应互换。
(贡献者:@ArvinTung)
……只要图案边缘处的颜色是
#58
就行……
#58
应为 #58a
。
(贡献者:@zhuxinghan)
……但有着它自已独有的局限……
“自已” 应为 “自己”。
(这是译者 @cssmagic 在提交译稿时出现的笔误,向大家致歉!)
……(它会以元素自身的宽高作为基数度进行换算)……
“基数度” 应为 “基数”。
(贡献者:@zhuxinghan)
以该元素相同的尺寸和位置……
考虑到正确的表述结构应为 “与……相同”,这一句存在语病。因此改为:“参照该元素的尺寸和位置……”。
(贡献者:@zhuxinghan)
……因为
border-radius
会无耻地忽视透明部分……
border-radius
应为 box-shadow
。
(这是译者 @cssmagic 在提交译稿时出现的笔误,向大家致歉!)
(贡献者:图灵社区 WooJane)
cosθ = y/r
sinθ = x/r
图中公式中的 cos
应为 sin
,sin
应为 cos
。
(贡献者:@peacelee)
代码块的右上角应加上 “SCSS” 标记。
(贡献者:@zhuxinghan)
……其实不管怎样,这本来就是你早该做好的份内之事。
“份内” 应为 “分内”。
(贡献者:@zhuxinghan)
有一个办法可以解决这个问题,那就是用
background-position
把向条纹向底部移动.5em
……
“把向条纹” 应为 “把条纹”。
(贡献者:@zhuxinghan)
……
background-sizetext-shadow
……
background-size
和 text-shadow
之间应加逗分进行分隔。
(贡献者:@zhuxinghan)
……因为我们只需要使用
text-shadows
属性的扩张参数就可让投影变大,然后看起来就像给文字勾边了一样。
需作如下修正:
text-shadows
应为 text-shadow
。(这是译者 @cssmagic 在提交译稿时出现的笔误,向大家致歉!)
需在 position: fixed;
之上添加一行:
content: '';
(贡献者:@OfficialYoungX)
slider.insertBefore(img, div);
本行应为:
slider.insertBefore(div, img);
(原载于英文原书勘误表,中文版整理者:@keyfoxth)
用
input:in-range
来代替简单的input
选择符,是为了只在浏览器支持范围输入控件时才对它设置样式。然后,我们就可以利用层叠机制……
应改为:“如果用 input:in-range
来代替简单的 input
选择符,就可以只在浏览器支持范围输入控件时才对它设置样式。进而可以利用层叠机制……”
(这是译者 @cssmagic 的翻译错误,向大家致歉!)
……如果列表中只有少量列表项,我们可以为第一项展示出多行预览文字……
“第一项” 应为 “每一项”。
(贡献者:@zhuxinghan)
如果把
width
这一行声明注释掉,你会发现其实没有影响。……只有把 width 显式地设置为 900px 之外(或大或小)的其他值,我们才有可能看出区别。
应为:
“如果把 max-width
这一行声明注释掉,你会发现其实没有影响。……只有把容器的 width
属性指定为 900px 之外(或大或小)的其他值,我们才有可能看出区别。”
(原载于英文原书勘误表,中文版整理者:@cssmagic)
⚠️ 注意:在某些印次中,前半句没有修改。
……我们希望下落方向上的调速函数是加速的(
ease-out
),而弹起方向上是减速的(ease-in
)……
ease-out
和 ease-in
应对调。
(贡献者: 图灵社区 406298102)
……而当用户的鼠标移到图上时,它会缓慢地向左滚动……
改为 “……而当用户的鼠标移到图上时,图片会缓慢地向左滚动……”
(贡献者: 图灵社区 Alex.)
……圆形区域显露出的是图片的右半部分……
“右半部分” 应为 “左半部分”。
(贡献者:@zhuxinghan)
by O’Reilly Media, Inc.
background: #58a linear-gradient(#77a0bb, #58a);
请问这种写法是为了提供回退机制吗?
但是根据我的测试。。因为 IE7 8 9 不识别 linear-gradient
会导致整行的 background
属性失效。。从而失去背景色。。但是在 IE5 下面的有效的。。所以很奇怪这种写法是什么意思。。
谢谢魔法哥的解答。。
本文是早期译版,未经校审。仅供参考。
Prerequisites
- Basic font embedding through
@font-face
rules背景知识
- 通过
@font-face
规则实现基本的字体嵌入
You will find many hymns to the humble ampersand in typographic literature. No other character can instantly add the elegance a nicely designed ampersand has the power to add. Entire websites have been devoted to finding the font with the best looking ampersands. However, the font with the nicest ampersand is not necessarily the one you want for the rest of your text. After all, a really beautiful and elegant effect for headlines is the contrast between a nice sans serif font and beautiful, intricate serif ampersands.
在文学作品的字体排印中,你会发现简写的 & 符号倍受推崇。没有其他字符可以像精心设计过的 & 字符那样迅速传递出优雅的气质。所有网站都殚精竭虑,试图找出一种能够体现 & 字符之美的最佳字体。不过,可以显示出优美 & 字符的那些字体往往并不适用于页面中的其他文本。毕竟,对标题来说,真正美丽而优雅的效果正是来源于清爽的无衬线字体与华丽的衬线 & 符号之间的对比。
图 5.18
A few nice ampersands in fonts that are readily available in most computers; from left to right: Baskerville, Goudy Old Style, Garamond, Palatino (all italic)
绝大多数电脑都包含了某种可以将 & 符号显示得很好看的字体;从左到右的字体分别为 Baskerville、Goudy Old Style、Garamond 和 Palatino(均以斜体风格显示)。
Web designers realized this a while ago, but the techniques employed to achieve it are rather crude and tedious. They usually involve wrapping every ampersand with a <span>
, through a script or manually, like so:
网页设计师们在很早以前就意识到这一点了,但所能找到的实现方法却是十分的粗糙和繁琐。这些方法往往要求我们通过脚本或纯手工地把每个 & 符号用一个 <span>
标签包起来,就像这样:
HTML <span class="amp">&</span> CSS
Then, we apply the font styling we want to just the .amp
class:
然后,再给 .amp
这个类指定我们想要的字体样式:
.amp {
font-family: Baskerville, "Goudy Old Style",
Garamond, Palatino, serif;
font-style: italic;
}
图 5.19
Our “HTML & CSS” headline, before and after the ampersand treatment
这个 “HTML & CSS” 标题中的 & 符号在美化前后的效果对比。
This works fine and you can see the before and after in Figure XX.XX. However, the technique to achieve it is rather messy and sometimes even downright impossible, when we cannot easily modify the HTML markup (e.g., when using a CMS). Can’t we just tell CSS to style certain characters differently?
这种方法确实可以奏效,你可以在 图 5.19 中看到美化前后的对比图。但是,这个实现方法相当杂乱,而且有时还是完全不可行的——在某些情况下(比如在 CMS 环境下),我们根本无法轻易地修改 HTML 结构。难道我们就不能让 CSS 去单独美化某个特定字符吗?
It turns out that we can, indeed, style certain characters (or even ranges of characters) with a different font, but the way to do it is not as straightforward as you might have hoped.
原来我们真的可以用另一种字体来单独美化某个特定字符(或是某个区间内的多个字符),但这个过程可能没有你想像的那么简单明了。
We usually specify multiple fonts (font stacks) in font-family
declarations so that in case our top preference is not available, the browser can fall back to other fonts that would also fit our design. However, many authors forget that this works on a per-character basis as well. If a font is available, but only contains a few characters, it will be used for those characters and the browser will fall back to the other fonts for the rest. This applies to both local and embedded fonts included through @font-face
rules.
我们通常会在 font-family
声明中同时指定多个字体(即 “字体队列”),这是为了万一当我们最优先指定的那款字体不可用时,浏览器还可以回退到其他符合整体设计风格的字体。但是,很多开发者都忽略了一点:这个机制对单个字符来说也是有效的。如果某款字体可用,但仅包括某几个字符时,那它就只会用来显示这几个字符;而在显示其他字符时,浏览器就会回退到其他字体。这个规则对本地字体和通过 @font-face
规则引入的嵌入字体都是有效的。
It follows that if we have a font with only one character (guess which one!), it will only be used for that one character, and all others will get the second, third, etc. font from our font stack. So, we have an easy way to only style ampersands: create a web font with just the ampersand we want, include it through @font-face
, then use it first in your font stack:
在这个规则之下,如果我们有一款字体只包含一个字符(你肯定猜到是哪个了吧),那这款字体将只会用于显示这个字符,而其他字符将会由字体队列中排在第二、第三位或更后面的字体来显示。因此,我们只美化 & 符号的方法就浮出水面了:创建一种只包含我们想要的 & 字形的 Web 字体,通过 @font-face
将其引入到网页中,然后把它排在字体队列中的第一位:
@font-face {
font-family: Ampersand;
src: url("fonts/ampersand.woff");
}
h1 {
font-family: Ampersand, Helvetica, sans-serif;
}
While this is very flexible, it’s suboptimal if all we wanted was to style ampersands with one of the built-in fonts. Not only is it a hassle to create a font file, it also adds an extra HTTP request, not to mention the potential legal issues, if the font you were going for forbids subsetting. Is there a way to use local fonts for this?
这个方法算是比较灵活的,但如果我们只想用系统内建字体中的某一款来美化 & 符号,那这个方法就不够理想了。不仅生成字体文件很麻烦,而且它还会增加一个额外的 HTTP 请求;如果你看中的这款字体并不允许拆解使用,那你可能还要面对版权上的问题。有没有一种办法可以用本地字体来实现这种效果呢?
You might know that the src
descriptor in @font-face
rules also accepts a local()
function, for specifying local font names. Therefore, instead of a separate web font, you could instead specify a font stack of local fonts:
你可能已经了解到 @font-face
规则中的 src
描述符还可以接受 local()
函数,用于指定本地字体的名称。因此,不需要用到任何外部的 Web 字体,你可以直接在字体队列中指定一款本地字体:
@font-face {
font-family: Ampersand;
src: local('Baskerville'),
local('Goudy Old Style'),
local('Garamond'),
local('Palatino');
}
However, if you try to apply the Ampersand font now, you will notice that our serif font was applied to the entire text (Figure XX.XX), as these fonts include all characters. This doesn’t mean we’re going the wrong way; it just means we are missing a descriptor to declare that we are only interested in the ampersand glyph from these local fonts. Such a descriptor exists, and its name is unicode-range
.
但是,如果你马上就想试试 Ampersand 字体,你会发现整段文本都会被应用为我们指定的某款衬线字体(图 5.20),因为这些字体本身涵盖了这段文本的所有字符。不过这并不表示我们走错了路;这只表示我们还漏了一步没有走——我们还需要一个描述符来声明我们想用这几款本地字体来显示哪些字符。这个描述符确实是存在的,它叫作 unicode-range
。
图 5.20
Including local fonts through
@font-face
results in them being applied to the whole text by default通过
@font-face
引入本地字体,导致这些字体会默认应用到整段文本上。
The unicode-range
descriptor only works inside @font-face
rules (hence the term descriptor; it is not a CSS property) and limits the characters used to a subset. It works with both local and remote fonts. Some browsers are even smart enough to not download remote fonts if those characters are not used in the page!
这个 unicode-range
描述符只在 @font-face
规则内部生效(因此这里用了 “描述符” 这个术语;它并不是一个 CSS 属性),它可以把字体作用的字符范围限制在一个子集内。它对本地字体和远程字体都是有效的。某些聪明的浏览器甚至可以做到当网页中的所有字符都用不到远程字体时就不去下载!
Unfortunately, unicode-range
is as cryptic in its syntax as it is useful in its application. It works with Unicode codepoints, not literal characters. Therefore, before using it, you need to find the hexadecimal codepoint of the character(s) you want to specify. There are numerous online sources for that, or you can just use the following snippet of JS in the console:
这个 unicode-range
在实践中非常实用,但不幸的是它在语法上却非常晦涩。它的语法是基于 “Unicode 码位” 的,而不是基于字符的字面形态。因此,在使用之前,你需要查出你想指定的这些字符的十六进制码位。有不少网上工具可以做这件事情,或者你也可以在控制台试试下面这句 JS 代码:
// returns 26
"&".charCodeAt(0).toString(16); // 返回 26
{警告!!}
String#charCodeAt()
returns incorrect results for Unicode characters beyond the BMP. However, 99.9% of the characters you will need to look up will be in it. If the result you get is in the D800-DFFF range, it means you have an “astral” character and you’re better off using a proper online tool to figure out what its Unicode codepoint is. The ES6 methodString#codePointAt()
will solve this issue.对于 BMP(Basic Multilingual Plane,基本多文种平面)之外的 Unicode 字符来说,
String#charCodeAt()
将会返回错误的结果。不过我们日常所需要查询的 99.9% 的字符应该都是在这个范围之内的。如果你得到的结果在 D800-DFFF 区间之内,则意味着这个字符太过 “超凡脱俗”,你最好换用一个靠谱的网上工具来查出它的真实码位。不过 ES6 的String#codePointAt()
方法已经修复了这个问题。
Now that you have the hex codepoint(s), you can prepend them with U+
and you’ve already specified a single character! Here’s how the declaration would look for our ampersand use case:
这样你就得到了字符的十六进制码位,然后你需要在码位前面加上 U+
作为前缀。这样一来,我们终于算是指定了一个字符!以 & 符号为例,我们需要这样来声明:
unicode-range: U+26;
If you wanted to specify a range of characters, you would still need one U+
, like so: U+400-4FF
. In fact, for that kind of range, you could have used wildcards and specified it as U+4??
instead. Multiple characters or ranges are also allowed, separated by commas, such as U+26, U+4??, U+2665-2670
. In this case, however, a single character is all we need. Our code now looks like this:
如果你想指定一个字符区间,你还是要加上 U+
前缀,比如这样:U+400-4FF
。实际上对于这个区间来说,你还可以使用通配符,以这样的方式来写:U+4??
。同时指定多个字符或多个区间也是允许的,把它们用逗号隔开即可,比如这样:U+26, U+4??, U+2665-2670
。不过在我们的例子中,只要能指定单个字符就足够了。我们的代码现在就变成这样了:
@font-face {
font-family: Ampersand;
src: local('Baskerville'),
local('Goudy Old Style'),
local('Palatino'),
local('Book Antiqua');
unicode-range: U+26;
}
h1 {
font-family: Ampersand, Helvetica, sans-serif;
}
图 5.21
Applying a different font to our ampersands, with the help of font stacks and the
unicode-range
descriptor借助字体队列和
unicode-range
描述符,我们给 & 符号应用了不同的字体。
If you try it out (Figure XX.XX), you will see that we did, in fact, apply a different font to our ampersands! However, the result is still not exactly what we want. The ampersand in Figure XX.XX was from the italic variant of the Baskerville font, as in general, italic serif fonts tend to have much nicer ampersands. We’re not styling the ampersands directly, so how can we italicize them?
如果你亲手试一试(结果参见 图 5.21),就会发现我们终于给 & 符号应用了不一样的字体!不过,这个结果还不完全是我们所期望的。图 5.19 中的 & 符号是用 Baskerville 字体的斜体风格来显示的,因为一般来说,斜体的衬线字体往往可以显示出更美观的 & 符号。但我们并不是在对 & 符号单独设置样式啊,我们该如何把它设为斜体呢?
Our first thought might be to use the font-style
descriptor in the @font-face
rule. However, this does not have the effect we want at all. It merely tells the browser to use these fonts in italic text. Therefore, it will make our Ampersand font be completely ignored, unless the whole headline is italic (in which case, we will indeed get the nice italic ampersand).
我们的第一个想法可能是在 @font-face
规则中使用 font-style
描述符。不过这并不会产生我们想要的效果。它只不过是告诉浏览器只在斜体文本中使用这些字体。因此,它会让我们的 Ampersand 字体被完全忽略掉,除非整个标题都是斜体的(确实在这种情况下,& 符号会显示为我们想要的样子)。
Unfortunately, the only solution here is a bit of a hacky one: instead of using the font family name, we need to use the PostScript Name of the individual font style/weight we want. So, to get the italic versions of the fonts we used, the final code would look like this:
很遗憾,我们在这里唯一的出路不得不用到一点儿奇技淫巧:我们在这里不去指定字体的家族名(family name),而是直接指定字体中我们想要的单个风格/字重所对应的 “PostScript 名称”。因此,为了指定这些字体的斜体版本,最终的代码会变成这样:
{原书注释!}
To find a font’s PostScript Name in Mac OS X, select it in the FontBook application and press ⌘I.
如果要在 Mac OS X 中查出某款字体的 “PostScript 名称”,则可以在字体簿程序中选中该字体,然后按
⌘I
。
@font-face {
font-family: Ampersand;
src: local('Baskerville-Italic'),
local('GoudyOldStyleT-Italic'),
local('Palatino-Italic'),
local('BookAntiqua-Italic');
unicode-range: U+26;
}
h1 {
font-family: Ampersand, Helvetica, sans-serif;
}
And this finally works great to give us the ampersands we wanted, just like in Figure XX.XX. Unfortunately, if we need to customize their styling even more (e.g., to increase their font size, reduce their opacity, or anything else), we would need to go the HTML element route. However, if we only want a different font and font style/weight, this trick works wonders. You can use the same general idea to also style numbers with a different font, symbols, punctuation--the possibilities are endless!
最终,这段代码可以完美地将 & 符号显示为我们想要的样式,跟 图 5.19 中的效果如出一辙。不过,如果我们还想进一步对它的样式进行自定义的话(比如增加大字号、改变透明度等等),就只能倒回到修改 HTML 的那条老路了。当然,如果我们只想把它设置为不同的字体或字体中特定的某个风格/字重,那这个技巧堪称完美。你还可以举一反三,用不同的字体来美化数字、符号、标点等等——各种创意完全停不下来!
{致敬}
Hat tip to Drew McLellan for coming up with the first version of this effect.
向 Drew McLellan 脱帽致敬,感谢他提出 这个效果的最初版本。
Related specs
相关规范
@hax 问道:
用“选择符”而不是“选择器”有什么讲究吗?
我本人倾向于将 JavaScript/jQuery 中的 querySelector/selector 译作 “选择器”,而将 CSS 中的 selector 译作 “选择符”。
我本人倾向于将 JavaScript/jQuery 中通过 CSS selector 匹配查找元素的 .querySelector()
/.query()
/$()
函数译作 “选择器”,而将 CSS selector 译作 “选择符”。
原因在于,前者的工作方式是主动获取,将其视为工具;而后者的工作方式是被动匹配,将其视为一种描述符号。
本讨论源自:#1 (comment)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.