Sass Map 响应式排版详解教程

作者:袖梨 2022-06-25

让段落的font-size根据响应式的断点保持一定的轨道(track),从h1到h6为每个断点给字体大小设置一个变量,如此把事情变得更加繁琐,特别当类型不在一个线性比例上时。

如果你试图在响应式中解决这样的排版,下面的代码看起来非常的熟悉:

p { font-size: 15px; }

@media screen and (min-) {
  p { font-size: 16px; }
}
@media screen and (min-) {
  p { font-size: 17px; }
}
@media screen and (min-) {
  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 Maps管理字体大小

根据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-) {
  p { font-size: 16px; }
}
@media screen and (min-) {
  p { font-size: 17px; }
}
@media screen and (min-) {
  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-) {
  h1 {
    font-size: 31px;
  }
}

@media screen and (min-) {
  h1 {
    font-size: 33px;
  }
}

@media screen and (min-) {
  h1 {
    font-size: 36px;
  }
}

字体大小一致的元素可以这样调用:

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

编译出来的CSS:

p, ul, ol {
  font-size: 15px;
}

@media screen and (min-) {
  p, ul, ol {
    font-size: 16px;
  }
}

@media screen and (min-) {
  p, ul, ol {
    font-size: 17px;
  }
}



Sass

// ----
// libsass (v3.2.5)
// ----

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

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

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

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

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


CSS

h1 {
  font-size: 28px;
}

@media screen and (min-) {
  h1 {
    font-size: 31px;
  }
}

@media screen and (min-) {
  h1 {
    font-size: 33px;
  }
}

@media screen and (min-) {
  h1 {
    font-size: 36px;
  }
}

p, ul, ol {
  font-size: 15px;
}

@media screen and (min-) {
  p, ul, ol {
    font-size: 16px;
  }
}

@media screen and (min-) {
  p, ul, ol {
    font-size: 17px;
  }
}

@media screen and (min-) {
  p, ul, ol {
    font-size: 19px;
  }
}


解决断点分段

等等,新问题又来了。如果我们想要的段落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-) {
  p { font-size: 16px; }
}
@media screen and (min-) {
  p { font-size: 17px; }
}
@media screen and (min-) {
  p { font-size: 18px; }
}
@media screen and (min-) {
  p { font-size: 19px; }
}
@media screen and (min-) {
  p { font-size: 20px; }
}


Sass

// ----
// libsass (v3.2.5)
// ----

$breakpoints: (
  small : 480px,
  medium: 700px, // Previously 640px
  large : 1024px
);

$p-font-sizes: (
  null  : 15px,
  small : 16px,
  medium: 17px,
  900px : 18px,
  large : 19px,
  1440px: 20px,
);

$h1-font-sizes: (
  null  : 28px,
  small : 31px,
  medium: 33px,
  large : 36px
);


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

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

CSS

p {
  font-size: 15px;
}

@media screen and (min-) {
  p {
    font-size: 16px;
  }
}

@media screen and (min-) {
  p {
    font-size: 17px;
  }
}

@media screen and (min-) {
  p {
    font-size: 18px;
  }
}

@media screen and (min-) {
  p {
    font-size: 19px;
  }
}

@media screen and (min-) {
  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-sizeMap中的值是不是一个列表。如果是一个列表,通过nth()函数索引出需要的值。理论上第一个值是字体大小,第二个是行高的值。我们来看看效果:

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

编译出来的CSS:

p {
  font-size: 15px;
  line-height: 1.3;
}

@media screen and (min-) {
  p {
    font-size: 16px;
  }
}

@media screen and (min-) {
  p {
    font-size: 17px;
    line-height: 1.4;
  }
}

@media screen and (min-) {
  p {
    font-size: 18px;
  }
}

@media screen and (min-) {
  p {
    font-size: 19px;
    line-height: 1.45;
  }
}

@media screen and (min-) {
  p {
    font-size: 20px;
  }
}


Sass

// ----
// libsass (v3.2.5)
// ----

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

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

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

CSS

p {
  font-size: 15px;
  line-height: 1.3;
}

@media screen and (min-) {
  p {
    font-size: 16px;
  }
}

@media screen and (min-) {
  p {
    font-size: 17px;
    line-height: 1.4;
  }
}

@media screen and (min-) {
  p {
    font-size: 18px;
  }
}

@media screen and (min-) {
  p {
    font-size: 19px;
    line-height: 1.45;
  }
}

@media screen and (min-) {
  p {
    font-size: 20px;
  }
}



最后的解决方案是可易扩展,可以适应其他属性的加入,比如font-weight、margin等属性。关键是要修改make-font-size这个混合宏,并且通过nth()函数从列表中索引出相匹配的值。


结语

处理响应式排版和Vertical Rhythm的方法有很多,然而,这是我很多次工作中总结出来的经验之谈。

相关文章

精彩推荐