精通响应式-Web-设计-全-
精通响应式 Web 设计(全)
原文:
zh.annas-archive.org/md5/14CB11AB973C4F1BAA6102D9FEAB3F3B
译者:飞龙
前言
在响应式 Web 设计之前,网页设计师和前端开发人员的工作主要集中在将印刷布局转化为网站和应用程序。元素和尺寸是固定的,它们需要适应和缩放的需求并不是我们今天必须考虑的概念的一部分。
设备有各种形状和大小。针对支持(或不支持)某些 HTML、CSS 和 JavaScript 技术、UX 原则、可用性最佳实践的操作系统和浏览器,以及了解移动设备如何影响我们生活的世界,现在是作为网页设计师和前端开发人员所做的工作的“复杂”部分。
在这本书中,我提供了大量关于 RWD 如何为更好的网站和 Web 应用程序提供路径的技术和概念信息。安装和使用 Sass,处理图像和视频,以及创建稳健的排版比例来构建响应式电子邮件是本书中您将能够阅读到的一些内容宝石。
升级的时候到了!
本书涵盖的内容
第一章,“利用 Sass 的力量进行响应式 Web 设计”,从安装 Sass 的最简单的步骤开始;然后我们学习如何让 Sass“监视”我们的 SCSS 文件。然后,有关基本 Sass 概念的易于理解的解释,如变量、混合、参数、嵌套、部分文件、@import
指令、源映射和 Sass 注释。我们还学会了自动添加供应商前缀并使用 Prepros 自动编译我们的 SCSS 文件。我们讨论了创建混合以尽可能轻松地处理媒体查询,考虑内容如何定义断点。
第二章,“使用 HTML5 标记我们的内容”,澄清了 HTML 是一种标记语言,而不是代码。然后,我们讨论了最常用的 HTML5 元素,这些元素允许我们语义化地标记我们的内容。以简单的方式改善我们构建的可访问性与 ARIA 角色也是我们要解决的问题。我们还讨论了 RWD 所需的不同元标记,然后有一个将所有内容整合在一起的示例。
第三章,“移动优先还是桌面优先?”,揭示了为什么以及何时应该使用移动优先或桌面优先。通过示例,我们将学习如何使用自适应 Web 设计和响应式 Web 设计来改造网站。我们将了解Respond.js和条件类,以支持在构建移动优先时的旧版浏览器。
第四章,“CSS 网格、CSS 框架、UI 工具包和 Flexbox 用于响应式 Web 设计”,帮助我们理解什么是网格,如何使用它以及为什么。有了这个理解,我们在构建网站或 Web 应用程序时可以做出明智的决定。我们还使用浮动技术和 Flexbox 创建自定义 CSS 网格。我们将再次使用条件类来解决旧版浏览器的问题,并借助一个小脚本,我们可以使用.ie10
特定选择器来处理 IE10 的怪癖。
第五章,“设计由大手指驱动的小型 UI”,展示了可用性和可访问性在本章中起着重要作用。我们还找到了关于目标区域的不同大小、控件的位置(链接、按钮、表单字段等)以及不同设备上的触摸区域的解释。还有三个关于如何创建菜单按钮的示例,以及三个关于移动导航模式的示例。
第六章,响应式网页设计中的图像和视频处理,是本书中最有趣的章节之一,因为 RWD 中的图像是一个“事物”。我们将讨论使用<picture>
元素和srcset
属性为不同的图像提供不同的方式。本章还介绍了使用 CSS、jQuery 和 JavaScript 使视频具有响应性。我们还将学习使用基于矢量的文件,如图标字体和 SVG。
第七章,响应式网页设计中的有意义的排版,讨论使用相对单位是理想的,因为它们提供了可伸缩性,而这正是 RWD 所关注的。本章的重点是我们将学习如何使用模块化比例来创建和谐的排版比例。我们还将使用Flowtype.js来提高我们文本的可读性。
第八章,响应式电子邮件,表明电子邮件在移动设备上的打开次数比在台式机上更多;响应式电子邮件在移动设备上的参与度比非响应式电子邮件更高;人们在台式机上点击电子邮件的次数比在移动设备上更多。我们还将创建一个电子邮件模板作为示例。我们将学习使用 CSS 重置块来规范那些古怪的电子邮件客户端,并了解到电子邮件的最佳宽度不超过 600 像素。
所有这些章节都有 CodePen 演示。
您需要为本书准备什么
在阅读本书的示例时,需要考虑以下几点:文本编辑器或 IDE(本书中使用 Sublime Text),互联网访问和在您的计算机上安装应用程序的管理员权限。
您可能还需要图像编辑软件,如 Photoshop、Fireworks 或 GIMP。如果您使用其他软件,也完全可以。
如果可能的话,您可以使用一种或两种真实的移动设备来体验示例和演示的正确环境。否则,使用 Chrome 的 DevTool 的设备模式功能也可以。
这本书是为谁写的
如果您已经了解一些 HTML 和 CSS,并且理解响应式网页设计的原则,那么这本书适合您。无论您是网页设计师还是网页开发人员,无论您是初学者还是经验丰富的网络专业人士,这本书都有您需要学习的内容。
对 HTML 和 CSS 的良好理解是必需的,因为 RWD 在很大程度上依赖于这些技术。对 jQuery 的一些了解也是推荐的,但不是强制的。
约定
在本书中,您会发现一些文本样式,用于区分不同类型的信息。以下是一些这些样式的示例,以及它们的含义解释。
文本中的代码单词、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:“sizes
属性也可以与<picture>
元素一起使用,但我们将专注于使用sizes
属性与<img>
标签。”
代码块设置如下:
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
任何命令行输入或输出都会以以下方式书写:
gem install sass
新术语和重要单词以粗体显示。您在屏幕上看到的单词,比如菜单或对话框中的单词,会以这种方式出现在文本中:“点击下一步按钮会将您移动到下一个屏幕”。
注意
警告或重要提示会以这样的方式显示在一个框中。
提示
提示和技巧会以这样的方式出现。
第一章:利用 Sass 为响应式网页设计赋能
在我们深入掌握使用 HTML5 和 CSS3 进行响应式网页设计之前,我们需要就技术达成共识,就我们的情况而言,CSS 预处理器,特别是 Sass。
在本书中,所有的 CSS 都将以 SCSS 格式写成 Sass。我们编写 CSS 的方式已经改变,改进非常大。
CSS 预处理器如 Sass、LESS 和 Stylus 为网络/移动设计师和开发人员提供了新的超能力。是的,我用了超能力这个词,因为这正是我第一次使用 Sass 仅仅几个小时后的感受,而我使用的只是最基本的东西:
.navigation-bar {
display: flex;
li {
padding: 5px 10px;
}
}
看到嵌套的li
选择器了吗?是的,那就是 Sass 在起作用。当前面的代码被编译时,就会变成这样:
.navigation-bar {
display: flex;
}
.navigation-bar li {
padding: 5px 10px;
}
提示
下载示例代码
您可以从您在www.packtpub.com
的帐户中下载示例代码文件,用于您购买的所有 Packt Publishing 图书。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support
并注册,以便直接通过电子邮件接收文件。
让我们来看看本章给我们带来了什么:
-
Sass 是如何工作的?
-
要考虑的 Sass 的基本概念响应式网页设计(RWD)
Sass 是如何工作的?
了解 Sass 的工作原理涉及理解几个基本的技术概念:
-
Sass 可以基于两种不同的技术:Ruby 或 LibSass。在本书中,我们将使用基于 Ruby 的 Sass。
-
Sass 是一个 Ruby gem。Gems 是用于 Ruby 的软件包。Ruby gem 是一种只能在 Ruby 上运行的软件。Ruby 是一种编程语言,就像 PHP、.NET、Java 等一样。
-
我们可以通过命令行运行 Sass,但也可以使用第三方应用程序运行 Sass,从而不需要使用命令行。
-
Sass 是一种用于创建 CSS 的编程/脚本语言。
-
CSS 是一种非常重复的语言。Sass 允许作者优化这些重复的任务,更快、更高效地创建 CSS。
-
Sass 工作流程的一部分是当 Sass 正在监视一个 SCSS 文件时,例如
book-styles.scss
。当它检测到该 SCSS 文件的更改时,它会将其编译成一个 CSS 文件book-styles.css
。
提示
监视一个 SCSS 文件意味着 Sass 监视器在后台监视 SCSS 文件的任何更改。
安装 Sass
以下是我们将要遵循的步骤:
-
下载 Ruby 安装程序
-
打开命令行
-
安装 Sass gem
下载 Ruby 安装程序
Windows:从以下链接下载 Ruby 安装程序:
Mac:Ruby 预装在所有的 Mac 上,所以不需要下载任何东西。
打开命令行
Windows 和 Mac:打开命令行。
提示
Windows 提示!
按下Windows + R,输入CMD
,然后按Enter。
安装 Sass gem
在命令提示符中键入以下命令(无论您在哪个文件夹中都可以):
Windows,使用以下命令:
gem install sass
Mac,使用以下命令:
sudo gem install sass
安装 Sass 需要几秒钟时间。
提示
在撰写本文时,Sass 的最新版本是 3.4.14。版本/修订可能在书出版时有所不同。
就是这样!Sass 现在已经安装在您的计算机上。
使用 Sass
我将要向您展示的内容与其他任何 Sass 教程告诉您要做的完全不同。大多数教程都把事情复杂化了。这是您将阅读到的使用 Sass 的最简单的方法。
以下的屏幕截图是在 Windows 上的,但是这个过程可以在任何平台上完全相同地应用。
在接下来的步骤中,您将看到创建后的必要文件夹和文件的示例,而不是如何创建它们:
-
在你的驱动器的任何位置创建一个
/Demo
文件夹: -
在该文件夹中,创建两个子文件夹,
/css
和/scss
: -
创建一个
.scss
文件。进入/scss
文件夹并创建一个名为styles.scss
的文件:
提示
注意文件扩展名.scss
?这是你的 Sass 文件。是的,现在里面什么都没有,它是空的。
-
回到命令行一分钟,按照以下步骤操作:
-
在命令行中,输入
cd <空格>
-
在
cd
后加一个空格意味着改变目录。从你的文件管理器中,将/Demo
文件夹拖放到命令提示符/终端窗口中,然后按Enter。 -
你现在应该在
/Demo
文件夹中。
- 通过在命令行中输入以下内容,让 Sass 监视你的
/scss
和/css
文件夹:
sass --watch scss:css
- 让 Sass 监视
/scss
和/css
文件夹。
就是这样!你现在正在使用 Sass!
提示
--watch
标志告诉 Sass 关注/scss
和/css
文件夹,这样当我们对.scss
文件(在我们的例子中是styles.scss
)进行更改时,Sass 将检测到更改并将 SCSS 编译成我们将在网站或应用程序中使用的最终 CSS 文件。
-
编辑
.scss
文件并观察 Sass 将其编译成.css
文件: -
打开你的文本编辑器(我用 Sublime Text)。
-
打开
styles.scss
文件。 -
向其中添加一些 CSS。
-
保存
styles.scss
文件。 -
从你的命令行/终端中,验证编译是否成功。
-
打开你的
styles.css
文件,享受你的新作品。
RWD 的 Sass 基本概念
首先,Sass 是一种编程/脚本语言。我打赌你没有想到。是的,它是一种专注于提高网页设计师和开发人员创建 CSS 效率的编程/脚本语言。在本书中,我们将专注于 Sass 的简单部分,这些部分可以帮助我们更有效地编写 CSS,更重要的是,我们会在其中获得乐趣。
实施 RWD 是耗时的:编码、测试、创建资产、浏览器故障排除,然后再进行更多测试。我们简化编码过程的程度越高,重复性工作越少,我们就变得越有效率,为项目、团队、业务甚至最终用户增加的价值也就越多。Sass 将会做到这一点——帮助我们简化 CSS 的编码。
让我们先讨论以下概念:
-
Sass 或 SCSS
-
变量
-
混合
-
参数
-
嵌套
-
部分文件
-
@import
-
源映射
-
Sass 注释
Sass 或 SCSS
我们可以用两种方式编写 Sass 风格的 CSS:Sass 语法和 SCSS 语法。
提示
不要误解;Sass 是大写 S,其余都是小写,而 SCSS 全部大写。
Sass 语法
Sass 语法,也被称为缩进语法,是最初和唯一的编写 Sass 的方式。但它看起来与常规 CSS 有些不同,使学习曲线比实际需要的更陡峭。
这种语法没有使用任何大括号或分号。在某些情况下,它使用等号而不是冒号。与 SCSS 不同,缩进非常严格且是强制性的。许多开发人员对 Sass 语法的这些方面并不太喜欢。
这是一个基本的例子:
.selector-a
float: left
.selector-b
background: orange
这将编译成以下代码:
.selector-a {
float: left;
}
.selector-a, .selector-b {
background: orange;
}
SCSS 语法
当 SCSS 在 Sass 的第 3 个版本中引入时,对于我们这些不是程序员但想要利用 Sass 功能的人来说,事情变得更容易了。
注意
SCSS 代表Sassy CSS。
如果你已经写 CSS,那么你已经写了 SCSS。我们在编写 CSS 时已经使用的所有东西,在使用 SCSS 语法编写 Sass 时也是一样的。因此,学习曲线最初是不存在的。
然后,你会意识到你还可以使用一些增强你已经知道的 Sass 功能,这使得学习 Sass 成为一种很棒的体验,因为你可以相当快地变得擅长它。说实话,这感觉就像你正在获得超能力。我不是在开玩笑。
以下是我们之前看到的相同示例,使用 SCSS 语法:
.selector-a {
float: left;
}
.selector-a, .selector-b {
background: orange;
}
等一下!那是 CSS!是的,它也是 SCSS。
让我们以不同的方式使用 SCSS 语法看同一个例子:
.selector- {
&a {
float: left;
}
&a, &b {
background: orange;
}
}
在 SCSS 中,&
符号允许我们将父选择器的名称添加到嵌套选择器中,而无需输入整个内容,使我们保持DRY的状态。
注意
DRY 表示不要重复自己。
这两个 SCSS 示例编译为以下代码:
.selector-a {
float: left;
}
.selector-a, .selector-b {
background: orange;
}
Sass 变量
首先让我们了解一些事情:
-
变量只是一种存储值以供以后使用的方法
-
这个值通常与一个简单的用户友好单词相关联
-
Sass 变量必须以美元符号(
$)
开头 -
变量的巨大好处是,如果我们需要更改值,我们只需在一个地方进行更改,而不是在整个文档中查找和替换值
提示
在列出多个变量时,每个变量的末尾应该有一个分号(;
)。如果只有一个变量,则不需要分号。然而,即使只有一个变量,最好也以分号结束变量,这是一个好习惯。
以下是 Sass 变量的一个例子:
$brandBlue: #416e8e;
提示
我建议您使用驼峰命名法来命名变量,以便将它们与以破折号分隔的类名和 CSS 属性区分开。在扫描 SCSS 文档时,这非常有帮助,因为变量更容易检测到。
正如我们所看到的,我们正在存储一个颜色值。我们使用的名称brandBlue
肯定比#416e8e
更用户友好。此外,我们使用了美元符号($
)并以分号(;
)结束,以防我们需要添加更多变量。现在,如果以后需要更改值,我们只需要在一个位置进行更改。
变量应始终包含在 SCSS 文件的顶部,以便 Sass 知道在使用它们时应该去哪里。您还可以通过部分文件包含它们,但我们将在本章后面讨论部分文件是什么。
以下是如何使用 SCSS 变量的示例:
$brandBlue: #416e8e;
body {
background: $brandBlue;
}
上述代码编译为以下内容:
body {
background: #416e8e;
}
Sass mixin
Mixin 是 Sass 最强大的功能之一。Mixin是一组 CSS 声明(属性和值),可以存储以供以后使用,就像变量一样。因此,我们不必一遍又一遍地输入所有这些 CSS 声明,只需输入 mixin 的名称。
关于 Sass mixin 需要考虑的几件事情如下:
-
它们以
@mixin
指令开头 -
使用
@include
指令调用 mixin -
我们可以在 mixin 中存储任意数量的 CSS/SCSS 数据
-
尝试在创建 mixin 时使用参数,这样它就更具可扩展性
提示
我们还没有看到参数是什么,但现在提到这个词很重要,这样你就可以开始熟悉不同的 Sass 术语。我们将在下一节中介绍 Sass 参数。
让我们看一个 mixin 的例子:
$brandBlue: #416e8e;
$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
我们在 SCSS 文件中调用 mixin 如下:
.selector-a {
@include genericContainer;
}
编译后,在 CSS 中看起来像这样:
.selector-a {
padding: 10px;
border: #416e8e 1px solid;
background: #cccccc;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}
让我们回顾一下我们在 mixin 中所做的事情。
我们使用了@mixin
指令:
$brandBlue: #416e8e;
$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
我们使用驼峰命名约定来区分 mixin 的名称和以破折号分隔的类名和 CSS 属性:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
我们在 mixin 中使用了 Sass 变量:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
在box-shadow
颜色属性中使用关键字black
,而不是使用十六进制#000
或rgb (0, 0, 0)
值:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
为此,我们也可以像这样使用我们的变量名:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba($brandBlue, .3);
}
我们还省略了 alpha 值中的0
(.3
)。这实际上不是 Sass 的特性;这是 CSS 的特性:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer {
padding: 10px;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba($brandBlue, .3);
}
提示
在以零开头的小数值上,零可以被省略。
同样,上述 mixin 编译为以下 CSS:
.selector-a {
padding: 10px;
border: #416e8e 1px solid;
background: #cccccc;
box-shadow: 1px 1px 1px rgba(65, 110, 142, 0.3);
}
Sass 参数
在我们的第一个 mixin 示例中,我们没有任何参数。这实际上并不理想,因为它不允许我们在相同的属性中使用不同的值。实际上,在 mixin 中不使用任何参数并不比每次需要它们时键入相同的属性和值有任何不同。我们并没有真正做到 DRY。
参数是 mixin 的一部分,您可以根据需要放入自己的值。参数使 mixin 值得创建。
在前面提到的 mixin 示例中,让我们添加一个参数:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer($padding) {
padding: $padding;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
padding
参数允许我们设置任何我们想要的值。我们并不强制每次都将填充设置为10px
。
这是我们如何设置参数的值:
.selector-a {
@include genericContainer(10px);
}
这编译为以下内容:
.selector-a {
padding: 10px;
border: #416e8e 1px solid;
background: #cccccc;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}
但是,参数存在潜在问题;如果我们不为padding
设置值,编译时会出现错误。
因此,这里的解决方案是设置一个默认值;如果由于某种原因我们没有为padding
定义一个值,Sass 将采用默认值并在编译时使用它而不会抛出错误。
以下是如何设置参数的默认值:
$brandBlue: #416e8e;$supportGray: #ccc;
@mixin genericContainer($padding: 8px) {
padding: $padding;
border: $brandBlue 1px solid;
background: $supportGray;
box-shadow: 1px 1px 1px rgba(black, .3);
}
这是我们如何调用 mixin,而不声明任何padding
值:
.selector-a {
@include genericContainer;
}
编译后的 CSS 如下:
.selector-a {
padding: 8px;
border: #416e8e 1px solid;
background: #cccccc;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}
如何在同一个 mixin 中使用多个参数
在前面的 mixin 基础上,让我们添加一些更多的参数,使其更加健壮和可扩展:
@mixin genericContainer ($padding, $bdColor, $bgColor, $boxShdColor) {
padding: $padding;
border: $bdColor 1px solid;
background: $bgColor;
box-shadow: 1px 1px 1px $boxShdColor;
}
这是我们在包含 mixin 时如何声明参数的方式:
.selector-a {
@include genericContainer(2%, $brandBlue, #ccc, black);
}
我们可以使用相同的 mixin 并获得不同的样式,而无需重复输入所有属性。
前面的 mixin 及其参数编译为以下代码:
.selector-a {
padding: 2%;
border: #416e8e 1px solid;
background: #cccccc;
box-shadow: 1px 1px 1px #000000;
}
在多个参数中设置默认值
有时,我们需要定义一些默认值,以防我们只需要声明一个或几个参数。换句话说,通过在我们的参数中声明默认值,我们将始终确保创建一个值,并且在编译我们的 SCSS 文件时不会出现任何错误。
以下是我们如何在参数中设置默认值:
@mixin genericContainer ($padding: 5px, $bdColor: orange, $bgColor: #999, $boxShdColor: #333) {
padding: $padding;
border: $bdColor 1px solid;
background: $bgColor;
box-shadow: 1px 1px 1px $boxShdColor;
}
如果我们只需要声明第一个属性padding
,我们可以这样做:
.selector-a {
@include genericContainer(25px);
}
这编译为以下内容:
.selector-a {
padding: 25px;
border: orange 1px solid;
background: #999999;
box-shadow: 1px 1px 1px #333333;
}
提示
某些 Sass 编译器将简写的颜色十六进制值#333
转换为长格式值#333333
。
正如我们所看到的,只有第一个参数padding
被声明。其他参数使用了它们的默认值并成功编译。
但是,假设我们仍然只想声明一个参数,而不是padding
,它是参数列表中的第一个。假设我们想声明背景颜色!
在这种情况下,我们需要通过输入变量的名称来声明值:
.selector-a { @include genericContainer($bgColor: $brandBlue); }
提示
如果我们只想声明一个与第一个参数不同的单个参数,我们需要声明整个参数名称。
还有更高级的声明参数的方法,但这对于本书的范围来说已经足够了。
Sass 中的嵌套
Sass 中的嵌套是使我们的 SCSS 更易读的完美方式。就像在 HTML 中,标签基于其父元素进行嵌套一样,Sass 使用完全相同的结构。
以下是导航栏的两级选择器嵌套示例:
$brandBlue: #416e8e;nav {
ul {
display: flex;
margin: 0;
padding: 0;
list-style: none;
}
li {
margin: 5px;
background: #000;
}
a {
display: block;
padding: 5px 15px;
text-decoration: none;
color: $brandBlue;
}
}
提示
注意深层嵌套!最佳实践建议最多嵌套三个级别。否则,我们将在未来遇到选择器特异性和可维护性问题。
您是否注意到我再次使用了$brandBlue
颜色变量?前面的导航栏的 SCSS 编译为以下 CSS:
nav ul {
display: flex;
margin: 0;
padding: 0;
list-style: none;
}
nav li {
margin: 5px;
background: #000;
}
nav a {
display: block;
padding: 5px 15px;
text-decoration: none;
color: #416e8e;
}
Sass 中的局部文件(partials)
局部文件是我们创建的用于存放 SCSS 片段的 SCSS 文件。局部文件允许我们模块化我们的文件,例如,_variables.scss
。局部文件以下划线符号(_
)开头,并以扩展名.scss
结尾。下划线符号告诉编译器,这个文件及其内容不需要编译成单独的 CSS 文件。
局部文件使用@import
指令调用,就像在 CSS 中一样。主要区别在于无需指定下划线符号和文件扩展名。
让我们创建一个局部文件,并把这些颜色变量放在里面。我们将称这个局部文件为_variables.scss
。_variables.scss
局部中的变量(片段)如下:
$brandBlue: #416e8e;
$brandRed: #c03;
$brandYellow: #c90;
然后我们假设我们的主 SCSS 文件名为styles.scss
。现在我们有两个文件:styles.scss
和_variables.scss
。
提示
项目的主 SCSS 文件不以下划线符号开头。
我们使用@import
指令将_variables.scss
调用到styles.scss
中:
@import "variables";
注意,在引用局部文件时,下划线符号和文件扩展名是不需要的;它们可以被省略。但是,如果你想添加它们,也可以。省略它们可以使代码更清晰。
Sass 扩展/继承功能
许多专业人士说,扩展或继承是 Sass 最有用的功能之一。其他人实际上建议远离它。本书的建议是:尽可能多地使用 Sass,并尝试不同的功能,这样你就可以形成自己的观点。当你有足够的经验时,你可以决定加入哪一方。
在 Sass 中扩展意味着我们可以在另一个选择器中使用选择器的属性,而不必再次输入所有这些属性。这就是所谓的继承。我们使用@extend
指令来实现这一点。
例如,考虑以下选择器:
$brandBlue: #416e8e; .generic-container {
padding: 10px;
border: $brandBlue 1px solid;
background: #ccc;
box-shadow: 1px 1px 1px rgba(black, .3);
}
假设我们想要在不同的选择器上继承这个选择器的所有属性。我们还要修改一个属性,因为它们几乎是相同的,使用@extend
指令在第二个选择器中重用第一个选择器的样式:
.box-customer-service {
@extend .generic-container;
padding: 25px;
}
这编译成以下内容:
.generic-container, .box-customer-service {
padding: 10px;
border: #416e8e 1px solid;
background: #cccccc;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}
.box-customer-service {
padding: 25px;
}
注意.generic-container
和.box-customer-service
在同一条规则中;这意味着.box-customer-service
继承了.generic-container
的所有属性和值。然后,有一个单独的规则为.box-customer-service
,只声明了padding
属性,因为这是两个容器之间的唯一区别。
Sass 注释
由于我们知道 CSS 文档是有效的 SCSS 文档,因此使用 CSS 注释语法也是有效的:
/* This is a traditional CSS comment */
在 Sass 中,还有另一种方法。我们可以在开头使用双斜杠(//
)进行注释。
// This is a Sass-style comment
两种样式之间的区别在于使用/**/
语法的传统 CSS 注释会添加到编译后的文件中,而使用//
的 Sass 注释则不会添加。
Sass 语法中的注释非常有用,可以在不必担心所有这些注释被编译并使最终的 CSS 文件变得臃肿的情况下记录我们的 SCSS 文件。以下示例中的 Sass 注释不会被编译:
$brandBlue: #416e8e; //Mixin for generic container across the app
.generic-container {
padding: 10px;
border: $brandBlue 1px solid;
background: #ccc;
box-shadow: 1px 1px 1px rgba(black, .3);
}
然而,传统的 CSS 注释确实被编译了:
$brandBlue: #416e8e;
/* Mixin for generic container across the app */
.generic-container {
padding: 10px;
border: $brandBlue 1px solid;
background: #ccc;
box-shadow: 1px 1px 1px rgba(black, .3);
}
提示
现在,根据编译器上设置的选项,最终的 CSS 可以被最小化。因此,传统的 CSS 注释将被剥离以优化文件大小。
供应商前缀
供应商前缀基本上是为尚未被广泛使用或最终包含在 CSS3 规范中的 CSS3 属性或值添加特定的标签。
供应商部分指的是代表创建浏览器的公司名称的缩写标签:Mozilla、Opera 和 Microsoft。
不过,有一个例外,苹果。尽管苹果创建了 Safari,但供应商前缀是基于浏览器的布局引擎而不是公司名称。
-
Mozilla:
-moz-
-
Opera:
-o-
-
微软:
-ms-
-
Webkit(苹果):
-webkit-
前缀部分指的是在 CSS 属性或 CSS 值之前添加供应商标签的描述。每个供应商前缀只在自己的浏览器中有效,因此对于上述列表,这里是它们所属的浏览器:
-
Mozilla:这个前缀
-moz-
在 Firefox 中有效 -
Opera:这个前缀
-o-
在 Opera 中有效 -
微软:这个前缀
-ms-
在 Internet Explorer 中有效 -
Webkit(苹果):这个前缀
-webkit-
在 Safari 中有效
如果你想知道谷歌 Chrome 在这一切中的位置,这有一个简单的解释。
尽管谷歌创建了 Chrome,但 Chrome 没有特定的前缀。起初,Chrome 使用与 Safari 相同的布局引擎:Webkit。因此,基于 Webkit 的前缀不仅影响了 Safari,还影响了 Chrome 和其他基于 Chromium 的产品。
然而,谷歌浏览器不再使用 Webkit;它现在使用自己的布局引擎称为 Blink。然而,为了保持兼容性并避免进一步分裂网络,Chrome 仍然支持-webkit-
前缀。
Opera 有一个类似的故事,他们有自己的布局引擎 Presto,然后切换到 Webkit。现在它使用 Blink。除了之前提到的浏览器供应商之外,还有其他浏览器供应商,他们也使用自己的前缀,比如 Konqueror 浏览器的前缀-k-
。
这是一个带有供应商前缀的 CSS 属性的例子:
-moz-box-sizing: border-box;
这里有一个带前缀的 CSS 值的例子:
background-image: -webkit-linear-gradient(red, blue);
供应商前缀的顺序
事实上,我们列出供应商前缀的顺序并不重要;重要的是我们总是将非供应商前缀的版本放在最后。
以 linear-gradient 属性为例,我们应该这样做:
*, *:before, *:after {
background-image: -webkit-linear-gradient(red, blue);
background-image: -moz-linear-gradient(red, blue);
background-image: -ms-linear-gradient(red, blue);
background-image: -o-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
提示
如果你喜欢,你也可以使用background: linear-gradient(red, blue);
。
非供应商前缀的声明应该始终放在最后,因为如果浏览器供应商修改其前缀或停止支持它,最后一行将始终覆盖上面的任何内容,因为级联。这使整个 CSS 规则更具未来性。此外,我们不必在供应商更改内容时重写样式表。
现在,许多 CSS3 属性和值不需要所有供应商前缀。大多数情况下,它们只需要一些供应商前缀,其他时候非供应商前缀的属性或值就足够了。
但是,我们如何知道哪些 CSS3 属性和值可以加前缀,哪些不需要,这样我们就可以创建受某些旧浏览器支持的样式,而不必记住太多信息?
答案是自动化供应商前缀的过程。
自动添加供应商前缀
供应商前缀带来了一些问题,如果我们希望一些 CSS3 属性在当前浏览器和/或某些旧浏览器中工作,我们就无法摆脱这些问题。供应商前缀是肮脏的工作,我们不必须这样做。
那么,我们如何在尽可能保持 DRY 的情况下自动化供应商前缀的过程呢?有几种方法。
使用 Compass
Compass 是一个帮助我们更有效地编写 CSS 的 Sass 框架。Compass 有一个庞大的 mixin 库,我们可以使用它来处理供应商前缀。
Compass 的安装超出了本书的范围,因此我们将专注于处理供应商前缀的基本用法,并假设它已经安装在您的机器上。请参考 Compass 网站,了解如何安装它的详细说明(compass-style.org/
)。
一旦我们安装了 Compass,我们需要导入包含我们需要的 mixin 的特定模块。
继续使用之前使用的线性渐变示例,让我们将 Compass 的images
模块导入到我们的 SCSS 文件中。将其放在主 SCSS 文件的顶部:
@import "compass/css3/images";
然后,我们可以使用相应的 mixin:
header {
@include background-image(linear-gradient(red, blue));
}
这将编译为以下内容:
header {
background-image: url('data:image/svg+xml;base64,…');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, red), color-stop(100%, blue));
background-image: -moz-linear-gradient(red, blue);
background-image: -webkit-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
这里有一些新东西。
第一个声明使用了一个 base64 嵌入的 SVG 文件。这是因为旧版 IE 和旧版 Opera 存在渲染渐变的问题,因此 SVG 是它们的备用方案。按照今天的标准,处理这些问题是完全不必要的。
header {
background-image: url('data:image/svg+xml;base64,…');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, red), color-stop(100%, blue));
background-image: -moz-linear-gradient(red, blue);
background-image: -webkit-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
background-size: 100%;
参数用于使嵌入的 SVG 覆盖整个容器。再次处理这样的事情只是浪费时间。此外,我们的代码不断膨胀,试图支持旧技术。考虑下面的代码块:
header {
background-image: url('data:image/svg+xml;base64,…');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, red), color-stop(100%, blue));
background-image: -moz-linear-gradient(red, blue);
background-image: -webkit-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
第三个声明是旧的 CSS 线性渐变语法,只有 Webkit 浏览器支持;这在我们的文件中会导致不必要的代码膨胀:
header {
background-image: url('data:image/svg+xml;base64,…');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, red), color-stop(100%, blue));
background-image: -moz-linear-gradient(red, blue);
background-image: -webkit-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
第四和第五个声明基本上是为旧版 Firefox、Chrome 和 Safari 版本准备的:
header {
background-image: url('data:image/svg+xml;base64,…');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, red), color-stop(100%, blue));
background-image: -moz-linear-gradient(red, blue);
background-image: -webkit-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
最后一个声明是没有供应商前缀的建议语法:
header {
background-image: url('data:image/svg+xml;base64,…');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, red), color-stop(100%, blue));
background-image: -moz-linear-gradient(red, blue);
background-image: -webkit-linear-gradient(red, blue);
background-image: linear-gradient(red, blue);
}
正如我们所看到的,Compass 是一个非常方便的工具,它允许我们自定义输出。然而,这可能会变得比必要的工作更多。
在得出 Compass 是否是我们的最佳解决方案之前,有一些事情需要考虑:
-
需要安装 Compass。这通常是通过命令行完成的。
-
一旦安装了 Compass,我们就不必再使用命令行来使用它的 mixin。
-
Compass 有一个庞大的 mixin 库,可以帮助处理供应商前缀和许多其他事情。
-
每次我们需要处理特定的 CSS3 属性或值时,我们必须在我们的主 SCSS 文件中使用
@import
指令导入相应的模块。这意味着我们必须花费大量时间找到我们需要的模块并学会使用它们。 -
使用 Compass 的学习曲线是中等的,我们需要在其他技术方面有一定的了解才能使用 Compass,即使是最基本的使用也是如此。
-
Compass 有很好的文档,并且是一个不断发展的项目。
-
有一个类似的著名的 mixin 库叫做 Bourbon:
bourbon.io/
。
使用“-prefix-free”
-prefix-free
是由 Lea Verou 创建的 JavaScript 文件。当浏览器调用该脚本时,它会检测到,然后将该浏览器特定的前缀添加到 CSS 中。 -prefix-free
文件足够智能,可以确定需要哪些前缀,并且只注入那些前缀。
使用-prefix-free
很简单。只需调用 JavaScript 文件。根据 Lea Verou 的建议,最好在样式表之后包含此脚本,以减少未样式内容的闪烁(FOUC)。
您可以访问-prefix-free
项目:leaverou.github.io/prefixfree/
。
由于我们的 HTML 代码如此简短,我们可以遵循之前提到的提示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page Title</title>
<link href="css/styles.css" rel="stylesheet">
<script src="img/prefixfree.min.js"></script>
</head>
<body>
Site content...
</body>
</html>
使用这种方法肯定是诱人的,因为调用一个简单的 JavaScript 文件来处理自动添加供应商前缀听起来就像是最好的主意。
让我们看一下在决定使用-prefix-free
之前需要考虑的事项的简短列表:
-
它非常容易使用。
-
这是一个额外的 HTTP 请求。我们的网站/页面的请求越少,它们就越快,因此我们为用户提供的用户体验就越好。这对 SEO 也是有益的。
-
这是一个额外的文件要管理。是的,一旦我们上传了 JavaScript 文件,我们可能不需要再回头看它——除非我们要更新它,这意味着我们需要在本地进行广泛的测试,以免在生产环境中出现任何问题。
-
它会给用户的浏览器增加一些负担,因为所有事情都发生在浏览器中。
-
它在使用
@import
指令调用的文件中不起作用。这也可以被视为一件好事,因为如果我们使用@import
来导入文件,我们就会面临一个不同甚至更大的问题。 -
如果我们从与我们的主站点不同的域名提供样式表,那么
-prefix-free
将无法在这些外部 CSS 文件上工作。 -
Chrome 和 Opera 在允许
-prefix-free
在本地工作方面存在问题。虽然这很容易解决,但它只是增加了我们工作流程的复杂性。 -
如果有内联样式,一些未添加前缀的 CSS 值和属性在 IE 中将无法工作。
有了这个列表,我们现在可以更好地做出一个更明智的决定,这将使项目、我们自己和我们的用户受益。
使用 Autoprefixer
Autoprefixer 是一个CSS 后处理器,它使用 CanIUse.com 数据库为已编译的 CSS 文件添加供应商前缀。
术语后处理器意味着它在创建 CSS 之后(后)处理它。换句话说,如果我们有一个名为styles.scss
的 SCSS 文件,当我们保存它时,该文件会被编译为styles.css
。在那一刻,Autoprefixer 获取生成的styles.css
文件,打开它,为每个属性和值添加所有必要的供应商前缀,保存文件,并关闭它。此外,您还可以配置它创建一个新的单独文件。完成后,我们可以在我们的网站/应用程序中使用此文件。
这种方法相对于任何其他自动供应商前缀方法的主要优势是它使用 CanIUse.com 数据库;这意味着一旦浏览器供应商不再需要其前缀用于 CSS 属性或值,我们只需通过 Autoprefixer 运行我们的 CSS 文件,它将在几秒钟内更新。
Autoprefixer 的主要缺点是它有太多的使用方式,对一些人来说可能有点压倒性。举几个例子,我们可以通过命令行使用它,但首先需要安装Node.js
:
npm install --global autoprefixer
autoprefixer *.css
我们也可以在 Compass 中使用 Autoprefixer,但首先需要安装 Ruby:
gem install autoprefixer-rails
我们可以在 Mac 上使用 CodeKit,在 Windows/Mac/Linux 上使用 Prepros 或 Koala App。我们还可以为 Sublime Text、Brackets 或 Atom Editor 安装插件。还有 Grunt 和 Gulp 插件。
在决定使用 Autoprefixer 之前,让我们看一下需要考虑的事项的简要清单:
-
它使用 CanIUse.com 数据库的事实是远远超过任何其他自动供应商前缀应用程序的最佳功能和优势,因为我们始终可以确保我们的 CSS 文件具有最新的前缀,或者如果浏览器供应商删除了其中任何一个。
-
它可以集成到许多应用程序中。
-
对于新的网页设计师或开发人员来说,安装可能有点令人生畏。
-
Autoprefixer 已经预装在其他应用程序中,所以我们只需要运行这些应用程序,就可以自动使用 Autoprefixer,而无需进行任何设置。
Autoprefixer 可以从github.com/postcss/autoprefixer
下载。
使用 Pleeease
是的,它是三个e的Pleeease。Pleeease 也是一个类似 Autoprefixer 的 CSS 后处理器,它也依赖于已安装的Node.js
。它只能通过命令行运行,但实际上非常简单。Pleeease 使用 Autoprefixer,这意味着它也使用 CanIUse.com 数据库来定义哪些 CSS 属性和/或值需要前缀。
安装 Pleeease 后,我们需要创建一个配置文件(JSON 文件),其中我们需要定义的最重要的事情是源 CSS 文件和目标 CSS 文件:
{
"in": "style.css",
"out": "styles.fixed.css"
}
一旦我们设置了配置文件,我们在命令行中运行这个命令:
pleeease compile
Pleeease 获取style.css
文件,添加所有必要的供应商前缀,并创建styles.fixed.css
,这是我们在生产中使用的文件。
在这一点上,Pleeease 还有其他重要的事情:
-
将相同的媒体查询编译为一个
@media
块 -
将
@import
样式表内联(这很棒,因为我们最终只会得到一个单一的 CSS 文件用于生产) -
最终文件进行了最小化/压缩
如果您习惯使用命令行和 JSON 文件,Pleeease 可以成为您工具库中非常有用的一部分。如果您更喜欢远离命令行,也没关系;还有其他更友好的方法来自动添加供应商前缀。
在决定是否使用 Pleeease 自动添加供应商前缀之前,有一些需要考虑的事项:
-
需要使用命令行进行安装和使用,但命令非常简单。
-
它使用 JSON 文件来配置其设置。
-
它使用 Autoprefixer,这意味着它也使用 CanIUse.com 数据库。这使得它在知道哪些属性和/或值需要或不需要前缀时非常强大。
-
它对最终的 CSS 文件进行了几项其他改进,比如将相同的媒体查询打包在一个
@media
规则中,最小化结果等等。 -
它可以与 Grunt、Gulp、Brunch 和 Node.js 工作流集成。
您可以从pleeease.io/
下载 Pleeease。
使用 Emmet
Emmet 使我们能够更快地编写 CSS 和 HTML。它是文本编辑器的插件,如 Sublime Text、Coda、TextMate,甚至 Dreamweaver。
Emmet 还帮助我们为 CSS3 属性和值添加供应商前缀,这是我们接下来要重点关注的。
提示
Emmet 以前被称为Zen Coding。
一旦 Emmet 插件安装在我们喜爱的文本编辑器中,我们在 SCSS 文件中输入以下内容:
.selector-a {
-trf
}
提示
-trf
是 CSS3 属性transform的缩写。
然后我们在键盘上按下Tab,代码会自动更改为这样:
.selector-a {
-webkit-transform:;
-ms-transform:;
-o-transform:;
transform:;
}
我们只需要在缩写的开头加一个破折号(-
)来添加供应商前缀。这告诉 Emmet 在按下Tab键时需要添加必要的供应商前缀。
提示
在上一个例子中未定义变换值,因为我们想展示使用 Emmet 的结果。显然,我们最终需要添加这些值。
在决定是否使用 Emmet 自动添加供应商前缀之前,有一些事情需要考虑:
-
由我们来定义什么需要加前缀,什么不需要,所以我们可能最终会给不再需要前缀的属性和值加上前缀。因此,我们最终会使我们的 CSS 文件变得臃肿。
-
如果我们忘记在属性/值的开头添加破折号,它就不会被加前缀,也许这个属性/值确实需要前缀。因此,我们会花更多时间进行故障排除。
-
Emmet 与最流行的文本编辑器兼容,所以我们很可能能够使用它。
-
使用 Emmet 的学习曲线非常低。
-
Emmet 不依赖于使用命令行。
-
Emmet 有很好的文档,并且在不断发展。
您可以从emmet.io/
下载 Emmet。
使用第三方应用程序
正如我们所见,以前用于自动添加供应商前缀的方法是各种各样的,从通过命令行使用的方法到让您在使用 JavaScript 解决方案之前找到特定模块导入的方法。
提到的所有功能中最重要的是 Autoprefixer 使用 CanIUse.com 数据库。这基本上是我们想要使用的,因为我们只需要编写 CSS3 属性和值,然后完全忘记供应商前缀,让 Autoprefixer 和 CanIUse.com 为我们添加它们。
幸运的是,已经有第三方应用程序安装了 Autoprefixer。这意味着我们不需要通过命令行设置任何东西,也不需要安装插件,或者类似的东西。只需安装应用程序,激活 Autoprefixer 复选框,然后开始使用!
之前我们提到了几个应用程序:CodeKit、Prepros 和 Koala 应用。它们基本上都做同样的事情,但它们在两个方面表现出色:
-
它们可以监视我们的 SCSS 文件并为我们编译它们。
-
它们可以通过 Autoprefixer 自动添加供应商前缀。
这两个功能对我们的工作流程有很大影响,使我们能够将精力集中在重要的事情上,比如 RWD 和更好的用户体验。
在决定使用第三方应用程序是否是添加供应商前缀的最佳解决方案之前,有一些事情需要考虑:
-
Prepros 和 CodeKit 是付费应用程序。Koala 是免费的,但通过小额捐赠支持作者对他的工作表示感激。然而,它们绝对不贵;当我们第一次编译文件时,收益是十倍的价值。
-
它们非常容易设置。
-
它们有很好的文档、社区,并且由作者不断开发。
-
对于许多与 CSS 和 HTML 一起工作的非前端开发人员来说,这些应用程序使他们能够专注于其他重要事项,如用户体验、设计、可用性和 SEO,而不必担心 JSON 文件、命令行、插件等等。
推荐的供应商前缀方法
本书建议您使用 CodeKit、Prepros 或 Koala 应用程序来处理供应商前缀。这些应用程序不仅可以编译 SCSS 文件,还可以在保存这些 SCSS 文件时自动通过 Autoprefixer 运行它们。
所以让我们来看看 Prepros,它可以在 Windows、Linux 和 Mac 等最流行的操作系统上运行。
使用第三方程序进行编译
使用命令行编译我们的 SCSS 文件真的并不那么困难:
--sass watch scss:css
这就是我们在命令行中需要做的一切,以便 Sass 监视/scss
文件夹中的 SCSS 文件,并将它们编译到/css
文件夹中。真的就是这么简单。
以前的情况是,每次我们需要在不同的项目上工作时,都需要运行这个命令。虽然我们可以用许多不同的方式来自动化这个过程,但有些人觉得使用命令行要么令人生畏,要么只是不必要的。
Prepros 应用程序
Prepros 是一个面向网页设计师和开发人员的工具,涉及到常规工作流程的许多部分:编译、CSS 前缀、实时刷新、JavaScript 合并、文件最小化、图像优化、浏览器测试同步、为编译文件创建源映射、内置服务器、FTP 等等。
在本书的范围内,我们将重点介绍它如何帮助我们在自动添加供应商前缀的同时编译我们的 SCSS 文件。
您可以从prepros.io/
下载它。Prepros 是一个付费应用程序。不过,花 29 美元并不会让你破产。我向你保证,第一次编译之后,这个应用程序就会为自己赚回成本。
还有一种方法可以免费使用 Prepros 并享受应用程序的所有功能。不过,这是以不得不每 5 分钟左右关闭购买应用程序弹出窗口为代价的。
这是 Prepros 的当前欢迎界面(可能已经改变):
还记得安装 Sass 时的步骤吗?我们创建了一个/Demo
文件夹,并在其中创建了两个子文件夹/scss
和/css
?我们将把/Demo
文件夹拖放到 Prepros 界面上:
一个悲伤的表情出现了,让我们知道项目是空的。这是真的,因为我们还没有向/scss
文件夹中添加任何文件:
所以,让我们在/scss
文件夹中创建一个.scss
文件:
Prepros 将自动检测新的styles.scss
文件并将其编译为styles.css
文件,保存在/css
文件夹中。
单击styles.scss
文件将显示文件的默认设置:
让我们修改一些设置,以便 Prepros 可以自动执行以下操作:
-
添加供应商前缀。
-
创建源映射。
-
不压缩我们编译的 CSS(至少暂时不压缩)。
提示
source map
是一个带有.map
扩展名的文件,它与我们的 CSS 文件一起生成。这个映射文件包含了将我们的 CSS 文件的每一行链接到我们的 SCSS 文件和局部文件中相应行的必要信息。当我们需要通过任何现代网页浏览器的 DevTools 检查元素的样式时,这一点至关重要。
在输出样式部分,我们将把设置保留为Expanded。
四种输出样式之间的区别很简单:
扩展输出
这是传统的 CSS 样式,其中每个选择器、属性和值都在单独的一行上:
header {
background: blue;
}
header .logo {
float: left;
}
.container {
float: right;
}
嵌套输出
你可以看到第二个规则是缩进的,这意味着它属于header
选择器:
header {
background: blue;
}
header .logo {
float: left;
}
.container {
float: right;
}
紧凑输出
所有规则都在一行中,如下所示:
header { background: blue; }
header .logo { float: left; }
.container { float: right; }
压缩输出
这是被压缩的版本,这是我们在生产中应该使用的版本:
header{background:blue;}header .logo{float:left;}.container{float:right;}
就是这样。我们现在让 Prepros 运行。它将添加所有供应商前缀,并在我们保存时编译 SCSS 文件。让我们看看它的运行情况。
添加一些 CSS,让 Prepros 应用程序完成剩下的工作!
每次我们点击保存,Prepros 都会在屏幕右下角显示以下对话框中的一个。
成功将给我们以下输出:
错误将给我们以下输出:
让我们拿出我们的styles.scss
文件,然后添加一个需要一些供应商前缀的简单 CSS 规则。
当我们保存styles.scss
文件时,Prepros 会显示绿色/成功的对话框,并将我们的 SCSS 文件编译成styles.css
。
这是编译后的文件,自动添加了所有前缀:
定义要为前缀添加支持的旧版浏览器版本数量
随着浏览器的发展,CSS3 属性和值被标准化,越来越少的属性需要供应商前缀。我们的 CSS 文件应该反映这一点,这样我们就不会在样式表中填充不必要的前缀。
Prepros 允许我们定义在应用前缀时要支持多少个旧版浏览器版本。步骤如下:
-
在顶部点击更多选项菜单:
-
从下拉菜单中点击项目选项:
-
点击CSS菜单选项:
-
滚动到底部,在AutoPrefixer字段中输入数字
2
: -
完成这些操作后,保存
styles.scss
文件。我们会发现,CSS3 线性渐变属性在 Prepros 编译 CSS 文件后实际上不需要添加前缀:
提示
如果你看不到线性渐变属性在开头被加上前缀,尝试将值更改为非常高的值,比如40
,这样它就会显示最后 40 个版本。保存你的 SCSS 文档,然后再次检查你的 CSS 文件。
就是这样。
只有一个编译器
在我们继续之前,有一点非常重要的说明。到目前为止,我们已经讨论了通过--watch
标志使用命令行以及使用 Prepros 来编译我们的 SCSS 文件。请注意,只需要运行一个编译器。同时运行 CMD 和 Prepros 编译相同的 SCSS 文件是不必要的。
Sass mixins 来存放我们的媒体查询
有许多方法可以创建一个 Sass mixin 来存放媒体查询:只有变量的 mixin,为不支持媒体查询的旧版浏览器提供No Queries回退的 mixin,以及(对于 Compass)插件,比如 Breakpoint。还有其他技术,比如命名媒体查询。另一种技术是一个简单的三行 mixin,可以用于我们想要的任何东西。
它们都很好,而且非常强大。然而,在本书的范围内,我们将专注于两种简单的方法,这将使我们能够高效,保持简单,并利用 mixin 的功能。
到目前为止,你学到的关于 Sass 的一切,特别是关于 mixin 的部分,都体现在创建一个用于存放 RWD 媒体查询的部分文件中。
请记住,部分文件是我们创建的用于存放 SCSS 片段的 SCSS 文件。它们的文件名以下划线符号开头,以.scss
扩展名结尾。
媒体查询 mixin 方法
命名媒体查询和断点的方法和网页设计师和前端开发人员一样多。每个人都有自己的方式和风格。
无论您使用哪种方法,重要的是开始使用 Sass mixin 来自动化这个过程。随着我们构建站点或应用程序并成为更好的网页设计师/前端开发人员,我们会发现其他解决方案可能效果更好。
有几种方法可以命名您的媒体查询 mixin:
-
让内容定义断点。换句话说,当您在测试期间调整浏览器窗口大小,并且看到内容中断或无法以理想的、可读的方式显示时——创建一个断点(这是推荐的方法)。
-
使用抽象名称命名媒体查询,如
small
、medium
和large
,或s
、m
和l
。 -
使用特定设备名称(我不建议使用此方法)。
在本书中,我们将只关注前面列表中提到的第一种和第二种方法。
让内容定义断点
由于我们不知道我们的内容会在哪里中断,我们需要一个初始 mixin,我们可以在构建响应式站点/应用程序时添加值,我们将从一些已知的、特定宽度的值开始。请理解这些值很可能会改变,并且会向这个 mixin 添加许多其他值。
我们将把这个文件命名为_mediaqueries.scss
。媒体查询 mixin 看起来像这样:
//Mobile-first
@mixin minw($point) {
@if $point == 320 {
@media (min-width: 20em) { @content; }
}
@else if $point == 640 {
@media (min-width: 40em) { @content; }
}
@else if $point == 768 {
@media (min-width: 47.5em) { @content; }
}
}
这是我们在主 SCSS 文件中使用 mixin 的方法:
header {
width: 50%; //Properties for small screens
background: red;
@include minw(640) {
width: 100%; //Properties for large screens
background: blue;
}
}
这是 mixin 编译的结果:
header {
width: 50%;
background: red;
}
@media (min-width: 40em) {
header {
width: 100%;
background: blue;
}
}
在本书的媒体查询示例中,我们将使用em
单位而不是像素来声明宽度值。这是因为使用em
有助于更好地缩放所有值,独立于屏幕密度。让我们看看这里发生了什么。
Mixin
首先,我们看到 Sass 风格的注释,描述这个 mixin 是为移动优先方法而设计的:
//Mobile-first
然后,我们有开放的@mixin
指令。这个指令包含 mixin 的名称minw
,它是minimum-width的缩写。我们将保持这个名称简单,因为我们将经常输入它,所以输入minw
比输入minimum-width更快,同时仍然保持有意义的术语。
括号中,我们有($point)
参数,它将存储我们在定义要使用的断点时指定的值:
@mixin minw($point)
然后,我们有一个开放的@if
语句。记住我们说过 Sass 是一种编程/脚本语言吗?有什么比if-else
语句更能代表编程语言呢?
@if
语句后面是等于(==
) 320 像素宽度的$point
变量。两个等号(==
)表示它绝对等于值,即320
:
@if $point == 320
之后,我们有 CSS @media
指令,我们以前见过很多次。在这个指令中,我们以em
为单位指定宽度,在这个例子中是20em
。
@media (min-width: 20em)
然后,我们有@content
指令,允许我们在括号之间放任何内容:
@media (min-width: 20em) { @content; }
接下来是带有@else
语句的$point
变量,两个等号(==
)和值640
。如果定义的值是640
而不是320
,那么 mixin 可以继续使用这个特定的媒体查询,适用于 640 像素宽度。
@else if $point == 640
这意味着 640 像素是40em
:
@media (min-width: 40em) { @content; }
最后,我们有相同的媒体查询结构,适用于 768 像素宽度。768 像素等于47.5em
。
在选择让内容定义断点的方法之前,请考虑以下几点:
-
使用特定宽度值(记住,这些值是基于内容的)作为媒体查询名称(320、640 或 768)的好处是,当我们使用 mixin 时,我们真正知道我们要定位的具体宽度是什么。
-
这意味着无论我们有多少个断点,我们都将始终知道我们要定位的宽度。
-
我们可以有尽可能多的断点,而且我们永远不必回到 mixin 去提醒我们哪个名称对应哪个宽度。
命名媒体查询
这是许多前端开发人员喜爰的。这个 mixin 几乎与我们刚刚看到的那个相同;不同之处在于,它不是使用特定的宽度并知道这些宽度将改变并添加其他宽度,而是使用设备特定宽度的抽象名称,通常已经定义了断点列表。
这是这个 mixin 的样子:
//Mobile-first
@mixin breakpoint($point) {
@if $point == small {
@media (min-width: 20em) { @content; }
}
@else if $point == medium {
@media (min-width: 40em) { @content; }
}
@else if $point == large {
@media (min-width: 48em) { @content; }
}
}
这是我们如何使用它的方式:
header {
width: 50%; //Properties for small screens
background: red;
@include breakpoint(medium) {
width: 100%; //Properties for large screens
background: blue;
}
}
这是编译后的样子:
header {
width: 50%;
background: red;
}
@media (min-width: 40em) {
header {
width: 100%;
background: blue;
}
}
在选择命名媒体查询方法之前,请考虑以下几点:
-
如果你有很多断点,使用抽象名称可能会令人困惑。
-
在某个时候,你要么会用尽抽象名称,要么会有太多抽象名称,以至于你真的记不住哪个名称对应哪个宽度。
基本 mixin
这是在处理媒体查询时推荐使用的 mixin,它具有以下优点:
-
它允许我们在定义宽度时继续以像素为单位思考,但输出是以相对单位(
em
)为单位。 -
这很容易理解和扩展。
-
如果我们使用桌面优先的方法,我们只需要将 mixin 名称从
mobileFirst
更改为desktopFirst
,并将min-width
关键字更改为max-width
。 -
如果我们想使用基于像素的宽度,我们只需要从除法中去掉
16
:/16+em
。 -
由于它不使用命名变量来表示不同的宽度,所以不需要记住哪个命名变量对应哪个宽度。
-
我们永远不会用尽命名变量,因为它不使用它们。
现在,考虑到我们的建议是让内容定义断点,这里是 mixin:
@mixin mobileFirst($media) {
@media (min-width: $media/16+em) { @content; }
}
就是这样——一个仅有三行的 mixin。这是我们如何使用它的方式:
header {
width: 50%; //Properties for small screensbackground: red;
@include mobileFirst(640) {
width: 100%; //Properties for large screensbackground: blue;
}
}
这是它编译成的样子:
header {
width: 50%;background: red;
}
@media (min-width: 40em) {
header {
width: 100%;background: blue;
}
}
现在,你可能会问自己,“em
值是从哪里来的?”
这很简单。我们将期望的宽度除以 16。我们除以 16 的原因是因为16px
是所有浏览器的默认字体大小。通过这样做,我们得到了以em
单位为单位的值。
如果你想使用16px
作为默认字体大小,请考虑以下示例:
-
320px/16px = 20em
-
640px/16px = 40em
-
768px/16px = 47.5em
如果你决定你的默认字体大小不是16px
而是18px
,那么同样的过程适用。将期望的宽度除以18px
:
-
320px/18px = 17.77em
-
640px/18px = 35.55em
-
768px/18px = 42.66em
选择权在你手中。
提示
我们所有的示例都将基于16px
的默认字体大小。
摘要
在本章中,我们涵盖了很多内容,但最好的还在后面。我们学会了如何安装 Sass 以及如何让它监视我们的 SCSS 文件。我们还了解到有两种不同的语法:Sass 和 SCSS。我们现在知道任何 CSS 文件都是有效的 SCSS 文件,如果我们现在知道如何编写 CSS,我们也知道如何编写 SCSS。我们讨论了 Sass 的不同基本概念,如变量、mixin、参数、嵌套、部分文件、@import
指令、源映射和 Sass 注释。
我们还学会了什么是供应商前缀和帮助自动化这个过程的不同方法。我们决定使用 Prepros 来执行以下任务:监视、编译 SCSS 文件和自动添加前缀。我们学会了创建一个部分文件来容纳我们的媒体查询 mixin,名为_mediaqueries.scss
。我们还学会了使用基本 mixin 来命名媒体查询的不同方法,这个 mixin 向我们展示了如何简单地处理媒体查询,同时遵循让内容定义断点的最佳实践。
在下一章中,我们将深入研究 HTML5 以及如何标记我们的内容以准备进行 RWD。准备好你的浮潜装备!
第二章:用 HTML5 标记我们的内容
许多人认为 HTML 是代码。嗯,不是的。HTML——任何版本的 HTML——都是标记语言。
标记语言是一种可以被人类阅读和理解的计算机语言。它使用标签来定义内容的各个部分。HTML 和 XML 都是标记语言。
为了更好地区分,编码语言涉及更复杂的抽象、脚本、数据库连接、通过复杂协议以某种形式传输数据等等。编码确实是一个神奇的世界。
HTML 可以做到这一切,但它远没有那么复杂,更容易理解。
在本章中,我们将专注于标记内容背后的科学。内容可以以许多不同的形式呈现:文本、图像、视频、表单、错误消息、成功消息、图标等等。此外,特定类型的内容在浏览器中的行为或用户与之交互的方式将告诉我们应该将特定内容标记为什么类型的 HTML 元素。
例如,许多网页设计师将锚链接<a href="#">开始 30 天试用</a>
看起来像按钮。许多网页开发人员使相同的锚链接行为像按钮。为什么不直接使用<input type="button" value="开始 30 天试用">
元素呢?更好的是,使用<button>开始 30 天试用</button>
元素,它的行为完全相同,更容易样式化,并且允许添加 HTML 内容。
我们的目标是尽可能地保持语义标记。语义标记基本上意味着我们使用 HTML 标签来描述特定内容是什么。保持语义标记有很多好处:
-
对于继承我们工作的其他网页设计师或开发人员也非常有帮助,因为他们将花费更少的时间来逆向工程我们所做的工作,更多的时间来增强它。
-
在可访问性方面也非常有帮助,因为它允许辅助技术将元素命名为它们本来的样子:一个按钮实际上是一个
<button>
,而不是一个被样式化成按钮的链接<a href="#">
。 -
语义标记对 SEO 有很大的好处,因为它可以让搜索引擎更快、更准确地索引内容。
密切关注内容对于链条中的每个人都有很大帮助——帮助我们在项目中,帮助项目本身,最终帮助我们的用户,无论是否使用辅助技术。
我可以给你的最好建议是在标记内容时倾听内容;它会和你交流。真的会。
我们将在本章中涵盖以下主题:
-
HTML5 元素的实际应用
-
使用Web Accessibility Initiative - Accessible Rich Internet Applications (WAI-ARIA)地标角色来增加可访问性
-
响应式网页设计的重要元标签
-
带有 ARIA 角色和元标签的完整 HTML5 示例页面
那么,现在我们可以使用哪些 HTML 元素,以确保我们的网站/应用在所有浏览器中都能正常显示呢?答案是所有元素。
2014 年 10 月 28 日,W3C 完成了 HTML5 标准。然而,所有主要浏览器多年来一直支持 HTML5 元素。
对我们来说,这意味着即使在 W3C 完成 HTML5 标准之前,我们已经可以使用任何 HTML5 元素。所以,如果你一直在使用 HTML5 构建网站/应用,继续使用吧;如果你还没有因为任何特定原因开始使用 HTML5,那么现在是开始的时候了。
元素
根据Mozilla Developer Network (MDN)的定义:
HTML 主要元素(
<main>
)可以用作文档的主要内容的容器。主要内容区域包括与部分的中心主题直接相关或扩展的内容,或应用程序的中心功能。这些内容应该是文档独有的,不包括在一组文档中重复的内容,例如侧边栏、导航链接、版权信息、站点标志和搜索表单(除非文档的主要功能是作为搜索表单)。与<article>
和<section>
不同,这个元素不会对文档大纲产生影响。
以下是关于<main>
元素的几个重要要点:
-
页面的顶层内容应包含在
<main>
元素中。 -
内容应该是独占且独特的。
-
<main>
元素不应包含在<header>
、<footer>
、<nav>
、<aside>
或<article>
元素内。 -
每个页面只能有一个
<main>
元素。
考虑以下例子:
<body>
<main class="main-container" role="main">Content goes here
</main>
</body>
提示
为了谨慎起见,使用 HTML 实体表示特殊字符,例如,和字符(&)是&
,省略号字符(…)是…
。
<article>
元素
根据 MDN 的定义:
HTML 文章元素(
<article>
)代表文档、页面、应用程序或站点中的独立组成部分,旨在独立分发或重复使用,例如在联合中。这可以是论坛帖子、杂志或报纸文章、博客文章或任何其他独立的内容项。每个<article>
应该被识别,通常通过在<article>
元素的子元素中包含标题(h1
-h6
元素)来实现。
以下是关于<article>
元素的几个重要要点:
- 任何自包含的内容应放在
<article>
元素内。
“自包含”意味着如果我们将<article>
元素及其内部内容移到另一个上下文中,所有内容都是不言自明的,不需要其他东西来理解。
-
<article>
可以嵌套在另一个<article>
元素内。 -
一个页面可以有多个
<article>
元素。
考虑以下例子:
<body>
<main class="main-container" role="main">
<article class="article-container flex-container">
Content goes here
</article>
</main>
</body>
<section>
元素
根据 MDN 的定义:
HTML 节元素(
<section>
)代表文档的一个通用部分,即内容的主题分组,通常带有标题。每个<section>
应该被识别,通常通过在<section>
元素的子元素中包含标题(h1
-h6
元素)来实现。
以下是关于<section>
元素的几个重要要点:
-
<section>
元素可用于封装一组相关内容。这些相关内容不一定需要在页面上下文之外有意义。 -
使用
<section>
元素的一种安全有效的方式是将其放在<article>
元素内。当然也可以单独使用<article>
元素。建议在使用<section>
元素时包含标题元素(<h1>
、<h2>
、<h3>
等),但不是必需的。 -
什么时候使用
<section>
元素,什么时候使用<article>
元素可能会令人困惑。如果不确定,可以选择任何一个元素。 -
一个页面可以有多个
<section>
。
考虑以下例子:
<body>
<main class="main-container" role="main">
<article class="article-container flex-container">
<section class="main-content">
<header>
<h1>The <code><main></code> element </h1>
</header>
<p>As per the MDN definition:</p> <blockquote>
<p>The HTML Main Element (<code><main></code>) represents…</p>
</blockquote>
</section>
</article>
</main>
</body>
根据 MDN 的定义:
HTML
<aside>
元素代表页面上与其余内容有轻微关联的内容部分,可以被视为与该内容分开的部分。这些部分通常表示为侧边栏或插入内容。它们通常包含侧边栏上的定义,例如词汇表中的定义;也可能包含其他类型的信息,例如相关广告;作者的传记;网络应用程序;博客上的个人资料信息或相关链接。
以下是关于<aside>
元素的几个重要要点:
-
与主要内容相关的内容可以包含在
<aside>
元素中。如果这些内容与主要内容分开,它们仍然可以独立存在。 -
在单个页面中可以有多个
<aside>
。
考虑以下例子:
<body>
<main class="main-container" role="main">
<article class="article-container flex-container">
<section class="main-content">
<header>
<h1>The <code><main></code> element </h1>
</header>
<p>As per the MDN definition:</p>
<blockquote>
<p>The HTML Main Element (<code><main></code>)
represents…</p>
</blockquote>
</section>
<aside class="side-content" role="complementary">
<h2>What Does "Semantic HTML" Mean?</h2>
<p>Semantic markup basically means that we use HTML tags
to describe what a specific piece of content is.</p>
</aside>
</article>
</main>
</body>
提示
切题内容意味着内容涉及手头的主题,但不是主要信息的一部分。如果<aside>
元素内的内容被移除,主要信息不会受到影响。
<header>
元素
通常,我们认为网站/应用的顶部部分是页眉,这是正确的。该顶部部分的编辑名称是标志。
然而,从 HTML5 的角度来看,标志和页眉之间有区别。
标志是网站/应用的主要页眉,只能有一个。它通常包含标志、一些导航,可能还有搜索字段等。页眉可以被认为是任何部分的顶部区域,可以有多个页眉。
请注意,我们还没有讨论<header>
元素,至少目前还没有。
标志可以使用<header>
元素构建,但<header>
元素也可以在同一页面的其他部分使用。
以下是 MDN 的定义:
HTML
<header>
元素代表一组介绍性或导航辅助信息。它可能包含一些标题元素,还可能包含其他元素,如标志、包装部分的页眉、搜索表单等。
以下是关于<header>
元素的几个重要要点:
-
一个很好的经验法则是在
<section>
元素内使用<header>
元素。 -
如果我们认为有必要,可以将标题(
h1
到h6
)包装在<header>
元素内,但这并不是一种常见做法或必需的。 -
在单个页面中可以有多个
<header>
元素。
在以下示例中,有两个突出显示的<header>
部分,标志和<section>
元素内的页眉:
<body>
<header class="masthead" role="banner">
<div class="logo">Mastering RWD with HTML5 & CSS3</div>
<div class="search" role="search">
<form>
<label>Search:
<input type="text" class="field">
<button>Search Now!</button>
</label>
</form>
</div>
</header>
<main class="main-container" role="main">
<article class="article-container flex-container">
<section class="main-content">
<header>
<h1>The <code><main></code> element</h1>
</header>
<p>As per the MDN definition:</p>
<blockquote>
<p>The HTML Main Element (<code><main></code>) represents…</p>
</blockquote>
</section>
<aside class="side-content" role="complementary">
<h2>What Does "Semantic HTML" Mean?</h2>
<p>Semantic markup basically means that we use HTML tags to describe what a specific piece of content is.</p>
</aside>
</article>
</main>
</body>
<footer>
元素
根据 MDN 的定义:
HTML 页脚元素(
<footer>
)代表其最近的分区内容或分区根元素的页脚。页脚通常包含有关该部分作者的信息、版权数据或相关文档的链接。
以下是关于<footer>
元素的几个重要要点:
-
它应始终包含有关其包含的父元素的任何信息。
-
尽管术语页脚暗示着页面、文章或应用的底部部分,但
<footer>
元素不一定非要在底部。 -
在单个页面中可以有多个
<footer>
元素。
考虑以下例子:
<body>
<header class="masthead" role="banner">
<div class="logo">Mastering RWD with HTML5 & CSS3</div>
<div class="search" role="search">
<form>
<label>Search:
<input type="text" class="field">
<button>Search Now!</button>
</label>
</form>
</div>
</header>
<main class="main-container" role="main">
<article class="article-container flex-container">
<section class="main-content">
<header>
<h1>The <code><main></code> element</h1>
</header>
<p>As per the MDN definition:</p>
<blockquote>
<p>The HTML Main Element (<code><main></code>) represents…</p>
</blockquote>
</section>
<aside class="side-content" role="complementary">
<h2>What Does "Semantic HTML" Mean?</h2>
<p>Semantic markup basically means that we use HTML tags to describe what a specific piece of content is.</p>
</aside>
</article>
<footer class="main-footer" role="contentinfo">
<p>Copyright ©</p>
<ul class="nav-container" role="navigation">
<li><a href="#">Footer Link 1</a></li>
<li><a href="#">Footer Link 2</a></li>
<li><a href="#">Footer Link 3</a></li>
<li><a href="#">Footer Link 4</a></li>
<li><a href="#">Footer Link 5</a></li>
</ul>
</footer>
</main>
</body>
<nav>
元素
根据 MDN 的定义:
HTML 导航元素(
<nav>
)代表页面中链接到其他页面或页面内部部分的部分:一个带有导航链接的部分。
以下是关于<nav>
元素的几个重要要点:
-
它用于对链接列表或集合进行分组。这些链接可以指向外部资源或站点/应用内的其他页面。
-
在
<nav>
元素内使用无序列表<ul>
来构造链接是一种常见做法,因为这样更容易进行样式设置。 -
在
<header>
元素中包含<nav>
也是一种常见做法,但不是必需的。 -
并非所有链接组都必须在
<nav>
元素内。如果我们在<footer>
标签内有一组链接列表,那么在<nav>
中也没有必要包含这些链接。 -
在单个页面中可以有多个
<nav>
元素,例如主导航、实用导航和<footer>
导航。
考虑以下例子:
<body>
<header class="masthead" role="banner">
<div class="logo">Mastering RWD with HTML5 & CSS3</div>
<div class="search" role="search">
<form>
<label>Search:
<input type="text" class="field">
<button>Search Now!</button>
</label>
</form>
</div>
</header>
<nav class="main-nav" role="navigation">
<ul class="nav-container">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
</ul>
</nav>
<main class="main-container" role="main">
<article class="article-container flex-container">
<section class="main-content">
<header>
<h1>The <code><main></code> element</h1>
</header>
<p>As per the MDN definition:</p>
<blockquote>
<p>The HTML Main Element (<code><main></code>) represents…</p>
</blockquote>
</section>
<aside class="side-content" role="complementary">
<h2>What Does "Semantic HTML" Mean?</h2>
<p>Semantic markup basically means that we use HTML tags to describe what a specific piece of content is.</p>
</aside>
</article>
<footer class="main-footer" role="contentinfo">
<p>Copyright ©</p>
<ul class="nav-container" role="navigation">
<li><a href="#">Footer Link 1</a></li>
<li><a href="#">Footer Link 2</a></li>
<li><a href="#">Footer Link 3</a></li>
<li><a href="#">Footer Link 4</a></li>
<li><a href="#">Footer Link 5</a></li>
</ul>
</footer>
</main>
</body>
使用 WAI-ARIA 地标角色来增加可访问性
网络最被忽视的一个方面是可访问性,除非你是致力于这个主题的团体的一部分。作为网页设计师和开发者,我们很少考虑残障用户如何访问网页并使用屏幕阅读器和其他辅助技术来访问我们的网站或应用程序。我们实际上更关心支持旧版浏览器,而不是增加产品的可访问性。
在本章中,我们将介绍WAI-ARIA landmark roles是什么,以及如何在我们的标记中轻松实现它们,以增强我们文档的语义,为那些使用辅助技术的用户在任何现代浏览器上使用键盘浏览我们的网站/应用程序时提供更好和愉快的体验。
注意
WAI-ARIA代表Web Accessibility Initiative – Accessible Rich Internet Applications。
WAI-ARIA landmark roles
WAI-ARIA landmark roles 也可以称为ARIA roles,所以这是我们将使用的术语。
当在 HTML5 元素中实现 ARIA 角色时,它看起来像这样:
<header role="banner">
我们可以使用多个 ARIA 角色,但在本书中,我们将专注于那些更容易实现并且能够有效增强我们网站/应用程序可访问性的角色。
横幅角色
以下是一些重要的要点需要记住:
-
这个角色通常应用于页面的顶部
<header>
。 -
页眉区域包含页面的最突出的标题。
-
通常,具有
role="banner"
的内容会在整个站点中持续出现,而不是在单个特定页面中出现。 -
每个页面/文档只允许一个
role="banner"
。
考虑以下示例:
<header class="masthead" role="banner">
<div class="logo">Mastering RWD with HTML5 & CSS3</div>
<div class="search" role="search">
<form>
<label>Search:
<input type="text" class="field">
<button>Search Now!</button>
</label>
</form>
</div>
</header>
导航角色
以下是一些重要的要点需要记住:
-
这个角色通常应用于
<nav>
元素,但也可以应用于其他容器,如<div>
或<ul>
。 -
它描述了一组导航元素/链接。这些链接可以是用于导航站点或者出现在页面上的链接。
-
每个页面可以有多个
role="navigation"
。
考虑以下示例,其中角色应用于主要的<nav>
元素:
<nav class="main-nav" role="navigation">
<ul class="nav-container">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
</ul>
</nav>
考虑以下示例,其中角色应用于页脚导航的<ul>
元素:
<footer class="main-footer" role="contentinfo">
<p>Copyright ©</p>
<ul class="nav-container" role="navigation">
<li><a href="#">Footer Link 1</a></li>
<li><a href="#">Footer Link 2</a></li>
<li><a href="#">Footer Link 3</a></li>
<li><a href="#">Footer Link 4</a></li>
<li><a href="#">Footer Link 5</a></li>
</ul>
</footer>
对于我们将navigation
角色添加到哪个元素并没有特定的偏好。如果我们将其添加到<nav>
元素或<ul>
元素中,效果是一样的。
主要角色
以下是一些重要的要点需要记住:
-
这个角色通常应用于页面的
<main>
元素。 -
页面的主要/中心主题的容器应该标记有这个角色。
-
每个页面/文档只允许一个
role="main"
。
考虑以下示例:
<body>
<main class="main-container" role="main">Content goes here
</main>
</body>
内容信息角色
以下是一些重要的要点需要记住:
-
这个角色通常应用于页面的主要
<footer>
元素。 -
这是包含有关文档/站点/应用程序的信息的部分。
-
如果该部分包含例如版权链接、脚注、隐私声明链接或条款和条件链接,那么它是
role="contentinfo"
的一个很好的候选者。 -
每个页面/文档只允许一个
role="contentinfo"
。
考虑以下示例:
<footer class="main-footer" role="contentinfo">
<p>Copyright ©</p>
<ul class="nav-container" role="navigation">
<li><a href="#">Footer Link 1</a></li>
<li><a href="#">Footer Link 2</a></li>
<li><a href="#">Footer Link 3</a></li>
<li><a href="#">Footer Link 4</a></li>
<li><a href="#">Footer Link 5</a></li>
</ul>
</footer>
搜索角色
以下是一些重要的要点需要记住:
-
这个角色通常应用于页面/应用程序搜索功能所属的
<form>
元素。 -
如果搜索表单包裹在
<div>
元素中,这个角色也可以应用于该<div>
元素。如果是这种情况,那么就不需要将其添加到子<form>
元素中。 -
每个页面可以有多个
role="search"
,只要控件是实际的搜索功能。例如,在联系表单上使用role="search"
是不正确和不语义化的。
考虑以下示例,其中角色应用于站点的搜索<form>
元素:
<div class="search">
<form role="search">
<label>Search:
<input type="text" class="field">
<button>Search Now!</button>
</label>
</form>
</div>
表单角色
以下是一些重要的要点需要记住:
-
这个角色通常应用于包含某种表单的
<div>
元素,除了站点/应用的主搜索表单,例如联系表单、注册表单、付款表单等。 -
不应该应用于实际的
<form>
元素,因为该元素已经具有默认的角色语义,可以帮助技术支持。
考虑以下示例:
<div class="contact-form" role="form">
<header>
<h2>Have Questions About HTML5?</h2>
</header>
<form>
<div class="flex-container">
<label class="label-col">Name: <input type="text" class="field name" id="name" required></label>
<label class="label-col">Email: <input type="email" class="field email" id="email" required></label>
</div>
<label for="comments">Comments:</label>
<textarea class="comments" id="comments" cols="50" required></textarea>
<button>Send Question!</button>
</form>
</div>
补充角色
以下是一些重要的要点:
-
这个角色通常应用于
<aside>
元素。 -
它应该用于包含支持内容的区域;即使与内容分开,它仍然可以独立理解。这基本上是
<aside>
元素的描述。 -
页面上可以有多个
role="complementary"
。
考虑以下示例:
<aside class="side-content" role="complementary">
<h2>What Does "Semantic HTML" Mean?</h2>
<p>Semantic markup basically means that we use HTML tags to describe what a specific piece of content is.</p>
</aside>
注意
WAI-ARIA 角色解释
如果您对 ARIA 角色列表感兴趣,可以访问 Web 平台网站,那里的解释简单易懂:specs.webplatform.org/html-aria/webspecs/master/#docconformance
RWD 的重要元标记
网页设计师和开发人员使用元标记的方式有很多,但这些广泛的解释超出了本书的范围,所以我们将专注于对 RWD 有用且按预期工作的一些要点。
以下元标记对我们的响应式网站/应用非常重要。这些元标记不仅适用于 HTML5 页面,它们也适用于任何 HTML 版本。
让我们开始吧。
视口元标记
viewport
元标记是 RWD 中最重要的元标记。它是由苹果在其移动 Safari 浏览器中引入的。现在,其他移动浏览器也支持它。奇怪的是,这个元标记并不属于任何网页标准,但如果我们希望我们的响应式网站/应用在小屏幕上正确显示,它是必不可少的。
这个元标记的推荐语法如下:
<meta name="viewport" content="width=device-width, initial-scale=1">
以下是一些重要的要点:
-
name="viewport"
指令描述了元标记的类型。 -
content="width=device-width, initial-scale=1"
指令执行了几项任务: -
width
属性定义了viewport
元标记的大小。我们也可以使用特定的像素宽度,例如width=960
。 -
device-width
值是 CSS 像素中 100%缩放时屏幕的宽度。 -
initial-scale
值定义了页面首次加载时应显示的缩放级别。1 等于 100%缩放,1.5 等于 150%缩放。 -
使用这种语法,用户可以根据需要进行缩放。这是用户体验的最佳实践。
注意
本书强烈不建议使用以下viewport
属性:maximum-scale=1
和user-scalable=no
。通过使用这些viewport
属性,我们剥夺了用户在我们的网站/应用中进行缩放的能力。我们永远不知道缩放对任何人都可能很重要,所以最好远离包含这些 viewport 属性。
为了帮助尚未响应式的网站在小屏幕上显示得更好,添加网站构建时的特定像素宽度。例如,如果一个网站宽度为 960px,就给它添加这个viewport
元标记:
<meta name="viewport" content="width=960">
如果您对viewport
元标记的详细阅读感兴趣,MDN 解释得很好:developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
。
X-UA-Compatible 元标记
X-UA-Compatible
元标记仅针对 Internet Explorer 及其兼容性视图功能。众所周知,Microsoft 在 IE8 中引入了兼容性视图。
这个元标记的推荐语法如下:
<meta http-equiv="X-UA-Compatible" content="IE=edge">
以下是一些重要的要点:
-
http-equiv="X-UA-Compatible"
指令告诉 IE 需要使用特定的渲染引擎来渲染页面。 -
content="IE=edge"
指令告诉 IE 应该使用其最新的渲染 HTML 和 JavaScript 引擎。 -
使用这个元标记来触发 IE 的最新 HTML 和 JavaScript 引擎非常好,因为 IE 的最新版本总是具有最新的安全更新和对更多功能的支持。
提示
不再需要使用chrome=1
值,因为 Chrome Frame 在 2014 年 2 月被停用了。
注意
Google Chrome Frame 是一个针对旧版本 IE 的插件。安装后,它会替换 IE 中的某些模块,如渲染和 JavaScript 引擎,从而改善用户体验。换句话说,它就像在 IE 上安装了一个小版本的 Google Chrome。
字符集元标记
charset
元标记告诉浏览器使用哪种字符集来解释内容。有人说包含它并不那么重要,因为服务器本身会通过 HTTP 头将字符集发送给浏览器。但在我们的页面中包含它总是一个好的措施。
如果 HTML 中没有声明charset
,并且服务器没有将字符集发送给浏览器,那么一些特殊字符可能会显示不正确。
在 HTML5 中,这个元标记的推荐语法是这样的:
<meta charset="utf-8">
以下是一些重要的要点需要记住:
-
这个元标记是专门为 HTML5 文档创建的。主要好处是写的代码更少。
-
对于 HTML 4 和 XHTML,你应该使用以下语法
:
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- 另一个常见的值是
ISO-8859-1
,但UTF-8
更广泛使用,因为浏览器正确解释内容的机会更大。
注意
UTF-8代表Unicode 转换 格式-8。
一个带有 ARIA 角色和元标记的完整 HTML5 示例页面
现在我们已经了解了一些基本的 HTML5 元素,它们可以应用的 ARIA 角色,以及适合显示的正确元标记,让我们在一个完整的 HTML5 页面中将它们可视化:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mastering RWD with HTML5 & CSS3</title>
<link rel="stylesheet" href="css/site-styles.css">
</head>
<body>
<header class="masthead" role="banner">
<div class="logo">Mastering RWD with HTML5 & CSS3</div>
<div class="search" role="search">
<form>
<label>Search:
<input type="text" class="field">
<button>Search Now!</button>
</label>
</form>
</div>
</header>
<nav class="main-nav" role="navigation">
<ul class="nav-container">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
</ul>
</nav>
<main class="main-container" role="main">
<h1>Chapter 2: Marking Our Content with HTML5</h1>
<p>Many consider that HTML is "code". Well, it's not. HTML, any version of it, is a "markup" language. </p>
<article class="article-container flex-container">
<section class="main-content">
<header>
<h1>The <code><main></code> element </h1>
</header>
<p>As per the MDN definition:</p>
<blockquote>
<p>The HTML Main Element (<code><main></code>) represents…</p>
</blockquote>
</section>
<aside class="side-content" role="complementary">
<h2>What Does "Semantic HTML" Mean?</h2>
<p>Semantic markup basically means that we use HTML tags to describe what a specific piece of content is.</p>
</aside>
</article>
<div class="contact-form" role="form">
<header>
<h2>Have Questions About HTML5?</h2>
</header>
<form>
<div class="flex-container">
<label class="label-col">Name: <input type="text" class="field name" id="name" required></label>
<label class="label-col">Email: <input type="email" class="field email" id="email" required></label>
</div>
<label for="comments">Comments:</label>
<textarea class="comments" id="comments" cols="50" required></textarea>
<button>Send Question!</button>
</form>
</div>
<footer class="main-footer" role="contentinfo">
<p>Copyright ©</p>
<ul class="nav-container" role="navigation">
<li><a href="#">Footer Link 1</a></li>
<li><a href="#">Footer Link 2</a></li>
<li><a href="#">Footer Link 3</a></li>
<li><a href="#">Footer Link 4</a></li>
<li><a href="#">Footer Link 5</a></li>
</ul>
</footer>
</main>
</body>
</html>
作为奖励,让我们来看看将所有这些整合成一个漂亮响应式页面的 SCSS。
注意
以下的 SCSS 代码是使用桌面优先的方法构建的,因为我们将会逐步进入移动优先的方式。
这是 SCSS:
//Media Query Mixin - Desktop-first
@mixin forSmallScreens($media) {
@media (max-width: $media/16+em) { @content; }
}
//Nav
.main-nav {
max-width: 980px;
margin: auto;
padding: 10px 5px;
background: #555;
@include forSmallScreens(420) {
padding: 5px 0;
}
}
//All Navigations
.nav-container {
display: flex;
justify-content: center;
list-style-type: none;
margin: 0;
padding: 0;
@include forSmallScreens(420) {
flex-wrap: wrap;
}
li {
display: flex;
width: 100%;
margin: 0 5px;
text-align: center;
@include forSmallScreens(420) {
display: flex;
justify-content: center;
flex-basis: 45%;
margin: 5px;
}
}
a {
@extend %highlight-section;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
padding: 10px;
color: white;
}
}
//Header
.masthead {
display: flex;
justify-content: space-between;
max-width: 980px;
margin: auto;
padding: 10px;
background: #333;
border-radius: 3px 3px 0 0;
@include forSmallScreens(700) {
display: block;
text-align: center;
}
}
.logo {
@extend %highlight-section;
padding: 0 10px;
color: white;
line-height: 2.5;
@include forSmallScreens(420) {
font-size: .85em;
}
}
//Search field
.search {
@extend %highlight-section;
padding: 5px;
color: white;
@include forSmallScreens(420) {
font-size: .85em;
}
.field {
width: auto;
margin: 0 10px 0 0;
}
button {
@include forSmallScreens(420) {
width: 100%;
margin-top: 10px;
}
}
}
//Main Container
.main-container {
max-width: 980px;
margin: auto;
padding: 10px;
background: #999;
border-radius: 0 0 3px 3px;
}
//Article
.article-container {
@extend %highlight-section;
margin-bottom: 20px;
padding: 10px;
}
//Main Content of the Page
.main-content {
@extend %highlight-section;
width: 75%;
margin-right: 10px;
padding: 10px;
@include forSmallScreens(600) {
width: 100%;
}
h1 {
margin: 0;
}
}
//Side Content
.side-content {
@extend %highlight-section;
width: 25%;
padding: 10px;
font-size: .8em;
background: #999;
@include forSmallScreens(600) {
width: 100%;
margin-top: 12px;
}
h2 {
margin: 0;
}
ol {
padding-left: 20px;
}
a {
color: #eee;
}
}
//Contact Form
.contact-form {
@extend %highlight-section;
width: 540px;
margin: 0 auto 20px;
padding: 20px;
@include forSmallScreens(600) {
width: 100%;
}
h2 {
margin-top: 0;
}
label, button {
display: block;
}
.comments {
height: 100px;
}
.flex-container {
justify-content: space-between;
@include forSmallScreens(600) {
display: flex;
}
@include forSmallScreens(400) {
display: block;
}
}
.label-col {
width: 48%;
@include forSmallScreens(400) {
width: 100%;
}
}
}
//Form Elements
.field,
.comments {
width: 100%;
margin-bottom: 10px;
padding: 5px;
@include forSmallScreens(420) {
width: 100%;
}
}
//Footer
.main-footer {
color: white;
padding: 10px;
background: #333;
p {
margin-top: 0;
}
}
//Placeholder
%highlight-section {
border: white 1px solid;
border-radius: 3px;
background: rgba(white, .1);
}
//Helper Classes
.flex-container {
display: flex;
@include forSmallScreens(600) {
display: block;
}
}
//General
*,
*:before,
*:after {
box-sizing: border-box;
}
body {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
blockquote {
font-style: italic;
}
桌面和移动设备的输出截图
以下截图代表了线框和样式化模式下的原型/演示。您将能够看到桌面(宽 980 像素)以及移动设备(宽 320 像素)的输出。
在线框截图中,白色轮廓和不同色调的灰色背景基本上是视觉提示,帮助你理解每个元素的边界在哪里,而不必使用浏览器的开发工具。
另一方面,样式化截图向你展示了用少量 CSS 可以实现什么。线框和样式化页面都使用完全相同的标记。
页面的演示可以在这里看到:
-
访问
codepen.io/ricardozea/pen/717c6ab2dab9646f814f0429153a6777
查看线框页面 -
访问
codepen.io/ricardozea/pen/244886bac2434369bd038294df72fdda
查看样式化页面
让我们看看截图。
桌面输出[线框]如下:
桌面输出[样式化]如下:
移动设备的输出[线框]如下:
移动设备的输出[样式化]如下:
总结
这是一个简短的章节,但它肯定充满了重要信息。
我们学到了 HTML 是标记而不是代码。我们还看到了各种 HTML5 元素的作用。这将帮助我们理解可以用哪些 HTML5 元素来标记我们提供的内容。
我们还学会了如何使用 ARIA 角色标记 HTML,以使我们的站点/应用对使用辅助技术的用户更加可访问。
我们还讨论了一些重要的元标记,这些标记将帮助您的页面和标记在不同设备上正确显示,并触发 Internet Explorer 中的最新 HTML 和 JavaScript 引擎。
最后,我们看到所有上述主题在一个实际的完整 HTML5 示例中实现,以及它的 SCSS。该示例是使用桌面优先方法构建的;这将使我们能够有条不紊地将我们的思维模式转变为移动优先技术。
下一章将讨论何时以及如何使用移动优先和/或桌面优先方法,以及如何使用每种方法论。拿出你的水晶球!
第三章:移动优先还是桌面优先?
在我多年的响应式网站设计和构建经验中,我发现为了更好地查看内容和消息,在线框和设计阶段使用桌面优先方法更容易可视化事物。
由于桌面优先方法允许我们在给定布局中看到更多内容,因此我们可以将提供给我们的内容的层次结构转化为代表该层次结构的布局。在 320 像素宽的小画布上进行此操作比需要更困难。
当您完成了这种层次结构,它将在小屏设备上保持不变,唯一改变的是布局。
最佳实践建议首先构建移动,但许多网络专业人员实际上并不知道为什么我们首先构建移动。双关语。
所以,让我们澄清一下。我们首先构建移动的原因是由 Luke Wroblewski 提到的三个原则,他实际上在 2009 年创造了移动优先这个术语。您会注意到这些原则都与 HTML、CSS 和/或 JavaScript 无关。换句话说,您不是因为 HTML、CSS 或 JavaScript 的任何优势而首先构建移动。有关更多信息,请访问www.lukew.com/ff/entry.asp?933
。
考虑以下几点:
-
移动正在爆炸:嗯,移动已经爆炸了。这基本上意味着人们更容易、更快速、更方便地使用移动设备访问网络。因此,如果您首先构建与移动设备兼容的网站,就有更好的机会提供更好的用户体验,并被更多人查看,而不是拥有仅限桌面的网站/应用程序。
-
移动迫使您专注:由于移动设备屏幕上的空间比桌面屏幕上的空间少得多,因此有必要进行优先排序。这意味着最重要的任务和/或消息需要立即可见。
-
移动扩展了您的能力:移动设备具有桌面设备没有的功能:GPS、加速计、多点触控输入、语音识别、前后摄像头等。在进行移动优先时,您可以使用这些先进技术来创建更丰富、更令人兴奋的体验。
现在您有了最终设计,现在您需要将该设计实施到 HTML、CSS 和 JavaScript 中。在这个阶段,您应该使用移动优先方法,并考虑我们之前提到的三个原因:
-
构建移动优先意味着您的网站/应用程序可以被更多人看到
-
这使您优先考虑内容
-
如果需要,它将允许您使用移动设备的高级功能和能力
在本章中,我们将涵盖以下主题:
-
在桌面优先视图中创建您的设计,但使用移动优先进行实施。
-
移动优先和桌面优先媒体查询的 Sass 混合。
-
处理旧版浏览器。
-
如何处理高密度屏幕。
-
为什么 RWD 有时并不一定是正确的解决方案。
-
使用 RWD 改造旧网站。
在桌面优先视图中创建您的设计,但使用移动优先进行实施
让我们看看一些术语,以便我们在同一页面上:
-
线框:这是使用仅轮廓的非常基本的布局的视觉表示。没有颜色,没有品牌,也没有任何定义的样式。
-
设计/构图:这是一个带有颜色、品牌和样式的充实线框。它是最终页面/站点/应用程序的非常接近表示(通常说,接近最终产品的 95%)而不涉及任何标记或任何类型的编码。
-
HTML 模拟或 HTML 模板:这是当设计已经被实现到一个实际的 HTML 页面中,带有 CSS 和—有时—JavaScript。它只能在浏览器中查看。它几乎是页面/站点/网络应用的最终产品的精确表示(接近 99%)。
术语明确后,让我们继续。
一些专业人士,包括我在内,建议使用更现代和高效的技术来创建视觉资产,以优化线框和设计/构图过程中的时间。诸如样式瓷砖、情绪板、元素拼贴和原子设计等技术与传统的线框和设计/构图方法有所不同。它们提供了独立于屏幕宽度、技术甚至内容创建的布局和样式的探索机会。
在本书的范围内,我们将专注于如何在掌握 HTML5 和 CSS3 的响应式网页设计(RWD)的初期阶段最大化利用时间的同时,仍然可以利用传统线框和设计/构图方法的一些内容。
为什么要以桌面优先的方式创建设计?
以桌面优先的方式创建设计的原因很简单:房地产(空间)。
作为设计师,我们需要以视觉方式反映内容的层次结构。为了实现这一点,我们使用许多设计原则,如节奏、接近性、空白、模式、对比、平衡、网格、对称等等。
当我们创建线框或设计/构图的画布足够大,可以尝试不同的排列和布局,我们就有了必要的灵活性来探索可以代表所述内容层次结构的不同方式。
例如,我们正在使用一个 12 列网格,我们提供的内容决定了以下内容层次结构:
-
这家企业希望用户能够提供他们的电子邮件 ID 以接收我们的新闻通讯。
-
我们希望展示编辑部选择的特色文章。
有了前面的内容层次结构,我们可以立即开始构想不同的布局来传达这个层次结构:
-
为了让用户提供他们的电子邮件地址,我们将创建一个带有标题、段落、电子邮件输入类型和按钮的表单。这个表单将位于页眉下方的左上角,并且可能有三到四列的宽度。我认为可能四列太宽了,但让我们先画线框看看感觉如何,以及这可能会有什么可用性、可访问性和可读性问题或好处。
-
对于特色文章,我们将使用剩余的列。如果电子邮件表单是三列宽,我们将使用剩下的九列;如果电子邮件表单是四列宽,我们将只使用剩下的八列。特色文章有更多的内容,如标题、作者、日期、类别、摘要、缩略图和指向完整文章的链接。
在我们的设计/线框应用程序中有一个宽敞的画布,我们可以尝试这些不同的方法,并最终得出一个合理的建议布局,以满足企业或利益相关者的要求,并且代表内容层次结构。
以移动优先的方式创建这样一个布局几乎是不可能的。小型房地产屏幕非常受限制和有限。但当事情开始增长时,我们需要每次考虑特定断点时进行这个探索过程。
提示
实际上,我们在这一点上不应该考虑断点(无意冒犯),因为内容而不是特定的设备宽度决定了需要添加新断点的位置。
一旦我们定义了反映内容层次结构的布局,我们就会处于一个良好的位置,因为当这些内容在较小的屏幕上重新排列时,无论宽度是多少,层次结构都将保持完整。
为什么要以移动优先的方式实施?
首先澄清一个术语:implement意味着根据线框图或设计/构图创建一个带有 CSS 和必要时 JavaScript 的 HTML 模型。
本章开头提到的原因是回答“为什么要首先使用移动设备实施?”记住:移动设备正在爆炸(实际上已经爆炸了),移动设备迫使你集中注意力,并扩展了你的能力。
除了第二个前提(这是一个巨大的也许)之外,这些原因都无法通过桌面优先实现。
让我们换个话题,转向一个更加技术性的主题,这将帮助我们了解 Sass mixin 如何帮助我们掌握移动设备优先和桌面优先方法。
因此,让我们回顾一下。使用桌面优先来创建你的设计和线框图。有一个大画布可以让我们探索不同的布局,并正确安排内容的层次结构。当需要实施(创建 HTML 模型)时,使用移动设备优先。
移动设备优先和桌面优先媒体查询的 Sass mixin
对于我们的示例,在本书中我们将使用两种类型的 Sass mixin:一种使用min-width
属性的移动设备优先 mixin,另一种使用max-width
属性的桌面优先 mixin。我们已经在第一章中看到了以下 mixin 及其工作原理,利用 Sass 实现响应式网页设计的强大功能,但这里是一个复习。
移动设备优先 mixin
我们将使用以下移动设备优先 mixin:
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content; }
}
这就是我们使用它的方式:
header {
//Properties for small screens
width: 50%;
background: red;
@include forLargeScreens(640) {
//Properties for large screens
width: 100%;
background: blue;
}
}
这编译成以下内容:
header {
width: 50%;
background: red;
}
@media (min-width: 40em) {
header {
width: 100%;
background: blue;
}
}
桌面优先 mixin
这是我们将要使用的桌面优先 mixin:
@mixin forSmallScreens($media) {
@media (max-width: $media/16+em) { @content; }
}
这就是我们使用它的方式:
header {
//Properties for large screens
width: 100%;
background: purple;
@include forSmallScreens(640) {
//Properties for small screens
width: 50%;
background: yellow;
}
}
@include forSmallScreens
这编译成以下内容:
header {
width: 100%;
background: purple;
}
@media (max-width: 40em) {
header {
width: 50%;
background: yellow;
}
}
提示
使用这些 mixin 的好处是,非常容易找出正在使用的方法,因为我们可以在整个 SCSS 文件中看到forLargeScreens
或forSmallScreens
这个术语被重复使用。如果其他人要编辑我们最初做的任何工作,他们将清楚地了解我们用哪种方法构建了我们的站点/应用,只需扫描 SCSS 文件。
处理旧版浏览器
在“移动设备优先还是桌面优先?”的问题中,有一个领域我们需要涵盖一下,那就是旧版浏览器。每个项目,每个客户及其相应的分析(如果有的话,他们应该有),都有不同的要求,影响我们应该如何处理那些旧版浏览器。
如果你是用桌面优先的方法构建的,你当前的工作流程应该保持不变,因为这几乎就是在响应式网页设计变得几乎是强制性之前我们一直在做的事情。
这意味着你仍然会使用类似这样的东西:
header {
//Desktop-first declaration
width: 50%;
@include forSmallScreens(768) {
//Target small screens (mobile devices)
width: 100%; }
}
这编译成以下内容:
header {
width: 50%;
}
@media (max-width: 48em) {
header {
width: 100%;
}
}
IE7 和 IE8 不支持媒体查询,但前面的代码将正常工作,因为header { width: 50%; }
规则不在媒体查询内。
然而,如果你是以移动设备为先,那么header { width: 50%; }
将在媒体查询内,因此 IE7 和 IE8 将无法看到该规则:
.article {
//Mobile-first declaration
width: 100%;
//IE7 and IE8 won't be able to see this rule.
@include forLargeScreens(768) {
width: 50%;
}
}
这编译成以下内容:
header {
width: 100%;
}
@media (min-width: 48em) {
header {
width: 50%;
}
}
那么你该怎么办?解决方案非常简单:使用Respond.js
脚本。
如何使用 Respond.js 进行 RWD
Respond.js
是一种称为polyfill的脚本。根据最初提出这个术语的人 Remy Sharp 的说法,polyfill 是一段代码,提供了我们,网页开发人员,期望浏览器本地提供的技术。
在网页设计和开发中,polyfill 比 JavaScript 实现更丰富,例如 Scott Jehl 的Respond.js
。但我们也可以说 CSS 中也有 polyfill,例如 Eric Meyer 的著名的reset.css
和 Nicolas Gallagher 和 Jonathan Neal 的Normalize.css
。
Respond.js
脚本是一种 polyfill,使旧版浏览器(IE6/7/8)支持它们从未支持过的特定 CSS 功能:媒体查询。
您可以从github.com/scottjehl/Respond
下载Respond.js
。
提示
尽管我建议使用 polyfill,但我们需要注意网站/应用程序需要进行额外的 HTTP 请求以获取此 JavaScript 文件。我们的网站/应用程序发出的请求越少,它们就会越快,从而带来许多好处,如改善用户体验和积极的 SEO 影响。
因此,您需要做以下事情:
-
确保对
Respond.js
的调用在调用 CSS 文件之后(希望只有一个 CSS 文件)。 -
调用
Respond.js
脚本。
提示
性能最佳实践建议将非必要的脚本放在标记的底部,就在关闭的</body>
标签之前。由于Respond.js
针对的是旧版浏览器,让我们继续这样做。将脚本放在标记底部的另一个好处是有助于避免阻塞页面的渲染。
这是我们的示例 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mastering RWD with HTML5 & CSS3</title>
<link href="styles.css" rel="stylesheet">
</head>
<body>
<header>Logo goes here…</header>
<article>Content goes here…</article>
<script src="img/respond.min.js"></script>
</body>
</html>
在我们的styles.scss
文件中,我们输入以下行:
//Mobile-first declaration
article { background: red;
//Target screens 640px wide and larger
@include forLargeScreens(640) {
& { background: green; }
}
}
这编译为以下内容:
article {
background: red;
}
@media (min-width: 40em) {
article {
background: green;
}
}
因此,当您调整 IE7 或 IE8 浏览器窗口大小时,如果窗口宽度为 640 像素或更小,则它将能够显示红色背景,如果窗口为 641 像素或更大,则显示绿色背景。
IE 特定样式表的时代已经过去
自从我开始编写 CSS 以来,我一直避免创建 IE 特定的样式表。这样做的原因很简单:
-
文件管理:在进行生产时,文件越少,每个过程就越顺利;更不用说更不容易出错。
-
可扩展性:如果需要添加、删除或编辑样式,您和您的团队知道最终的更改需要出现在您的主要且唯一的 CSS 文件中,我们的情况下是 SCSS 文件。
-
组织:在添加、删除或编辑正确的 CSS 文件(在我们的情况下是 SCSS 文件)中的 IE 特定样式时,让每个人都保持一致。
-
性能:少一个 HTTP 请求是一件好事,非常好的事情。无论多么小,我们为性能所做的任何事情都可以为良好的用户体验带来很大帮助;更不用说快速的网站对 SEO 有好处。
不使用 IE 特定样式表的其他好处
在旧版浏览器中,当它们尝试下载 IE 特定的样式表时,页面渲染不会被阻塞。此外,故障排除更容易。那么我们该使用什么呢?
有几种方法可以通过将所有内容放在一个样式表中来处理 IE:
-
使用 CSS hack(不建议)。
-
使用
Modernizr.js
。 -
在
<html>
标签中使用条件类。
让我们再谈谈一个流行的方法,使用条件类。
在<html>
标签中使用条件类
Paul Irish 在 2008 年的文章(www.paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
)指定了一个我推荐的方法,原因有几个:
-
它很容易实现;只需将此标记块复制并粘贴到我们的 HTML 文件顶部即可。
-
这并不具有侵入性,因为没有任何人需要处理额外的文件(用户、浏览器、服务器和我们)。
-
它不需要 JavaScript 即可工作;如果访问者无法使用或禁用 JavaScript,一切仍将正常工作。
这是我使用的一个:
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
提示
IE10 及以上不再支持条件注释,这就是条件类标记中没有提及 IE10 的原因。
有了前面的条件类,针对特定 IE(此示例中为 IE7)的定位如下:
.ie7 nav li {
float: left;
}
如果我们需要针对所有 IE 进行定位,我们会这样做:
.ie7, .ie8, .ie9 {
nav li {
float: left;
}
}
这编译为以下内容:
.ie7 nav li, .ie8 nav li, .ie9 nav li {
float: left;
}
对于所有其他浏览器,我们会这样做:
nav {
display: flex;
}
无论您使用哪种方法,Modernizr.js
或条件类,都是个人偏好。使用这两种方法中的任何一种都是正确的做法。
记住,无论如何都要避免 CSS hack。作为网页设计师和网页开发人员,我们有责任为每个人创造一个更好的网络。
如何处理高密度屏幕
网络上有很多文章解释了每英寸点数(DPI),每英寸像素(PPI)和密度无关像素(DP/DiP)是什么。虽然了解这些技术和术语的复杂细节可能很重要,但让我们把本书的范围限制在高密度屏幕的基础和我们需要了解的内容上。
位图还是矢量适用于高密度屏幕?
像 SVG、图标字体或常规字体这样的矢量是数学方程的视觉表示,因此无论它们的大小如何,它们永远不会失去质量。
为了使位图图像在高密度屏幕上显示良好,我们必须导出正常质量图像的高分辨率版本。这意味着我们需要为我们计划使用的每个位图图像创建两个文件(或更多):一个用于非高密度屏幕(标准液晶显示器,旧 TFT 监视器,一些电视等)的正常质量图像,以及一个(或更多)用于高密度屏幕(例如任何视网膜设备和 Super AMOLED 显示器)的高质量图像。
这就是良好的设计判断发挥作用的地方,因为有时我们可能并不一定需要每次都导出两个(或更多)位图图像。
当我们必须考虑高密度屏幕时,有几种技术可以用来处理图像。这些技术在《第六章》响应式网页设计中的图像和视频处理中有详细解释。
有时 RWD 并不一定是正确的解决方案
例如,大多数旅行网站的预订部分。这类网站管理的大量信息和类型使得响应式网站变得非常困难。当访问谷歌搜索结果中排名前八的旅行网站时,我看到了以下情况:
-
主页:响应
-
预订页面:不响应
-
主页:响应
-
预订页面:响应
-
主页:不响应
-
预订页面:响应
-
主页:响应
-
预订页面:响应
-
主页:不响应
-
预订页面:不响应
-
主页:不响应
-
预订页面:不响应
-
主页:不响应
-
预订页面:不响应
-
主页:不响应
-
预订页面:不响应
以下是我们的一些发现的简要列表:
-
自从 Expedia 收购了 Travelocity,它们共享相同的平台。区别在于品牌定位;因此,我将考虑这两个网站为一个。
-
七个网站中有五个(71%)的主页不响应。
-
七个网站中有五个(71%)的预订页面不响应。
-
七个网站中只有一个(Expedia/Travelocity)是完全响应的(14%)。
-
七个网站中有四个(57%)根本没有 RWD。
我们可以得出结论,最受欢迎的旅行网站尚未完全采用 RWD,但有些是固定宽度和响应式布局的混合体。这就是为什么所有这些网站都有单独的移动应用程序。对于它们来说,RWD 可能不是优先考虑的,因此它们依赖于它们的移动应用程序来弥补这一不足。
尽管这在今天已经非常罕见,但有时我们可能需要构建一个不响应的网站或页面。实际上,今天有一些页面是不响应的。
CodePen 是最受欢迎的前端沙箱之一,而 CodePen 的编辑器不是响应式的。为什么?因为它不需要。开发人员很少会使用手机去 CodePen 编写 HTML、Sass 和 JavaScript。
话虽如此,如果您需要构建一个不需要响应的站点/页面,就 CSS 网格系统而言,有两个很好的选择:
-
使用我们的老朋友,960 网格系统(
960.gs/
)。 -
使用 1140 网格系统(
www.1140px.com/
)。
有几件事需要考虑:
-
960 网格系统针对 1024px 宽的屏幕。
-
1140 网格系统针对 1280px 宽的屏幕。
-
1140 网格系统默认包含媒体查询,因此我们需要考虑并决定是最好保留它们还是最好删除它们以减小文件大小并减少 IE6-IE9 中的选择器限制。
因为我一直认为 960 网格系统左右各有 10px 的填充使内容离主容器的边缘太近,我在每一侧增加了 10 个像素,将填充增加到 20px,将 960 网格系统变成了 980 网格系统。从现在开始,我们将称其为 980GS。
使用 RWD 改装旧网站
如果有必要,我们需要准备使非响应式或固定宽度的站点/应用程序变得响应式。
有两种改装非响应式或固定宽度的站点/应用程序的方法。一种是使用自适应 Web 设计(AWD)技术,使用绝对单位(即像素)。另一种是使用 RWD,并使用非常简单的公式将所有像素值转换为百分比。
无论我们使用哪种技术,我们都必须使用桌面优先的方法,因为我们处理的站点只适用于宽屏。这意味着我们将在媒体查询中使用max-width
属性。
在我们查看两种改装技术之前,我们需要一个基础页面来开始。
基础页面
您在此处看到的图形与 12 列 980GS 布局成比例。浏览器窗口宽度为 1024px,页面宽度为 980px:
提示
我们灰色的主容器宽度为 980px,左右已经有 10px 的填充。这意味着内部的部分总是需要加起来等于960px。
以下是容器的组件:
-
灰色的主容器宽度为 980px,左右各有 10px 的填充。
-
绿色的头部和红色的页脚分别为 960px 或 12 列宽:940px 带有左右各 10px 的边距。
-
蓝色的导航部分宽度为 240px 或 3 列:220px 带有 10px 的左边距和右边距。
-
黄色的内容部分宽度为 710px 或 9 列:700px 带有 10px 的右边距。
-
白色的间距宽度为 20px,即导航右边距为 10px,内容左边距为 10px。
-
因此,220px 导航 + 710px 内容 + 20px 间距 + 10px 边距 = 960px。
HTML
这是代表我们基础页面的标记:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Retrofitting with Adaptive Web Design</title>
<link href="css/styles.css" rel="stylesheet">
</head>
<body>
<main class="container_12 clear">
<header class="grid_12">Header</header>
<nav class="grid_3">Nav</nav>
<section class="grid_9">Content</section>
<footer class="grid_12">Footer</footer>
</main>
</body>
</html>
CSS/SCSS
关于我们的 CSS/SCSS,我们只需要创建一个部分,即包含固定宽度网格的_980gs.scss
文件。
然后,我们将创建一个styles.scss
文件,我们将执行以下操作:
-
导入
_980gs.scss
文件。 -
包括我们简单的桌面优先 Sass mixin 来处理媒体查询。
-
使用
max-width
属性创建所有必要的媒体查询。 -
将其编译为
styles.css
并在我们的页面中使用。
创建 _980gs.scss 文件
_980gs.scss
文件包含基本网格,如下所示:
//Globals
*, *:before, *:after {
box-sizing: border-box;
}
//Container
.container_12 {
width: 980px;
padding: 0 10px;
margin: auto;
}
//Grid >> Global
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 10px;
}
}
//Grid >> 12 Columns
.container_12 {
.grid_1 { width: 60px; }
.grid_2 { width: 140px; }
.grid_3 { width: 220px; }
.grid_4 { width: 300px; }
.grid_5 { width: 380px; }
.grid_6 { width: 460px; }
.grid_7 { width: 540px; }
.grid_8 { width: 620px; }
.grid_9 { width: 700px; }
.grid_10 { width: 780px; }
.grid_11 { width: 860px; }
.grid_12 { width: 940px; }
}
//Clear Floated Elements - http://davidwalsh.name/css-clear-fix
.clear, .row {
&:before,
&:after { content: ''; display: table; }
&:after { clear: both; }
}
//Use rows to nest containers
.row { margin-bottom: 10px;
&:last-of-type { margin-bottom: 0; }
}
//Legacy IE
.clear { zoom: 1; }
使用 AWD 进行改装
与 RWD 不同,其宽度是流体和弹性的(ems 和百分比),因此术语相对单位,在 AWD 中,宽度是固定的(像素)。因此,我们使用术语绝对单位,当我们调整浏览器窗口大小时,元素将捕捉到这些固定宽度。
在 AWD 中,我们几乎每个宽度都使用像素,甚至我们的媒体查询也是如此。
创建 styles.scss 文件
我们要做的第一件事是在styles.scss
文件中导入部分_980gs.scss
文件:
//Retrofitting with Adaptive Web Design
@import "980gs";
然后,我们将包含我们简单的桌面优先 mixin 来处理媒体查询。然而,请记住我之前提到过这个 mixin 是可扩展的,如果我们想要的话,我们可以使它编译基于像素的值?我们所需要做的就是从除法$media/16+em
中移除值/16+em
:
//Retrofitting with Adaptive Web Design
@import "980gs";
//Desktop-first Media Query Mixin
@mixin forSmallScreens($media) {
@media (max-width: $media) { @content; }
}
以下规则仅用于样式目的,以实现我们在之前截图中看到的相同设计:
//Retrofitting with Adaptive Web Design
@import "980gs";
//Desktop-first Media Query Mixin
@mixin forSmallScreens($media) {
@media (max-width: $media) { @content; }
}
//Basic styling
.container_12 {
background: #aaa;
font-size: 30px;
text-shadow: 0 1px 1px rgba(black,.5);
}
header { background: #429032; }
nav { background: #2963BD; }
section { background: #c90; }
footer { background: #c03; }
//Give heights to elements for better perception of sections
header, footer { height: 150px; }
nav, section { height: 440px; }
此时,我们的页面宽度为 980px,看起来和最初看到的截图一样。
让我们定义基本页面捕捉的宽度:
-
在 980px 时,我们将把页面捕捉到 768px。
-
在 768px 时,我们将把页面捕捉到 640px。
-
在 640px 时,我们将把页面捕捉到 480px。
-
在 480px 时,我们将把页面捕捉到 320px。
这就是乐趣开始的地方。让我们通过为每个部分创建媒体查询来开始改造这个页面。
980px 到 768px(AWD)
以下媒体查询针对 768px:
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
.grid_12 { //Header and Footer sections
@include forSmallScreens(980px) {
width: 728px;
}
}
.grid_3 { //Nav section
@include forSmallScreens(980px) {
width: 200px;
}
}
.grid_9 { //Content section
@include forSmallScreens(980px) {
width: 508px;
}
}
}
诚然,从 980px 到 768px,书中的差异有点难以察觉,但相信我,以下截图完全代表了浏览器窗口宽 980px,页面宽 768px:
正如你所看到的,一旦屏幕宽度达到 980px,我们的主容器(.container_12
)的宽度就从 980px 变为 768px。我们的主容器左右各有 10px 的填充,因此所有其他部分的宽度应该加起来匹配 748px。
让我们来看看。
我们的Header和Footer使用相同的类.grid_12
,现在宽度为 728px。因此,如果我们加上:728px + 10px 左边距 + 10px 右边距 = 748px。
如果我们将Nav(.grid_3
)和Content(.grid_9
)部分的宽度相加:
-
200px Nav + 508px Content = 708px
-
708px + 20px gutter = 728px
-
728px + Nav 的左边距 10px + Content 的右边距 10px = 748px
跟着我,我保证这会非常有趣。
768px 到 640px(AWD)
以下媒体查询针对 640px:
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
.grid_12 { //Header and Footer sections
@include forSmallScreens(980px) {
width: 728px;
}
@include forSmallScreens(768px) {
width: 600px;
}
}
.grid_3 { //Nav section
@include forSmallScreens(980px) {
width: 200px;
}
@include forSmallScreens(768px) {
width: 160px;
}
}
.grid_9 { //Content section
@include forSmallScreens(980px) {
width: 508px;
}
@include forSmallScreens(768px) {
width: 420px;
}
}
}
好的,这个布局现在是单列页面。我们开始看到一些结果了。不错!
再次,请记住,我们的主容器左右各有 10px 的填充,因此所有其他部分的宽度应该加起来匹配 620px。
让我们确保我们的数字加起来:
我们的Header和Footer使用相同的类.grid_12
,现在宽度为 600px。因此,如果我们加上:600px + 10px 左边距 + 10px 右边距 = 620px。
如果我们将Nav(.grid_3
)和Content(.grid_9
)部分的宽度相加:
-
160px Nav + 420px Content = 580px
-
580px + 20px gutter = 600px
-
600px + Nav 的左边距 10px + Content 的右边距 10px = 620px
让我们把这个页面变得更小!
640px 到 480px(AWD)
以下媒体查询针对 480px:
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
@include forSmallScreens(640px) {
width: 480px;
}
.grid_12 { //Header and Footer sections
@include forSmallScreens(980px) {
width: 728px;
}
@include forSmallScreens(768px) {
width: 600px;
}
}
.grid_3 { //Nav section
@include forSmallScreens(980px) {
width: 200px;
}
@include forSmallScreens(768px) {
width: 160px;
}
}
.grid_9 { //Content section
@include forSmallScreens(980px) {
width: 508px;
}
@include forSmallScreens(768px) {
width: 420px;
}
}
.grid_3,
.grid_9,
.grid_12 {
@include forSmallScreens(640px) {
width: 440px;
}
}
}
我们取得了一些应得的进展!在这里,浏览器窗口宽 640px,页面宽 480px:
请记住,我们的主容器左右各有 10px 的填充,因此所有其他部分的宽度应该加起来匹配 460px。
现在,我们将从 2 列布局更改为 1 列布局。这意味着所有部分现在具有完全相同的宽度。
这也意味着在我们的 SCSS 文件中,我们可以为所有三个类创建一个单一的媒体块:
.grid_3,
.grid_9,
.grid_12 {
@include forSmallScreens(640px) {
width: 440px;
}
}
现在,让我们确保我们的数字加起来:
我们的Header,Nav,Content和Footer部分现在宽度为 440px,依次堆叠在一起。因此,如果我们加上:所有部分的 440px + 10px 左边距 + 10px 右边距 = 460px。
我们来了,这个谜题的最后一块!
480px 到 320px(AWD)
以下媒体查询针对 320px:
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
@include forSmallScreens(640px) {
width: 480px;
}
@include forSmallScreens(480px) {
width: 320px;
padding: 0;
}
.grid_12 { //Header and Footer sections
@include forSmallScreens(980px) {
width: 728px;
}
@include forSmallScreens(768px) {
width: 600px;
}
}
.grid_3 { //Nav section
@include forSmallScreens(980px) {
width: 200px;
}
@include forSmallScreens(768px) {
width: 160px;
}
@include forSmallScreens(640px) {
height: 50px; //This is only for styling
}
}
.grid_9 { //Content section
@include forSmallScreens(980px) {
width: 508px;
}
@include forSmallScreens(768px) {
width: 420px;
}
}
.grid_3,.grid_9,.grid_12 {
@include forSmallScreens(640px) {
width: 440px;
}
@include forSmallScreens(480px) {
width: 300px;
}
}
}
我们来了!在这个屏幕截图中,浏览器窗口宽度为 320px,内容也是 320px 宽,非常合适:
我们已经知道我们的主容器左右各有 10 像素的填充。在这种情况下,我们将去掉填充以获得这 20 像素,因为我们的屏幕空间现在非常小:
@include forSmallScreens(480px) {
width: 320px;
padding: 0;
}
左右各 10 像素的间距现在将由其他部分的左右边距创建。这意味着每个部分的宽度应为 300 像素。
添加新的 320 像素断点很容易:
.grid_3,
.grid_9,
.grid_12 {
@include forSmallScreens(640px) {
width: 440px;
}
@include forSmallScreens(480px) {
width: 300px;
}
}
现在,让我们确保我们的数字加起来:
我们的标题、导航、内容和页脚部分现在都是 300 像素宽,依次堆叠在一起。所以如果我们加上:所有部分的 300 像素 + 10 像素左边距 + 10 像素右边距 = 320 像素。
就是这样。我们现在已经使用 AWD 技术将固定宽度页面改为响应式。
最终的 SCSS 如下:
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
@include forSmallScreens(640px) {
width: 480px;
}
@include forSmallScreens(480px) {
width: 320px; padding: 0;
}
.grid_12 { //Header and Footer sections
@include forSmallScreens(980px) {
width: 728px;
}
@include forSmallScreens(768px) {
width: 600px;
}
}
.grid_3 { //Nav section
@include forSmallScreens(980px) {
width: 200px;
}
@include forSmallScreens(768px) {
width: 160px;
}
@include forSmallScreens(640px) {
height: 50px; //This is only for styling
}
}
.grid_9 { //Content section
@include forSmallScreens(980px) {
width: 508px;
}
@include forSmallScreens(768px) {
width: 420px;
}
}
.grid_3, .grid_9, .grid_12 {
@include forSmallScreens(640px) {
width: 440px;
}
@include forSmallScreens(480px) {
width: 300px;
}
}
}
它编译成以下 CSS:
@media (max-width: 980px) {
.container_12 {
width: 768px;
}
}
@media (max-width: 768px) {
.container_12 {
width: 640px;
}
}
@media (max-width: 640px) {
.container_12 {
width: 480px;
}
}
@media (max-width: 480px) {
.container_12 {
width: 320px;
padding: 0;
}
}
@media (max-width: 980px) {
.container_12 .grid_12 {
width: 728px;
}
}
@media (max-width: 768px) {
.container_12 .grid_12 {
width: 600px;
}
}
@media (max-width: 980px) {
.container_12 .grid_3 {
width: 200px;
}
}
@media (max-width: 768px) {
.container_12 .grid_3 {
width: 160px;
}
}
@media (max-width: 640px) {
.container_12 .grid_3 {
height: 50px;
}
}
@media (max-width: 980px) {
.container_12 .grid_9 {
width: 508px;
}
}
@media (max-width: 768px) {
.container_12 .grid_9 {
width: 420px;
}
}
@media (max-width: 640px) {
.container_12 .grid_3, .container_12 .grid_9,.container_12 .grid_12 {
width: 440px;
}
}
@media (max-width: 480px) {
.container_12 .grid_3,.container_12 .grid_9,.container_12 .grid_12 {
width: 300px;
}
}
提示
正如你所看到的,我们的最终 CSS 文件中重复了几个断点。这是 Sass 的一个问题。然而,这实际上并不是一个问题,也不是我们需要担心的事情,因为当服务器对该文件进行 gzip 压缩时,它将以最大限度进行压缩。如果我们最小化最终输出(无论如何我们都应该这样做),我们将进一步压缩文件。重复的@media
断点对性能几乎没有任何影响。
现在,让我们看看使用百分比和 RWD 改装相同页面的样子。
使用 RWD 进行改装
我们刚刚看到了如何使用像素来实现 AWD。通过 RWD 和一个非常简单的方程,我们可以使用相对单位(在我们的情况下是百分比)来改装网站。更不用说这将比使用 AWD 要容易得多。
RWD 的魔法公式
由 Ethan Marcotte 发现/创造,他创造了响应式网页设计这个术语,RWD 的魔法公式是一个非常简单的方程:
(目标 ÷ 上下文)x 100 = 结果 %
在我们开始将像素转换为百分比之前,我们需要看看我们的上下文将是哪个宽度。
主容器
我们的上下文将是页面的主容器.container_12
,最大宽度为 980 像素。然而,主容器和列之间存在一个问题,将这个 980 像素的上下文变成 960 像素。请注意.container_12
部分的左右 10 像素填充和.grid
规则中的左右 10 像素边距:
.container_12 {
width: 980px;
padding: 0 10px;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 10px;
}
}
.grid
规则中的 10 像素左右边距意味着所有列的宽度都增加了 20 像素。所以,例如,宽度为 940 像素的标题和页脚实际上是 960 像素宽。box-sizing: border-box;
属性只考虑减去盒模型内部的内容(填充),而不考虑外部的内容(边距)。
一个解决方案是去掉.container_12
的左右 10 像素填充,并在.grid
规则中增加左右边距为 20 像素,以保持间距;否则,列会相互接触。
现在,间距变得更宽,这可能不是出于设计目的,而且——信不信由你——在最宽的容器中会多出 1 像素。在我们的情况下,它会添加到标题和页脚中。
作为设计师,我知道如果不得不处理这些问题,我是不想要的。
第二种解决方案更简单:将上下文设为 960 像素。这样,我们可以全局去掉多余的 10 像素,而不会影响主容器和列的完整性,由于我们得到的是百分比,所以结果几乎相同。
换句话说:(960 像素 ÷ 980 像素) x 100 = 97.95918367346939% (97.95%)
这实际上等同于:(940 像素 ÷ 960 像素) x 100 = 97.91666666666667% (97.91%)
在第二种解决方案中,1 像素的问题确实会发生,但是在调整浏览器宽度时会在随机宽度时发生。然而,在第一种解决方案中,1 像素的问题是永久性的,无论浏览器的宽度如何。
弄清楚这一点后,我们将把所有基于像素的宽度转换为使用 960 像素作为上下文的百分比。
标题和页脚部分
Header和Footer部分的宽度都是 940px。知道它们的上下文是 960px,让我们继续使用魔术公式找到它们的百分比宽度:(940px ÷ 960px) x 100 = 97.91666666666667%。
你可能会问自己,“这么多小数点有必要吗?”不是所有的小数点,但至少建议使用两位。
所以我们最终得到Header和Footer部分为 97.91%。
一些开发人员建议使用所有小数,并让浏览器决定要使用多少。过去,我决定挑战这个建议,只使用两位小数来看看会发生什么。自从我开始使用两位小数以来,在任何浏览器中都没有遇到任何不良行为或宽度问题。
Firefox 和 IE11 会将多余的小数点截断为两位。另一方面,Chrome 会保留所有小数点。我建议至少使用两位小数,这也是我们在书中使用的方式,以保持简单和简洁。但是,如果您更喜欢使用所有小数点,那就尽管去做!这在这一点上是个人偏好的问题。
提示
避免四舍五入值,并让浏览器处理小数点。这样做也可以让您专注于最重要的事情:高效并尝试为用户创造令人难忘的体验。
Nav 部分
为了找到Nav部分的百分比宽度,我们同样使用 960px 作为上下文:(220px ÷ 960px) x 100 = 22.91666666666667%。
使用两位小数,我们最终得到Nav部分为 22.91%。
Content 部分
为了找出Content部分的百分比宽度,我们的公式看起来几乎一样。唯一的区别是我们正在改变第一个值,即Content部分的宽度(以像素为单位):(700px ÷ 960px) x 100 = 72.91666666666667%。
仅使用两位小数,我们最终得到Content部分为 72.91%。
这就是我们初始的改装 RWD SCSS 文件的样子:
.container_12 {
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
}
.grid_9 { //Content section
width: 72.91%;
}
}
现在,让我们退一步,先处理一些其他基于像素的宽度。还记得主容器.container_12
左右各有 10px 的填充吗?我们也需要将这 10px 转换为百分比。
使用我们的魔术公式,我们这样做:
(10px ÷ 960px) x 100 = 1.041666666666667%。
仅使用两位小数,我们最终得到左右填充为 1.04%。
让我们将这个值添加到我们的 SCSS 中:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.container_12 {
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
}
.grid_9 { //Content section
width: 72.91%;
}
}
此外,我们所有的列左右各有 10px 的边距。由于我们已经知道 10px 等于 1.04%,让我们将这个值添加到我们 SCSS 中的所有列中:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 1.04%;
}
}
.container_12 {
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
}
.grid_9 { //Content section
width: 72.91%;
}
}
现在,我们有一个宽度为 1024px 的浏览器窗口,一个宽度为 980px 的布局,以及所有列都具有相应的百分比值。实际上,这几乎是不可能的,除非查看代码以在固定宽度和基于百分比的布局之间进行视觉区分。
我们做得很好!
让乐趣开始吧。让我们添加我们的第一个媒体查询。
980px 到 768px(RWD)
以下媒体查询针对 768px:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 1.04%;
}
}
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
}
.grid_9 { //Content section
width: 72.91%;
}
}
由于Header、Footer、Nav和Content部分的宽度、填充和边距现在都以百分比设置,我们不必为它们声明任何媒体查询——至少目前还不需要,因为布局还没有改变。
当我们调整浏览器窗口大小时,Header、Footer、Nav和Content部分会自动响应,按比例缩小,正确对齐,并适应主容器.container_12
的新宽度,而不会破坏布局。如下截图所示:
太棒了!
让我们添加另一个断点。
768px 到 640px(RWD)
在以下断点(640px)中,我们的布局将变为单列。因此,我们将添加一个新的媒体查询,使Nav和Content部分与Header和Footer部分一样宽,并使它们堆叠在一起。
以下媒体查询针对 640px,并使Nav和Content部分全宽:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 1.04%;
}
}
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
}
.grid_9 { //Content section
width: 72.91%;
}
.grid_3, .grid_9 { //Nav and Content sections
@include forSmallScreens(640px) {
width: 97.91%;
}
}
}
好的,我们现在有了单列布局。还不错,还不错!
640px 到 480px(RWD)
现在我们的宽度已经缩小到 480px,单列布局不会改变,只有所有容器的宽度会改变。
以下媒体查询针对 480px:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 1.04%;
}
}
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
@include forSmallScreens(640px) {
width: 480px;
}
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
@include forSmallScreens(640px) {
height: 50px; //This is only for styling
}
}
.grid_9 { //Content section
width: 72.91%;
}
.grid_3, .grid_9 { //Nav and Content sections
@include forSmallScreens(640px) {
width: 97.91%;
}
}
}
我们的布局变窄了,我们需要做的就是添加一个新的媒体查询,就这样!不需要在其他容器上瞎折腾;它们都完美地适应了我们定义的任何宽度。
480px 到 320px(RWD)
最后,我们解决了 320px 的宽度,而不修改单列布局。我们去掉了.container_12
上的填充,以利用所有可用的屏幕空间。
以下媒体查询针对 320px:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 1.04%; }
}
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
@include forSmallScreens(640px) {
width: 480px;
}
@include forSmallScreens(480px) {
width: 320px; padding: 0;
}
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
@include forSmallScreens(640px) {
height: 50px; //This is only for styling
}
}
.grid_9 { //Content section
width: 72.91%;
}
.grid_3, .grid_9 {
@include forSmallScreens(640px) {
width: 97.91%;
}
}
}
再次,我们不必添加任何内容到Header、Footer、Nav和Content部分,因为它们现在都是 97.91%宽。这使它们具有响应性,我们不必担心其他任何事情。
最终的 SCSS,结合所有断点和宽度,如下所示:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid {
&_1, &_2, &_3, &_4, &_5, &_6, &_7, &_8, &_9, &_10, &_11, &_12 {
float: left;
margin: 0 1.04%;
}
}
.container_12 {
@include forSmallScreens(980px) {
width: 768px;
}
@include forSmallScreens(768px) {
width: 640px;
}
@include forSmallScreens(640px) {
width: 480px;
}
@include forSmallScreens(480px) {
width: 320px; padding: 0;
}
.grid_12 { //Header and Footer sections
width: 97.91%;
}
.grid_3 { //Nav section
width: 22.91%;
}
.grid_9 { //Content section
width: 72.91%;
}
.grid_3, .grid_9 { //Nav and Content sections
@include forSmallScreens(640px) {
width: 97.91%;
}
}
}
它编译成以下 CSS:
.container_12 {
width: 980px;
padding: 0 1.04%;
margin: auto;
}
.grid_1, .grid_2, .grid_3, .grid_4, .grid_5, .grid_6, .grid_7, .grid_8, .grid_9, .grid_10, .grid_11, .grid_12 {
float: left;
margin: 0 1.04%;
}
@media (max-width: 980px) {
.container_12 {
width: 768px;
}
}
@media (max-width: 768px) {
.container_12 {
width: 640px;
}
}
@media (max-width: 640px) {
.container_12 {
width: 480px;
}
}
@media (max-width: 480px) {
.container_12 {
width: 320px;
padding: 0;
}
}
.container_12 .grid_12 {
width: 97.91%;
}
.container_12 .grid_3 {
width: 22.91%;
}
.container_12 .grid_9 {
width: 72.91%;
}
@media (max-width: 640px) {
.container_12 .grid_3, .container_12 .grid_9 {
width: 97.91%;
}
}
正如你所看到的,使用 RWD 比 AWD 来改造站点的代码要少得多。当然,这些例子是对站点/应用布局的极端简化,但现在你已经了解了在使用 AWD 或 RWD 时做出决定的基本概念。
总结
在本章中,我们讨论了很多有趣的内容。我们看到,使用桌面优先来创建设计和线框图是有益的,因为拥有一个大画布可以让我们探索不同的布局,并正确安排内容的层次结构。
在创建 HTML 模型时,使用移动优先更好,因为移动友好的站点将具有更广泛的覆盖范围,允许专注的内容,并利用移动设备的技术。
我们能够使用魔术公式对固定宽度的站点进行 AWD 和 RWD 的改造。我们还讨论了 RWD 的好处,因为它需要的代码要少得多。然而,对旅行网站的分析清楚地告诉我们,RWD 有时并不是正确的解决方案。
我们还看到了Respond.js
如何用于使旧版浏览器支持媒体查询,如果我们采用移动优先的方法构建。使用条件类是一种很好的技术,因为它不会侵入,很容易实现,并且没有 JavaScript 依赖。
在下一章中,我们将讨论 RWD 世界中一些最有趣的主题:CSS 网格、CSS 框架和 Flexbox 的强大。
让我们开始吧!
第四章:CSS 网格、CSS 框架、UI 工具包和 Flexbox 用于 RWD
响应式网页设计 (RWD)为所有构建响应式网站和应用程序的人引入了一层新的工作。当我们必须在不同设备和不同尺寸上测试我们的工作时,无论内容在哪里中断,我们都需要添加一个断点并重新测试。
这可能会发生很多次。因此,构建网站或应用程序将比以前花费更多的时间。
为了使事情更有趣,作为网页设计师和开发人员,我们需要注意内容在不同尺寸上的布局以及网格如何帮助我们将内容结构化到不同的布局中。
既然我们提到了网格,你有没有问过自己,“我们到底用网格做什么?”
借用设计行业的一些术语来回答这个问题,我们使用网格来让内容具有节奏、比例和平衡。目标是让使用我们网站/应用的人对我们的内容有更愉快的体验,因为它将更容易扫描(节奏)、更容易阅读(比例)和有组织(平衡)。
为了加快设计和构建过程,同时保持所有内容在不同尺寸下正确格式化,许多作者和公司创建了包含网格以及许多其他功能和样式的 CSS 框架和 CSS 网格,可以通过使用简单的类名来利用。
随着时间的推移,浏览器开始支持越来越多的 CSS3 属性,比如 Flexbox,使用布局将变得更加容易。这将使 CSS 框架中的网格几乎变得不必要。
让我们看看 CSS 网格、CSS 框架、UI 工具包和 Flexbox 是什么,以及它们如何帮助我们实现 RWD。
在本章中,我们将涵盖以下主题:
-
什么是网格?
-
CSS 网格
-
CSS 网格在 RWD 中的优缺点
-
CSS 框架
-
UI 工具包
-
CSS 框架在 RWD 中的优缺点
-
创建自定义 CSS 网格
-
使用自定义 CSS 网格构建示例页面
-
使用 Flexbox
-
使用 Flexbox 构建示例页面
什么是网格?
网格是一组视觉指南(垂直、水平或两者兼有,因此称为网格),它们有助于定义元素的放置位置。一旦元素被放置,我们就得到了一个布局。
使用网格的好处是放置在上面的元素将在页面上具有和谐的流动,增强用户体验,提高可读性、布局一致性和元素之间的良好比例。
CSS 网格
CSS 网格基本上是由形成列的垂直指南的组合。这些列的属性在 CSS 文件中定义。该文件包含一个具有特定宽度的类列表,与特定网格构建的列数相匹配。
我们在第三章中已经见过了,当时我们使用980 Grid System (980GS)来改造一个旧的固定宽度站点。这是 SCSS 文件:
*, *:before, *:after {
box-sizing: border-box;
}
//Container
.container-12 {
width: 980px;
padding: 0 10px;
margin: auto;
}
//Grid >> Global
.grid {
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
float: left;
margin: 0 10px;
}
}
//Grid >> 12 Columns
.container-12 {
.grid-1 { width: 60px; }
.grid-2 { width: 140px; }
.grid-3 { width: 220px; }
.grid-4 { width: 300px; }
.grid-5 { width: 380px; }
.grid-6 { width: 460px; }
.grid-7 { width: 540px; }
.grid-8 { width: 620px; }
.grid-9 { width: 700px; }
.grid-10 { width: 780px; }
.grid-11 { width: 860px; }
.grid-12 { width: 940px; }
}
//Clear Floated Elements - http://davidwalsh.name/css-clear-fix
.clear, .row {
&:before,
&:after { content: ''; display: table; }
&:after { clear: both; }
}
//Use rows to nest containers
.row { margin-bottom: 10px;
&:last-of-type { margin-bottom: 0; }
}
//Legacy IE
.clear { zoom: 1; }
提示
记住,我们将 960GS 变成了 980GS,因为内容看起来离主容器的边缘太近,主容器的左右各有 10px 的间距。因此,我们在每一侧都添加了 10px,并将主容器的宽度设为 980px。
因为我们正在使用 HTML5 和 CSS3 掌握 RWD,让我们看看相同的 980GS,使用百分比使其流动起来。
RWD 的魔法公式是(目标 ÷ 上下文) x 100 = 结果 %。
在这种情况下,我们的上下文是 980px,如下所示:
//Container
.container-12 {
width: 100%;
max-width: 980px;
padding: 0 1.02%;
margin: auto;
}
//Grid >> Global
.grid {
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
float: left;
margin: 0 1.02%;
}
}
//Grid >> 12 Columns
.container-12 {
.grid-1 { width: 6.12%; }
.grid-2 { width: 14.29%; }
.grid-3 { width: 22.45%; }
.grid-4 { width: 30.61%; }
.grid-5 { width: 38.78%; }
.grid-6 { width: 46.94%; }
.grid-7 { width: 55.10%; }
.grid-8 { width: 63.27%; }
.grid-9 { width: 71.43%; }
.grid-10 { width: 79.59%; }
.grid-11 { width: 87.76%; }
.grid-12 { width: 95.92%; }
}
//Clear Floated Elements - http://davidwalsh.name/css-clear-fix
.clear, .row {
&:before,
&:after { content: ''; display: table; }
&:after { clear: both; }
}
//Use rows to nest containers
.row { margin-bottom: 10px;
&:last-of-type { margin-bottom: 0; }
}
//Legacy IE
.clear { zoom: 1; }
在网页设计中,网格通常由 12 或 16 列组成。960GS 几乎是最著名的之一,尽管它一直是一个固定宽度的网格。但其他作者已经将其移植成流动的,比如Fluid 960 Grid System,但不是响应式的。960GS 还有 24 列的选项,但不像 12 列版本那么受欢迎。
还有其他用于网页设计的网格,它们没有定义的框架宽度或列数,而是可以有无限数量的列,比如基于自适应 Web 设计(AWD)的无框网格。这意味着主容器的宽度捕捉到由它容纳的列数计算出的特定断点。
CSS 网格用于 RWD 的优缺点
列出 CSS 网格用于 RWD 的优缺点的想法是,当我们计划使用某种类型的网格时,我们应该能够做出最明智的决定。这有助于澄清客户的期望和我们自己的期望,因为使用某种网格将影响时间表、设计、布局和许多 UX 因素。
优点如下:
-
布局元素变得更容易,因为列作为放置的指南。
-
如果使用预先构建的 CSS 网格,则无需进行任何数学计算来处理列和间距宽度。这已经由网格的作者处理了。
-
我们可以更快地构建,因为我们只需要在我们的 HTML 容器中添加特定的类,而大部分布局将立即发生。
-
了解网页设计中的网格相对简单,因此在已建立的项目中增强/编辑其他人的标记和代码比如果根本没有使用 CSS 网格要少痛苦。
-
如果网格是响应式或自适应的,我们就不必太担心断点。
-
如果我们使用第三方 CSS 网格,任何跨浏览器问题都已经得到解决。
缺点如下:
-
一些 CSS 网格的学习曲线比其他的陡峭。
-
对于许多 CSS 网格,我们被锁定在作者创建的命名约定中。
-
我们可能需要改变/调整我们编写 HTML 的方式。
-
有太多 CSS 网格可供选择,对一些人来说可能会感到不知所措。
-
如果我们的内容在网格不支持的某些点上中断,我们必须花时间修改原始网格以适应每种情况。
CSS 框架
CSS 框架是一组预构建功能,基本上帮助加快 Web 前端开发。这些 CSS 框架的作者已经处理了许多重要但细微的细节,因此决定使用它们的人可以专注于手头的任务,同时将许多决定留给 CSS 框架本身。
许多开发人员和设计师相信(我也是)任何 CSS 框架的真正价值在于它们的 CSS 网格,有时我们会不遗余力地提取 CSS 网格并自定义它以满足我们的需求。
在本书中,我们将专注于 CSS 网格来掌握 RWD,而不是从 CSS 框架或 UI 工具包中剥离一个(如果它确实提供一个)。我们很快就会谈到这一点。
以下列表描述了一些 CSS 框架的特点和特征:
-
CSS 框架专注于基于 Web 的开发,而不是原生移动应用程序。
-
CSS 框架总是提供 CSS 网格。
-
许多 UI 工具包还提供用户界面组件(就像 UI 工具包一样),例如滑块、分页、导航栏、排版、按钮等,以 HTML 和 CSS 的形式。
-
CSS 框架和面向 Web 的 UI 工具包都可以称为前端框架。
UI 工具包
与 CSS 框架类似,还有另一种称为 UI 工具包的前端框架。然而,UI 工具包可以是一种独特的类型。
说实话,有时很难区分 CSS 框架和 UI 工具包。但不要过多地深究哪一个是哪一个,重要的是要理解我们首先为什么使用它们以及它们如何帮助我们构建更好、更快速的响应式网站和应用程序。
以下列表描述了一些 UI 工具包的特点和特征:
-
基本上有两种类型的 UI 工具包:一种是使用 Web 技术(HTML 和 CSS)构建的,可以用来原型化基于 Web 的应用程序,另一种是由(通常是)Photoshop(PSD)文件制作的,用来帮助设计本机移动应用程序的模拟和设计。
-
很少有面向网络的 UI 工具包提供某种网格。
-
UI 工具包专注于提供用户界面组件,如滑块、分页、导航栏、对话框、覆盖/模态、按钮、排版、工具提示、列表、手风琴、选项卡系统、旋转木马/幻灯片、表单等。
-
在面向网络的 UI 工具包中,架构非常模块化。这意味着每个组件都可以并入任何 CSS 框架。
RWD 的 CSS 框架的优缺点
以 RWD 作为我们在布局与屏幕房地产方面做出的任何决定的主要驱动力,让我们来看看 CSS 框架的优点和不足之处:
优点如下:
-
它们非常有用,可以快速构建响应式原型,而不是显示静态线框。
-
跨浏览器问题已经得到解决。
-
它们以一种好的方式,迫使你创建基于网格的布局。
-
它们为构建提供了一个坚实的起点。
-
模块化允许你手动选择你想要的组件。例如,你可以只使用 CSS 网格模块,或者你可以使用
forms
模块。 -
更改样式以适应你的设计相对容易。
-
如果你对 CSS 不太擅长,你仍然可以使用 CSS 框架来实现自己的设计。
缺点如下:
-
它们可能会用你永远不会使用的 CSS 来膨胀你的项目。
-
如果你决定使用整个 CSS 框架,它们的占用空间很大。
-
你可能需要改变你的习惯和编写 HTML 和 CSS 的方式,以适应你正在使用的 CSS 框架。
-
它们可能会持有自己的观点,所以如果你不喜欢事物的命名方式,你几乎没有选择自定义。
-
定制 CSS 框架是可行的,但可能非常耗时和危险。将一个名称更改为其他名称,几乎没有办法知道对框架的其他部分会产生什么影响。
-
如果默认样式没有改变以适应你的品牌/设计,你的网站或应用将不会是独特的,看起来会像其他人的,失去了用户的信任。
-
如果需要构建简单的东西,使用 CSS 框架就太过了。
-
每个网站/应用程序或项目都是不同的,所以你可能最终会花费大量时间为每个项目更改和覆盖属性。
-
他们试图解决每一个前端问题。
现在我们已经看到了 CSS 网格、CSS 框架和 UI 工具包的优缺点,是时候做出决定并回答这个问题了:哪种方法对 RWD 最好?
答案并不是最令人鼓舞的,我承认,但这是事实:这取决于情况。
如果我们是自由职业者,自己做所有事情,或者在一个非常小的团队中工作,也许根本不需要使用任何框架。我们可以根据主要框架建立的原则自定义构建一些东西。显然,我们希望自动化任何重复的过程,以便高效利用我们的时间。
但如果我们在一个庞大的团队中工作,一个由内部和离岸资源组成的网络专业人士的大熔炉,也许使用框架会有所帮助。这是因为每个人都需要遵守框架的结构,以确保一切都是一致的。
创建自定义 CSS 网格
由于我们正在掌握 RWD,我们有奢侈的创造我们自己的 CSS 网格。然而,我们需要聪明地工作,而不是努力地工作。所以我们要做的是利用可变网格系统应用程序,并将其结果与我们自己的方法相结合,制作一个移动优先、流动、自定义构建和坚实的 CSS 网格,从中我们可以创建强大的响应式设计。
让我们列出我们的 CSS 网格需求:
-
它应该有 12 列。
-
它应该是 1200 像素宽,以适应 1280 像素的屏幕。
-
它应该是流体的,使用相对单位(百分比)来定义列和间距。
-
它应该使用移动优先方法。
-
它应该使用 SCSS 语法。
-
它应该可以重复使用在其他项目中。
-
它应该简单易懂。
-
它应该很容易扩展。
这就是我们的 1200 像素宽和 12 列宽 20px 的网格的样子:
左右两侧的填充都是 10px。我们将在此过程结束时将这 10px 转换为百分比。
进行数学计算
我们将使用 RWD 魔法公式:(目标 ÷ 上下文) x 100 = 结果 %。
我们的上下文将是 1200px。所以让我们转换一个列:80 ÷ 1200 x 100 = 6.67%。
对于两列,我们必须考虑 20px 的间距。换句话说,我们不能说两列确切地是 160px。这并不完全正确。
两列分别是:80px + 20px + 80px = 180px。
现在让我们转换两列:180 ÷ 1200 x 100 = 15%。
对于三列,现在我们必须考虑两个间距:80px + 20px + 80px + 20px + 80px = 280px。
现在让我们转换三列:280 ÷ 1200 x 100 = 23.33%。
现在你能看到模式了吗?每次我们添加一列,我们只需要将值增加 100。这个值也包括了间距!
检查我们刚才看到的网格的屏幕截图,你可以看到列的值递增 100。
所以,所有的方程式如下:
1 column: 80 ÷ 1200 x 100 = 6.67%
2 columns: 180 ÷ 1200 x 100 = 15%
3 columns: 280 ÷ 1200 x 100 = 23.33%
4 columns: 380 ÷ 1200 x 100 = 31.67%
5 columns: 480 ÷ 1200 x 100 = 40%
6 columns: 580 ÷ 1200 x 100 = 48.33%
7 columns: 680 ÷ 1200 x 100 = 56.67%
8 columns: 780 ÷ 1200 x 100 = 65%
9 columns: 880 ÷ 1200 x 100 = 73.33%
10 columns: 980 ÷ 1200 x 100 = 81.67%
11 columns: 1080 ÷ 1200 x 100 = 90%
12 columns: 1180 ÷ 1200 x 100 = 98.33%
让我们为 12 列网格创建 SCSS:
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
提示
使用连字符(-
)来分隔单词可以更容易地选择代码编辑时的术语。
添加 UTF-8 字符集指令和 Credits 部分
不要忘记在文件顶部包含 UTF-8 编码指令,让浏览器知道我们正在使用的字符集。让我们通过在顶部添加一个 Credits 部分来装饰我们的代码。代码如下:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
提示
注意 Credits 是用 CSS 样式注释注释的:/* */
。这种类型的注释,取决于我们如何编译我们的 SCSS 文件,不会被剥离。这样,Credits 总是可见的,这样其他人就知道谁编写了文件。这对团队可能有用,也可能没有。此外,显示 Credits 对文件大小的影响是微不可见的。
包括 box-sizing 属性和移动优先 mixin
包括box-sizing
属性允许浏览器的盒模型考虑容器内的填充;这意味着填充被减去而不是添加,从而保持了定义的宽度。
由于我们的自定义 CSS 网格的结构将是移动优先的,我们需要包括处理这一方面的 mixin:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
主容器和将 10px 转换为百分比值
由于我们使用移动优先方法,我们的主容器默认情况下将是 100%宽;但我们还将给它一个最大宽度为 1200px,因为要求是创建这样大小的网格。
我们还将把 10px 转换为百分比值,所以使用 RWD 魔法公式:10 ÷ 1200 x 100 = 0.83%。
然而,正如我们之前看到的,10px,或者在这种情况下 0.83%,不足够的填充会使内容看起来离主容器的边缘太近。所以我们将填充增加到 20px:20 ÷ 1200 x 100 = 1.67%。
我们还将使用margin: auto;
来水平居中主容器。
提示
没有必要声明零值来使顶部和底部边距水平居中。换句话说,margin: 0 auto;
是不必要的。只需声明margin: auto;
就足够了。
现在让我们包括这些值:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Main Container
.container-12 {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else.
max-width: 1200px;
padding: 0 1.67%;
margin: auto;
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
提示
在padding
属性中,如果我们输入0.83%
或.83%
都是一样的。我们可以省略零。保持我们的代码尽可能简洁是一种良好的实践。这与当我们使用十六进制简写值时的原理相同:#3336699
和#369
是一样的。
使其移动优先
在小屏幕上,所有列都将是 100%宽。由于我们使用的是单列布局,我们不使用间距;这意味着我们至少现在不必声明边距。
在 640px 处,网格将启动并为每个列分配相应的百分比,因此我们将在40em
(640px)媒体查询中包含列并将它们浮动到左侧。在这一点上,我们需要间距。因此,我们声明左右填充为.83%
。
提示
我任意选择了40em
(640px)作为起点。记住要创建基于内容而不是设备的断点。
代码如下:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Main Container
.container-12 {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else.
max-width: 1200px;
padding: 0 1.67%;
margin: auto;
}
//Grid
.grid {
//Global Properties - Mobile-first
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
width: 100%;
}
@include forLargeScreens(640) { //Totally arbitrary width, it's only a starting point.
//Global Properties - Large screens
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
float: left;
margin: 0 .83%;
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
}
添加行和浮动清除规则
如果我们在 HTML 结构中使用行或向标签添加.clear
类,我们可以在单个嵌套规则中使用:before
和:after
伪元素声明所有的浮动清除值。
提示
在声明伪元素时,使用单冒号或双冒号是一样的。双冒号是 CSS3 语法,单冒号是 CSS2.1 语法。这个想法是为了能够一眼区分它们,以便开发人员可以知道它们是在哪个 CSS 版本上编写的。然而,IE8 及以下版本不支持双冒号语法。
浮动清除技术是对 David Walsh 的 CSS 片段的改编(davidwalsh.name/css-clear-fix
)。
我们还为行添加了一个底部间距为 10px 的规则,以便将它们彼此分开,同时从最后一行中去除该间距,以避免在底部创建不必要的额外间距。最后,我们为旧版 IE 添加了清除规则。
现在让我们包括这些规则:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Main Container
.container-12 {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else.
max-width: 1200px;
padding: 0 1.67%;
margin: auto;
}
//Grid
.grid {
//Global Properties - Mobile-first
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
width: 100%;
}
@include forLargeScreens(640) { //Totally arbitrary width, it's only a starting point.
//Global Properties - Large screens
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
float: left;
margin: 0 .83%;
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
}
//Clear Floated Elements - http://davidwalsh.name/css-clear-fix
.clear, .row {
&:before,
&:after { content: ''; display: table; }
&:after { clear: both; }
}
//Use rows to nest containers
.row { margin-bottom: 10px;
&:last-of-type { margin-bottom: 0; }
}
//Legacy IE
.clear { zoom: 1; }
让我们回顾一下我们的 CSS 网格要求:
-
12 列:从
.grid-1
到.grid-12
。 -
为了适应 1280px 屏幕而设置为 1200px 宽:
.container-12
容器的max-width: 1200px;
-
流动和相对单位(百分比)用于列和间距:百分比从 6.67%到 98.33%。
-
移动优先:我们添加了移动优先的 mixin(使用
min-width
)并将网格嵌套其中。 -
SCSS 语法:整个文件都是基于 Sass 的。
-
可重用:只要我们使用 12 列并且使用移动优先的方法,我们可以多次使用这个 CSS 网格。
-
简单易用和理解:类名非常直观。
.grid-6
网格用于跨越 6 列的元素,.grid-7
用于跨越 7 列的元素,依此类推。 -
易于扩展:如果我们想使用 980px 而不是 1200px,我们只需要改变
.container-12 max-width
属性中的值。由于所有元素都使用相对单位(百分比),一切都会按比例适应新的宽度 - 无论是任何宽度。如果你问我,这真是太棒了。
使用自定义 CSS 网格构建示例页面
这是我们在这个例子中将要使用的 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mastering RWD with HTML5 & CSS3</title>
<link rel="stylesheet" href="css/site-styles.css">
<!--[if lt IE 9]>
<script src="img/html5.js">
</script>
<![endif]-->
</head>
<body>
<h1>Basic Layout Using a Custom CSS Grid</h1>
<main class="container-12 clear" role="main">
<header class="grid-12" role="banner">Header (.grid-12)</header>
<nav class="grid-4" role="navigation">Nav (.grid-4)</nav>
<section class="grid-8">
<div class="row">
<div class="grid-6 black">.grid-6</div>
<div class="grid-6 black">.grid-6</div>
</div>
<div class="row">
<div class="grid-4 black">.grid-4</div>
<div class="grid-4 black">.grid-4</div>
<div class="grid-4 black">.grid-4</div>
</div>
<div class="row">
<div class="grid-3 black">.grid-3</div>
<div class="grid-3 black">.grid-3</div>
<div class="grid-3 black">.grid-3</div>
<div class="grid-3 black">.grid-3</div>
</div>
<div class="row">
<div class="grid-2 black">.grid-2</div>
<div class="grid-7 black">.grid-7</div>
<div class="grid-3 black">.grid-3</div>
</div>
<p>Content (.grid-8)</p>
</section>
<footer class="grid-12" role="contentinfo">Footer (.grid-12)</footer>
</main>
</body>
嵌套容器
请注意,有几个嵌套容器在它们自己的行内(黑色背景)。这里的想法是突出显示添加到 12 列的嵌套内容部分。
嵌套列是任何网格系统的主要优势。在这本书中,我们正在利用这种力量,以便不会以任何方式限制设计。
提示
我们使用 HTML5 Shiv polyfill 为 IE8 及以下版本添加 HTML5 支持。
在小屏幕上(320px 宽),容器如下所示:
在宽度为 40em(640px)及以上的大屏幕上,布局如下:
您可以在 CodePen 上看到我创建的演示codepen.io/ricardozea/pen/d6ab6e0293be9b6bac2e16ad37942ed5
。
停止使用 CSS 网格,使用 Flexbox!
我打赌你没有看到这一点,哈!
事实上,Flexbox 是一种令人惊奇的 CSS 属性,它为布局提供了新的可能性。以下是关于 Flexbox 的一些事情:
-
它在现代浏览器中的浏览器支持是完美的。
-
IE8 和 IE9 不支持它。但不用担心,使用条件类技术来解决这两个浏览器非常简单,如第三章中提到的,Mobile-first or Desktop-first?
-
IE10 仅支持 2012 语法,但 Autoprefixer(在 Prepros 中)会自动为我们处理这些旧的供应商前缀。
-
在使用 Flexbox 时,我们需要小心,因为旧的
display: box;
语法会导致浏览器在布局中进行多次传递,从而降低性能。 -
相比之下,新/当前的语法
display: flex
;对性能没有任何影响。自从旧语法以来,浏览器性能问题现在已得到解决,所以我们应该没问题。
提示
Paul Irish 和 Ojan Vafai 在文章Flexbox layout isn't slow中对此进行了很好的解释,该文章可以在updates.html5rocks.com/2013/10/Flexbox-layout-isn-t-slow
找到。
让我们开始吧,好吗?
使用 Flexbox 构建示例页面
在下面的示例中,我们将使用 Flexbox 属性构建与使用自定义 CSS 网格构建的相同布局。这将帮助我们更好地理解 Flexbox 的强大之处,并最终摆脱完全使用 CSS 网格,同时在我们的 HTML 中保持更语义化的结构。
提示
Chris Coyer 的一篇很棒的文章A Complete Guide to Flexbox可以在css-tricks.com/snippets/css/a-guide-to-flexbox/
找到。
关于示例页面的一些注意事项:
-
我们在
<html>
元素中包含条件类,以支持旧版浏览器,并避免使用 JavaScript 文件依赖项从服务器请求。 -
由于我们不使用 CSS 网格,嵌套容器只会在其中显示术语Content。
-
我们将使用 HTML5 Shiv polyfill 来支持 IE8 对所有必要的 HTML5 标签。
-
由于 IE10 在 Flexbox 中存在一些数学计算问题,我们需要通过在
<html>
元素中添加.ie10
类来定位它。我们将使用 Louis Lazaris 创建的一个简单脚本来实现这一点,该脚本位于 IE 排除条件注释中,以便 IE8/9 不运行该脚本。有关此脚本的所有信息可以在文章中找到:www.impressivewebs.com/ie10-css-hacks/
。
提示
我们用于定位 IE10 的脚本不使用用户代理嗅探。UA 嗅探不被认为是一个好的做法。该脚本使用条件编译语句。有关@cc_on
语句的更多信息可以在Microsoft Developer Network (MSDN)找到:msdn.microsoft.com/en-us/library/8ka90k2e(v=vs.94).aspx
。
这是小屏幕(320px 宽)上 Flexbox 布局的样子:
这是大屏幕上的样子。这个屏幕宽度为 768px,但内容为40em
(640px):
HTML
这是我们将在示例页面中使用的标记:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Basic Layout Using Flexbox</title>
<!--[if lt IE 9]>
<script src="img/html5.js">
</script>
<![endif]-->
<!--[if !IE]><!-->
<script>
if (/*@cc_on!@*/false && document.documentMode === 10) {
document.documentElement.className+=' ie10';
}
</script>
<!--<![endif]-->
</head>
<body>
<h1>Basic Layout Using Flexbox</h1>
<main class="main-container" role="main">
<header role="banner">Header</header>
<!-- Flexible elements need to be wrapped in a container -->
<div class="flex-container">
<nav role="navigation">Nav</nav>
<section>
<div class="flex-container row-1">
<div class="level-1">content</div>
<div class="level-1">content</div>
</div>
<div class="flex-container row-2">
<div class="level-1">content</div>
<div class="level-1">content</div>
<div class="level-1">content</div>
</div>
<div class="flex-container row-3">
<div class="level-1">content</div>
<div class="level-1">content</div>
<div class="level-1">content</div>
<div class="level-1">content</div>
</div>
<div class="flex-container row-4">
<div class="level-1 content-a">content</div>
<div class="level-1 content-b">">content</div>
<div class="level-1 content-c">content</div>
</div>
<p>Content</p>
</section>
</div>
<footer role="contentinfo">Footer</footer>
</main>
</body>
</html>
SCSS
SCSS 代码有几个部分与 CSS 网格中使用的代码类似。但是,有重要的区别。
让我们来分析一下。
我们将从创建 Credits 部分开始,box-sizing: border-box;
参数用于考虑容器内部而不是外部的填充,首先是移动优先的 mixin 和主容器属性:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
//Main container
.main-container {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
添加 Flexbox 容器
现在,让我们为 Flexbox 容器添加属性,该容器在某种程度上类似于 CSS 网格中的.row
。代码如下:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
//Main container
.main-container {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
正如你所看到的,我们添加了margin-bottom: 10px;
来分隔内容行。然而,我们在最后一个 Flexbox 容器上移除了该边距,以防止在末尾产生不必要的额外填充。
然后,我们将包含针对 640px(40em
)屏幕宽度的移动优先 mixin。这意味着我们只会在大屏幕上使用 Flexbox,但在小屏幕上,我们不会使用它。
提示
如果所有列的宽度相等,则无需使用 Flexbox。在我们的示例中,小屏幕上的列宽度为 100%。
Flexbox 容器内的 DIV
现在,让我们在大屏幕上为列添加.83%
的左右边距。在小屏幕上,列没有边距。记住10px = 0.83%。
我们将使用带有星号/星号的属性选择器,以便可以针对所有包含类名中至少一个值为level-
的 DIV 进行定位。我们还将删除第一个容器的左边距和最后一个容器的右边距,以便我们的 DIV 与其父容器的边缘对齐。代码如下:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
//Main container
.main-container {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
//DIVs inside the flex container
[class*="level-"] {
width: 100%;
@include forLargeScreens(640) {
margin: 0 .83%;
}
&:first-of-type { margin-left: 0; }
&:last-of-type { margin-right: 0; }
}
标题、页脚、导航和部分容器
现在,标题和页脚部分在小屏幕和大屏幕上都是 100%宽,因此它们不需要任何特定的规则。然而,这个示例为标题和页脚部分添加了一些属性,但只是出于样式原因,而不是布局原因。尽管如此,导航和部分容器确实根据可用屏幕宽度具有特定的宽度。
在小屏幕上,导航和部分容器的宽度为 100%,而在大屏幕上它们并排显示;导航容器在大屏幕上宽度为 33%,右边距为 1.67%(相当于 20px)以创建间距。部分容器在大屏幕上宽度为 65.33%。这里是公式:33% + 1.67% + 65.33 = 100%。
让我们继续为导航和部分容器定义这些属性:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
//Main container
.main-container {
width: 100%;
//Change this value to ANYTHING you want, no need to edit anything else
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
//DIVs inside the flex container
[class*="level-"] {
width: 100%;
@include forLargeScreens(640) {
margin: 0 .83%;
}
&:first-of-type { margin-left: 0; }
&:last-of-type { margin-right: 0; }
}
//Nav
nav {
width: 100%;
@include forLargeScreens(640) {
width: 33%;
margin-right: 1.67%;
}
}
//Content area
section {
width: 100%;
@include forLargeScreens(640) {
width: 65.33%;
}
}
嵌套容器
最后,对于这个示例,我们将为具有黑色背景的不同内容部分定义宽度,这样你就可以清楚地了解如何嵌套容器。
基本上,我们正在为该行的第一个和第三个内容区域.content-a
和.content-c
分配特定但不同的宽度。除非我们想要,否则不需要为第二个内容区域分配宽度。Flexbox 将使第二个容器完全占据第一个和第三个内容区域之间的所有剩余空间。
提示
IE10 在计算嵌套容器值时存在问题,因此我们需要为这些容器创建特定的宽度。我们将在为 IE8 和 IE9 创建的同一规则中包含 IE10 的宽度。
我使用任意值如 30%和 42%是为了向你展示,我们可以随意调整这些值,而 Flexbox 会尽量保持这些比例,只要有空间可用。
现在让我们为不同的嵌套容器添加这些属性:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
.main-container {
//Change this value to ANYTHING you want, no need to edit anything else.
width: 100%;
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
//DIVs inside the flex container
[class*="level-"] {
width: 100%;
@include forLargeScreens(640) {
margin: 0 .83%;
}
&:first-of-type { margin-left: 0; }
&:last-of-type { margin-right: 0; }
}
//Nav
nav {
width: 100%;
@include forLargeScreens(640) {
width: 33%;
margin-right: 1.67%;
}
}
//Content area
section {
width: 100%;
@include forLargeScreens(640) {
width: 65.33%;
}
}
//Different width containers
.content- {
@include forLargeScreens(640) {
&a { width: 30%; }
&c { width: 42%; }
}
}
支持旧版 IE
使用 Flexbox 也会带来与 IE8、IE9 和 IE10 相关的注意事项。
与传统浏览器一样,调整数值并进行测试是获得最佳结果的关键。记住,网站在每个浏览器中不必看起来完全相同。
让我们澄清一些事情。类.ie8
和.ie9
来自<html>
元素中的条件类。类.ie10
来自 IE 排除条件注释中的脚本。因此,IE8 和 IE9 无法运行此脚本。但不用担心,解决方案很简单,你会看到的。让我们来看看它们。
一条规则支配所有规则
我们首先要做的是为 IE8、IE9 和 IE10 创建一个规则。在这个规则中,我们将以百分比声明嵌套容器的宽度。事实上,我们也可以用像素声明这些宽度,但出于一致性的原因,我们将使用百分比,与所有其他响应式示例保持一致。
这就是那条规则……好吧,支配它们所有:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
.main-container {
//Change this value to ANYTHING you want, no need to edit anything else.
width: 100%;
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
//DIVs inside the flex container
[class*="level-"] {
width: 100%;
@include forLargeScreens(640) {
margin: 0 .83%;
}
&:first-of-type { margin-left: 0; }
&:last-of-type { margin-right: 0; }
}
//Nav
nav {
width: 100%;
@include forLargeScreens(640) {
width: 33%;
margin-right: 1.67%;
}
}
//Content area
section {
width: 100%;
@include forLargeScreens(640) {
width: 65.33%;
}
}
//Different width containers
.content- {
@include forLargeScreens(640) {
&a { width: 30%; }
&c { width: 42%; }
}
}
//All IEs
.ie8, .ie9, .ie10 {
//Exact values (desired width − 0.83% = result %) are commented, but they need tweaked to have one value for all IEs
section {
.row-1 .level-1 { width: 49.17%; }
//Exact value is 32.17%
.row-2 .level-1 { width: 32.20%; }
//Exact value is 24.17%
.row-3 .level-1 { width: 23.75%; }
.row-4 {
.content-a { width: 19.17%; }
.content-b { width: 49.17%; }
//Exact value is 29.17%
.content-c { width: 28.3%; }
}
}
}
IE8 和 IE9 的规则
我们现在将声明处理 IE8 和 IE9 值的规则。我们声明overflow: hidden;
来清除父容器中的浮动,即.flex-container
DIVs。然后我们将 Nav 和 Content 部分浮动到左侧,并给它们一个高度;这个高度仅用于样式目的。
我们给 Nav 部分设置宽度和右边距为 1%,以保持简洁。我们也给 Content 部分分配了宽度。然后,我们使用 Footer 来清除浮动的 Nav 和 Content 部分,使用clear: both;
和zoom: 1;
参数以确保。
以下是 IE8/9 的 SCSS:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
.main-container {
//Change this value to ANYTHING you want, no need to edit anything else.
width: 100%;
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
//DIVs inside the flex container
[class*="level-"] {
width: 100%;
@include forLargeScreens(640) {
margin: 0 .83%;
}
&:first-of-type { margin-left: 0; }
&:last-of-type { margin-right: 0; }
}
//Nav
nav {
width: 100%;
@include forLargeScreens(640) {
width: 33%;
margin-right: 1.67%;
}
}
//Content area
section {
width: 100%;
@include forLargeScreens(640) {
width: 65.33%;
}
}
//Different width containers
.content- {
@include forLargeScreens(640) {
&a { width: 30%; }
&c { width: 42%; }
}
}
//All IEs
.ie8, .ie9, .ie10 {
//Exact values (desired width − 0.83% = result %) are commented, but they need tweaked to have one value for all IEs
section {
.row-1 .level-1 { width: 49.17%; }
//Exact value is 32.17%
.row-2 .level-1 { width: 32.20%; }
//Exact value is 24.17%
.row-3 .level-1 { width: 23.75%; }
.row-4 {
.content-a { width: 19.17%; }
.content-b { width: 49.17%; }
//Exact value is 29.17%
.content-c { width: 28.3%; }
}
}
}
//IE8/9
.ie8, .ie9 {
.flex-container { overflow: hidden; }
nav, section { float: left; min-height: 440px; }
nav { width: 29%; margin-right: 1%; }
section { width: 70%; }
footer { clear: both; zoom: 1; }
}
IE8 和 IE9 的特定规则
最后,我们通过一些规则来解决旧版浏览器的问题:为 IE8 制定一条规则,为 IE9 使用属性选择器制定另一条规则,适用于所有嵌套容器。
对于 IE8,我们给嵌套容器display: inline-block;
而不是float: left;
,以使嵌套容器的组在相应的行中居中。如果我们不这样做,所有行的右侧将会出现奇怪的间隙。我们还将声明左右边距为.2%。经过测试,任何更大的值都会使嵌套容器换行。
对于 IE9,我们将把嵌套容器浮动到左侧。
让我们来看看这两条规则:
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Flexbox-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content }
}
.main-container {
//Change this value to ANYTHING you want, no need to edit anything else.
width: 100%;
max-width: 1200px;
//Any value you want
padding: 0 1.67%;
margin: auto;
}
//Flexbox container
.flex-container {
margin-bottom: 10px;
//Remove the margin from the last flexbox container
&:last-of-type {
margin-bottom: 0;
}
@include forLargeScreens(640) {
display: flex;
}
}
//DIVs inside the flex container
[class*="level-"] {
width: 100%;
@include forLargeScreens(640) {
margin: 0 .83%;
}
&:first-of-type { margin-left: 0; }
&:last-of-type { margin-right: 0; }
}
//Nav
nav {
width: 100%;
@include forLargeScreens(640) {
width: 33%;
margin-right: 1.67%;
}
}
//Content area
section {
width: 100%;
@include forLargeScreens(640) {
width: 65.33%;
}
}
//Different width containers
.content- {
@include forLargeScreens(640) {
&a { width: 30%; }
&c { width: 42%; }
}
}
//All IEs
.ie8, .ie9, .ie10 {
//Exact values (desired width − 0.83% = result %) are commented, but they need tweaked to have one value for all IEs
section {
.row-1 .level-1 { width: 49.17%; }
//Exact value is 32.17%
.row-2 .level-1 { width: 32.20%; }
//Exact value is 24.17%
.row-3 .level-1 { width: 23.75%; }
.row-4 {
.content-a { width: 19.17%; }
.content-b { width: 49.17%; }
//Exact value is 29.17%
.content-c { width: 28.3%; }
}
}
}
//IE8/9
.ie8, .ie9 {
.flex-container { overflow: hidden; }
nav, section { float: left; min-height: 440px; }
nav { width: 29%; margin-right: 1%; }
section { width: 70%; }
footer { clear: both; zoom: 1; }
}
//IE8
.ie8 {
[class*="level-"] {
display: inline-block;
margin: 0 .2%;
}
}
//IE9
.ie9 {
[class*="level-"] { float: left; }
}
总结
在这一章中有很多内容需要消化,是吧?
然而,我们现在知道什么是网格,以及它的用途,这是我们许多人以前从未真正质疑过的东西。我们还更加了解 CSS 网格、CSS 框架和 UI 工具包;尽管你愿意使用它们,只要你清楚它们如何帮助我们在构建响应式网站和应用程序时更加高效。
使用传统的浮动技术创建我们的自定义 CSS 是一种识别模式的问题,其中添加新列只是通过增加 100 的值。现在,我们可以在任何宽度上创建一个 12 列网格。
借助 Flexbox,我们现在明白了响应式和流动布局的未来在哪里。由于有如此出色的浏览器支持,毫无疑问 Flexbox 是传统 CSS 网格的一个重要竞争者。在旧版浏览器中,使用条件类是支持复杂布局的一个不错的选择。此外,对于 IE10,我们需要使用条件编译脚本,只有 IE10 才能看到。因此,我们可以使用.ie10
特定选择器来针对 IE10。
在下一章中,当我们谈论为小屏幕上的大手指构建响应式界面时,我们将深入了解可用性和用户体验的世界。是时候来测试那些大手指了!
第五章:设计由大手指驱动的小 UI
触摸屏设备的普及并不新鲜,对于我们——网络/移动设计师和开发人员来说。因此,我们不会谈论市场份额、统计数据或分析数字。相反,我们将讨论我们需要考虑的事项,如目标大小、导航模式、导航图标、最佳实践和移动设备人体工程学。
在本章中,我们将涵盖以下主题:
-
小 UI 上的理想目标大小。
-
姿势模式和触摸区域。
-
RWD 需要考虑的基本准则。
-
RWD 的导航模式。
小 UI 上的理想目标大小
所有供应商对小屏幕设备上理想目标大小的规则和指南都有不同的集合。其中一些是以像素表示这些尺寸,其他的是以点表示,还有一些是以英寸、毫米或厘米为单位。
无论这些供应商使用的单位是什么,他们都同意一个基本概念:使目标大小足够大,以避免意外点击。这与菲茨定律相一致,该定律规定目标越小,用户到达目标所需的时间就越长。
显然,作为网页设计师,我们必须注意在我们的设计中大意味着什么,因此我们需要平衡目标大小的建议与良好的设计原则。我们的目标是让消息传达给用户,并且他们应该能够舒适地使用我们的界面。
需要记住的一件事是,RWD 的目标大小指南大多基于移动应用程序设计模式。让我们直奔主题。
成年人食指的平均宽度约为 16 毫米至 20 毫米。这接近 45px 至 57px。
根据苹果的 iOS 人机界面指南,推荐的最小目标大小为 44pt x 44pt。
提示
一些用户界面指南使用点和毫米作为测量单位的原因是为了提供一个与设备无关的一致比例尺。这是因为一个设备中的 1px 在另一个设备中不一定意味着确切的 1px。尽管如此,一些供应商确实提供了像素的指南,但主要是为了让我们了解元素的比例关系。
在过去,苹果确实建议他们的目标大小以像素为单位,44px x 44px,但当引入视网膜显示屏时,iPhone 3 的 1 像素变成了 iPhone 4 上的 4 像素。不再是 1:1 的比例。
这意味着在非视网膜设备上,44pt x 44pt 实际上是 44px x 44px,但在视网膜设备上是 88px x 88px。每次苹果发布具有更高密度屏幕的新设备时,这些像素值都会再次改变。
在 RWD 世界中,对于苹果设备或任何设备的屏幕密度有很好的理解是必不可少的。这样,我们在创建设计时总是可以考虑到这些技术细节,以免妨碍用户体验和我们网站和应用的可用性。
另一方面,微软的 Windows 8 触摸指导文档建议理想的目标大小为 7 毫米 x 7 毫米(40px x 40px)。如果准确性至关重要,例如关闭或删除,Windows 8 触摸指导指南建议目标大小为 9 毫米 x 9 毫米(50px x 50px)。此外,当屏幕空间有限且需要适应时,最小推荐的目标大小为 5 毫米 x 5 毫米(30px x 30px)。
这些尺寸适用于非高密度屏幕。
Windows 8 触摸指导指南甚至建议元素之间的最小填充:2 毫米(10px),无论目标大小如何(这很好)。
Android 开发者指南建议最小目标大小为 48dp,约为 9 毫米。推荐的最小和最大目标大小分别为 7 毫米和 10 毫米。
Android 开发者指南还建议元素之间的最小间距为 8dp。
提示
在这里,dp表示密度无关像素。这意味着在正常密度屏幕上,1dp 与 1px 相同。就像苹果使用点(pt)一样,他们试图定义一个全球和屏幕密度无关的单位。
还有Ubuntu文档建议界面元素不应小于 1 厘米(约 55px)。
正如我们所看到的,推荐的最小和最大目标尺寸因供应商而异。但它们之间的差距并不大。
我们可以从提到的所有目标尺寸中得出结论,即适当的尺寸为(在低密度屏幕上):
-
推荐的目标尺寸为 48dp×48dp = 48px×48px。
-
最小目标尺寸为 5 毫米×5 毫米= 30px×30px。
-
最大目标尺寸为 10 毫米×10 毫米= 55px×55px。
-
任何元素之间的填充为 2 毫米= 10px。
姿势模式和触摸区域
无论我们的触摸目标的尺寸有多可用,如果它们没有放置在正确的位置,我们所有的努力基本上都是无用的。
我们不能谈论小型 UI 和大手指而不提到 Luke Wroblewski 在他的文章《响应式导航:优化跨设备触摸》中的广泛工作(www.lukew.com/ff/entry.asp?1649
)。
姿势模式
在他的文章中,Luke 谈到了大多数用户在握住智能手机、平板电脑和触摸笔记本电脑时的姿势模式:
这些模式使我们能够定义布局内容的最佳方式,以便易于使用和访问。
了解用户的姿势模式将使我们能够了解我们的目标何时可以是正确的大小,甚至如果屏幕空间不足,则可以略小一些,或者如果需要精度,则可以略大一些,因为当有人使用大拇指时与使用食指时是不同的。
触摸区域
Luke 还谈到了“触摸区域”,基本上是设备上易于或难以触及的区域,这取决于姿势。
在所有主要类型的设备(智能手机、平板电脑和触摸笔记本电脑)中,理想的触摸区域为深绿色,ok触摸区域为浅绿色,难以触及的区域为黄色:
在 RWD 中,要彻底改变单个页面的布局,更不用说许多页面(至少目前还没有)像独立的应用程序那样,需要大量的工作。此外,有很高的可能性会对用户体验产生负面影响,并保持内容层次结构。
RWD 与内容策略密切相关,因此无论我们的网站/应用程序在哪种设备上查看,都需要保留内容层次结构。我们需要确保元素本身足够大,以便大手指的人能够正确使用。这些元素包括链接、按钮、表单字段、导航项、任何类型的控件(如分页、手风琴中的展开/折叠控件、选项卡系统等)。
现在,在 RWD 中非常多用途的一个网站/应用程序元素是菜单按钮。
为了触发导航,有一个非常特殊的元素,UX 社区对此有非常强烈的意见:汉堡图标(≡)。目前,我们将称其为更通用的名称:导航图标。我将其称为导航图标,因为它不一定是汉堡图标/图形,它可以是另一种类型的图标或一个词。
导航图标的位置、行为和设计以及导航项本身的变化与设计师的数量一样多。对其他人有效的方法未必对我们有效,反之亦然。因此,测试成为决定用户感觉舒适的方法。
尽管如此,有一些导航图标的 UX 指南值得一提,我们将在接下来看到。
导航图标- RWD 要考虑的基本指南
导航图标可以用许多方式表示。响应式网页设计从移动应用中借鉴了模式,因为小屏幕应用和网站有许多相似的隐喻。
让我们来看看常见的导航图标模式:
-
汉堡包图标。
-
单词“菜单”。
-
汉堡包图标加上单词“菜单”。
汉堡包图标
这是迄今为止最流行的用于表示导航按钮的图标:≡。
汉堡包图标是由 Norm Cox 于 1981 年创建的。Norm 设计这个图标的初衷是“……模仿显示的菜单列表的外观。”(gizmodo.com/who-designed-the-iconic-hamburger-icon-1555438787
)。
换句话说,汉堡包图标的真正名称是“列表”图标。
现在,如果我们想一想,汉堡包图标在语义上是正确的,因为它确切地代表了触发时显示的内容:一系列项目。然而,一些用户体验研究表明,汉堡包图标并不像我们想象的那么有效,但我们在响应式网站和移动应用中随处可见它。
尽管汉堡包图标有一些缺点,但几乎每个人都能在不久的将来认出这个图标代表导航。
关键是,只要我们遵循目标大小建议,并使导航栏内的链接在小屏幕上易于点击,使用汉堡包图标并没有什么问题。
优点如下:
-
它很容易被某些人群识别,尤其是年轻人。
-
在设计中占用很少的空间。
-
它不依赖语言。
-
使用 Unicode 字符 2261(≡)制作起来很容易,全球支持率达到 96%。
缺点如下:
-
它不容易被某些人群识别,尤其是年长者。
-
尽管非常流行,很多人很难理解汉堡包图标代表菜单。
-
它促进了低发现性,因为网站的导航通常会被隐藏。
如果您打算使用汉堡包图标,不要使用任何类型的图像或任何带有边框或框阴影的 CSS 技术。保持简单。您只需要使用 Unicode 字符 2261(≡)。
在接下来的示例中,我们将使用一个众所周知的技术来隐藏内容(有一些变化以适应我们的演示):凯勒姆方法。这种方法绝不是任何欺骗或类似的东西;我们并不打算用这种方法欺骗我们的用户或搜索引擎。我们实际上非常注意通过将文本留在标记中来提高导航图标的可访问性,以便使用辅助技术的用户仍然可以访问菜单。考虑以下示例。
HTML 如下:
<button class="hamburger-icon"><span>Menu</span></button>SCSS
//Hamburger Icon
.hamburger-icon {
//Basic styling, modify if you want
font-size: 40px;
color: #666;
background: #efefef;
padding: 0 10px;
border-radius: 3px;
cursor: pointer;
//Hamburger Icon
&:before {
content: '≡';
}
//Hide the term Menu from displaying without sacrificing accessibility
span {
display: inline-block;
width: 0;
height: 0;
text-indent: -100%;
overflow: hidden;
white-space: nowrap;
}
}
结果如下:
提示
出于可访问性原因,单词“菜单”应始终包含在标记中。当使用辅助技术(AT)的用户将焦点放在汉堡包图标上时,屏幕阅读器将朗读单词“菜单”。此外,将单词“菜单”包含在<span>
标记中允许我们隐藏单词,而不会损害链接的可访问性。
单词菜单
网上一些非正式测试表明,使用单词“菜单”是解决汉堡包图标缺点的最可信赖的解决方案。
然而,需要注意的是,许多作者进行的研究和测试,比较了汉堡包图标和单词“菜单”,可能会产生误导,因为它们测试的是不同的视觉语言:图标与单词。
要使这些测试完全可靠,它们必须测试图标与图标,单词与单词。例如,测试汉堡包图标与向下指的箭头或单词“菜单”与单词“导航”。
让我们来看看单词“菜单”的优缺点。
优点如下:
-
这是不言自明的。
-
几乎任何人群的任何人都可以理解它的含义。
-
它可以用于任何语言。
-
它在设计中占用的空间很小。
缺点如下:
- 它可能会与图标系统冲突,因为它是一个单词。
考虑以下示例。
这是 HTML:
<button class="word-menu">Menu</button>
这是 CSS:
//Word "Menu"
.word-menu {
//Basic styling, modify if you want
display: inline-block;
padding: 16px 8px;
color: #666;
font: 12px Arial, "Helvetica Neue", Helvetica, sans-serif;
background: #efefef;
border-radius: 3px;
cursor: pointer;
}
这就是结果:
提示
在这个例子中,我使用了类名.word-menu
来明确表示我对这本书的意图,但这不是为生产命名元素的正确方式。使用更有意义和通用的命名约定,例如.menu-trigger
可能是一个替代方案。使用通用类名将允许我们在不改变标记的情况下使用任何导航图标设计。
汉堡图标加上单词菜单
汉堡图标与单词菜单讨论的一个替代方案是同时使用两者。一些人认为这样做可以兼顾两全。
优点是:
-
它是不言自明的。
-
几乎任何人都可以理解它的含义。
-
它可以用于任何语言。
-
它在设计中仍然占用很小的空间。
-
使用 Unicode 字符 2261(≡)很容易,全球支持率为 96%。
缺点是:
- 根据设计,单词菜单可能太小。
让我们看看我们可以用来表示这种模式的两种样式。
考虑以下示例。
HTML 如下:
<button class="hamburger-icon-plus-menu style-1">Menu</button>
SCSS 如下:
//Hamburger Icon Plus Word "Menu" – Style 1
.hamburger-icon-plus-menu {
//Basic styling, modify if you want
display: inline-block;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
background: #efefef;
color: #666;
border-radius: 3px;
cursor: pointer;
}
.style-1 {
padding: 16px 8px;
font-size: 16px;
//Hamburger Icon
&:before {
content: '≡ ';
}
}
结果如下:
提示
注意在≡
后面的空格,这样可以在不使用任何边距的情况下将图标与单词“菜单”分开。
考虑以下示例。
HTML 是:
<button class="hamburger-icon-plus-menu style-2">Menu</button>
SCSS 是:
//Hamburger Icon plus Word "Menu" – Style 2
.hamburger-icon-plus-menu {
//Basic styling, modify if you want
display: inline-block;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
background: #efefef;
color: #666;
border-radius: 3px;cursor: pointer;
}
.style-2 {
padding: 4px 12px 6px;
font-size: 10px;
line-height: .8;
text-align: center;
//Hamburger Icon
&:before {
display: block;
content: '≡';
font-size: 40px;
}
}
这就是结果:
您可以在codepen.io/ricardozea/pen/f4ddc6443bc060004b58a7301aae83db
上看到我在 CodePen 中创建的演示。
RWD 的导航模式
RWD 最令人费解的特点之一是导航。它可以简单也可以复杂,取决于我们的需求。
在这一部分,我将向您展示如何构建三种常用的导航模式:
-
切换导航:这是基于 Brad Frost 的切换菜单演示(
codepen.io/bradfrost/pen/sHvaz/
)。 -
侧边或屏幕外导航:这是基于 Austin Wulf 的 SitePoint 纯 CSS 屏幕外导航菜单演示(
codepen.io/SitePoint/pen/uIemr/
)。 -
基于 Flexbox 的导航:这是我们的自定义解决方案。
在我们查看每个细节之前,让我们澄清一下关于上述模式的一些特点:
设计
在小屏幕上,所有导航模式都使用汉堡图标作为触发器,除了基于 Flexbox 的导航。在大屏幕上,所有示例中的导航栏都是水平链接组,链接居中。
为了改善切换和侧边导航的可用性,汉堡图标会添加/删除类.active
,以提供视觉提示,显示该项目已被点击。这是通过一点 jQuery 完成的。
包括 jQuery 是这些演示的一部分,因此需要调用它才能使它们工作。
范围
所示的标记仅用于菜单本身,元素和指令,如<html>
标记和 HTML5 Doctype 已经被故意省略。
这些示例适用于所有主要浏览器,这些浏览器支持相对先进的 CSS3 属性。它们不使用 FastClick 脚本来消除移动设备默认的 300 毫秒延迟。
供应商前缀已被省略;毕竟,我们应该使用 Autoprefixer 来处理这些问题。
第三方演示
由于没有必要重复造轮子,以下示例基于其他作者的演示,例如 Brad Frost 和 Austin Wulf 的演示。
然而,所有原始演示都已被分叉并大幅缩放、增强、清理、优化、重新设计和移植到 Sass,以适应本书的范围和风格。换句话说,您将看到的标记和代码已经专门为您进行了大量定制。
让我们开始吧。
侧栏或屏幕外导航
这是迄今为止在 RWD 和移动应用中最常用的导航模式。它使用汉堡图标作为菜单的触发器,当点击时触发菜单。这时,主容器向右滑动以显示左侧的菜单,再向左滑动以隐藏它。
这个示例不依赖于 JavaScript 来工作。但是,它使用了一些非语义元素来使其工作:<input>
和<label>
元素。为了支持这种方法,它使用了:checked
伪类,在各方面都有完美的支持。
这是我们的 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="img/jquery.min.js"></script>
</head>
<body>
<!-- Checkbox whose checked/unchecked states trigger the navigation -->
<input type="checkbox" id="nav-trigger" class="nav-trigger">
<!-- Hamburger icon -->
<label for="nav-trigger" class="label-trigger"><span>Menu</span></label>
<!-- Navigation -->
<nav role="navigation">
<ul class="menu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
<li><a href="#">Link 5</a></li>
</ul>
</nav>
<!-- Main container -->
<main class="main-container" role="main">
<h1>The "Off-Canvas" or "Off-Screen" Navigation</h1>
<p>On <strong>small screens</strong>, the menu is triggered with a hamburger icon. The menu slides left/right.</p>
<p>On <strong>large screens</strong> starting at 40em (640px), the menu is a horizontal nav.</p>
</main>
</body>
</html>
这是我们的 SCSS:
*, *:before, *:after { box-sizing: border-box; }
//Globals
html, body {
height: 100%;
width: 100%;
margin: 0;
}
//Mobile-first Media Query Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content; }
}
//Mixin for animating the hamburger icon
@mixin animation-nav-icon ( $direction: left, $duration: .2s) {
transition: $direction $duration;
}
//Menu itself
.menu {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
list-style: none;
@include forLargeScreens(640) {
max-width: 980px;
min-height: 50%;
margin: 10px auto 0;
position: relative;
text-align: center;
border: #999 1px dotted;
}
//List items
li {
width: 100%;
border-bottom: 1px dotted #999;
@include forLargeScreens(640) {
display: inline;
border: none;
}
//Links themselves
a {
display: block;
padding: 1em;
color: #2963BD;
text-decoration: none;
@include forLargeScreens(640) {
display: inline-block;
}
}
}
}
//Main Container
.main-container {
max-width: 980px;
min-height: 100%;
margin: auto;
padding: 20px 0 20px 80px;
position: relative;
top: 0;
bottom: 100%;
left: 0;
z-index: 1;
background: #eee;
@include forLargeScreens(640) {
padding: 20px;
}
}
//Navigation Trigger - Hide the checkbox
.nav-trigger {
position: absolute;
clip: rect(0, 0, 0, 0);
}
//Label that triggers the checkbox
.label-trigger {
position: fixed;
left: 10px;
top: 10px;
z-index: 2;
height: 50px;
width: 50px;
cursor: pointer;
background: #fff;
border-radius: 2px;
border: 1px solid #ccc;
//Hamburger icon
&:before {
display: block;
padding-top: 25px;
text-align: center;
content: '≡';
font-size: 3em;
line-height: 0;
}
//Active hamburger icon
&.active {
background: #333;
color: #fff;
}
//Hide the term 'Menu' from displaying without sacrificing accessibility
span {
display: inline-block;
text-indent: -100%;
overflow: hidden;
white-space: nowrap;
}
}
//Animate the menu
.nav-trigger {
& + label {
@include animation-nav-icon;
//Hide the checkbox and label in large screens
@include forLargeScreens(640) {
display: none;
}
}
//Animate the label when checkbox is checked
&:checked + label {
left: 215px;
}
//Animate the main container when checkbox is checked
&:checked ~ .main-container {
left: 200px;
box-shadow: 0 0 5px 1px rgba(black, .15);
}
}
//Animate the main container
.main-container {
@include animation-nav-icon;
}
//Avoid horizontal scrollbars due to repositioning of elements
body, html { overflow-x: hidden; }
//Styling stuff not needed for demo
html, body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; }
h1, p { margin: 0 auto 1em; }
p { line-height: 1.5; }
这是 jQuery 脚本:
$(function() {
//Set up the click behavior
$(".label-trigger").click(function() {
//Toggle the class .active on the hamburger icon
$(this).toggleClass("active");
});
});
让我们来看一下截图。
这是在折叠状态下小屏幕上的样子:
这是在展开状态下的样子:
这是在大屏幕上的样子:
您可以在codepen.io/ricardozea/pen/fd504cbcf362069320d15a4ea8a88b27
看到我创建的演示。
切换导航
在切换模式中,当点击汉堡图标时,导航栏会下滑,链接会堆叠。再次点击汉堡图标时,导航栏会折叠。
HTML 如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="img/jquery.min.js"></script>
</head>
<body>
<!-- Hamburger icon -->
<button class="menu-link"><span>Menu</span></button>
<!-- Navigation -->
<nav id="menu" class="menu" role="navigation">
<ul>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
<li><a href="#">Link 5</a></li>
</ul>
</nav>
<!-- Main container -->
<main class="main-container" role="main">
<h1>The Toggle Navigation</h1>
<p>On <strong>small screens</strong>, the menu is triggered with a hamburger icon. The menu slides down/up.</p>
<p>On <strong>large screens</strong> starting at 40em (640px), the menu is a horizontal nav.</p>
</main>
</body>
</html>
SCSS 如下:
*, *:before, *:after { box-sizing: border-box; }
//Mobile-first Media Query Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content; }
}
//General Styling
.main-container, .menu {
width: 98%;
max-width: 980px;
margin: auto;
padding: 20px;
background: #eee;
}
//Link that triggers the menu
.menu-link {
//Change to float: left; if you want the hamburger menu on the left side
float: right;
margin: 0 1% 5px 0;
padding: 1.5em 1em 1em;
background: #f6f6f6;
line-height: 0;
text-decoration: none;
color: #333;
border-radius: 2px;
cursor: pointer;
//Hamburger icon
&:before {
display: block;
padding: 10px 0;
content: '≡';
font-size: 3em;
line-height: 0;
}
//Active hamburger icon
&.active {
background: #333;
color: #fff;
}
//Hide the term 'Menu' from displaying without sacrificing accessibility
span {
display: inline-block;
text-indent: -100%;
overflow: hidden;
white-space: nowrap;
}
//On large screens hide the menu trigger
@include forLargeScreens(640) {
display: none;
}
}
//If JavaScript is available, hide the menu.
.js .menu {
overflow: hidden;
max-height: 0;
@include forLargeScreens(640) {
max-height: inherit;
}
}
//Menu itself
.menu {
padding: 0;
clear: both;
transition: all .3s ease-out;
//Define height of the menu
&.active {
max-height: 17em;
}
//Normalize the unordered list and add a bit of styling
ul {
margin: 0;
padding: 0;
list-style-type: none;
border: 1px #999 dotted;
border-bottom: none;
text-align: center;
//In large screens remove the border
@include forLargeScreens(640) {
background: #fff;
}
}
//List items
li {
//Links themselves
a {
display: block;
padding: 1em;
border-bottom: 1px #999 dotted;
text-decoration: none;
color: #2963BD;
background: #fff;
@include forLargeScreens(640) {
border: 0;
background: none;
}
}
//On large screens make links horizontal
@include forLargeScreens(640) {
display: inline-block;
margin: 0 .20em;
}
}
}
//Styling stuff not needed for demo
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; }
p { line-height: 1.5; }
h1 { margin: 0; }
jQuery 如下:
$(function() {
//Add class .js to the body if JS is enabled
$("body").addClass("js");
//Set up the click behavior
$(".menu-link").click(function() {
//Toggle the class .active on the hamburger icon
$(this).toggleClass("active");
//Toggle the class .active on the menu to make it slide down/up
$(".menu").toggleClass("active");
});
});
让我们来看一下截图。
这是在小屏幕上折叠状态下的样子:
这是展开状态下的样子:
这是在大屏幕上的样子:
您可以在codepen.io/ricardozea/pen/e91a5e6ea456d41f4128d9bd405ccaa0
看到我创建的演示。
您还可以访问responsive-nav.com/
了解漂亮的切换导航功能。
基于 Flexbox 的导航
这个使用 Flexbox 的自定义解决方案非常灵活,不一定需要使用媒体查询。另外两个菜单解决方案(切换导航和侧栏导航)需要媒体查询。
使用这个解决方案,菜单项会适应可用空间,使目标区域尽可能大,自动增强菜单的可用性。这个基于 Flexbox 的解决方案的另一个主要优点是它不依赖于 JavaScript。
这是 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<nav role="navigation">
<ul class="menu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
<li><a href="#">Link 5</a></li>
</ul>
</nav>
<!-- Main container -->
<main class="main-container" role="main">
<h1>The Flexbox-based Navigation</h1>
<p>On both <strong>small and large screens</strong> the menu and its items are always visible.</p>
<p>However, on <strong>small screens</strong> the links are more clearly defined and occupy all the available space.</p>
</main>
</body>
</html>
现在是 SCSS:
*, *:before, *:after { box-sizing: border-box; }
//Mobile-first Media Query Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content; }
}
//Menu itself
.menu {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
max-width: 980px;
margin: auto;
padding: 2px;
list-style: none;
border: #999 1px dotted;
//List items
li {
//Expand to use any available space
flex-grow: 1;
margin: 3px;
text-align: center;
flex-basis: 100%;
@include forLargeScreens(320) {
flex-basis: 30%;
}
@include forLargeScreens(426) {
flex-basis: 0;
}
//Links themselves
a {
display: block;
padding: 1em;
color: #2963bd;
text-decoration: none;
background: #eee;
@include forLargeScreens(426) {
background: none;
}
}
}
}
//Main Container
.main-container {
max-width: 980px;
margin: auto;
padding: 20px;
background: #eee;
}
//Styling stuff not needed for demo
body { margin: 8px; font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; }
p { line-height: 1.5; }
h1 { margin: 0; }
让我们来看一下截图。
这是在小屏幕(320px)上的样子:
这是在小屏幕(426px)上的样子:
这是在大屏幕(980px)上的样子:
您可以在codepen.io/ricardozea/pen/022b38c6c395368ec4befbf43737e398
看到我创建的演示。
总结
我们现在已经掌握了使用 HTML5 和 CSS3 进行 RWD 的一半。这是一个巨大的里程碑!非常感谢您走到这一步!
RWD 显然不仅仅是媒体查询、Sass 混合和 CSS 网格。它还涉及理解我们目标区域的不同尺寸,控件的位置(链接、按钮、表单字段等),以及不同设备中的触摸区域。
创建菜单按钮总会有不同的方法,只要确保功能在任何屏幕尺寸上都不会出现问题。一旦我们定义了菜单按钮的样式,我们就可以确定哪种导航模式最适合我们的内容和用户。
在菜单按钮或导航模式方面,实际上并没有一个单一的、最佳的解决方案;一切都取决于每个项目的具体条件。我建议的是,无论你构建什么,都要确保始终保持高水平的浏览器支持、可扩展性和性能,这样用户就可以获得很好的体验,客户/公司也可以实现其目标。
现在我们谈论性能,下一章我们将讨论 RWD 的“丑孩子”:图片。
让我们跳舞吧!
第六章:在响应式网页设计中使用图像和视频
我一直把图像称为RWD的“丑陋之子”。为什么?直到最后一刻,我总是试图避免处理它们。我要使用图像精灵吗?如果是的话,我应该将我的透明 PNG 导出为 8 位还是 24 位,或者 32 位?一些旧版 IE 不支持带有 alpha 通道的 PNG,所以我必须导出一个 GIF 精灵。我可以使用 SVG,但 IE8 及更早版本不支持 SVG。我可以使用图标字体,但如果图标字体加载失败会怎么样?那我就得查一些分析数据。有一种新的高密度屏幕的iDevice?现在我每次都得导出两个(或更多)图像。太好了!但我不能为小屏设备提供超过正常尺寸图像两倍大小的高质量图像!是的,它可能看起来很好,但下载速度会很慢,他们甚至在第一个 H1 加载之前就可能离开网站。
你明白了。这只是刚刚开始涉及响应式网页设计中媒体工作的冰山一角。
其中一些想法今天仍然非常活跃,但多年来我学到了一些常识,并且紧跟解决所有这些问题的技术,拥有一个简单直接的处理图像(和视频)的系统可以走得更远。
和其他章节一样,我们将保持简单但有意义。在涉及图像时并没有银弹,特别是在响应式网页设计中,我们可能整天都在这里,这绝对不是我们这本书想要的。我希望你尽快构建出色的响应式网站。但我鼓励你花一些时间研究更多关于响应式网页设计中图像的内容;这确实是网页设计和开发者社区中一个令人难忘的话题。
在本章中,我们将讨论以下主题:
-
导出图像并在保持图像质量的同时显著减小其最终文件大小的技巧。
-
如何使用
srcset
和sizes
属性,以及<picture>
元素。 -
使用
Retina.js
。 -
制作响应式视频。
-
使用
FitVids.js
。 -
使用矢量格式:图标字体和 SVG。
-
使用正确的 CSS 图像替换技术。
现在,这是我们在示例中要使用的图像:
这些了不起的人物是中国少林寺的两位功夫大师。他们的名字是释德如和释德阳。
注意
释德如和释德阳由释德如(刘祥阳)拍摄,他是这张照片的唯一所有者和版权持有者,该照片是在少林寺前门拍摄的。它在维基百科上以 CC BY-SA 3.0 许可证发布。可以在en.wikipedia.org/wiki/Shaolin_Kung_Fu#/media/File:Shi_DeRu_and_Shi_DeYang.jpg
找到。
由于我们还在用 HTML5 和 CSS3精通响应式网页设计,我觉得这张照片与我们的使命非常契合。
我将要描述的功夫大师的原始图像的属性将有助于理解为响应式网页设计优化图像前后效果设定基线。
以下是原始图像的属性:
-
这是一张 24 位 JPG 图像。
-
文件大小为 556KB,但由于 JPG 算法的魔力而被压缩(解压后约为 12MB)。
-
尺寸为 2496 x 1664 像素,约为 4.15 百万像素。换个角度看,这张图像的分辨率比我客厅里的 55 英寸 LED 电视还要高。
在本书结束时,我向你保证两件事。一,你将绝对准备好构建响应式网站和应用。二,当是时候开始一个新项目时,你将从座位上站起来,并摆出这些大师们正在做的同样的姿势。
图像编辑超出了本书的范围,以下步骤将需要某种形式的图像处理。在这一点上,您可以使用您喜欢的图像编辑器。我个人使用 Adobe Fireworks(确实如此),但绝大多数人使用 Photoshop。
如果您不使用其中任何一个,您可以随时使用GNU 图像处理软件(GIMP)或 Paint.NET-两者都是免费的。您可以从这里下载它们:
-
GIMP:
www.gimp.org/
-
Paint.NET:
www.getpaint.net/
您还可以使用在线图像编辑工具。但是,我必须承认,我从未使用过其中任何一个,所以我无法推荐任何一个。在这一点上,我可以说的是尝试其中一些,并选择最适合您需求的那个。
让我们开始吧。
用于 RWD 图像文件大小减小的提示
在设计中,创建图像副本的经验法则是从大到小进行,而不是相反。换句话说,图像越大,其后续副本的质量就越好。
调整大小
仅通过将图像从 2496 x 1664 像素调整为 1024 x 683 像素,文件大小现在为 331 KB。与 556 KB 相比,这几乎是文件大小的 40%减少。这是一个巨大的改进,但我们还没有到达目标。
模糊背景
模糊背景实际上本身就非常有效,但从艺术指导的角度来看,它还有另一个好处:它有助于吸引对图像的重要部分的注意力。
在模糊背景之后,文件现在重量为 185 KB。与 556 KB 相比,文件大小减少了约 67%。我们开始有所进展了。
这是带有模糊背景的新图像:
优化的巨大胜利!
暗化或变亮不重要的区域
暗化或变亮不重要的区域非常主观,许多人可能不一定同意。在特殊情况下,这个过程-就像背景模糊技术一样-可以帮助减小文件大小并突出图像的重要部分。
我们基本上试图通过暗化或变亮图像来减少颜色的数量,从而创建纯色区域,或者至少尽可能纯色。换句话说,我们正在减少对比度。谨慎使用这个技巧。
在我们的功夫宗师的情况下,在暗化背景中不重要的部分后,图像现在重量为 178 KB。诚然,这与以前的过程没有太大不同(只有 7 KB 的差异),但是我们可以从图像中提取的每一个千字节而不影响质量都是一件好事,178 KB 大约是文件大小的 68%减少。
这是在稍微暗化背景后图像的外观:
每一个千字节都很重要。
优化图像
这是过程的最后一步。这一步实际上可以分为两个较小的步骤。
使用 Adobe Fireworks(可选)
保存一个在质量与文件大小之间平衡的 JPG。没有确定的值可以始终应用于每个单独的图像。这一切都是即兴发生的。在执行此步骤时,您不希望以太低的质量保存图像,因为图像将经历另一个优化步骤。
我实际上要使用的是 Adobe 在 2013 年 5 月停止开发的软件:Fireworks。
Fireworks 以其优越的图像优化引擎而闻名,比起 Photoshop,我自己进行了测试,Fireworks 的压缩与质量总是表现最好。Fireworks 对于今天的网页设计流程和工作流程与任何其他图像编辑软件一样相关。因此,请放心使用它。
从 Fireworks 以 80%的质量导出图像后,功夫宗师的图像现在只有 71 KB。与原始的 556 KB 相比,文件大小减少了约 87%。
压缩图像
通过另一个图像优化工具运行图像,可以是一个独立的应用程序,如 Mac 的 ImageOptim 或 Windows 的 Radical Image Optimization Tool(RIOT),或者通过在线服务,如tinypng.com/
或www.jpegmini.com/
。
我们将使用tinypng.com/
在线图像压缩服务。在通过tinypng.com/
从 Fireworks 导出的图像后,文件大小现在约为 52 KB,比原始的 556 KB 减少了约 91%。这对于图像优化来说是一个巨大的胜利。
提示
如果你没有先通过 Fireworks 运行图像,不要担心。即使您的图像可能会稍大一些,它仍然会被极大地优化,这是我们的目标。
这是 556 KB 图像和最终 52 KB 图像之间的前(左)后(右)比较:
第三方图像调整服务
我们必须承认,如果手动优化图像的过程在需要调整大小和优化许多图像的情况下可能会非常乏味和耗时,那么手动操作可能不是最佳选择。
有一些第三方和服务器端服务可以自动为我们完成这个过程。我们将把如何实现这些服务的教程留给另一本书。但是,我们将列出一些最受欢迎的服务,以便您在需要深入了解时有一个参考。
以下是一些第三方图像调整大小服务的示例:
-
Sencha.io Src来自 Sencha.com (
www.sencha.com/learn/how-to-use-src-sencha-io/
) -
ReSRC由 Dom Fee 和 Ed Thurgood (
www.resrc.it/
) -
WURFL Image Tailor (
web.wurfl.io/#wit
)
以下是一些服务器端(.htaccess
和/或.php
)图像调整大小服务的示例:
-
Matt Wilcox 的自适应图像 (
adaptive-images.com/
) -
RESS.io (
ress.io/
)
元素和 srcset 和 sizes 属性
首先,我要说的是,在 RWD 中没有 100%的最佳解决方案。这是因为当前对推荐属性的支持不足,或者因为资产双重下载。尽管戴夫·牛顿在ww1.smashingmagzine.com/
的文章中,如何避免响应式图像中的重复下载,试图解决这个问题(www.smashingmagazine.com/2013/05/10/how-to-avoid-duplicate-downloads-in-responsive-images/
)。
然而,这种解决方案非常冗长。如果你必须处理许多图像,这种解决方案可能不是最佳选择,允许双重下载开始变得更有意义。每个项目都是不同的,因此尽可能做出最明智的决定非常重要。
一旦浏览器供应商决定完全支持这里提到的任何解决方案,就不需要担心双重下载或任何类型的 polyfill 了。
我们需要使用 polyfill 的唯一原因是为了支持那些(传统和现代的)尚未实现对它们的支持的浏览器。
提示
<picture>
元素和srcset
属性都有一个针对不支持它们的浏览器的回退功能。您可以选择使用 polyfill,但不是必须的。如果您认为使用 polyfill 可以增强用户体验,那就尽管使用。阅读 Picturefill polyfill 的创建者 Scott Jehl 的这篇文章(www.filamentgroup.com/lab/to-picturefill.html
)。
现在有很多 polyfill,这里是我们今天可以使用的一些简要列表:
-
由 Scott Jehl 的 Picturefill(由 RICG 推荐:
scottjehl.github.io/picturefill/
)提供支持 -
由 Andrea Verlicchi 的 PicturePolyfill(
verlok.github.io/picturePolyfill/
)提供支持 -
由 Alexander Farkas 的 respimage(
github.com/aFarkas/respimage
)提供支持
在 Web 设计和 Web 开发社区中,一些人强烈认为,考虑到新的 HTML 元素(<picture>
)并不是解决我们在 RWD 中遇到的图像问题的解决方案。他们认为解决方案应该来自已经存在的<img>
标签。
提示
sizes
属性也可以与<picture>
元素一起使用,但我们将专注于在<img>
标签中使用sizes
属性。
对我们来说很好,解决方案有两种。使用哪种方法来负责负责您的图像并不重要,重要的是您应该使用其中一种方法。如果您已经在使用,那太棒了。如果没有,不要担心。以下的解释将帮助您解决任何关于这个问题的疑问。
何时使用,何时使用 srcset
何时使用<picture>
,何时使用srcset
?这是一个非常合理的问题,我自己在第一次听到这些术语时也无法理解。所以我决定在俄亥俄州戴顿市的一次布拉德·弗罗斯特(Brad Frost)的研讨会上向他请教。
推荐的方法归结为这个概念:艺术指导。在响应式图像中,艺术指导基本上意味着您有不同的图像,以某种方式裁剪,以便图像的不太重要的部分被剔除,从而专注于重要的部分。
这与只调整相同的图像不同。当然,您可以使用任何您想要的方法,但为了保持简单,当您想要提供艺术指导图像时,可以使用<picture>
元素,当您只想提供相同图像的调整版本时,可以使用srcset
属性。
在我们深入标记之前,让我们看一个关于艺术指导图像与使用功夫宗师照片的调整图像的视觉示例:
让我们看看这里发生了什么。原始图像周围有很多空间,我们可以看到后面的树和建筑物。调整大小的版本保持了原始图像的所有方面和比例 1:1。
然而,艺术指导图像有很多不同之处。第一个艺术指导图像被裁剪以显示两位宗师的特写;第二个艺术指导图像被裁剪得更多,以突出对 Shi DeRu(左侧的宗师)的关注。我们本可以裁剪图像以便关注 Shi DeYang(右侧的宗师),但这是我想要给图像的“艺术指导”。这是一个主观的决定,但基于坚定的意图。
现在,让我们看看Picturefill polyfill/script的实际效果。
实施 Picturefill polyfill
我们需要做的第一件事是下载 JavaScript 文件,可以从github.com/scottjehl/picturefill/blob/2.3.0/dist/picturefill.min.js
下载。
然后,我们需要做的就是将它包含在文档的<head>
部分中:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="img/picturefill.min.js"></script>
<title>Picturefill polyfill</title>
</head>
使用元素
使用<picture>
元素时,您(作者)告诉浏览器在哪个断点使用哪个图像。这样做的好处是,我们可以通过使用媒体查询来精确定义何时显示某个图像。媒体查询的工作方式与 CSS 中使用的媒体查询完全相同,甚至看起来完全相同。
这是一个基本的<picture>
片段的样子:
<picture>
<source srcset="images/grandmasters-small.jpg" media="(max-width: 40em)">
<source srcset="images/grandmasters-medium.jpg" media="(max-width: 64em)">
<source srcset="images/grandmasters-default.jpg">
<img src="img/grandmasters-default.jpg" alt="Fallback image">
</picture>
即使使用了 polyfill,IE9 对<picture>
元素也存在问题。尽管听起来很奇怪,但我们需要在 IE9 中插入一个<video>
标签以正确工作。
这是为 IE9 修改后的标记样式:
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="images/grandmasters-small-ad.jpg" media="(max-width: 40em)">
<source srcset="images/grandmasters-medium-ad.jpg" media="(max-width: 64em)">
<source srcset="images/grandmasters-default.jpg">
<!--[if IE 9]></video><![endif]-->
<img src="img/grandmasters-default.jpg" alt="Fallback image">
</picture>
正如您所看到的,我还突出显示了<img src="img/grandmasters-default.jpg" alt="Fallback image">
标签。这是那些不支持<picture>
元素的浏览器的回退图像。
请记住的一件事是,不久之前,这个回退图像在一些现代浏览器中导致了双重下载。我的最后测试显示,在 Chrome 和 Firefox 中并非如此,它们支持<picture>
元素。因此,请确保您运行所有必要的测试,以查看您的情况,然后考虑解决方案,如果您需要支持那些旧版浏览器。
这是我在 CodePen 上创建的演示:codepen.io/ricardozea/pen/cf6c0965785d552bad5e200acb761ffe
使用srcset
和sizes
属性
srcset
和sizes
属性实际上来自<picture>
规范,但在<img>
元素中实现。使用srcset
和sizes
属性时,浏览器会决定在每种特定情况下使用哪个图像。如果需要,您还可以使用媒体查询,尽管不是必需的。单词vw
表示视口宽度,用于让浏览器知道它应该根据视口宽度的百分比显示图像。如果看到类似80vw
的东西,这意味着图像应该是当前视口宽度的 80%。
w
描述符表示图像的宽度。如果看到类似255w
的东西,浏览器将了解特定图像的宽度为 255px。
让我们看看带有srcset
和sizes
属性的<img>
标签:
<img src="img/grandmasters-default.jpg"
srcset="images/grandmasters-small-rsz.jpg 255w,
images/grandmasters-medium-rsz.jpg 511w"
sizes="(min-width: 30em) 80vw, 100vw"
alt="Mastering RWD with HTML5 and CSS3">
rsz
这几个字母是resize一词的缩写。这是因为对于在 RWD 中只会被调整大小的图像,srcset
属性使事情变得简单一些。
以下标记被截断,以便更容易专注于特定的解释。
我们首先看到的是已知的src
属性,它充当回退图像:
<img src="img/grandmasters-default.jpg"…
请记住,浏览器不理解srcset
的话,将不会使用图像grandmasters-default.jpg
。换句话说,在支持srcset
的浏览器中,默认图像将是列表中的第一个图像。在我们的情况下,它是grandmasters-small-rsz.jpg
。然后,我们看到srcset
属性。
这就是魔术开始发生的地方:
srcset="images/grandmasters-small-rsz.jpg 255w,images/grandmasters-medium-rsz.jpg 511w"
在这个例子中,我们的计划是在支持srcset
的浏览器中显示两个不同的图像文件。这是通过用逗号分隔的图像列表来实现的。此外,每个图像后面定义的值是图像的宽度:
images/grandmasters-small-rsz.jpg 255w
提示
我们也可以使用高度:
grandmasters-small-rsz.jpg 170h
然而,最常见的用例是处理宽度并允许高度按比例调整,这样作者对图像有更多的控制。
向浏览器提供图像的大小将使其能够根据sizes
片段中的媒体查询更明智地决定使用哪个图像:
sizes="(min-width: 30em) 80vw, 100vw"
记住,30em
等同于 480px。使用媒体查询min-width: 30em
,浏览器经历以下过程:
-
如果我的视口是 30em(480px)或更小,则应显示宽度为 255px 的图像。在只有 480px 的视口中,没有必要显示宽度为 511px 的图像。这是浪费带宽!
-
但是,如果我的视口大于30em(480px),那么我应该显示宽度为 511px 的图像。
sizes
属性的最后部分是视口宽度:80vw, 100vw
。
sizes="(min-width: 30em) 80vw, 100vw"
这意味着如果视口是 30em(480px)或更小,浏览器将以 80%的宽度显示图像。如果超过 30em(480px),它将以 100%的宽度显示图像。
最后,我们有alt
属性:
alt="Mastering RWD with HTML5 and CSS3">
为图像添加alt
属性对于使用辅助技术的用户来说总是一个良好的可访问性实践。此外,如果图像没有加载,浏览器可以显示这个文本。
提示
属性的顺序并不重要。换句话说,你可以先使用srcset
,然后是alt
,然后是sizes
,然后是src
属性(或者反之亦然)。
使用 srcset 定位高密度屏幕
高密度屏幕将永远是 RWD 世界中我们无法摆脱的东西。所以如果你无法打败它们,就加入它们。
这是一个解决普通和高密度屏幕的片段:
<img src="img/grandmasters-default.jpg"
srcset="images/grandmasters-small-rsz.jpg 1x,images/grandmasters-medium-rsz.jpg 2x">
正如你所看到的,这是一个更短更简洁的标记。它真的很简单明了:在没有srcset
支持的情况下使用备用图像。如果有支持,那么如果设备具有普通密度显示,则使用1x
图像。如果设备具有高密度显示,那么必须使用2x
图像。如果我们支持的设备密度甚至更高,就应该添加一个 3x 后缀。
sizes
属性是不是必需的。如果你的设计或条件需要使用sizes
属性,你可以自由使用它。
这是我在 CodePen 上为此创建的演示:codepen.io/ricardozea/pen/a13993f05a4cdc5f714a311a94f48a69
<picture>
与srcset
一些网页设计师和开发人员表示,在 HTML 中使用媒体查询,就像我们在<picture>
和srcset
中看到的那样,违反了关注点分离的原则:样式和标记应始终保持分离,独立的资源。
正如我之前提到的,其他人认为新的 HTML 元素是不必要的,任何解决方案都应该基于增强和扩展已经存在的元素,比如<img>
标签。
我只能说,在最后,这一切都无关紧要。重要的是,作为网页设计师和开发人员,我们应该利用我们手头的一切资源来让用户满意,创造令人难忘的体验,同时遵循持久实施的最佳实践。
使用 Retina.js 在运行时将 1x 图像替换为 2x 图像
Retina.js
脚本是那些使事情变得更简单的脚本之一,有时你会想为什么响应式图像如此困难。
如果你还没有准备好处理<picture>
和/或srcset
和sizes
属性,我不怪你。这很可怕,但我建议你继续努力理解这些工具,因为这是响应式图像的最新技术。
Retina.js
脚本是由 Imulus 的人员开发的(imulus.com/
)。Retina.js
脚本不仅仅是 JavaScript 解决方案;他们还有一个 Sass mixin,可以在不依赖 JavaScript 的情况下产生相同的结果。
让我们先看一下 JavaScript 解决方案。
Retina.js - 一个 JavaScript 解决方案
使用这个脚本非常简单。我们需要从github.com/imulus/retinajs/blob/master/dist/retina.min.js
下载脚本。
然后,我们将脚本放在 HTML 的底部,就在闭合的<body>
标签之前:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Retina.js - JavaScript Solution</title>
</head>
<body>
...
<script src="img/retina.min.js"></script>
</body>
</html>
提示
Retina.js
脚本不依赖于框架。换句话说,它不需要 jQuery 或 Mootools 或 Dojo 或任何框架来……嗯,工作。
然后,我们在我们的标记中添加一个图像:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Retina.js - JavaScript Solution</title>
</head>
<body>
<img src="img/grandmasters-default.jpg" alt="">
<script src="img/retina.min.js"></script>
</body>
</html>
就是这样!我们不必对标记做任何事情,除非我们想要排除被替换的图像。我将在接下来的内容中解释如何做到这一点。
Retina.js
的 JavaScript 解决方案的基本功能是查找页面中的图像,并在服务器上存在高分辨率版本时用高分辨率版本替换它们。
您需要在高分辨率图像的名称末尾加上@2x
修饰符。
换句话说,如果您有以下图像:
<img src="img/grandmasters-default.jpg" alt="">
Retina.js 用以下内容替换它:
<img src="img/strong>.jpg" alt="">
只要服务器上存在@2x
图像,Retina.js
就会替换它。如果图像不存在,它就不会替换。
不包括图片
如果您已经排除或希望排除图像被Retina.js
替换,您可以为图像添加data-no-retina
属性:
<img src="img/grandmasters-default.jpg" alt="" data-no-retina>
Retina.js——Sass mixin 解决方案
嗯,这很奇怪——一个 JavaScript 解决方案,竟然也有 CSS 解决方案?太棒了!请注意,这个 Sass mixin 是用于应用背景高分辨率图片的。
Sass mixin 如下所示:
@mixin at2x($path, $ext: "jpg", $w: auto, $h: auto) {
$at1x_path: "#{$path}.#{$ext}";
$at2x_path: "#{$path}@2x.#{$ext}";
background-image: url("#{$at1x_path}");
@media all and (-webkit-min-device-pixel-ratio : 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
background-image: url("#{$at2x_path}");
background-size: $w $h;
}
}
使用方法非常简单:
.hero {
width: 100%;
height: 510px;
@include at2x('../images/grandmasters-default', jpg, 100%, auto);
}
我们需要声明文件扩展名、宽度和高度,用逗号分隔的值。前面的 Sass 代码片段将编译为这样:
.hero {
width: 100%;
height: 510px;
background-image: url("../images/grandmasters-default.jpg");
}
@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3 / 2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) {
.hero {
background-image: url("../images/grandmasters-default@2x.jpg");
background-size: 100% auto;
}
}
这是我在 CodePen 上创建的演示:codepen.io/ricardozea/pen/c3af015b325da6ee56cf59e660f3cc03
提示
使用background-size: 100% auto;
,背景图像将拉伸到其父容器的最大宽度。但是,如果容器更宽,图像将被重复。
使视频响应式
我们要讨论的视频是嵌入在我们的好朋友<iframe>
元素中的视频,比如来自 YouTube、Vimeo、Dailymotion 等的视频。有几种方法可以使视频响应式,有些方法比其他方法更复杂。让我们来分解一下。
使用 HTML 和 CSS 创建响应式视频
YouTube 是一个令人惊叹的视频服务,使视频作者、网页设计师和开发人员的生活更加轻松。YouTube 负责视频的托管、流媒体和技术条件,这些条件包括不支持 Flash(iOS)或不支持<video>
标签(旧版浏览器)的浏览器,这真是太棒了。
我们需要做的第一件事是创建一个容器来容纳视频。这个容器是我们将要操作的,以便在保持其宽高比的同时给视频所需的宽度:
<div class="video-container"></div>
然后,我们创建一个用于嵌入视频的容器:
<div class="video-container">
<div class="embed-container"></div>
</div>
然后,我们嵌入视频,视频位于<iframe>
元素中:
<div class="video-container">
<div class="embed-container">
<iframe width="560" height="315" src="img/vpRsLPI400U" frameborder="0" allowfullscreen></iframe>
</div>
</div>
好了,这就是我们的标记。现在,让我们从内到外处理 CSS。
让我们给<iframe>
元素添加一些属性:
.embed-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
然后,让我们给.embed-container
包装器添加一些上下文:
.embed-container {
position: relative;
padding-bottom: 56.25%;
padding-top: 35px; /* This padding is only needed for YouTube videos */
height: 0;
overflow: hidden;
}
现在<iframe>
元素将被正确定位并占据其父容器的所有空间。父容器将确保视频可见,任何超出父容器的部分将被隐藏。
提示
对于 16:9 宽高比的视频,请使用padding-bottom: 56.25%;
。
对于 4:3 宽高比的视频,请使用padding-bottom: 75%;
。
现在我们需要做的就是定义整个东西的宽度。我们通过为外部容器.video-container添加宽度来实现这一点:
.video-container {
width: 80%; /* This can be any width you want */
}
使用 jQuery 创建响应式视频
如果您是 jQuery 的粉丝,这个插件适合您。当您需要在网站上已经发布的视频上进行改装,或者需要手动更新太多视频时,它也可能会派上用场。
这个插件叫做 FitVids.js。它是由 Chris Coyer 和 Paravel 的人开发的。使用 FitVids.js 非常简单。首先,我们需要从以下 URL 下载 FitVids JavaScript 文件:github.com/davatron5000/FitVids.js/blob/master/jquery.fitvids.js
然后,在文档的<head>
中调用 jQuery 和 FitVids.js 文件。最后,在我们的标记底部添加一个脚本来调用fitVids
函数。基本上就是这样。
提示
FitVids.js
的实际文件名是jquery.fitvids.js
。这是我们将在示例中看到的文件名。
这是一个包含两个视频的 HTML 片段,分别来自 YouTube 和 Vimeo 的<iframe>
:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="img/jquery.min.js"></script>
<script src="img/jquery.fitvids.js"></script>
<title>Responsive Videos with: jQuery Using FitVids.js</title>
</head>
<body>
<h1>Responsive Videos with: jQuery Using FitVids.js</h1>
<main class="main-container" role="main">
<h2>YouTube</h2>
<iframe width="560" height="315" src="img/vpRsLPI400U" frameborder="0" allowfullscreen></iframe>
<h2>Vimeo</h2>
<iframe width="560" height="315" src="img/101875373" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</main>
<script>
$(function(){
//Look for all the videos inside this element and make them responsive
$(".main-container").fitVids();
});
</script>
</body>
</html>
如果你对FitVids.js
如何修改 DOM 以使视频响应式感兴趣,这是标记:
<div class="fluid-width-video-wrapper" style="padding-top: 56.25%;">
<iframe src="img/vpRsLPI400U" frameborder="0" allowfullscreen="" id="fitvid0"></iframe>
</div>
提示
文档对象模型(DOM):当你读到或听到有人说修改 DOM时,基本上意味着修改生成的 HTML。
这是我在 CodePen 上为此创建的演示:codepen.io/ricardozea/pen/9e994c213c0eeb64ccd627e132778a42
。
使用纯 JavaScript 响应式视频
如果你不使用 jQuery 或不想要任何框架依赖,但仍需要一个简单的 JavaScript 解决方案,最好的选择是使用 Todd Motto 开发的脚本:Fluidvids.js
。
使用它也很简单。首先,我们需要下载 Fluidvids JavaScript 文件:github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.min.js
然后,我们需要在文档的<head>
元素中调用fluidvis.js
文件。一旦我们完成这一步,我们在标记底部添加一个小的脚本片段。就是这样。脚本将阅读标记,修改 DOM,并使它找到的任何视频响应式。
提示
确保始终为<iframe>
元素提供width
和height
值。否则,页面上会出现空白空间。
这是你需要使其工作的 HTML 片段:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="img/fluidvids.min.js"></script>
<title>Responsive Videos with: Plain JavaScript - FluidVids.js</title>
</head>
<body>
<h1>Responsive Videos with: Plain JavaScript - FluidVids.js</h1>
<main class="main-container" role="main">
<h2>YouTube</h2>
<iframe width="560" height="315" src="img/vpRsLPI400U" frameborder="0" allowfullscreen></iframe>
<h2>Vimeo</h2>
<iframe width="560" height="315" src="img/101875373" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</main>
<script>
fluidvids.init({
selector: ['iframe'],
players: ['www.youtube.com', 'player.vimeo.com']
});
</script>
</body>
</html>
这是修改后的 DOM:
<div class="fluidvids" style="padding-top: 56.2%;">
<iframe src="img/vpRsLPI400U" width="560" height="315" frameborder="0" allowfullscreen class="fluidvids-item" data-fluidvids="loaded"></iframe>
</div>
这是我在 CodePen 上为此创建的演示:codepen.io/ricardozea/pen/fda7c2c459392c934130f28cc092dbbe
第三方服务来嵌入视频
我能说什么呢?你只需要将浏览器指向embedresponsively.com/
,并选择你想要使用的视频服务的选项卡。让我们选择 Vimeo。输入你想要使其响应式的视频的 URL,点击嵌入按钮,然后,你需要使用的 HTML 和 CSS 就会出现在示例视频的正下方。
这是由embedresponsively.com生成的用于关于 RWD 的 Dan Mall 视频的 HTML 和 CSS 片段(已经格式化以便阅读):
HTML 如下:
<div class='embed-container'>
<iframe src='https://player.vimeo.com/video/101875373' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
CSS 如下:
.embed-container {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
max-width: 100%;
}
.embed-container iframe,
.embed-container object,
.embed-container embed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
然而,使用以下片段,视频的容器看起来比应该高得多。为了使前面的片段正常工作,我们需要将嵌入容器包装在外部容器内。这是修改后的标记和 CSS。
HTML 如下:
<div class="video-container">
<div class='embed-container'>
<iframe src='https://player.vimeo.com/video/101875373' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
</div>
CSS 如下:
.video-container {
width: 100%;
}
.embed-container {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
background: red;
}
.embed-container iframe,
.embed-container object,
.embed-container embed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.video-container
包装器是我们操纵的,以便定义任何我们想要的宽度,同时保持视频的纵横比。现在,我们只需要将标记放在我们的 HTML 文档中,将 CSS 片段放在我们的 SCSS 文件中。
这是我在 CodePen 上为此创建的演示:codepen.io/ricardozea/pen/10262216eeb01fc9d3b3bedb9f27c908
矢量格式
我们将看到一些 HTML 和 CSS/SCSS 片段,以了解如何使用图标字体和SVG,但我们不会详细介绍这些资产的创建过程,因为这个过程超出了本节的范围。
矢量或位图/光栅图像
当人们询问矢量和位图/光栅图像之间的区别时,我经常听到的答案通常围绕着“如果你放大它,它不会失去质量。对移动设备也不用担心。”虽然这是真的,但它并没有完全回答这个问题。所以这里是区别:
矢量图像是由数学方程组成的文件。这些方程的结果由图形(线条、形状、颜色)表示。如果图像的大小以任何方式改变,这些方程的值将被重新计算,生成的图形将被重新绘制。
位图或光栅图像是由像素组成的文件。这些像素具有特定/定义的宽度、高度和颜色。如果图像被放大,像素就会被拉伸,这就是为什么图像看起来模糊或呈像素化的原因。
有了这些定义,让我们来谈谈用于 RWD 的一些矢量格式。矢量格式包括:
-
Web 字体
-
图标字体
-
SVG
让我们看看如何快速实现图标字体和 SVG;Web 字体将在下一章中讨论。
图标字体
图标字体基本上是一个字体文件,但它不是字母,而是图标。有些人喜欢图标字体(我喜欢),有些人对它们并不太喜欢,特别是因为 SVG 变得如此受欢迎。
让我们看看图标字体的优缺点。
一些优点是:
-
图标字体的文件大小很可能比它们的 SVG 对应文件要小。我们可以在单个字体文件中有更多的图标,而且它的重量要比有一个 SVG 精灵要轻得多。
-
图标字体的属性可以用于修改文本的任何属性,例如颜色、字体系列、字重等。毕竟,它是一个字体。这意味着我们不必学习任何新的语法或属性。
-
它们相对容易实现。一旦所有的
@font-face
属性被设置,调用一个图标字体只是在 HTML 中添加一个类,并在 CSS 中调用一个特定的 Unicode 点代码。 -
图标字体是矢量图形,因此它们在任何屏幕密度、屏幕尺寸和缩放级别上都保持最佳质量。
-
它们非常适合设计。一个单独的图标字体可以被包裹在一个有颜色的容器中,图标可以被保留(挖空),但仍然是相同的图标,不需要单独的文件。
一些缺点是:
-
更新自定义设计的图标可能需要一些工作,因为我们需要使用第三方应用程序来生成我们的图标字体文件。
-
图标字体只能使用单一颜色。我真的不认为这是一个缺点。
-
图标字体的主要缺点之一是,实现一个备用方案以防字体文件加载失败有点复杂,而且如果你问我,有点啰嗦。这种模式的名称是“字体卫士”。如果你想了解更多,请查看 Zach Leatherman 的文章Bulletproof Accessible Icon Fonts(
www.filamentgroup.com/lab/bulletproof_icon_fonts.html
)。GitHub 仓库可以在github.com/filamentgroup/a-font-garde
找到。
在使用图标字体时,我可以给你一些建议:
-
如果可能的话,避免在关键内容中使用它们。
-
在使用图标字体的元素中始终提供一个
title=""
属性。如果字体文件加载失败,至少可以看到标题标签中的文本。 -
如果你愿意,可以使用额外的 HTML 元素来容纳图标。如果图标字体文件加载失败,无论用户是否使用辅助技术,都可以使用图标字体代表的功能。
-
在我的多年经验中,我从未见过图标字体文件加载失败,但这并不意味着它不可能发生。因此,我建议及时查看服务器日志,以确定图标字体文件是否被下载。如果没有,那么您需要尽快解决这个问题。
然后我们来实现一个图标字体。
实现图标字体
获取图标字体文件的最快方法是使用像 IcoMoon.io 或 Fontello.com 这样的第三方网络应用程序。您也可以获得 Font Awesome 的副本。
提示
在考虑使用 Font Awesome 时要小心。使用一个包含数十个图标的完整字体文件,只使用其中的一小部分是浪费带宽的。如果你只打算使用少量图标字体,使用 IcoMoon.io 或 Fontello.com 进行自定义图标选择是一个更好的选择。
一旦你能解压提供的文件,你唯一需要的文件就是.woff
文件。你只需要这个文件的原因是因为浏览器对.woff
文件的支持一直可以追溯到 IE9。除非你想/需要支持旧版浏览器(桌面和移动端),你可以使用.eot
、.ttf
和.svg
文件。
提示
我建议你保持简单,避免在尝试支持旧版浏览器中出现不必要的麻烦。他们只会得到文本而不是图标,或者在title=""
属性中显示文本。
让我们将图标字体文件命名为icon-font.woff
。创建一个/fonts
文件夹,并将icon-font.woff
文件保存在其中。这是我们要尝试实现的:一个带有左侧图标的浅蓝色链接,没有下划线,以及 40px Arial/Helvetica 字体:
使用伪元素
使用伪元素的好处是我们的源标记始终保持清晰。在这种情况下,我们将使用:before
伪元素,但这种技术也适用于:after
伪元素。
让我们来看一下构建。
这是 HTML 片段:
<a href="#" class="icon icon-headphones" title="Headphones">Headphones</a>
这是 SCSS。我们需要的第一件事是一个 mixin 来处理任何自定义网络字体。在这种情况下,它是一个图标字体:
//Web font mixin
@mixin fontFace($font-family, $file-path) {
@font-face {
font: {
family: $font-family;
weight: normal;
style: normal;
}
src: url('#{$file-path}.woff') format('woff');
}
}
提示
注意font: {…}
块中的嵌套属性。通过这样做,我们保持代码的 DRY,并避免重复术语font用于以下实例:font-family
、font-weight
和font-style
。
然后,我们使用属性选择器创建一条规则来处理图标字体的基本样式属性:
//Icon Font specific rule
[class^="icon-"], [class*=" icon-"] {
font: {
family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;
weight: normal;
style: normal;
variant: normal;
}
text-transform: none;
line-height: 1;
speak: none;
// Improve Font Rendering
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
提示
注意属性选择器中的^
和*
字符。第一个意味着选择以术语 icon-
开头的元素,第二个选择包含术语 icon-
的元素。
然后,我们需要调用fontFace
mixin 来将字体引入编译后的 CSS 文件中:
@include fontFace(icon-font, '/fonts/icon-font');
fontFace
mixin 的好处是我们只需要声明字体名称,然后是文件路径。不需要声明文件扩展名;这由 mixin 来处理。
这将编译为:
@font-face {
font-family: icon-font;
font-weight: normal;
font-style: normal;
src: url("/fonts/icon-font") format("woff");
}
这是使用:before
使魔法发生的规则:
.icon-headphones:before {
content: "\e601";
margin-right: 10px;
}
为了基本的样式增强,我们创建了另外两条规则。但是,它们并不是必需的。代码如下:
.icon { font-size: 40px; }
a {
padding: 5px;
text-decoration: none;
color: #2963BD;
transition: .3s;
&:hover { color: lighten(#2963BD,20); }
&:focus { outline: 2px solid orange; }
}
最终编译的 CSS 如下:
[class^="icon-"], [class*=" icon-"] {
font-family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;
font-weight: normal;
font-style: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@font-face {
font-family: icon-font;
font-weight: normal;
font-style: normal;
src: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/icon-font.woff") format("woff");
}
.icon-headphones:before {
content: "\e601";
margin-right: 10px;
}
.icon {
font-size: 40px;
}
a {
padding: 5px;
text-decoration: none;
color: #2963BD;
-webkit-transition: .3s;
transition: .3s;
}
a:hover {
color: #6d9adf;
}
a:focus {
outline: 2px solid orange;
}
这是我在 CodePen 上为此创建的演示:codepen.io/ricardozea/pen/e62b201350efe7f59f91c934f9fc30fa
这是我在 CodePen 上创建的另一个演示,其中图标字体更加高级:codepen.io/ricardozea/pen/5a16adffb6565312506c47ca3df69358
使用额外的 HTML 元素
老实说,使用额外的 HTML 元素有点违背了将内容与样式分离的原则,因为出于样式原因添加额外的 HTML 元素并不是一些开发人员推荐的做法。然而,我们也可以说图标本身确实是内容,而不是样式。无论如何,这是概述。
这是 HTML 片段:
<a href="#" title="Headphones"><i class="icon-headphones" aria-hidden="true"></i>Headphones</a>
提示
为了隐藏屏幕阅读器中的不相关内容,我们使用aria-hidden="true"
指令。
前面示例中的 SCSS 代码几乎相同,只是我们将.icon
类中的font-size: 10px;
声明移到a
规则中,然后完全删除.icon
类。你还会看到一些额外的属性,但只是出于样式原因。
最终的 SCSS 如下:
//Web font mixin
@mixin fontFace($font-family, $file-path) {
@font-face {
font: {
family: $font-family;
weight: normal;
style: normal;
}
src: url('#{$file-path}.woff') format('woff');
}
}
//Icon Font specific rule
[class^="icon-"], [class*=" icon-"] {
font: {
family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;
weight: normal;
style: normal;
variant: normal;
}
text-transform: none;
line-height: 1;
speak: none;
// Improve Font Rendering
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@include iconFont(icon-font, '/fonts/icon-font');
.icon-headphones:before {
content: "\e601";
margin-right: 10px;
}
a {
font-size: 40px;
//Styling stuff
padding: 5px;
text-decoration: none;
color: #2963BD;
transition: .3s;
&:hover { color: lighten(#2963BD,20); }
&:focus { outline: 2px solid orange; }
}
编译后的 CSS 如下:
[class^="icon-"], [class*=" icon-"] {
font-family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;
font-weight: normal;
font-style: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@font-face {
font-family: icon-font;
font-weight: normal;
font-style: normal;
src: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/icon-font.woff") format("woff");
}
.icon-headphones:before {
content: "\e601";
margin-right: 10px;
}
a {
font-size: 40px;
padding: 5px;
text-decoration: none;
color: #2963BD;
-webkit-transition: .3s;
transition: .3s;
}
a:hover {
color: #6d9adf;
}
a:focus {
outline: 2px solid orange;
}
这是我在 CodePen 上为此创建的演示:codepen.io/ricardozea/pen/8ca49cb06aeb070f4643f0a8e064126c
。
可缩放矢量图形
SVG 图形非常快速地获得了令人难以置信的流行。浏览器支持度为 100%,甚至 Opera Mini 也支持 SVG 图像。让我们讨论一些 SVG 图像的优缺点:
SVG 的优点:
-
它们可以用文本编辑器创建和编辑。
-
它们 100%可访问。
-
它们可以有多种颜色。
-
它们对 SEO 友好,因为它们可以被索引。
-
由于它们是矢量图形,它们在任何屏幕密度、屏幕尺寸或缩放级别上都保持其质量。
-
它们可以被动画化,甚至是
<svg>
标签内的元素。 -
SVG 规范是由 W3C 开发的一个实际的开放标准。
-
这可能比使用字体进行图形更语义化。
-
第三方在线图标工具也可以导出为 SVG,除了图标字体。
-
现代浏览器中支持度为 100%。
SVG 的缺点:
-
一个 SVG 精灵文件可能比其图标字体对应文件更重。
-
如果需要支持旧版浏览器(IE8 及以下),则需要图像回退。
-
通常可以保存为 SVG 的软件会在最终文件中添加额外的不必要的标记,因此我们要么必须手动删除它,要么使用第三方优化工具为我们每个文件执行此操作。这反过来又给开发工作流程增加了另一层复杂性。
-
尽管 SVG 是用 XML 结构制作的,但需要相当高级的理解水平才能在文本编辑器中进行编辑。
SVG 文件基本上是一个 XML 格式的文件。这是耳机图形的标记样式:
<svg width="32" height="32" viewBox="0 0 32 32">
<path id="left-ear-pad" d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z"/>
<path id="right-ear-pad" d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z"/>
<path id="headband" d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>
</svg>
有许多使用 SVG 图像的方法:通过<img>
、<object>
、<use>
或<svg>
标签内联;作为 CSS 的背景图像;使用 Modernizr 在条件类中使用回退;或者使用 jQuery 或纯 JavaScript,使用第三方服务如 grumpicon.com 等。
为了保持简单,我们将专注于两种方法:
-
通过
<svg>
标签内联。 -
基于文件的
<img>
标签。
通过<svg>
标签内联
内联 SVG 是许多网页设计师和开发人员的首选方法。我们可以使用 CSS 和 JavaScript 控制 SVG 的各个部分,这使得它非常适合动画效果。
内联 SVG 标记的一个缺点是图像不可缓存。换句话说,每次图像出现时,浏览器都必须读取 SVG 的 XML。如果页面上有太多的 SVG,这可能对页面速度和最终用户体验造成潜在的危害。因此,请注意页面的目标和使用您的网站/应用程序的访问者类型。
这是 SVG 耳机的 HTML 片段,内联在链接标签中:
<a href="#">
<svg width="32" height="32" viewBox="0 0 32 32">
<path id="left-ear-pad" d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z" />
<path id="right-ear-pad" d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z" />
<path id="headband" d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>
</svg>Headphones
</a>
为了控制其大小、与文本的距离和外观,我们添加以下 CSS:
svg {
width: 40px;
height: 40px;
margin-right: 10px;
fill: #2963BD;
}
a {
font-size: 40px;
text-decoration: none;
color:#2963BD;
}
提示
通过<img>
标签调用的 SVG 文件不受CSS 的影响。如果要对其进行任何样式更改,必须在实际的 SVG 文件中进行更改,或者将 SVG 标记内联。
然而,这个标记有一个问题。它没有为旧版浏览器提供回退,特别是 IE8 及以下版本。让我们试着解决这个问题。
为内联 SVG 提供回退图像给旧版浏览器
为内联 SVG 提供回退图像的两种方法。
使用<foreignObject>
和<img>
标签
在<svg>
标签内创建一个<foreignObject>
元素,并包含调用回退图像的<img>
标签:
<a href="#">
<svg version="1.1" width="32" height="32" viewBox="0 0 32 32">
<path d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z"/>
<path d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z"/>
<path d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>
<foreignObject>
<img src="img/headphones.png" alt="Headphones">
</foreignObject>
</svg>Headphones
</a>
使用<image>
标签
众所周知,在 SVG 世界中没有<image>
标签...或者有吗?在 SVG 世界中,是有的!这个解决方案与第一种方法非常相似。两个不同之处在于我们不使用<foreignObject>
元素,并且使用<image>
标签。这一切都在<svg>
标签内部:
<a href="#">
<svg width="32" height="32" viewBox="0 0 32 32">
<path id="left-ear-pad" d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z" />
<path id="right-ear-pad" d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z" />
<path id="headband" d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>
<image src="img/headphones.png" xlink:href="" alt="Headphones">
</svg>Headphones
</a>
现在,这个方法有效的原因是因为我们将 SVG 和 HTML 的特性结合到一个元素中。
SVG 的特点是<image>
标签是 SVG 世界中的有效元素。现在,尽管听起来很奇怪,但所有浏览器都将<image>
标签视为一个超出标准的标签,类似于 HTML 中的<img>
标签。
HTML 的特点是,通常我们使用src
属性来指向资源的位置。在 SVG 世界中,资源被称为xlink:href
属性。如果我们添加一个指向资源的src
属性,并且将xlink:href
属性留空,那么旧版浏览器将看到备用图像,而现代浏览器不会,因为xlink:href
属性是空的。
我建议坚持第二种方法;它更简洁,更省事。只要记住,我们使用<image>
而不是<img>
。另外,为了本书的目的,我在标记中保留了xlink:href
属性,但这是可选的。如果它是空的,你可以根据需要完全删除它。
提示
在整本书中,我已经删除了自闭合标签的尾部斜杠/>
,比如<hr>
或<img>
元素。在 HTML5 中,可以选择带或不带。然而,在 SVG 的path
元素中,尾部斜杠是必需的,这就是为什么你在这些示例中看到它们的原因。
我刚刚提到的这些方法都不会在支持 SVG 的浏览器上导致双重下载。如果你问我,这是一个双赢的局面。
基于文件的 xlink:href 和 src 属性
SVG 是一种图像文件,因此在<img>
中调用它是完全有效的:
<img src="img/headphones.svg" alt="Headphones">
我们知道 SVG 在现代浏览器中有无缺的支持,但在旧版浏览器(IE8 及以下)中不显示先前的图像。
记得之前关于 SVG 和 HTML 中xlink:href
和src
属性的解释吗?嗯,我们要做的基本上和之前一样。不过,与其内联 SVG 标记,我们只是链接到一个 SVG 文件,同时为旧浏览器提供一个备用图像。
这个聪明的技巧是由 Alexey Ten 创建的。这是标记:
<a href="#">
<svg width="39" height="39">
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/headphones.svg" src="img/strong>" width="39" height="39">
</svg>Headphones
</a>
这里也有问题。Alexey 的技术不是问题,问题在于浏览器——特别是 IE9、10 和 11 以及 iOS 3 和 4。它们会同时下载 SVG 和备用图像。
如果这种双重下载对你来说是可以接受的,并且你理解后果,那就去做吧。尽管如此,记住在下一个项目中可以改进这样的事情。
这是我在 CodePen 上为此创建的演示:
codepen.io/ricardozea/pen/594e718f36976f8e77d4f9cf1640e29a
学习 SVG 的其他来源
我们谈论 SVG 时,不能不提到当今网络设计和开发行业中最引人注目的三个名字:Amelia Bellamy-Royds、Sara Soueidan 和 Chris Coyer。Amelia 和 Chris 创建了我读过的关于如何使用带有备用的 SVG 的最完整指南之一,《A Complete Guide to SVG Fallbacks》(css-tricks.com/a-complete-guide-to-svg-fallbacks/
)。
如果你想学习关于 SVG 的一切,Sara Soueidan 的博客是必读的:sarasoueidan.com/articles/
。
**# 总结
在这里,我们正在看着地平线,思考着srcset
或<picture>
?调整大小还是艺术方向?Retina.js
还是 Sass mixin?FitVids 还是 FluidVids?图标字体还是 SVG?内联 SVG 还是基于文件的 SVG?什么是为我们的访客提供最佳体验的最佳方式?
是的,我知道这种感觉。你知道吗?这是一个好问题。否则,我们就不会学习如何掌握 RWD 了。
由于大多数时候我们只是调整图片大小,srcset
是一个不错的选择。将我们的视频放入一个容器并加上几行 CSS,这样视频就能立即响应。太多的视频需要响应?没问题,FitVids.js
可以通过一个单独的 jQuery 函数实现。图标字体比它们的大哥 SVG 文件要轻,但要注意服务器日志,以防图标字体文件没有下载。使用 SVG 始终是一个胜利,即使有双重下载,但要通过使用不同的技术不断提升水平,并与他人分享你的发现和经验。
让我们换个话题,谈谈一个迷人的主题,它可以决定你的响应式设计成败:排版。
让我们出发吧!**
第七章:响应式网页设计的有意义的排版
正如我在 Dayton Web Developers 会议上的一次演讲中所说的:
"有了稳固的排版比例,甚至可能不需要在网站上使用任何图片。"
排版的力量一定是网页设计中最被低估的资产之一。诚然,我们看到越来越多的设计中,排版已经得到了充分考虑,在创建网站或应用程序预期氛围方面发挥了重要作用。
在本章中,我们的重点将放在从排版角度考虑 RWD 所需考虑的一些方面、技巧和窍门上。
我们将讨论:
-
像素、ems 或 rems 用于排版?
-
计算相对字体大小。
-
为和谐的排版创建模块化比例。
-
使用模块化比例进行排版。
-
网络字体及其对 RWD 的影响。
-
使用
FitText.js
进行流式标题大小。 -
使用
FlowType.js
来提高可读性。
像素、ems 或 rems 用于排版?
很难决定是使用像素、ems 还是 rems 进行排版。这是一个风格问题。一些网页设计师/开发人员仍然使用像素作为声明字体大小的单位。这样做只是更容易理解这些大小。
在设置像素字体大小方面的问题基本上出现在旧版 IE 上,如果用户因为任何原因想要放大页面,文本将保持在给定的像素大小不变。
现在,对于现代浏览器来说,这已经是过去的事情了。当你在任何现代浏览器中放大,如果放大到足够大,它将触发媒体查询,因此显示站点的移动版本。
基于像素的字体大小设置的另一个问题是很难进行缩放和维护。这基本上意味着我们需要在每个媒体查询中重复声明许多元素的字体大小。
另一方面,我们有相对单位,ems 和 rems,这基本上是设置我们的字体大小的推荐方式。
然而,ems 的问题在于我们必须跟踪(在脑海中、在 CSS/HTML 注释中或在某个文本文件中)父容器的大小,这很容易变成字体管理的噩梦。ems 中的字体大小取决于其父容器的字体大小。因此,如果我们有不同级别的嵌套容器,情况可能会变得非常复杂,因为跟踪父容器的字体大小并不容易。
但后来出现了rem。Rem 代表根 em。根是<html>
元素。
Rems 基本上融合了两者的优点:我们可以使用相对单位 ems 声明 rems 中的字体大小,但又能像使用像素一样轻松理解。使用 rems 的唯一问题是旧版浏览器不支持这个单位,因此需要考虑基于像素的字体大小回退值。这就是一个简短的 Sass mixin 出现并拯救一天的地方。
但在尝试任何 Sass 技巧之前,让我们先从本章的核心策略开始。
计算相对字体大小
还记得我们在第三章中提到的 RWD 魔法公式吗,Mobile-first or Desktop-first?:
(目标 ÷ 上下文)x 100 = 结果%
还有另一个类似的魔法公式,用于计算相对字体大小(ems),当字体大小已经以像素设置时。唯一的区别是我们不乘以 100。
这就是那个公式:
目标 ÷ 上下文 = 结果
目标是以像素定义的字体大小。上下文是在父容器中定义的字体大小。结果是以 ems 定义的值。
以下是一个示例,假设父容器中的字体大小,在这个例子中是 body,为 16px:
header {
font: 30px Arial, "Helvetica Neue", Helvetica, sans-serif;
}
要计算相对字体大小,我们使用以下公式:
30px ÷ 16px = 1.875em。
因此,我们的 CSS 规则将如下所示:
header {
font: 1.875em Arial, "Helvetica Neue", Helvetica, sans-serif;
}
我们需要为设计中的每个字体大小都这样做。
在理解数学方面是可以的。然而,真正的价值在于创造这些基于像素的值的思考过程。这就是模块比例的价值所在。
创建和谐的排版模块比例
模块比例是由 Tim Brown 创建的。有不同的方法来创建用于排版的模块比例。在我们的例子中,我们将使用两个基本数字和一个比例来创建一个模块比例。这些数字的乘积创建了一个在所有值之间和谐和成比例的比例。
最著名的比例是黄金比例,也被称为黄金分割,神圣比例等等。它的值是1.618。
现在,为了避免不必要的数学计算,黄金比例是基于斐波那契数列的:1, 1, 2, 3, 5, 8, 13, 21 等等。
这些数字有以下的模式:下一个数字是前两个数字相加的结果。例如:
0 + 1 = 1 + 1 = 2 + 1 = 3 + 2 = 5 + 3 = 8 + 5 = 13 + 8 = 21…
这里的想法是理解创建一组数字的意图,当它们一起使用时是和谐的。我们将使用相同的方法来创建一个排版比例,以便在我们的项目中使用模块比例网页应用程序,并忘记手动计算项目的相对字体大小。
所以让我们来看看由 Tim Brown 和 Scott Kellum 构建的模块比例网页应用程序:www.modularscale.com/
。
一旦网页应用程序打开,我们需要按照以下三个步骤来创建我们的模块比例:
-
定义第一个基本数字。
-
定义第二个基本数字。
-
选择一个比例。
提示
模块比例可以用于任何使用某种值的东西,不仅仅是排版。它可以用于填充
,边距
,行高
等等。然而,我们在本章的重点是排版。
定义第一个基本数字
定义第一个基本数字的推荐方法是使用正文文本大小,也就是段落中使用的字体大小。但请记住,使用正文文本大小作为第一个基本数字并不是强制性的。我们可以使用我们字体的 x 高度,或者在该字体中的其他长度,我们认为可能是一个很好的起点。
虽然我们可以选择任何字体大小,但让我们从我们都知道所有浏览器使用的默认字体大小开始,即 16px。所以我们在第一个基本字段中输入16px
。
点击加号图标并添加第二个基本字段。
提示
暂时不用担心应用程序的字体大小预览,因为你可以看到,当我们为我们的基本值输入数字时,右侧预览窗格中的字体大小会改变。我们将在下一步中介绍这一点。
定义第二个基本数字
第二个基本字段是我称之为魔术数字,因为这个数字完全是主观和任意的,但它与我们正在进行的项目紧密相关。
当我说紧密相关时,我的意思是例如使用主容器的宽度,例如 960px,980px,1140px 等。或者,它也可以是网格中使用的列数,例如 12 或 16。它也可以是站点最大宽度处的列宽,例如 60px,甚至是间距,比如 20px。
这个魔术数字可以是我们想要的任何东西,但它与我们的项目有直接关系。在这个例子中,假设我们的目标是针对最大宽度为 1280px 的屏幕,所以我们的主容器将具有最大宽度为 1140px。所以让我们在第二个基本字段中输入1140px
。
选择一个比例
这就是魔术发生的地方。选择一个比例意味着这个比例将与基本数字相乘,从而创建一个比例相关的值的比例。
这些比例是基于音乐音阶的,列表中还包括黄金比例(1.618),如果我们决定使用它的话。从比例下拉菜单中,选择1:1.618 - 黄金分割比例。
就是这样!我们现在已经创建了我们的第一个模块比例。
由模块比例提供的字体大小完全和谐,因为它们是相对于彼此的比例值,这些比例值与我们项目直接相关:
-
理想的正文字体大小是 16px
-
我们主容器的最大宽度是 1140px
-
黄金比例是 1.618
我们的排版现在有了坚实的模块基础,让我们使用它。
使用模块比例进行排版
如果您点击表格视图,所有文本现在都消失了,我们只剩下一系列字体大小,范围从非常小的值到非常大的值。但没关系。这就是模块比例的力量。
这是我们看到的:
如前面的图像所示,有三列:
-
第一列显示了像素单位的字体大小。
-
第二列显示了 em 单位的字体大小。
-
第三列显示了基准为 16px 时的字体大小。
我们需要专注于第一列和第二列。突出显示的 16px,或 1em 的行,将成为我们段落的字体大小。16px 是大多数浏览器中的默认字体大小。
然后,我们定义我们的标题元素。假设我们只定义h1
,h2
和h3
。这意味着我们将选择大于 16px 的行,具有更大的字体大小:
-
<h1>
:39.269px,即 2.454em -
<h2>
:25.888px,即 1.618em -
<h3>
:24.57px,即 1.517em
对于<small>
元素,如果网站上有任何免责声明,我们选择小于 16px 的字体大小:
<small>
:9.889px,即 0.618em
就是这样!模块比例中的所有数字都是和谐的,当一起使用时将提供清晰的视觉层次结构,以及通过其他方法难以获得的关系。
这里有一个例子。
这是 HTML:
<h1>Meaningful Typography for RWD</h1>
<blockquote>
<p>"With a solid typographic scale you might even get away with not using a single image on your website."</p>
<p>— Ricardo Zea</p>
</blockquote>
<h2>Creating a Modular Scale for a Harmonious Typography</h2>
<p>A Modular Scale is a combination of a ratio of two or more numbers, and a base number.</p>
<h3>The Golden Ratio</h3>
<p>The most well-known ratio is the Golden Ratio also known as the Golden Section, Divine Proportion, etc. It's value is 1.618.</p>
这是 SCSS:
//Mobile-first Media Query Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content; }
}
body {
font:16px/1.4 Arial, "Helvetica Neue", Helvetica, sans-serif;
@include forLargeScreens(640) {
font-size: 20px;
}
}
h1 { font-size: 2.454em; }
h2 { font-size: 1.618em; }
h3 { font-size: 1.517em; }
提示
注意我也包含了移动优先的 Sass mixin。
这是编译后的 CSS:
body {
font: 16px/1.4 Arial, "Helvetica Neue", Helvetica, sans-serif;
}
@media (min-width: 40em) {
body {
font-size: 20px;
}
}
h1 {
font-size: 2.454em;
}
h2 {
font-size: 1.618em;
}
h3 {
font-size: 1.517em;
}
在小屏幕上(宽 510px)模块比例如下:
在大屏幕上(宽 850px)也是这样:
我们在这里唯一可能遇到的问题是我之前提到的关于使用 em 的问题:跟踪父元素的字体大小可能会变成字体管理的噩梦。
使用像素是不可取的,因为在传统浏览器中存在可伸缩性问题。然而,使用 rems 可以保持在“相对字体大小”领域,同时提供基于像素的思维方式,但没有可伸缩性问题。这使我们能够支持不支持 rems 的传统浏览器。
这是我在 CodePen 为此创建的演示:
codepen.io/ricardozea/pen/0b781bef63029bff6155c00ff3caed85
rems-to-pixels Sass mixin
我们只需要一个 Sass mixin,允许我们设置没有特定单位的字体值,mixin 会负责为现代浏览器添加 rem 单位的字体大小,为传统浏览器添加像素单位的字体大小。
这是由 Chris Coyer 创建的 Sass mixin:
//Pixels to Rems Mixin
@mixin fontSize($sizeValue: 1.6) {
font-size: ($sizeValue * 10) + px;
font-size: $sizeValue + rem;
}
提示
我对 mixin 的原始名称进行了小修改,从使用破折号分隔改为驼峰命名法。我这样做的原因是因为在扫描文档时,从类名中更容易找到 mixin 的名称。
用法如下:
@include fontSize(2);
这个示例使用了前一章节中使用的相同标记,所以我只会展示 SCSS 和一些截图。
SCSS 如下:
//Mobile-first Media Query Mixin
@mixin forLargeScreens($media) {
@media (min-width: $media/16+em) { @content; }
}
//Pixels to Rems Mixin
@mixin fontSize($sizeValue: 1.6) {
font-size: ($sizeValue * 10) + px;
font-size: $sizeValue + rem;
}
//Base-10 model
html { font-size: 62.5%;
@include forLargeScreens(640) {
font-size: 75%;
}
}
h1 { @include fontSize(3.9269); }
h2 { @include fontSize(2.5888); }
h3 { @include fontSize(2.457); }
p { @include fontSize(1.6); }
考虑以下几点:
-
我们将根字体大小设置为 62.5%,将字体大小减小到 10px。这样声明字体值就容易得多。例如,1.2rem 的字体大小等同于 12px,.8rem 等同于 8px,依此类推。
-
在声明 rems 的字体大小时,我们需要将小数点从基于像素的值向左移一位。例如,根据我们的模块化比例,
<h1>
像素大小为 39.269px,所以在声明 rems 的字体大小时,我们声明为 3.9269,不带单位。
编译后的 CSS 如下:
html {
font-size: 62.5%;
}
@media (min-width: 40em) {
html {
font-size: 75%;
}
}
h1 {
font-size: 39.269px;
font-size: 3.9269rem;
}
h2 {
font-size: 25.888px;
font-size: 2.5888rem;
}
h3 {
font-size: 24.57px;
font-size: 2.457rem;
}
p {
font-size: 16px;
font-size: 1.6rem;
}
这是在小屏幕上(510 像素宽)使用 rems-to-pixels mixin 的模块化比例的样子:
这是在大屏幕上(850 像素宽)的样子:
这是我在 CodePen 上创建的演示:
codepen.io/ricardozea/pen/8a95403db5b73c995443720475fdd900
我们刚刚看到的示例使用了系统字体 Arial。让我们继续使用一些网络字体来提升这些示例的特色。
网络字体及其对 RWD 的影响
现在几乎必须使用网络字体,我说几乎是因为我们需要注意它们对我们项目的影响,如果必要,我们实际上可能根本不使用它们。
在深入研究如何使用网络字体之前,以下是一些可能对你们许多人有帮助的网络字体资源:
-
Font Squirrel (
www.fontsquirrel.com/
):我已经广泛使用了这项服务并取得了巨大成功。要使用这些字体,你需要下载文件,然后在你的 CSS 中使用@font-face
。他们有你能找到的最好的网络字体生成工具(www.fontsquirrel.com/tools/webfont-generator
) -
Google Fonts (
www.google.com/fonts
):我不能谈论网络字体资源而不提到 Google Fonts。如果我在 Font Squirrel 上找不到,我就来这里,反之亦然。你可以下载字体文件,或者使用 JavaScript。以下示例中使用的字体是从 Google Fonts 下载的(github.com/google/fonts/tree/master/ofl/oswald
)。 -
Adobe Edge Web Fonts (
edgewebfonts.adobe.com/
):这也是一个很棒的工具。这项服务由 TypeKit(第一个网络字体服务)提供支持。我也广泛使用了 TypeKit。但你不能下载字体,必须使用 JavaScript。
现在,让我们看看使用网络字体的利弊:
优点包括:
-
它们有助于突出品牌,并在不同媒体上创建一致性。
-
正确使用时,它们使设计看起来更吸引人。
-
不再需要使用图像替换技术。
-
这使文本保持为 HTML,使内容更易访问和可索引。
-
旧版浏览器支持网络字体。
-
免费字体的绝佳资源。
-
所有这些都有助于保持标记更清洁。
缺点包括:
-
它们由于 HTTP 请求或对第三方服务器的依赖而减慢网站/应用的速度。
-
并非所有网络字体在小尺寸和/或大尺寸下都可读。
-
如果需要支持旧版浏览器,则需要管理更多文件。
-
使用字体需要付费:每月、每个字体系列、每种字体样式等等。
-
一些免费字体制作不良。
-
有渲染副作用:
-
未样式文本的闪烁(FOUT):在现代浏览器上,当页面加载时,文本首先用系统字体呈现在屏幕上,然后一秒钟后被切换并用网络字体进行样式设置。
-
不可见文本的闪烁(FOIT):在旧版浏览器上,当页面加载时,文本是不可见的,但一秒钟后它会用网络字体呈现出来。
还有其他不值得深入的,比如备用文本的闪烁和伪文本的闪烁(FOFT)。
如何解决所有“闪烁文本”的问题不在本节的范围之内。但是,我鼓励您阅读 Zach Leatherman 在 Opera 博客上关于使用字体加载事件改进@font-face的文章(dev.opera.com/articles/better-font-face/
)。
用于实现网络字体的 Sass mixin
要实现网络字体,我们需要在我们的 CSS 中使用@font-face
指令...嗯,SCSS。
@font-face
声明块在其原始 CSS 形式中如下所示:
@font-face {
font-family: fontName;
src: url('path/to/font.eot'); /*IE9 Compat Modes*/
src: url('path/to/font.eot?#iefix') format('embedded-opentype'), /*IE6-IE8 */
url('path/to/font.woff') format('woff'), /*Modern Browsers*/
url('path/to/font.ttf') format('truetype'), /*Safari, Android, iOS*/
url('path/to/font.svg#fontName') format('svg'); /*iOS devices*/
font-weight: normal;
font-style: normal;
}
现在,如果您使用多种样式或字体系列,您需要为每个字体文件重复整个@font-face
声明块。这不是很干净(不要重复自己)。
提示
网络字体在文件大小和服务器请求方面都很昂贵,因此请适度使用网络字体。您使用的越多,您的网站/网络应用程序就会变得越慢。
是的,处理网络字体的 CSS 代码相当庞大,哦天啊。
为了保持理智,让我们将先前的@font-face
CSS 声明块转换为 Sass mixin:
@mixin fontFace($font-family, $file-path) {
@font-face {
font: {
family: $font-family;
weight: normal;
style: normal;
}
//IE9 Compat Modes
src: url('#{$file-path}.eot');
//IE6-IE8
src: url('#{$file-path}.eot?#iefix') format('embedded-opentype'),
//Modern Browsers
url('#{$file-path}.woff') format('woff'),
//Safari, Android, iOS
url('#{$file-path}.ttf') format('truetype'),
//Safari, Android, iOS
url('#{$file-path}.svg') format('svg');
}
}
使用一行代码调用字体文件。让我们使用 Oswald 字体:
@include fontFace(oswald-light, '../fonts/oswald-light');
在任何元素上使用它只需在字体堆栈的开头添加字体名称,如下所示:
p { font: 2.2rem oswald-bold, Arial, "Helvetica Neue", Helvetica, sans-serif; }
如果我们需要包含多个字体文件,只需添加另一行调用 mixin,但指定其他字体名称:
@include fontFace(oswald-light, '../fonts/oswald-light');
@include fontFace(oswald-regular, '../fonts/oswald-regular');
前两行代码将编译为以下 CSS:
@font-face {
font-family: oswald-light;
font-weight: normal;
font-style: normal;
src: url("../fonts/oswald-light.eot");
src: url("../fonts/oswald-light.eot?#iefix") format("embedded-opentype"), url("../fonts/oswald-light.woff") format("woff"), url("../fonts/oswald-light.ttf") format("truetype"), url("../fonts/oswald-light.svg") format("svg");
}
@font-face {
font-family: oswald-regular;
font-weight: normal;
font-style: normal;
src: url("../fonts/oswald-regular.eot");
src: url("../fonts/oswald-regular.eot?#iefix") format("embedded-opentype"), url("../fonts/oswald-regular.woff") format("woff"), url("../fonts/oswald-regular.ttf") format("truetype"), url("../fonts/oswald-regular.svg") format("svg");
}
这是一种非常巧妙的方式,只需两行代码就可以创建所有这些 CSS,是吧?然而,如果我们想做正确的事情,让我们分析一下我们在这里做什么:
-
我们支持旧版浏览器:
-
IE8 及以下版本使用
.eot
字体。 -
在 iOS 上的旧版 Safari 和 Android 上使用
.ttf
字体。 -
旧版 iOS 适用于几乎被遗忘的 iPhone 3 及以下版本,使用
.svg
文件。 -
现代浏览器只需要
.woff
字体。根据 CanIUse.com 的数据,.woff
字体文件有 99%的支持率,除了 Opera Mini 在撰写本书时(caniuse.com/#search=woff
)。
因此,问题是:我们是否可以优雅地降级旧版浏览器和操作系统的体验,并让它们使用系统字体?
当然可以!
在优化 mixin 以仅使用.woff
字体后,它看起来是这样的:
@mixin fontFace($font-family, $file-path) {
@font-face {
font: {
family: $font-family;
weight: normal;
style: normal;
}
//Modern Browsers
src: url('#{$file-path}.woff') format('woff');
}
}
使用方式完全相同:
@include fontFace(oswald-light, '../fonts/oswald-light');
@include fontFace(oswald-regular, '../fonts/oswald-regular');
编译后的 CSS 要短得多:
@font-face {
font-family: oswald-light;
font-weight: normal;
font-style: normal;
src: url("../fonts/oswald-light.woff") format("woff");
}
@font-face {
font-family: oswald-regular;
font-weight: normal;
font-style: normal;
src: url("../fonts/oswald-regular.woff") format("woff");
}
在几个元素上使用它看起来像这样:
h1 { font: 4.1rem oswald-regular, Arial, "Helvetica Neue", Helvetica, sans-serif; }
p { font: 2.4rem oswald-light, Arial, "Helvetica Neue", Helvetica, sans-serif; }
仅提供.woff
字体可以减少我们的文件管理工作量,有助于解放我们的大脑,让我们专注于最重要的事情:构建一个令人难忘的体验。更不用说,它使我们的 CSS 代码更加简洁和可扩展。
但等等,我们让旧版浏览器优雅地降级到系统字体,我们仍然需要为它们定义像素字体大小!
像素到 rems Sass mixin 来拯救!
记得在<html>
标签中查看十进制模型以便更容易计算:
//Base-10 model
html { font-size: 62.5%; }
然后让我们声明字体大小和字体系列:
h1 {
@include fontSize(4.1);
font-family: oswald-regular, Arial, "Helvetica Neue", Helvetica, sans-serif;
}
p {
@include fontSize(2.4);
font-family: oswald-light, Arial, "Helvetica Neue", Helvetica, sans-serif;
}
编译后的 CSS 如下所示:
h1 {
font-size: 41px;
font-size: 4.1rem;
font-family: oswald-regular, Arial, "Helvetica Neue", Helvetica, sans-serif;
}
p {
font-size: 24px;
font-size: 2.4rem;
font-family: oswald-light, Arial, "Helvetica Neue", Helvetica, sans-serif;
}
提示
我们在同一规则中声明了两个不同的字体大小,因此在这种情况下我们不能使用字体简写。
因此,通过利用两个简单的 Sass mixin 的超能力,我们可以轻松嵌入网络字体,并为我们的字体大小使用 rems,同时为旧版浏览器提供基于像素的字体大小。
这是一个强大可扩展性的很好的例子。
这是我在 CodePen 上创建的演示:
codepen.io/ricardozea/pen/9c93240a3404f12ffad83fa88f14d6ef
在不失去任何动力的情况下,让我们转变思路,谈谈如何通过使用 Simple Focus 的强大 FlowType.js jQuery 插件来实现最小行长,从而提高页面的可读性。
使用 FlowType.js 增加可读性
最具说服力的编辑原则之一是,最易读的排版的理想行长在 45 到 75 个字符之间。
如果你问我,这是一个相当不错的范围。然而,实际上让你的段落足够长,或者足够短,就像一个“盲人引导盲人”的游戏。我们怎么知道容器的宽度和字体大小的组合是否真正符合 45 到 75 个字符的建议?此外,在小屏幕或中等屏幕上,你怎么知道情况是这样的?
棘手的问题,对吧?
好吧,不用担心,因为有了 FlowType.js,我们可以解决这些问题。
你可以从simplefocus.com/flowtype/
下载这个插件。
我们需要的第一件事是 HTML,所以这是我们将要使用的标记:
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Meaningful Typography for RWD</title>
<script src="img/"></script>
<script src="img/flowtype.js"></script>
</head>
<body>
<main class="main-ctnr" role="main">
<h1>Meaningful Typography for RWD</h1>
<blockquote>
<p>"With a solid typographic scale you might even get away with not using a single image on your website."</p>
<p>— Ricardo Zea</p>
</blockquote>
<p>One of the most compelling editorial principles states that the ideal line length for the most legible typography is between 45 and 75 characters.</p>
</main>
</body>
</html>
一旦你熟悉了 FlowType.js,你可能会开始思考,“如果 FlowType 自动在几乎任何视口宽度下修改字体大小,我觉得我不需要在我的 SCSS 中声明任何字体大小!毕竟,它们将被 FlowType 覆盖。”
好吧,我们确实需要设置字体大小,因为如果 FlowType.js 没有加载,我们将受制于浏览器的默认样式,而我们设计师不想要这样。
说到这里,这是声明必要字体大小的 SCSS:
//Pixels to Rems Mixin
@mixin font-size($sizeValue: 1.6) {
font-size: ($sizeValue * 10) + px;
font-size: $sizeValue + rem;
}
//Base-10 model
html { font-size: 62.5%; }
h1 { @include fontSize(3.9269); }
p { @include fontSize(1.6); }
这将编译成以下 CSS:
html {
font-size: 62.5%;
}
h1 {
font-size: 39.269px;
font-size: 3.9269rem;
}
p {
font-size: 16px;
font-size: 1.6rem;
}
这就是魔法发生的地方。我们创建一个 jQuery 函数,可以指定要定位的元素。这个函数可以放在一个单独的 JavaScript 文件中,也可以放在标记中。
在我们的示例中,我们告诉 FlowType.js 将字体的调整应用到<html>
元素上。由于我们使用相对字体大小单位 rems,所有文本将在任何屏幕宽度下自动调整大小,保持理想的行长度。
这是 jQuery 函数:
$(function() {
$("html").flowtype();
});
定义阈值
我们刚刚看到的解决方案存在潜在问题:FlowType.js 将无限期地修改段落的字体大小。换句话说,在小屏幕上,文本将变得非常小,在大屏幕上,文本将变得太大。
我们可以用两种不同的阈值方法或两者结合来解决这个问题。
现在,我们需要澄清的一件事是,这部分需要一些调整和调整,以获得最佳结果,没有特定的值适用于所有情况。
我们将采用以下方法:
-
定义容器或元素的最小和最大宽度。
-
定义容器或元素的最小和最大字体大小。
阈值宽度
定义最小和最大宽度将告诉 FlowType.js 在哪些点停止调整大小。
让我们定义宽度阈值:
$(function() {
$("html").flowtype({
//Max width at which script stops enlarging
maximum: 980,
//Min width at which script stops decreasing
minimum: 320
});
});
提示
我选择的阈值专门适用于这个示例,可能不适用于其他情况。调整和测试,直到你得到适合推荐的每行 45-75 个字符的理想宽度。
阈值字体大小
就像宽度阈值一样,定义最小和最大字体大小将告诉 FlowType.js 应该将文本缩放到的最小和最大字体大小。
我们还将使用fontRatio
变量声明自己的字体大小;数字越大,字体越小,数字越小,字体越大。如果这感觉反直觉,可以这样理解:数字越大,压缩越大(因此变小),数字越小,压缩越小(因此变大)。
调整fontRatio
值是一个凭感觉的练习,所以像没有明天一样调整和测试。
让我们看一下字体大小的值:
$(function() {
$("html").flowtype({
//Max width at which script stops enlarging
maximum: 980,
//Min width at which script stops decreasing
minimum: 320,
//Max font size
maxFont : 18,
//Min font size
minFont : 8,
//Define own font-size
fontRatio : 58
});
});
提示
在列表中的最后一个值后面不需要包含逗号。
FlowType.js 真的很棒!
这是我在 CodePen 上创建的演示:
codepen.io/ricardozea/pen/c2e6abf545dbaa82a16ae84718c79d34
总结
所以在这里,我们在 RWD 的排版上升了一个层次。排版还有更多内容吗?当然有!这个令人惊叹的主题本身就是一个完整的行业,没有它我们就不会读到这本书。
现在我们可以说,我们明白为什么使用相对单位进行排版是一个好方法:可伸缩性。此外,使用我们的小魔法公式,我们可以计算设计中每个文本元素的相对字体大小,但为什么要经历这么多麻烦呢?排版的模块化比例在这方面拯救了我们,并为我们的项目注入了令人惊叹的排版和谐。谁知道,也许我们根本不需要使用图片!
品牌现在可以通过网络字体扩展到网络上,但我们需要谨慎考虑它们对我们网站/应用的影响。另外,就现代浏览器而言,我们只需要使用一种文件类型(WOFF 字体文件),这样可以更容易地管理——对于浏览器下载和用户享受都更方便。
FlowType.js 增强了我们的标题和正文文本,同时保持了良好的可读性水平。
现在,RWD 的一个重要部分是(信不信由你)做一些我们多年前做过的事情。在下一章中,我们将保持简单,讨论电子邮件中的 RWD。
是时候回到过去了!
第八章:响应式电子邮件
在我们回到过去之后,我们来想一想 90 年代末使用表格进行设计;是的,你没看错,使用表格进行设计。
今天,在创建电子邮件方面并没有任何不同:我们必须使用表格进行布局。为什么?很简单。没有任何战争。那就是电子邮件客户端之间的竞争。
与 1995 年的浏览器之战不同,当时 Netscape 和 Internet Explorer 为市场霸权而战,电子邮件客户端自从有记忆以来就一直过着各自独立的生活,几乎对彼此毫不在意。
由于浏览器之战,我们现在拥有了这样一些非常棒的符合标准的浏览器,它们充满了功能、定制能力、不断的更新等等,使每个人的在线生活变得更加轻松。
另一方面,电子邮件客户端以自己的步伐发展,而且步伐很慢,因为实际上没有任何竞争。此外,绝大多数公司已经与微软的 Outlook 绑定在一起。在 Office 的最新版本中,Outlook 实际上比早期版本更糟糕,因此并没有真正帮助电子邮件领域支持更现代的技术。
此外,有一些相对较新的电子邮件客户端彻底拒绝支持<style>
元素甚至媒体查询。
但是,无论技术水平如何,电子邮件都是一种非常高效和强大的营销工具,我们需要准备好迟早要使用它。
换句话说,作为一种沟通媒介,电子邮件不会很快消失,我们作为网页设计师/开发人员必须使用表格和内联样式设计电子邮件。
但不要担心,我会向你展示,通过使用 RWD 的基本原则,一点点常识,采用渐进增强,并且始终试图保持简单,设计和实现响应式电子邮件并不困难,而且可以很有趣。
在本章中,我们将讨论以下主题:
-
我们为什么需要担心响应式电子邮件?
-
不要忽视你的分析。
-
响应式电子邮件需要考虑的事项。
-
响应式电子邮件构建。
-
第三方服务。
我们为什么需要担心响应式电子邮件?
我们需要担心响应式电子邮件的主要原因很简单:大约 65%的电子邮件是在移动设备(智能手机和平板电脑)上打开的。其余 35%的电子邮件是在桌面上打开的。此外,响应式电子邮件比非响应式电子邮件有更多的参与度。
除此之外,在桌面上打开的电子邮件比在移动设备上打开的电子邮件有更多的参与度。
查看以下文章:
-
美国近 65%的电子邮件是在移动设备上打开的:
www.internetretailer.com/2014/01/23/nearly-65-e-mails-us-are-opened-mobile-devices
-
上季度 65%的营销电子邮件是在移动设备上打开的;安卓平板使用量翻了一番:
www.phonearena.com/news/65-of-marketing-emails-were-opened-on-a-mobile-device-last-quarter-Android-tablet-use-doubles_id51864
提示
术语参与意味着用户点击/轻敲。因此,更多参与简单地意味着更多点击/轻敲。
不要忽视你的分析
在开始推动像素、嵌套表格和样式元素之前,绝对必要的是我们看一下分析数据,全面了解我们将为之创建电子邮件的环境。
这样做将使我们了解:
-
我们的电子邮件是在何时被打开的。
-
哪些日子有更多的开放。
-
哪些时间段有更多的开放。
-
无论季节是否导致更多/更少的开放。
-
哪些设备被用来打开我们的电子邮件。
-
哪些电子邮件客户端被最多/最少使用。
例如,如果分析数据表明 Outlook 2013 很少被使用(这将是很棒的),那么我们可能根本不需要担心这个电子邮件客户端。
如果安卓上的 Yahoo Mail 应用是最常用的应用和平台,那么我们可以放心地使用更高级的 CSS 属性,并逐步增强,知道我们的想法将正确显示。
进行市场份额研究很重要,但最终决定如何制定电子邮件开发策略的是你自己的分析数据。
建议构建更好的响应式电子邮件
尽管一些电子邮件客户端在呈现电子邮件方面变得更好了,但还有其他电子邮件客户端并不如他们应该的那样好。这意味着我们需要构建一些基本的东西,并逐步增强以适应更好的电子邮件客户端。
在构建响应式电子邮件时,有一些重要的准则需要考虑:
-
确定支持 HTML 和 CSS 最少的电子邮件客户端:了解哪个电子邮件客户端对 HTML 和 CSS 的支持最少将在测试过程中节省我们不必要的麻烦和时间。再次强调,这就是分析数据至关重要的地方。
-
使用渐进增强:首先,设计和构建支持 CSS 和 HTML 最少的电子邮件客户端。然后,我们使用这个核心基础来增强设计和体验。
-
保持在 550px 至 600px 的宽度范围内:这非常重要,因为大多数电子邮件客户端的预览窗格非常窄。此外,600px 或更少在桌面客户端和网页浏览器上看起来都很好,而且在小屏幕上缩小或响应式时,电子邮件仍然可读。
-
使用表格进行布局:大多数电子邮件客户端对 HTML 和 CSS 的支持远远不及网页浏览器,因此使用表格来构建布局仍然是创建电子邮件的方法。
-
内联 CSS:许多电子邮件客户端会移除电子邮件的
<head>
部分,因此我们放在那里的任何东西都会被剥离。因此,我们需要内联 CSS 以实现必要的样式。 -
使用系统字体:虽然技术上可以使用网络字体,最好还是使用系统字体,这样可以使电子邮件在不同设备和不同电子邮件客户端上尽可能相似。但是,如果你决定使用网络字体,那就去做吧,并始终将它们作为渐进增强过程的一部分使用。
-
为背景图像提供备用颜色:使用背景图像并不是很困难。Outlook 是唯一需要特殊标记(条件注释)才能使其工作的客户端。然而,始终提供一个备用的背景颜色,以防图像无法加载。
-
始终在图像上使用 alt 属性:如果图像无法加载或加载速度过慢,电子邮件客户端将显示替代文本。确保在
alt
属性中放入一些描述性的内容。与其使用Logo,最好使用公司标志-标语之类的内容。 -
不需要先考虑移动端:因为我们正在进行渐进增强,我们从支持 HTML 和 CSS 最少的电子邮件客户端开始。因此,这个电子邮件客户端很可能不支持媒体查询或
viewport
元标记。因此,移动优先的方法可能并不是最佳选择,至少目前还不是。 -
使用 HTML5 DOCTYPE:我们当然可以使用旧的 HTML4 DOCTYPE,但也可以使用 HTML5 DOCTYPE,这总是一个好的措施。
-
避免使用 HTML5 元素:尽管我们可以使用 HTML5 DOCTYPE,但对 HTML5 元素的支持实际上几乎不存在。因此,在电子邮件中避免使用 HTML5 元素。
-
保持简单:大多数电子邮件的寿命很短,因此制作复杂的布局并不是必要的。创建一个简单的单列布局会节省我们很多麻烦。要着重关注设计本身。这就是一个坚实的排版模块化比例可以发挥奇迹的地方。
响应式电子邮件构建
定义电子邮件的特性也是构建的一部分,所以让我们来定义这些:
-
为排版创建一个模块比例。
-
创建两种设计来帮助预先可视化电子邮件:一种用于大屏幕,一种用于小屏幕。
-
电子邮件的最大宽度为 600px,最小宽度为 320px。
-
使用渐进增强。
排版的模块比例
为了构建我们的模块比例,我们将使用以下值:
-
基础一(16px):这是我们的基本字体大小。
-
基础二(600px):这是我们电子邮件的最大宽度。
-
比例(1.618):黄金比例。
这个模块比例可以在www.modularscale.com/?16,600&px&1.618&web&table
找到。
设计-大屏幕和小屏幕视图
以下的设计将有助于更好地了解大屏幕和小屏幕上的电子邮件。这是它在 600px 宽时的样子:
这是电子邮件在最小尺寸(320px 宽)下的样子:
让我们立即着手构建一个响应式的电子邮件。
设置基本的 HTML 模板
让我们从最基本的模板开始。然后,我们将添加需要的不同元素,以建立一个完整的模板。
以下是 HTML 的第一次尝试,其中包含<head>
部分的一些初始元素:
-
使用
lang
属性定义文档的语言,我们的情况下是英语。 -
由于我们的设计具有彩色背景,我们需要给
<html>
和<body>
元素一个 100%的高度。这样两个元素就会延伸到视口的全高度。否则,背景将在电子邮件底部结束,然后页面将显示白色背景。 -
添加一个
<title>
标签。 -
添加以下 meta 标签:
-
字符集 UTF-8
-
视口
-
使 Internet Explorer 使用可能的最新渲染引擎
-
移除 OSX/iOS 中电话号码的自动样式。
-
谁说我们不能使用网络字体?只有少数几个电子邮件客户端支持它们,不支持的将会回退到我们字体堆栈中的系统字体,很可能是 Arial 或 Helvetica。让我们使用 Roboto。
以下是 HTML:
<!DOCTYPE html>
<html lang="en" style="height: 100%;">
<head>
<title>Mastering RWD with HTML5 and CSS3</title>
<meta charset="utf-8">
<!-- Responsive: Tell browsers that this template is optimized for small screens -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- IE: Make IE use its best rendering engine rather than Compatibility View mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- OSX/iOS: Remove auto styling of phone numbers -->
<meta name="format-detection" content="telephone=no">
<!-- Webfont from Google Fonts -->
<link href='http://fonts.googleapis.com/css?family=Roboto:300,500' rel='stylesheet'>
</head>
<body style="height: 100%;">
</body>
</html>
使用 CSS 重置来规范显示
让我们添加必要的 CSS 重置样式,以帮助在尽可能多的电子邮件客户端上保持相对统一的显示。
以下列表概述了我们将在多个电子邮件客户端上重置(也称为规范化)的确切内容:
-
Outlook(所有版本):
-
强制它提供“在浏览器中查看”链接。
-
使其保持任何自定义行高的定义。
-
删除
<table>
元素左右两侧的空格。 -
修复填充问题。
-
OSX/iOS/Windows Mobile:
-
修复字体小的时候自动增加字体大小到 13px 的问题。
-
Yahoo:
-
修复段落问题。
-
IE:
-
修复调整大小的图像问题。
-
Hotmail/Outlook.com:
-
使其以全宽度显示电子邮件。
-
强制它显示正常的行间距。
-
所有电子邮件客户端:
-
移除链接图像周围的边框。
以下是嵌入的 CSS:
<!DOCTYPE html>
<html lang="en" style="height: 100%;">
<head>
<title>Mastering RWD with HTML5 and CSS3</title>
<meta charset="utf-8">
<!-- Responsive: Tell browsers that this template is optimized for small screens -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- IE: Make IE use its best rendering engine rather than Compatibility View mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- OSX/iOS: Remove auto styling of phone numbers -->
<meta name="format-detection" content="telephone=no">
<!-- Webfont from Google Fonts -->
<link href='http://fonts.googleapis.com/css?family=Roboto:300,500' rel='stylesheet'>
<style>
/*Force Outlook to provide a "View in Browser" link.*/
#outlook a { padding: 0; }
body {
width: 100% !important;
margin: 0;
padding: 0;
/*Outlook: Make Outlook maintain any custom line heights defined.*/
mso-line-height-rule: exactly;
/*OSX/iOS/Windows Mobile: Fix automatic increasing of font size to 13px when fonts are small.*/
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
/*Yahoo: Fix paragraph issue*/
p { margin: 1em 0; }
/*Outlook: Remove spaces on left and right side of a table elements.*/
table {
mso-table-lspace:0pt;
mso-table-rspace:0pt;
}
/*Outlook 07, 10: Fix padding issue.*/
table td { border-collapse: collapse; }
img {
outline: none;
text-decoration: none;
/*IE: Make resized images look fine.*/
-ms-interpolation-mode: bicubic;
}
/*Remove border around linked images.*/
a img { border: none; }
/*Prevent Webkit and Windows Mobile platforms from changing default font sizes, while not breaking desktop design.*/
/*Force Hotmail to display e-mails at full width.*/
.ExternalClass{ width:100%; }
/*Force Hotmail to display normal line spacing.*/
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
</style>
</head>
<body style="height: 100%;">
</body>
</html>
有了这个基本模板,让我们开始添加内容。
添加电子邮件内容
构建电子邮件基本上是一种“你必须做你必须做的事情!”的心态。换句话说,我们必须做任何我们必须做的事情,以便使事物显示为我们想要的样子。有时,我们必须使用不间断空格(
)来分隔事物,使用<br>
标签使事物进入下一行,甚至使用多个<br>
标签在元素之间创建空间。
然而,这并不意味着我们要把学到的所有好东西都抛到脑后,绝对不是。
让我们尽可能地保持清洁和简洁,必要时进行嵌套,并在需要时添加必要的样式。
提示
为了优化空间并帮助专注于重要部分,我们将只处理<body>
标签内的标记。
创建一个 100%宽度的包裹表格
这是我们最外层的表格容器,始终将其作为一个良好的实践。这个表格将允许我们处理我们设计中想要或需要的任何填充,因为在<body>
标签上添加填充可能不是一种可靠的方法。
如果我们的设计有背景颜色,我们也可以使用这个外部表格来添加背景颜色。我们将给这个外部表格设置 100%的宽度和高度。
我们还在单元格中添加了 20 像素的填充;这将给整个电子邮件留出一些空间,因为它不会触及视口/面板的顶部和底部边缘。代码如下:
<body style="height: 100%;">
<table width="100%" height="100%" cellpadding="20" cellspacing="0" border="0" bgcolor="#efefef" class="outer-container-table">
<tr>
<td align="center"> </td>
</tr>
</table>
</body>
提示
我在电子邮件中为一些元素添加了类,可能并不是立即使用。无论如何,我都会添加这些类,以防将来发生变化,我已经有了这些类,并且可以更快地进行编辑。
创建 600 像素的内部表格
我们使用 HTML 属性width
声明了这个内部表格的宽度,而不是在内联样式中声明宽度。我们还给这个表格添加了白色背景,这样我们的内容就可以覆盖它,并阻止宽容器的浅灰色背景。
可以使用边框简写添加 1 像素的边框。有人说不要在电子邮件中使用 CSS 简写!然而,在测试了几个电子邮件客户端之后,简写效果非常好。
在顶部添加 10 像素的边距将有助于给电子邮件留出更多空间。代码如下:
<body style="height: 100%;">
<table width="100%" height="100%" cellpadding="20" cellspacing="0" border="0" bgcolor="#efefef" class="outer-container-table">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" border="0" bgcolor="white" align="center" class="inner-container-table" style="margin-top: 10px; border: #999999 1px solid;">
<tr>
<td></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
注意我在.inner-container-table
的背景颜色上使用了术语white?这是因为我想告诉你,你也可以使用 HTML 颜色名称而不是十六进制值。所有电子邮件客户端都支持这个功能。这也更具描述性。
在公开网络上有很多资源列出了所有 HTML 颜色名称,但我特别喜欢这个,因为它将颜色名称按类别分组。因此,在设计中更容易使用:html-color-codes.info/color-names/
。
添加页眉图像
在空的<td>
元素中,我们需要做的就是添加调用页眉图像的<img>
标签。
图像默认为inline-block
元素。为了避免不需要的行为,请确保图像具有display: block;
和width: 100%;
元素,如下所示:
<body style="height: 100%;">
<table width="100%" cellpadding="0" cellspacing="20" border="0" bgcolor="#efefef" style="height: 100%;" class="outer-container-table">
<tr>
<td align="center">
<table width="580" cellpadding="0" cellspacing="0" border="0" bgcolor="white" align="center" class="inner-container-table" style="margin-top: 10px; border: #999999 1px solid;">
<tr>
<td>
<img src="img/header-email-devices.png" alt="Mastering RWD with HTML and CSS3" style="display: block; width: 100%;">
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
创建内容包装表格及其所有内容
这是大部分魔术发生的地方,因为我们现在正在创建电子邮件的主体,包括页脚。需要注意的几件事:
-
第一个表格的宽度为 88%。我这样做是为了向你展示,如果你愿意,你可以是任意的。此外,你不必每次都使用像素,使用百分比时也可以使用不同于 100%的其他值。
-
在某些部分,我大量使用
<br>
标签。这是因为我希望一些元素之间的间距在我想要的位置。在其他情况下,这将是一个相当糟糕的做法;在电子邮件中,这样做非常有用,也很常见。 -
我们将使用三行:一行用于主标题,一行用于正文,一行用于呼吁行动(CTA)按钮。这样做将允许我们独立处理每个部分,而无需担心在调试或样式化时影响其他两个部分。
-
页脚将与主要内容结构分开,因此我们可以轻松处理背景图片。
标记如下:
<body style="height: 100%;">
<table width="100%" height="100%" cellpadding="20" cellspacing="0" border="0" bgcolor="#efefef" class="outer-container-table">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" border="0" bgcolor=" white" align="center" class="inner-container-table" style="margin-top: 10px; border: #999999 1px solid;">
<tr>
<td>
<img src="img/header-email-devices.png" alt="Mastering RWD with HTML and CSS3" style="display: block; width: 100%;">
</td>
</tr>
<tr>
<td align="center">
<table width="88%" cellpadding="0" cellspacing="0" border="0" align="center" class="content-table">
<tr>
<td align="center">
<table width="100%" cellpadding="10" cellspacing="0" border="0" align="center">
<tr>
<td style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 500; font-size: 33.441px; text-align: center;"><br>Mastering RWD<br>with HTML5 and CSS3</td>
</tr>
<tr>
<td>
<h2 style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 500; font-size: 25.888px;">Responsive Emails</h2>
<p style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 300; font-size: 16px; line-height: 26px">And here we sare after traveling back in time: think of late 90's and designing with tables, oh yes you read right, designing with tables.</p>
<p style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 300; font-size: 16px; line-height: 26px"> And today things are not any different when it comes to creating e-mails: we have to use tables for layout.</p>
<p style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 300; font-size: 16px; line-height: 26px">Why? Simple. There aren't any wars. Email client wars that is… (continued).</p>
</td>
</tr>
<tr>
<td style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight:300; font-size: 25.888px; text-align:center;">
<br>
<a href="#" target="_blank" style="padding: 20px 30px; border: #663300 2px solid; border-radius: 5px; text-decoration: none; color: white; background: #ff8000;" class="main-cta">Get the Book! »</a>
<br><br><br>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="footer-table-ctnr" style="background: #666666; background: linear-gradient(#333, #666);">
<tr>
<td background="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/trianglify-black.png">
<table width="95%" align="center" cellpadding="30" cellspacing="0" border="0">
<tr>
<td style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 300; font-size: 12px; line-height: 20px; color: white;">
<p style="margin: 0;"><span style="font-weight: 500;">Book:</span> Mastering RWD with HTML5 and CSS3</p>
<p style="margin: 0;"><span style="font-weight: 500;">Author:</span> Ricardo Zea</p>
<p style="margin: 0;"><span style="font-weight: 500;">Publisher:</span> Packt Publishing</p>
<br>
<p>© All Rights Reserved - <a href="#" style="color: white;">Unsubscribe</a></p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
到目前为止,电子邮件看起来是这样的:
我们完成了!我们?还没有,我们还有一些事情要做:
-
为页脚和 CTA 按钮添加 Outlook 2007/2010/2013 条件注释的黑客。
-
添加媒体查询。
-
添加 Outlook 网络字体回退样式。
添加 Outlook 2007/2010/2013 条件注释黑客
就像在基于表格的布局时代的 IE 一样,Outlook 在桌面电子邮件客户端的领域中占据主导地位。因此,在创建电子邮件时,我们不能忽视这个客户端。
这一切都很好,问题在于大多数 Outlook 版本的 HTML 渲染能力非常差,因此通过条件注释进行 HTML hack(不幸的是)是必要的。它们并不难实现;您只需要知道何时实现它们。
条件注释对于背景图像和大型 CTA 按钮非常有用。在我们的示例中,我们都有:页脚中的黑色/灰色三角形背景图案和橙色获取图书» CTA(呼吁行动)。
在下面的标记中,您将能够注意到以下几点:
-
条件注释只包裹元素。换句话说,确保您不要包裹比所需更多的元素,否则我们将制造更多问题而不是解决方案。
-
页脚和 CTA 按钮都要求我们在两个地方进行编辑:元素本身和条件注释内部。
-
电子邮件条件注释看起来相当晦涩;它们不遵循任何标准,因为它们是专有技术。把它们看作是渐进增强的一部分而不是一部分。它们是一种彻头彻尾的 hack。
-
编辑条件注释并不太困难。可定制的部分要么是内联 CSS 属性/值,要么是图像的
src
属性——这些都不是我们以前没有见过的。
提示
为了清晰起见并涵盖本书的范围,我只会向您展示两个包含在条件注释中的部分。
页脚背景图像的条件注释
这就是 HTML 的样子:
<td background="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/trianglify-black.png">
<!--[if gte mso 9]>
<v:rect strokecolor="none" style="width: 600px; height: 184px;">
<v:fill type="frame" src="img/trianglify-black.png"></v:fill>
</v:rect>
<v:shape style="position: absolute; width: 600px; height: 184px;">
<![endif]-->
<table width="95%" align="center" cellpadding="30" cellspacing="0" border="0">
<tr>
<td style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight: 300; font-size: 12px; line-height: 20px; color: white;">
<p style="margin: 0;"><span style="font-weight: 500;">Book:</span> Mastering RWD with HTML5 and CSS3</p>
<p style="margin: 0;"><span style="font-weight: 500;">Author:</span> Ricardo Zea</p>
<p style="margin: 0;"><span style="font-weight: 500;">Publisher:</span> Packt Publishing</p>
<br>
<p>© All Rights Reserved - <a href="#" style="color: white;">Unsubscribe</a></p>
</td>
</tr>
</table>
<!--[if gte mso 9]>
</v:shape>
<![endif]-->
</td>
CTA 按钮的条件注释
以下片段改编自 Eli Dickinson 在 IndustryDive.com 的文章如何制作出色的 HTML 电子邮件按钮(www.industrydive.com/blog/how-to-make-html-email-buttons-that-rock/
)。
以下是标记的样子:
<td style="font-family: Roboto, Arial, Helvetica, san-serif; font-weight:300; font-size: 25.888px; text-align: center;">
<br>
<!--[if mso]>
<v:roundrect href="http:#" style="height: 60px; width: 300px; v-text-anchor: middle;" arcsize="10%" stroke="f" fillcolor="#ff8000">
<center style="color: #ffffff; font-family: Roboto, Arial, Helvetica, san-serif; font-weight:300; font-size: 25.888px;">
Get the Book! »
</center>
</v:roundrect>
<![endif]-->
<![if !mso]>
<a href="#" target="_blank" style="padding: 20px 30px; border: #663300 2px solid; border-radius: 5px; text-decoration: none; color: white; background: #ff8000;" class="main-cta">Get the Book! »</a>
<![endif]-->
<br><br><br>
</td>
添加媒体查询
在这封电子邮件中使用的媒体查询代码量很少。这是在创建任何 HTML 或 CSS 之前具有坚实的功能基础的结果。
使这封电子邮件成为坚实构建的因素如下所列:
-
设置排版模块化比例。
-
保持布局为单列。
-
首先为最棘手的电子邮件客户端构建。
-
使用渐进增强。
-
知道何时应用条件注释。
媒体查询就像这里显示的那样简单:
/*Responsive Styles*/
@media (max-width: 380px) {
.main-cta { padding:10px 30px !important; white-space: nowrap !important; }
}
@media (max-width: 600px) {
.inner-container-table { width: 95% !important; }
.footer-table-ctnr td { padding: 10px 0 10px 5px !important; }
}
提示
由于内联样式的特异性高于<style>
标签中的样式,我们需要在值的末尾添加!important
声明,以覆盖这些内联样式。
以下是我们在媒体查询中看到的内容:
-
由于我们采用了桌面优先的方法,我们使用
max-width
属性。 -
我们在 380px 处看到一个媒体查询,因为在这个宽度下,橙色 CTA 在小屏幕上看起来有点厚。因此,我们将上下填充从 20px 减少到 10px。
-
我们还添加了
white-space: nowrap !important;
元素,以防止按钮换行到第二行。 -
一旦视口达到 600px,我们将使
inner-container-table
的宽度为 95%。这将使电子邮件在两侧留有一些填充,使其能够呼吸,而不会在如此狭小的空间中感到受限。 -
然后,我们将减少页脚表格的填充。这有助于更充分地利用可用空间,同时保持每行信用在一行内。
Outlook 网络字体回退样式
Outlook 不会使用字体堆栈中的任何回退字体。它只会使用 Times New Roman,有时这并不是我们想要的。
因此,在条件注释中使用特定样式来针对 Outlook 是解决这个问题的方法。这个样式应该放在主嵌入样式表的</style>
标签之后。
这是它的样子:
<!--[if mso]>
<style>
/* Make Outlook fallback to Arial rather than Times New Roman */
h1, h2, p { font-family: Arial, sans-serif; }
</style>
<![endif]-->
就是这样!真的就是这样。这是我在 CodePen 上创建的演示:codepen.io/ricardozea/pen/d11a14e6f5eace07d93beb559b771263
各种电子邮件客户端的屏幕截图
此电子邮件已在以下电子邮件客户端和平台上进行了测试:
-
桌面:
-
Outlook 2010
-
Gmail
-
雅虎邮件
-
Outlook.com
-
移动(iPhone):
-
邮件应用
-
Gmail 应用(移动友好视图)
-
Gmail 应用(原始视图)
-
雅虎邮件应用
-
移动(Android):
-
Gmail 应用
以下是电子邮件在各种桌面和移动客户端上的图像:
在这里,一些电子邮件客户端,无论是桌面还是移动设备,实际上都能够使用我们使用的网络字体 Roboto。其余的使用了字体堆栈中的 Arial,这正是我们的计划。
令人惊讶的是,在桌面上,Outlook 2010 是唯一能够呈现 Roboto 的电子邮件客户端,尽管字体看起来比实际粗,但它仍然是唯一的。
在移动设备上,iPhone 的邮件应用和 Android 上的 Gmail 是能够使用 Roboto 的。
第三方服务
在构建响应式电子邮件时,我们必须补充我们的技巧、黑客和对电子邮件客户端的怪癖和故障的广泛理解,以及可以让我们更快地测试、优化我们的工作流程、提高我们的效率并学习更多现代技术的工具。
有很多工具,就像有很多网页设计师一样;我们要提到的工具与本书的主题密切相关。而且,所有这些工具都是免费的。让我们来看看。
Litmus's PutsMail
我必须承认,这个工具的名称并不是很描述性,也没有提到这个工具有多有用。使用 Litmus's PutsMail,我们可以将电子邮件发送到任何我们想要进行测试和调试的账户。只需点击一个按钮,PutsMail 就可以将电子邮件发送到几乎任意数量的电子邮件账户。
PutsMail 允许我们做以下事情:
-
添加任何电子邮件以发送测试
-
添加主题行
-
粘贴我们的 HTML
一旦我们做好了这些准备,我们只需点击发送电子邮件的按钮,然后就可以在所有设备上进行测试了。不需要在电子邮件管理平台中登录和使用繁琐的界面。
我使用这个工具发送了您在前面几段中看到的所有电子邮件客户端屏幕截图的图像。
优点是:
-
它非常容易使用,并且学习曲线非常低。
-
与一些电子邮件管理服务不同,PutsMail 立即发送测试邮件。
-
添加和删除电子邮件非常容易。
-
除了测试常规 HTML 电子邮件,它还允许您测试纯文本和 Apple Watch 版本。
-
如果需要,它可以内联 CSS。
-
标记在 HTML 字段中得到了清晰的突出显示。
-
它是免费的。
缺点是:
-
有时您需要删除一封电子邮件并再次添加才能接收测试。
-
每个电子邮件营销服务对发送电子邮件时会剥离或保留标记的规则都不同。因此,PutsMail 的规则可能与其他电子邮件营销提供商的规则不同。
Litmus's PutsMail 可以在以下网址找到:putsmail.com/
。
CSS 内联工具
编写内联 CSS 是一项相当繁琐的任务:如果我们的段落具有font-family: Arial, Helvetica, san-serif; font-style: italic; font-weight: bold; font-size: 18px;
,那么我们必须将所有这些属性复制并粘贴到每个段落中。或者,我们必须复制并粘贴相同的段落,并更改其中的文本。
甚至不要考虑使用字体速记。那么属性的更改呢?现在我们必须在每个段落中进行更改。查找和替换可能会有风险,这意味着需要更多的时间进行测试。这就是 CSS 内联工具的作用!
使用 CSS 内联工具,我们可以在电子邮件模板的<head>
部分中的<style>
标签中编写我们的 CSS,就像创建普通网页时所做的那样。完成后,我们将电子邮件模板上传到 CSS 内联工具中。该工具将自动内联CSS 到每个对应的 HTML 标签中。
所以如果我们有以下段落:
<p class="note__important">CSS inliners are an awesome tool!</p>
然后,我们在<head>
部分的<style>
标签中写入这个:
<style>
p.note__important {
font-family: Arial, Helvetica, san-serif;
font-style: italic;
font-weight: bold;
font-size: 18px;
}
</style>
CSS inliner 将执行以下操作:
<p class="note__important" style="font-family: Arial, Helvetica, san-serif;font-style: italic;font-weight: bold;font-size: 18px;" >CSS inliners are an awesome tool!</p>
优点如下:
-
我们可以在电子邮件模板的
<head>
部分的<style>
标签中包含所有样式,就像在常规网页构建中一样。 -
使用 CSS inliner 很简单:粘贴您的标记,按下内联按钮,完成。
-
这导致重复任务大大减少,因为在
<style>
标签中放置一个类就足够了——工具会完成其余工作。 -
大多数 CSS inliner 是免费的。
缺点如下:
- 测试电子邮件非常耗时,因此使用 CSS inliner 创建测试电子邮件会增加额外的步骤。
Litmus 的 PutsMail 是一个例外,因为它在发送测试电子邮件时有内联 CSS 的选项。
- CSS inliner 有不同的写样式的方式:有些在分号后添加空格,而其他则不添加。这可能与个人的风格一致,也可能不一致。
一些最受欢迎的 CSS inliner 如下:
-
Campaign Monitor(
inliner.cm/
) -
Dialect 的 Premailer(
premailer.dialect.ca/
) -
Zurb 的 Inliner(
zurb.com/ink/inliner.php
)
高级电子邮件框架
谁说我们不能使用 Sass、Grunt 和 Node.js 等现代和更先进的技术构建电子邮件?
对于那些有点更懂技术并且热爱前端开发的人来说,这些电子邮件框架可以极大地加快速度。
优点如下:
-
这些技术提高了开发和测试阶段的速度。
-
这些技术在本地机器上运行;这意味着一切都比使用第三方基于 Web 的服务执行得快得多。
-
如果您是熟悉这些技术的前端开发人员,学习使用任何电子邮件框架会更容易。
-
一些电子邮件框架允许我们重用组件,类似于使用包含,比如头部和页脚等。
-
在一些电子邮件框架中,创建纯文本电子邮件是一个选项。
-
每当我们使用开源项目时,我们都在帮助同行的网络专业人士发展他们的职业,以及这些项目周围的任何社区,使网络变得更好。
-
有来自开发者和项目贡献者生态系统的支持。
-
这些技术是免费的。
缺点如下:
-
如果不熟悉这些前端技术,学习曲线可能会很陡峭。
-
这需要事先了解和理解多个前端技术。
一些电子邮件框架如下:
-
Nathan Rambeck 的 Email Lab(
github.com/sparkbox/email-lab
)它使用以下内容: -
Node.js
-
Grunt
-
Bundler
-
Sass
-
Ruby
-
Premailer
-
Nodemailer
-
Handlebars/Assemble
-
Alex Ilhan 的 Zenith(
github.com/Omgitsonlyalex/ZenithFramework
)
您可以在 Litmus 找到教程litmus.com/community/learning/23-getting-started-with-sass-in-email
。它使用以下内容:
-
Sass
-
Compass
-
Premailer
-
Lee Munroe 的 Grunt Email Workflow(
github.com/leemunroe/grunt-email-workflow
)
它使用以下内容:
-
Grunt
-
Ruby
-
Node.js
-
Sass
-
Premailer
-
Mailgun(可选)
-
Litmus(可选)
-
Rackspace Cloud(可选)
响应式电子邮件模板服务
我一直相信亲自动手是学习的最佳方式。然而,在电子邮件世界中,亲自动手意味着花费大量时间以一种不再是良好实践的方式处理 HTML 和 CSS。使用表格进行布局(并非使用浮动更好),内联 CSS,处理古怪的电子邮件客户端等等,比必要的测试和调试花费了更多的时间,以及其他一切好东西。
加快速度的方法是使用第三方电子邮件模板,因为作者已经至少在很大程度上为我们做了繁重的工作。让我们来看看使用第三方响应式电子邮件模板的利弊。
优点是:
-
很可能已经进行了彻底的测试;这极大地减少了我们自己的测试时间。
-
如果我们对布局满意,我们只需要用我们自己的内容替换即可。
-
一些电子邮件模板服务甚至允许您在编辑后发送电子邮件本身。
-
有些服务不需要作者了解任何 HTML 或 CSS 就能创建响应式电子邮件。
-
下载电子邮件模板是一些电子邮件模板服务提供的选项。
-
大多数响应式电子邮件模板都是免费下载的。
-
一些付费的拖放电子邮件构建服务提供免费帐户,并且在其免费计划中提供了许多功能。
缺点是:
-
尽管很少,我们仍然需要进行一些自己的测试。
-
如果我们想要更改布局,有时是不可能的。这取决于电子邮件模板服务。
-
尽管一些电子邮件模板服务允许我们发送电子邮件,但它们并不提供任何分析或后端,让我们可以看到电子邮件的表现如何。
-
图像优化可能是理想的,也可能不是。没有办法知道。
-
在某些服务中,无法重复使用旧的电子邮件模板,因此如果我们打算使用相同的布局,就必须从头开始编辑一切。
一些常见的响应式电子邮件模板如下:
-
MailChimp 的 Email Blueprints (
github.com/mailchimp/Email-Blueprints
) -
Zurb Ink (
zurb.com/ink/templates.php
) -
Litmus 的 Slate (
litmus.com/resources/free-responsive-email-templates
) -
Brian Graves 的 Responsive Email Patterns (
responsiveemailpatterns.com/
)
以下是拖放电子邮件构建服务:
-
Stamplia Builder (
builder.stamplia.com/
) -
MailUp 的 BEE Free (
beefree.io/
)
BEE是Best E-mail Editor的缩写
查看电子邮件的构建方式
这个工具肯定是电子邮件开发和学习中最令人惊奇和有用的工具之一。Litmus 的Scope书签允许我们从任何网络邮件客户端中查看电子邮件模板的构建方式。
提示
bookmarklet是一个 JavaScript 组件,你可以存储在书签中,通常是在书签栏中。当你点击这个bookmarklet时,会显示特殊功能。bookmarklet本身并不是一个书签;它恰好存储在书签中,但提供的功能与常规书签非常不同。
Scope 的工作方式非常简单:
-
转到 Scope 网站:
litmus.com/scope/
。 -
将书签拖到浏览器的书签栏中。
-
打开您的网络邮件并查看任何电子邮件。
-
在您的书签栏中点击Scope It书签。
-
Scope 网站以design模式打开电子邮件。
-
点击code,设计面板将滑开,让我们可以看到所讨论的电子邮件的所有标记。
这对于了解其他人是如何在电子邮件中实现视频、渐变、响应等惊人的事情非常有用。这是一个截图,向我们展示了我们刚刚构建的响应式电子邮件模板在发送到我的 Gmail 帐户并且使用书签工具scope后的样子。
在左边是 Litmus 网站上的 Scope 侧面,右边是在 Sublime Text 中打开的文件。它们完全相同...甚至格式都是相同的。令人惊讶的工具!
使用 Litmus 的 Scope 的电子邮件模板
摘要
哇,我们成功了!
在关于响应式电子邮件的最后一章中,我们讨论了一些重要的事情,除了构建实际的电子邮件。
我们现在明白了为什么电子邮件在任何营销活动中如此重要,因为越来越多的电子邮件在移动设备上被打开。然而,人们更喜欢在他们的桌面上与电子邮件互动——这是使我们的电子邮件响应式的非常充分的理由。
分析是决定支持哪些电子邮件客户端的关键因素。我们希望明智地利用我们的时间。然后,设置一个基本的 HTML 模板可以走很长一段路,因为我们可以一次又一次地重用这样的模板。
像 CSS 重置、将内容放在 100%宽的表格中,以及创建内部表格这样的事情,基本上是任何电子邮件设计的常用流程。我们现在知道,电子邮件的最大宽度应该是 600 像素。
微软的 Outlook 2007/2010/2013 版本是电子邮件客户端的 IE6:它们对现代 HTML 和 CSS 的支持非常差,但它们是桌面上最流行的电子邮件客户端。因此,使用条件注释来实现漂亮的 CTA 和背景是一个好方法。
此外,为了尽可能高效,使用第三方电子邮件模板和拖放电子邮件构建服务始终是一个选择。
关于响应式电子邮件的最后一句话,我们已经完成了使用 HTML5 和 CSS3 掌握响应式 Web 设计的旅程,还有更多。如果您有任何问题,请随时联系我。我将非常乐意在任何时间、任何地点帮助同行的网络专业人士。
我们现在可以摆出少林寺的功夫宗师释德如和释德阳在第六章中所做的相同姿势了,在响应式 Web 设计中使用图像和视频。
嗨呀!
非常感谢您的阅读,希望您喜欢!