Responsive Typography With Sass Maps (通过sass地图来打造响应式印刷)

前言

原文来源:这里

作者: Jonathan Suh

译者: 临水照影

正文之前

这应该只是一篇小品文。

正文

管理一致的字体印刷不是一件容易的事情,又是是当需要字体能够响应式,事情会变得更加麻烦。Sass Map技术能够让响应式印刷更加易于管理。

写代码是一件事,保持跟踪字体大小值又是另一件事。从h1h6,每一段有不同的字体尺寸,跟追会变得很麻烦,尤其是这个类型不能够线性扩展。

如果你想要实现响应式字体,你可能会这么写:

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;

// I think you get the point…(2333)

这里将能体现到Sass maps和循环大强大之处。它们可以帮你管理z-index的值(可以看这篇文章)和颜色(可以看这篇文章)还有你很快就能看到的,字体尺寸。

用Sass Maps管理字体尺寸

让我们开始用键值对创建Sass maps,断点(这里可以理解为你所要响应的尺寸)作为键,字体大小作为值

$p-font-sizes: (
  null  : 15px,
  480px : 16px,
  640px : 17px,
  1024px: 19px
);

首先我们可以看到 null代表默认字体尺寸(并不是在媒体查询中),断点按升序排列。

接下来,在@mixin中,它将通过迭代一个Sass map来生成相应的媒体查询

@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,在[SassScript](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#sassscript)的帮助下实现了类似编程逻辑的 if /else 这类判断,甚至是each循环等。我强烈推荐你花点时间来看文档。该文档将会向你介绍sass的各个方面。

我们可以这么应用该@mixin

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; }
}

管理和跟踪这类字体尺寸将变得非常简单。

对每一个新元素,都创建一个map然后调用mixin。

$h1-font-sizes: (
  null  : 28px
  480px : 31px,
  640px : 33px,
  1024px: 36px
);

h1 {
  @include font-size($h1-font-sizes);
}

然后使各类元素保持一致。

 p, ul, ol {
   @include font-size($p-font-sizes);
  }

解决断点碎片

但是等等!假如当我们想要响应式700x中将17px 的p 和 33px 的h1 代替640px是,该如何做?为了解决这个问题,这需要我们改变640px中的每个实例。为了解决这个问题,我们又不经意间创建了另一个问题:断点碎片。

如果我们在Sass maps里面管理字体尺寸,我们可以做同样的断点对吗?当然!

让我们创建一个普通断点的map然后给它们取一个合适的名字。我们将这断点Map和字体尺寸Map进行映射建立联系

 $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
 );

这最后一步是在迭代字体尺寸map的时候在mixin里面混入一点东西,它在建立媒体查询之前将用断点的名称来从$breakpoints中获取对应的值。

@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;
      }
    }
  }
}

这里用到了map-has-key方法(Returns whether a map has a value associated with a given key.)

Examples:

map-has-key(("foo": 1, "bar": 2), "foo") => true
map-has-key(("foo": 1, "bar": 2), "baz") => false

它用来验证这个Key的名字是不是存在于$breakpoints,如果存在,它将用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; }
}

提升垂直间距的行高

行高是垂直间距的重要部分。让我们在这个解决方案中包含行高

通过一个列表中的字体尺寸和行高作为键值扩展字体尺寸map。

$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,
);

注意:即使行高值能用任何的CSS单位定义。为了避免继承意外的东西,推荐用无单位行高。(可以点这里看详细的内容,还有这里)

我们需要修改mixin

@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-sizes 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; }
}

这个最终方案很容易扩展,像字体宽度,margin等等。关键是用 make-font-sizenth

结论

还有很多方法可以实现响应式字体尺寸,但是我觉得这种对我来说是最好的。

利用mixin可以在你编译css时创建重复的媒体查询,但还有很多争议.点这里

我也意识到我的方法不是最好的,但是我觉得我更喜欢手工写复杂的媒体查询。

备用方案

Viewport 单位((vh, vw, vmin and vmax)(资料)可以创建响应式印刷。

一个viewport单位等于百分之一viewport宽度或者高度。

Viewport 单位还可以创建Hero文本但它不适合用在body里面。它会变得太大或太小。

Modular Scale是一个创建响应式印刷的很好的工具。 Sara Soueidan也有篇关于响应式印刷的很好的文章

Image source帮你把图片制作各种响应式版本提供下载。

译者

总的来说,这篇文章能帮助更好的了解sass。至于你用不用他的方案,我觉得这已经不是很重要了-0-不过我反正会去尝试用。因为我也喜欢将能掌握的东西都手写。

Table of Contents