《Sass for Web Designers》之Sass和媒体查询

本系列由zEx根据DAN CEDERHOLM的新书《SASS FOR WEB DESIGNERS》所译,整个系列共分四章,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明原作者相关信息。

——作者:DAN CEDERHOLM

——译者:zEx

Sass for Web Designers

第三章前言

在这本书中,我想要把主要精力都集中在讲解Sass的基础使用,和证明Sass并不会和你的工作流程脱节。但在最后一章中,我想讨论一些使用Sass结合媒体查询的高级技术和一些我每天工作中用到的将复杂CSS简化的例子。

只要你能想到的,Sass就可以办到。使用变量和一些mixin会让你的生活变得更轻松。如果你想的话,Sass完全不局限于这些简单的功能。下面这个例子再一次举重若轻的展示了Sass的建立兼容高清屏幕的响应式网站。

嵌套媒体查询

建立响应式网站的方法中,有一种就是使用CSS媒体查询。利用“监听”浏览器可视范围变化的尺寸,然后请求基于这个尺寸的样式来区别不同设备,灵活布局。

例如,你可能想要浏览器包含的元素适应小于800px的宽,使用媒体查询就可以了:

section.main {
    float: left;
    width: 65%;
    font-size: 16px;
    line-height: 1.4;
}
@media screen and (max-width: 800px) {
    section.main {
        float: none;
        width: auto;
    }
}

使用Sass,你可以在原型声明中嵌套媒体查询,在样式表编译之后,这些样式会根据写的查询判断条件,出现在各自查询范围内的声明中。这真是太好了。

section.main {
    float: left;
    width: 65%;
    font-size: 16px;
    line-height: 1.4;
    @media screen and (max-width: 800px) {
        float: none;
        width: auto;
    }
    @media screen and (max-width: 500px) {
        font-size: 12px;
        line-height: 1.4;
    }
}

上面这段编译之后:

section.main {
    float: left;
    width: 65%;
    font-size: 16px;
    line-height: 1.4;
}
@media screen and (max-width: 800px) {
    section.main {
        float: none;
        width: auto;
    }
}
@media screen and (max-width: 500px) {
    section.main {
        font-size: 12px;
        line-height: 1.4;
    }
}

嵌套媒体查询避免了每次你想要调节多个判断媒体的断点(断点的意思是在浏览器宽度变化时,布局跟着发生变化的临界点)时都重写选择器(如例子中的section.main)的问题。在原型声明中嵌入媒体查询声明为我们带来了极大的便利。我发现这样做更容易理解,一个元素在可视范围(viewports)发生变化时通过媒体查询上下文附近的样式来显示,而不是在样式表最后或单独的一个文档中写这个可视范围(viewport)的样式。(我理解的意思是在一个选择器中就可以看到不同浏览器宽度下的布局,而不是在样式表最后或其他导入到这个样式表中的文档中,按照可视范围(viewport)不同,把布局样式再写一遍,写在一个选择器中对于后期维护修改时是方便的。)

使用变量定义断点(断点的意思是在浏览器宽度变化时,布局跟着发生变化的临界点)

Sass配合媒体查询为响应式设计带来了很大的便利,但仍然还有一些重复的地方,不是很完美。在每次声明时,我们都需要指定的断点(前面例子中举例的800px和500px)。在设计的过程中经常发生这种情况,因为基于每个项目的设计不同,我需要在每个项目中修改这些断点来适应当前设计对应不同设备宽度的布局。换句话说,你每次指定嵌套的媒体查询断点是变化的。我想要一次定义所有的断点并且可以在一个地方编辑它们。Sass变量可以完成这个需求。

让我们创建3个断点变量,稍后会在媒体查询中使用它们。我会把命名定的灵活一些,不要让变量名表示一个指定的设备或是固定的值。

$width-small: 500px;
$width-medium: 800px;
$width-large: 1200px;

用Sass变量定义断点,我们就可以在整个文档中无论什么时候 使用嵌套媒体查询,都可以参考这些变量。例如:

section.main {
    font-size: 16px;
    line-height: 1.4;
    @media screen and (max-width: $width-large) {
        float: left;
        width: 65%;
    }
    @media screen and (max-width: $width-medium) {
        float: none;
        width: auto;
    }
    @media screen and (max-width: $width-small) {
        font-size: 12px;
        line-height: 1.4;
    }
}

编译之后:

section.main {
    font-size: 16px;
    line-height: 1.4;
    @media screen and (max-width: 1200px) {
        float: left;
        width: 65%;
    }
    @media screen and (max-width: 800px) {
        float: none;
        width: auto;
    }
    @media screen and (max-width: 500px) {
        font-size: 12px;
        line-height: 1.4;
    }
}

如果我们以后要修改这些断点,只需要在变量处修改一次,Sass就会帮我们在整个文档中,将所有我们用到这个变量的地方都更新一遍。

$width-small: 400px;
$width-medium: 760px;
$width-large: 1100px;

这些断点需要依赖于设计需求并与设计相互适应,调整合适的断点的范围在响应式设计开发的初始阶段是非常有帮助的。

甚至在断点中可以进行运算,比如我们可以对断点的值进行加减:

@media screen and (max-width: $width-small + 1) {
    font-size: 12px;
    line-height: 1.4;
}

编译后:

@media screen and (max-width: 401px) {
    font-size: 12px;
    line-height: 1.4;
}

可以进行减法运算:

@media screen and (max-width: $width-small - 1) {
    font-size: 12px;
    line-height: 1.4;
}

编译后:

@media screen and (max-width: 399px) {
    font-size: 12px;
    line-height: 1.4;
}

进一步,你也可以用一个变量定义整个媒体查询(不只用数值):

$mobile-first: "screen and (min-width: 300px)";
@media #{$mobile-first} {
    #content {
        font-size: 14px;
        line-height: 1.5;
    }
}

在这里需要解释一下用#{}包起来的$mobile-first。这个特别的方式是告诉Sass,这个符号里的东西是要按选择器或属性名来编译。

上面这段Scss将会编译为:

@media screen and (min-width: 300px) {
    #content {
        font-size: 14px;
        line-height: 1.5;
    }
}

当你在声明中嵌套媒体查询,变量会帮你节省大量的重复工作。但我们还可以使用Sass3.2中介绍的@content来进一步简化。

@content和mixins的结合用法

通过使用Sass的@content命令,你可以将整个样式代码传递给mixin,Sass将会在调用mixin的声明中替换这些代码块。尽管听起来有些复杂,但实际使用时是非常简单和便利的。

让我们创建一个名字叫responsive的mixin来处理3个不同断点,向每一个断点中添加@content占位符包含的样式。我们使用前面章节定义过的大中小的断点宽度。

$width-small:  400px;
$width-medium: 760px;
$width-large:  1200px;
@mixin responsive($width) {
    @if $width == wide-screens {
        @media only screen and (max-width: $width-large) { @content; }
    }
    @else if $width == medium-screens {
        @media only screen and (max-width: $width-medium) { @content; }
    }
    @else if $width == small-screens {
        @media only screen and (max-width: $width-small) { @content; }
    }
}

注意,Sass也支持@if和@else语句,在mixin内部我们对传递进去的变量$width进行评价。比如,如果我们传递给mixin一个变量值为medium-screens的变量,Sass就会设置我们的max-wideth 为$width-medium变量(760px),并且按这个宽度来编译。@content占位符允许我们将一段样式传递给mixin,并且可以在@content里面插入媒体查询。

使用刚才建立的mixin,我们可以在任意声明中调用它,用最少的代码来完成我们想要做的事情:

#content {
    float: left;
    width: 70%;
    @include responsive(wide-screens) {
        width: 80%;
    }
    @include responsive(medium-screens) {
        width: 50%;
        font-size: 14px;
    }
    @include responsive(small-screens) {
        float: none;
        width: 100%;
        font-size: 12px;
    }
}

编译后:

#content {
    float: left;
    width: 70%;
}
@media only screen and (max-width: 1200px) {
    #content {
        width: 80%;
    }
}
@media only screen and (max-width: 760px) {
    #content {
        width: 50%;
        font-size: 14px;
    }
}
@media only screen and (max-width: 400px) {
    #content {
        float: none;
        width: 100%;
        font-size: 12px;
    }
}

真是太神奇了!Sass提供样式给适合的媒体查询,并将声明(选择器)补充完整。使用@content模块书写媒体查询的上下文会使响应式设计简单很多-因为重复书写的少了。

这样也很容易控制如何让一个元素跨设备适应不同宽度-例如如何让一个标题的字体大小随着viewport的变窄而变化。这个过程需要循环输出到一个地方:

h1 {
    font-size: 40px;
    @include responsive(wide-screens) { font-size: 48px; }
    @include responsive(medium-screens) { font-size: 32px; }
    @include responsive(small-screens){ font-size: 20px; }
}

编译后:

h1 {
    font-size: 40px;
}
@media only screen and (max-width: 1200px) {
    h1 {
        font-size: 48px;
    }
}
@media only screen and (max-width: 760px) {
    h1 {
        font-size: 32px;
    }
}
@media only screen and (max-width: 400px) {
    h1 {
        font-size: 20px;
    }
}

要始终考虑输出

需要指出很重要的一点是,在编译好的CSS中,这个方法会导致每一个选择器都对应一个媒体查询语句,造成大量的重复。理想化的是,Sass中让我们在一个选择器中保持样式规则收集的上下文联系(即Sass中的写法是将媒体查询放在选择器中),但是当编译之后,把符合媒体查询条件的样式提取出来放在符合条件的媒体查询之中。

例如:

blockquote {
    width: 100%;
    @include responsive(wide-screens) { width: 80%; }
}
figure {
    margin: 0 0 20px 0;
    @include responsive(wide-screens) { margin: 0 0 30px 0; }
}
想让编译后的文件更有效率,就要让符合查询条件的样式放在同一个媒体查询中:

blockquote {
    width: 100%;
}
figure {
    margin: 0 0 20px 0;
}
@media only screen and (max-width: 1200px) {
    blockquote {
        width: 80%;
    }
    figure {
        margin: 0 0 30px 0;
    }
}

对于一个要适应多屏幕终端采用响应式设计,频繁使用媒体查询的大型样式表,这样做会让编译后的CSS文件大小减少很多。不幸的是,Sass还不能支持这种提炼相同媒体查询条件的样式放在同一查询下的做法,因为我上面的例子是杜撰的,所以对于大多数项目来说,需要权衡是让编译后的样式表变大一点还是让媒体查询嵌套在声明中,哪种是值得的。所以到目前为止,我们还在寻找平衡。

“Retina”类高分屏下的背景图片

好像对于我们web设计师来说事情并没有那么复杂,但高分屏的出现却制造了一个设计上的挑战。比如苹果公司推出的华丽的Retina屏幕,和正常屏幕下对比,Retina屏幕的像素是正常屏幕的两倍。这就意味着美丽清晰,与模糊的像素说再见了!除了需要你花时间创建一些非常锐利的图片以外。

对于在页面中的元素,典型的方法是创建一个两倍大小的图片,然后在标签上用width属性来压缩图片的宽为实际宽的一半。还有一种比较取巧的方法供选择,是通过媒体查询和JavaScript来处理选择的图片服务。例如Scott Jehl的brilliant Picturefill项目

让我们看一下Sasquatch Records网站logo(133px121px)是如何处理高清屏图片的例子(图4.1)。对于支持高分辨率的设备,我们创建了尺寸大小为两倍(266px242px)的图片版本,并在标签中用样式将宽缩小到133px来获取两倍的清晰度(图4.2)。

<img src="-/img/logo-peek-2x.png" width="133" />

Sass for Web Designers

图4.1-图4.2 左侧是一般分辨率下的logo,右侧是高分屏下的版本 对于背景图,我们仅仅使用CSS媒体查询(现代浏览器支持)就可以来决定是否显示高清版本还是普通版本的图片尺寸。

举一个在Sasquatch Records网站上的例子,我们在侧边栏有一个社交网络链接的列表。每一个链接的icon在样式表中指定了一个背景图片(图4.3)。

Sass for Web Designers

图4.3 在Sasquatch Records网站侧边栏中的社交网络链接

在Dribbble的链接中,CSS定位一个一般分辨率下位于文字左侧的icon的样式可能看起来是这样的:

ul.follow li.dribbble a {
    padding-left: 30px;
    background-repeat: no-repeat;
    background-position: 0 50%;
    background-image: url(../img/icon-dribbble.png);
}

对于高清屏,我们用一个两倍大小的图片来覆盖这个icon,然后使用CSS3的background-size属性来压缩到正确的大小(即一般分辨率下图片的大小)。为了探测是否需要显示高清版本,我们要使用媒体查询和CSS3的min-device-pixel-ratio属性(这个属性需要添加浏览器的私有属性)。

@media (-webkit-min-device-pixel-ratio: 1.5),
       (min--moz-device-pixel-ratio: 1.5),
       (-o-min-device-pixel-ratio: 3/2),
       (min-device-pixel-ratio: 1.5),
       (min-resolution: 1.5dppx) {
            ul.follow li.dribbble a {
                padding-left: 30px;
                background-repeat: no-repeat;
                background-position: 0 50%;
                background-image: url(../img/icon-dribbble-2x.png);
                -webkit-background-size: 24px 24px;
                   -moz-background-size: 24px 24px;
                        background-size: 24px 24px;
            }
}

实际上,如果当显示的像素比率是正常密度的至少1.5倍时,那么我们就调用用大图,48px48px(icon-dribbble-2x.png),而在屏幕上显示时,压缩到24px24px的大小。

当在高分屏下看icon就会看出差别了,显示非常锐利。所有icon边缘不会出现模糊。

Sass for Web Designers

图4.4 通过Sass书写的高分屏icon

你可以想象一下,要兼容高分屏,会让你的样式表有大量重复的东西,比如媒体查询和用第2张图片覆盖一般分辨率下的背景图。运用Sass可以让这些过程变得更轻松。

我们可以创建一个Sass的mixin来处理这些繁杂的事情,甚至用一些灵活的关联形成两个不同的文件名。

下面名为的retinize的mixin是我每天在项目中用的-我会将重要的代码段分解展示。

@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    @media (-webkit-min-device-pixel-ratio: 1.5),
           (min--moz-device-pixel-ratio: 1.5),
           (-o-min-device-pixel-ratio: 3/2),
           (min-device-pixel-ratio: 1.5),
           (min-resolution: 1.5dppx) {
                & {
                    background-image: url('../img/' + $file + '-2x.' + $type);
                    -webkit-background-size: $width $height;
                    -moz-background-size: $width $height;
                    background-size: $width $height;
                }
            }
}

mixin的第一行建立了4个参数,这4个参数是必填项,否则不能正确编译:

  • 文件名
  • 图片类型(JPG,GIF,PNG)
  • 图片在屏幕上的宽度
  • 图片在屏幕上德高度

这4个参数列表像下面这样:

@mixin retinize($file, $type, $width, $height){}

调用retinize的mixin时,只用传这4个参数的值就可以了。例如,我们为Dribbble的icon调用这个mixin,参数为24px * 24px的PNG图片:

li.dribbble a {
    @include retinize('icon-dribbble', 'png', 24px, 24px);}
li.flickr a {
    @include retinize('icon-flickr', 'png', 24px, 24px);
}
li.facebook a {
    @include retinize('icon-facebook', 'png', 24px, 24px);
}

回到mixin的代码中,第2行是Sass通过把字符串和参数连接起来,形成在一般分辨率下background-image的规则。

background-image: url('../img/' + $file + '.' + $type);

我们给图片添加文件路径,然后添加文件名和图片的类型,经过编译后就像下面这样:

background-image: url(../img/icon-dribbble.png);

在适合的地方使用正常的背景图片,现在我们添加媒体查询,为支持1.5倍像素密度或者更高倍数的设备使用2倍图片大小的版本来覆盖之前正常的背景图片。我们再一次要包含所有属性前缀,确保在各浏览器下都能正常工作。

@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    @media (-webkit-min-device-pixel-“ratio: 1.5),
           (min--moz-device-pixel-ratio: 1.5),
           (-o-min-device-pixel-ratio: 3/2),
           (min-device-pixel-ratio: 1.5),
           (min-resolution: 1.5dppx) {
           & {
                background-image: url('../img/' + $file + '-2x.' + $type);
            }
        }
}

接下来,我们需要一个不论什么选择器都可以申请这个媒体查询的引用方法,并且依赖于从哪里调用的这个mixin的选择器。幸运的是,我们可以使用前面章节中介绍过的特殊占位符&,它会将当前的选择器插入进来(在我们的例子中,是选择器li.dribbble a).

@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    @media (-webkit-min-device-pixel-ratio: 1.5),
           (min--moz-device-pixel-ratio: 1.5),
           (-o-min-device-pixel-ratio: 3/2),
           (min-device-pixel-ratio: 1.5),
           (min-resolution: 1.5dppx) {
            & {
                background-image: url('../img/' + $file + '-2x.' + $type);
            }
        }
}

需要注意,我们使用Sass连接的能力在文件名的后面添加-2x来表示大图。这是一个解决命名习惯的好办法-短的文本使得在Sass中管理资源和调用文件都很容易。

  • 正常图片: file-name.png
  • 高分屏下2倍大小图片: file-name-2x.png

你可以不使用-2x命名的方式;你可以随你喜欢命名:file-name-jumbo, file-name-twice-as-big, file-name-at-two-times等等。但我觉得-2x命名的方式更合适一些。

mixin中最后一块是background-size属性(和加-webkit,-moz前缀是等价的),这个属性是用来告诉浏览器要用多大展示这张背景图:

@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    @media (-webkit-min-device-pixel-ratio: 1.5),
           (min--moz-device-pixel-ratio: 1.5),
           (-o-min-device-pixel-ratio: 3/2),
           (min-device-pixel-ratio: 1.5),
           (min-resolution: 1.5dppx) {
            & {
                background-image: url('../img/' + $file + '-2x.' + $type);
                -webkit-background-size: $width $height;
                -moz-background-size: $width $height;
                background-size: $width $height;
            }
        }
}

只要通过创建2张图片资源和一行SCSS代码,就可以在任何你想要的选择器中添加一个可复用的,兼容高分屏背景图的mixin。

li.dribbble a {
    @include retinize('icon-dribbble', 'png', 24px, 24px);
}

mixin嵌套mixin

mixin可以包含其他的mixin。如果你愿意就可以。别担心,整个程序不会崩溃的。事实上,我们可以在不重复代码的道路上更进一步,提取min-device-pixel-ratio前缀到一个变量中并且background-size属性对应输出到各自的mixin中。这些提取出来的部分可以在样式表中被其他模块或mixin复用。另外一个优势是这些前缀属性都存放在一个地方,如果属性的写法变了或者前缀不需要增加浏览器私有属性的时候,那么我们只需要在此一处编辑或者删除它们即可。

首先,让我们用一个可复用的变量来替换掉媒体查询中的像素密度的值。正如在本章节前面提到的,在选择器中出现的一个变量需要使用#{}来包住变量:

@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    @media #{$is-hidpi} {
        & {
            background-image: url('../img/' + $file + '-2x.' + $type);
            -webkit-background-size: $width $height;
               -moz-background-size: $width $height;
                    background-size: $width $height;
        }
    }
}

然后,我们用数组定义像素密度变量,这样的话,当有需要的时候就可以在样式表中随处复用了。

$is-hidpi: "(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-device-pixel-ratio: 1.5),(min-resolution: 1.5dppx)";

接下来,我们创建一个background-size的mixin,里面包括width和height属性还有兼容个浏览器的前缀及无前缀的一个。任何时候,当我们想要在选择器中使用background-size的时候,我们就可以调用这个mixin了:

@mixin background-size($width, $height) {
    -webkit-background-size: $width $height;
       -moz-background-size: $width $height;
            background-size: $width $height;
}

让我们在retinize的mixin中包含这个background-size的mixin,在多传$width和$height两个变量即可:

@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    @media #{$is-hidpi} {
        & {
            background-image: url('../img/' + $file + '-2x.' + $type);
            @include background-size($width,$height);
        }
    }
}

现在你已经创建好了这个mixin。我们可以向原始的retinize的mixin中添加可复用的样式或者mixins来重构它。共享的样式在以后要更新或维护,我们只需要在一个地方进行修改即可,这将会使我们在Sass的代码中大量减少重复的工作。

结束语

我希望这本小粉书已经帮你开始使用伤了Sass并时常使用这些核心特性。我也希望这本书可以消除使用者对Sass承担的一些误解:

  • 你需要学习Ruby。
  • 你需要调整你写CSS的过程。
  • 你需要非常精通命令行。

就像我们讨论过的,Sass的要求要比上面简单的多。但Sass可以让你想做什么就能做什么。无论如何,这都是一个非常好的工具,它可以很好的适应你的系统,并且不会扰乱你多年写CSS的流程(或者,如果你只是刚开始写CSS几个月,那么。。。祝贺你刚开始写几个月CSS)。

现在用可复用的Sassy CSS代码块去简化你的样式表吧 节省你大量的时间,还有最重要的是,去做对的事情吧!

第三章前言

译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!

如需转载烦请注明出处:

中文出处:http://zciwp.github.io/c4/

w3cplus地址:http://www.w3cplus.com/preprocessor/sass-for-web-designers-chapter-4.html

返回顶部