本来要管理Rhythm排版一致不是一件易事,响应式中的Rhythm排版就更加困难。幸运的是,Sass的Map可以更好的管理和实现响应式排版。
Rhythm is... a strong, regular, repeated pattern of movement or sound
Vertical rhythm is clearly an important part of Web design, yet on the subject of baseline, our community seems divided and there is no consensus as to how it fits in — if at all — with our growing and evolving toolkit for designing online.
有关于Rhythm相关知识可以点击下面链接进行详细的了解:
编写代码是一回事,但让段落的 font-size
根据响应式的断点保持一定的轨道(track)是另外一回事。从 h1
到 h6
为每个断点给字体大小设置一个变量,如此把事情变得更加繁琐,特别当类型不在一个线性比例上时。
如果你试图在响应式中解决这样的排版,下面的代码看起来非常的熟悉:
p { font-size: 15px; } @media screen and (min-width: 480px) { p { font-size: 16px; } } @media screen and (min-width: 640px) { p { font-size: 17px; } } @media screen and (min-width: 1024px) { p { font-size: 19px; } }
Sass的变量能在一个项目中得到很好的重用,但用于管理响应式排版中的字体大小时就得非常的鸡肋:
$p-font-size-mobile : 15px; $p-font-size-small : 16px; $p-font-size-medium : 17px; $p-font-size-large : 19px; $h1-font-size-mobile: 28px; $h1-font-size-small : 31px; $h1-font-size-medium: 33px; $h1-font-size-large : 36px; // 我认为你是明白的…
这里演示了Sass的Map和循环的强大之处: 他们能帮助我们更好的理管 z-index
的值, 颜色 ,不一会你将看到他们帮助我们更好的管理字体大小。
根据Sass Map的 key-value
关系创建一个Sass Map。将断点设置为 key
,将字体大小设置为对应 key
的 value
:
$p-font-sizes: ( null : 15px, 480px : 16px, 640px : 17px, 1024px: 19px );
记住移动先行(Mobile-first),我们看到第一个断点的 key
设置为 null
,表示默认的字体大小(不是媒体查询),并且按断点的升序排列。
接下来创建一个 mixin
,遍历Sass map中的 key
,并生成适当的媒体查询。
@mixin font-size($fs-map) { @each $fs-breakpoint, $fs-font-size in $fs-map { @if $fs-breakpoint == null { font-size: $fs-font-size; } @else { @media screen and (min-width: $fs-breakpoint) { font-size: $fs-font-size; } } } }
注意:这是一个mixin,值得一提的是,Sass具有编程逻辑特性,Sass借助 SassScript 的扩展,可以在Sass中使用一些基本的编程结构,比如 @if/@else
、 @for
和 @each
等逻辑控制命令。我建议您花一些时间去阅读 相关文档 。Sass的特性中介绍了一些Sass的新特性,了解一些Sass可以做哪些事情。
现在我们可以要段落中调用前面声明的混合宏:
p { @include font-size($p-font-sizes); }
编译出来的CSS:
p { font-size: 15px; } @media screen and (min-width: 480px) { p { font-size: 16px; } } @media screen and (min-width: 640px) { p { font-size: 17px; } } @media screen and (min-width: 1024px) { p { font-size: 19px; } }
管理和跟踪元素的字体大小变得容易多。对于新元素增加,只需要创建一个新的Sass Map和选择器中调用混合宏:
$h1-font-sizes: ( null : 28px, 480px : 31px, 640px : 33px, 1024px: 36px ); h1 { @include font-size($h1-font-sizes); }
编译出来的CSS:
h1 { font-size: 28px; } @media screen and (min-width: 480px) { h1 { font-size: 31px; } } @media screen and (min-width: 640px) { h1 { font-size: 33px; } } @media screen and (min-width: 1024px) { h1 { font-size: 36px; } }
字体大小一致的元素可以这样调用:
p, ul, ol { @include font-size($p-font-sizes); }
编译出来的CSS:
p, ul, ol { font-size: 15px; } @media screen and (min-width: 480px) { p, ul, ol { font-size: 16px; } } @media screen and (min-width: 640px) { p, ul, ol { font-size: 17px; } }
等等,新问题又来了。如果我们想要的段落 p
的字体大小为 17px
和 h1
标题的字体大小为 33px
,而断点是 700px
,不是 640px
。如果使用上面的解决方案,需要在每个实例中更改断点 640px
。我们试图解决这个问题,无意之中创建了另一个:断点分段。
试想一下,我们可以使用Sass Map来管理字体大小,是不是可以使用Sass Map来管理断点,对吗?完全正确!
给常见的断点创建一个Sass Map,并且给每个值取一个适当的名称。我们也会改为 font-size
的Map中断点名称( key
的名称),名称和断点Map $breakpoints
相匹配。
$breakpoints: ( small : 480px, medium: 700px, // Previously 640px large : 1024px ); $p-font-sizes: ( null : 15px, small : 16px, medium: 17px, large : 19px ); $h1-font-sizes: ( null : 28px small : 31px, medium: 33px, large : 36px );
最后一步是调整mixin,通过断点的名称映射到字体断点,并且遍历整个map,取到适当的值:
@mixin font-size($fs-map, $fs-breakpoints: $breakpoints) { @each $fs-breakpoint, $fs-font-size in $fs-map { @if $fs-breakpoint == null { font-size: $fs-font-size; } @else { // If $fs-font-size is a key that exists in // $fs-breakpoints, use the value @if map-has-key($fs-breakpoints, $fs-breakpoint) { $fs-breakpoint: map-get($fs-breakpoints, $fs-breakpoint); } @media screen and (min-width: $fs-breakpoint) { font-size: $fs-font-size; } } } }
注意:mixin中默认的断点Map是 $breakpoints
。如果你的断点变量名不同,一定要在参数中修改成你需要的断点变量名。
瞧!现在,我们要想给元素添加一个新的断点中字体字体大小,而且这个断点在 $breakpoints
中并不存在。只需要在字体大小的map中设置,只需要把需要的断点设置为一个 key
,并且设置对应的 font-size
值。那么mixin将这样工作:
$p-font-sizes: ( null : 15px, small : 16px, medium: 17px, 900px : 18px, large : 19px, 1440px: 20px, ); p { @include font-size($p-font-sizes); }
在mixin中Sass的 map-has-key
函数 起到很强大的作用。他会检查Map的 key
的值是否在 $breakpoints
中,如果存在,它将使用 key
的值;如果不存在,它会认为 key
是一个定制的 key
,并且会使用这个定制 key
的值生成媒体查询。
p { font-size: 15px; } @media screen and (min-width: 480px) { p { font-size: 16px; } } @media screen and (min-width: 700px) { p { font-size: 17px; } } @media screen and (min-width: 900px) { p { font-size: 18px; } } @media screen and (min-width: 1024px) { p { font-size: 19px; } } @media screen and (min-width: 1440px) { p { font-size: 20px; } }
line-height
来控制Vertical Rhythm line-height
对于实现Vertical Rhythm来说是一个很重要的参数。所以,我们提供一个 line-height
的解决方案,来解决Vertical Rhythm。
扩展 font-size
的map,在里面增加 line-height
。这里使用了Sass的list:
$breakpoints: ( small : 480px, medium: 700px, large : 1024px ); $p-font-sizes: ( null : (15px, 1.3), small : 16px, medium: (17px, 1.4), 900px : 18px, large : (19px, 1.45), 1440px: 20px, );
注意: line-height
值可以使用任何有效的CSS单位来定义( %
、 px
、 em
等),不过更 建议 使用 不带任何单位 来定义 line-height
,用来避免继承造成意想不到的结果。
然后,修改mixin,生成包括 line-height
的CSS:
@mixin font-size($fs-map, $fs-breakpoints: $breakpoints) { @each $fs-breakpoint, $fs-font-size in $fs-map { @if $fs-breakpoint == null { @include make-font-size($fs-font-size); } @else { // If $fs-font-size is a key that exists in // $fs-breakpoints, use the value @if map-has-key($fs-breakpoints, $fs-breakpoint) { $fs-breakpoint: map-get($fs-breakpoints, $fs-breakpoint); } @media screen and (min-width: $fs-breakpoint) { @include make-font-size($fs-font-size); } } } } // Utility function for mixin font-size @mixin make-font-size($fs-font-size) { // If $fs-font-size is a list, include // both font-size and line-height @if type-of($fs-font-size) == "list" { font-size: nth($fs-font-size, 1); @if (length($fs-font-size) > 1) { line-height: nth($fs-font-size, 2); } } @else { font-size: $fs-font-size; } }
mixin检查 font-size
Map中的值是不是一个列表。如果是一个列表,通过 nth()
函数 索引出需要的值。理论上第一个值是字体大小,第二个是行高的值。我们来看看效果:
p { @include font-size($p-font-sizes); }
编译出来的CSS:
p { font-size: 15px; line-height: 1.3; } @media screen and (min-width: 480px) { p { font-size: 16px; } } @media screen and (min-width: 700px) { p { font-size: 17px; line-height: 1.4; } } @media screen and (min-width: 900px) { p { font-size: 18px; } } @media screen and (min-width: 1024px) { p { font-size: 19px; line-height: 1.45; } } @media screen and (min-width: 1440px) { p { font-size: 20px; } }
最后的解决方案是可易扩展,可以适应其他属性的加入,比如 font-weight
、 margin
等属性。关键是要修改 make-font-size
这个混合宏,并且通过 nth()
函数从列表中索引出相匹配的值。
有各种各样的方法来处理响应式排版和Vertical Rhythm,并不局限于我的建议。然而,这是我很多次工作中总结出来的经验之谈。
使用Sass的混合宏可以在你编译出来的CSS中会产生重复的媒体查询。有关于重复媒体查询和媒体查询分组有很多讨论。 使用 @extend
来替代mixin 有助于性能和文件更小,然而测试得到的结论,并没有太大区别。最坏的情况就是比较丑, 其他问题基本上是不存在 。
我也意识到,我的解决方案并不健壮(没有设计处理媒体查询的范围, max-width
或视窗方向)。这些特性可以在mixin中实现(我个人版本也将 px
转换为 em
做单位),但对于复杂的媒体查询,我更喜欢用手写。不要你别忘记了,从现在开始,你可以使用 map-get()
函数来检索出需要的值。
本文根据 @Jonathan Suh 的《 Responsive Typography With Sass Maps 》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处: http://www.smashingmagazine.com/2015/06/17/responsive-typography-with-sass-maps/ 。
常用昵称“大漠”,W3CPlus, Sass中国 创始人,目前就职于手淘。中国Drupal社区核心成员之一。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。