使用 SassMap 实现响应式排版

发布于 2021-12-20 23:25:34 字数 12594 浏览 1228 评论 0

本来要管理 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 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-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的字体大小为17pxh1标题的字体大小为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单位来定义(%pxem等),不过更建议使用不带任何单位来定义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-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-weightmargin 等属性。关键是要修改make-font-size这个混合宏,并且通过nth()函数从列表中索引出相匹配的值。

总结

有各种各样的方法来处理响应式排版和Vertical Rhythm,并不局限于我的建议。然而,这是我很多次工作中总结出来的经验之谈。

使用Sass的混合宏可以在你编译出来的CSS中会产生重复的媒体查询。有关于重复媒体查询和媒体查询分组有很多讨论。使用@extend来替代mixin 有助于性能和文件更小,然而测试得到的结论,并没有太大区别。最坏的情况就是比较丑,其他问题基本上是不存在

我也意识到,我的解决方案并不健壮(没有设计处理媒体查询的范围,max-width 或视窗方向)。这些特性可以在mixin中实现(我个人版本也将px转换为em做单位),但对于复杂的媒体查询,我更喜欢用手写。不要你别忘记了,从现在开始,你可以使用 map-get() 函数来检索出需要的值。

扩展阅读

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

瑾兮

暂无简介

0 文章
0 评论
21356 人气
更多

推荐作者

qq_aHcEbj

文章 0 评论 0

寄与心

文章 0 评论 0

13545243122

文章 0 评论 0

流星番茄

文章 0 评论 0

春庭雪

文章 0 评论 0

潮男不是我

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文