阅读 93

如何编写一个按钮样式系统

最近计划开源一个框架,这里把其中一部分拿出来做个引子。看一下我们如何完成一个按钮的制作。以下代码用SCSS编写 我们先来做一个简单的。

简单的上手

.btn{
    display: inline-block;
    font-weight: 400;
    text-align: center;
    vertical-align: middle;
    white-space: nowrap;
    line-height:26px;
}
复制代码

增加浏览器支持

够简单吧,下面我们逐步丰富一下

.btn{
    //上面写过的代码就省略了
    touch-action: manipulation;//解决移动端延迟问题
    cursor: pointer;//修改PC端鼠标样式
    white-space: nowrap;//对文字进行一下处理
}
复制代码
<div class="btn">这是个案例</div>
复制代码
这是个案例

禁用选择

如果用户出现了滑动选择的操作行为,浏览器的默认样式会看起来很难看,所以禁用一下选择

.btn{
    //上面写过的代码就省略了
    -webkit-user-select: none;
       -moz-user-select: none;
        -ms-user-select: none;
            user-select: none;
}
复制代码

案例展示

<div class="btn">这里是不能选择的</div>
复制代码
这里是不能选择的

进行封装

这个看起来不是特别友好所以封装起来,整体如下

@mixin user-select($select) {
  -webkit-user-select: $select;
     -moz-user-select: $select;
      -ms-user-select: $select;
          user-select: $select;
}
.btn{
    display: inline-block;
    font-weight: 400;
    text-align: center;
    vertical-align: middle;
    white-space: nowrap;
    line-height:26px;
    touch-action: manipulation;//解决移动端延迟问题
    cursor: pointer;//修改PC端鼠标样式
    white-space: nowrap;//对文字进行一下处理
    @include user-select(none);
}
复制代码

色彩系统与样式组合

现在我们给按钮上色,说到颜色,这个问题就比较难讲,从bootstrap开始,按钮的命名似乎就是一成不变的,所有的绿色都叫success,所有的红色都加error,这种现象已经持续了将近十年,几乎所有的UI框架都是这么做的。但是实际上存在一个问题,就是业务和展示融合在了一起,比如我需要做一个加入购物车的按钮,为什么我要命名为btn-error呢?或者有一天UI要将这个按钮的颜色从红色变成绿色,你又该如何操作呢。

在我的思维逻辑里,最希望看到的就是所想即所写,所写即所得。也就是说,当我看到设计稿的上绘制的是红色,我写的就应该是红色,网页显示的也应该是红色,同样的逻辑我们可以采用完全脱离业务的命名系统,通过class的组合来拓展样式,因此我们需要建立一个色彩系统,这里以赤橙黄绿青蓝紫来举例;

$bg_colorList:(
	'red': #ff4c48,
	'orange': #ff9800,
	'yellow': #ff6705,
	'green': #2eb252,
	'coffee': #853f3a,
	'blue': #25aaff,
	'purple': #7000C8,
	'black':#000000,
	'white':#ffffff
);
.btn{
    //上面写过的代码就省略了
    color: white;//设置文字颜色
}
@each $colorName,$colorValue in $bg_colorList{
  .btn.#{$colorName}{
    background-color: $colorValue;
  }
}
复制代码

案例展示

<div class="btn red"></div>
复制代码
this is a btn

样式翻转

在我们使用的按钮中并不是所有的都是实心白字的,很多时候会用到彩色的边框,彩色的字的按钮,因此我们给按钮一个翻转的样式

@each $colorName,$colorValue in $bg_colorList{
  .btn.#{$colorName}{
    background-color: $colorValue;
    &.reverse{
        background-color: white;
        border:1px solid $colorValue;
        color:$colorValue;
    }
  }
}
复制代码

案例展示

<div class="btn red reverse"></div>
复制代码
this is a btn

边框调整

现在问题出现了,翻转的按钮多了一像素的边框,高度增加了两个像素(看下面的案例),为了保证所有按钮的尺寸统一,我们可以采用两种方式,给实心按钮增加和背景色相同的边框,或者修改实心按钮的高度,这里我们采用第一种方式。

this is a btn
this is a btn

这里另个btn高度明显看到差一个像素

@each $colorName,$colorValue in $bg_colorList{
  .btn.#{$colorName}{
    background-color: $colorValue;
    border:1px solid $colorValue;//增加边框
    &.reverse{
        background-color: white;
        border:1px solid $colorValue;
        color:$colorValue;
    }
  }
}
复制代码

案例展示

<div class="btn red"></div>
<div class="btn red reverse"></div>
复制代码
this is a btn
reverse btn

调整后的效果

样式抽取合并

你会发现这里有很多样式是重复的,所以我们可以进行样式抽取

@mixin button-variant($color, $background, $border) {
  color: $color;
  background-color: $background;
  border-color: $border;
}
.btn{
    border: 1px solid transparent;
    @include button-variant(#666, #eee, #ccc);//设置默认色通常不会用到
    &.reverse{
        @include button-variant(#666, white, #ccc);
    }
}
@each $colorName,$colorValue in $bg_colorList{
  .btn.#{$colorName}{
    @include button-variant(white, $colorValue, $colorValue);
    &.reverse{
      @include button-variant(map-get($colorList,#{$colorName}), white, $colorValue);
    }
  }
}
复制代码

disable

接下来增加对disable和active的处理(这里只写出移动端的样式,PC端会稍复杂一些)

@mixin button-variant($color, $background, $border) {
  color: $color;
  background-color: $background;
  border-color: $border;
}
.btn{
    &.disabled,
    &[disabled],
    fieldset[disabled] & {
        cursor: not-allowed;
        opacity:.55;
        box-shadow:none
    }
}
a.btn {//a标签需要的额外处理
  &.disabled,
  fieldset[disabled] & {
    pointer-events: none; 
  }
}
复制代码
<div class="btn red disable"></div>
<div class="btn red reverse disable"></div>
复制代码
red disable
reverse and disable

效果展示

设置padding

接下来我们设置pading并调整一下按钮的高度,如果我们想做一个高度为36的按钮,行高26 边框1像素,那么上下的padding就应该是(36-26-2)/2 结果为4;水平方向没有影响,至于为什么会选择44 36 28 而不选择其他的数值作为默认值,不在我们本次的讨论范围内,以后会讲到。

.btn{
    padding:4px 15px;
}
复制代码

按钮当然不能只有一个尺寸,我们可以多设计几个,并把相关代码提取出来。

$btn-size-list:(//$height, $padding-horizontal, $font-size
	'base':(36px,15px,16px),
	'small':(28px,15px,14px),
	'big':(44px,15px,16px),
);
@mixin button-size($height, $padding-horizontal, $font-size) {
  padding: ($height - 26px - 2) / 2 $padding-horizontal;
  font-size: $font-size;
}
.btn{
    @include button-size(
        nth(map-get($btn-size-list,'base'),1),
        nth(map-get($btn-size-list,'base'),2),
        nth(map-get($btn-size-list,'base'),3)
    );
}
@each $btnSizeName,$btnSizeValue in $btn-size-list{
  @if(#{$btnSizeName}!='base'){
    .btn.#{$btnSizeName}{
      @include button-size(
          nth($btnSizeValue,1),
          nth($btnSizeValue,2),
          nth($btnSizeValue,3)
      );
    }
  }
}
复制代码
<div class="btn red small"></div>
<div class="btn red "></div>
<div class="btn red big "></div>
复制代码
small
default
big

效果展示

圆角处理

$border-radius-base:        4px !default;
.btn{
    &.radius{
        border-radius: $border-radius-base;
      }
}
复制代码
<div class="btn red radius"></div>
<div class="btn red reverse radius"></div>
复制代码
default
reverse

效果展示

分组支持

在一些表单展示中会出现练成一排的按钮,如果带有圆角的话通常只有第一个和最后一个带有圆角。因此我们增加一下分组的支持;

.btn-group{
  padding-left:1px;//抵消按钮负边距的距离
  @include clearfix();//清除浮动
  .btn{
    position: relative;//修改定位
    float: left;
    margin-left:-1px;//设置负边距让两个翻转按钮连在一起的时候边框只有一像素
    &.radius{
      border-radius:0;
      &:first-child{
        border-radius: $border-radius-base 0 0 $border-radius-base;
      }
      &:last-child{
        border-radius: 0 $border-radius-base  $border-radius-base 0;
      }
    }
  }
}
复制代码
<div class="btn-group">
    <div class="btn red reverse radius"></div>
    <div class="btn red reverse radius"></div>
    <div class="btn red reverse radius"></div>
</div>

复制代码
reverse
reverse
reverse

效果展示

块级支持

接下来我们再给按钮增加一个铺满一行的状态,这个通常是用于展示一个比较长的按钮,比如登陆注册,加入购物车,立即购买等。

.btn.block {
  display: block;
}
.btn.block + .btn.block{
  margin-top: 5px;
}
input[type="submit"],
input[type="reset"],
input[type="button"] {
  &.btn.block {
    width: 100%;
  }
}
复制代码
<div class="btn red block"></div>
复制代码
宽度占据整个容器的btn

效果展示

完整结构

至此一个按钮就基本完成了,我们来看一下完整代码;

$border-radius-base:4px;
$btn-size-list:(//$height, $padding-horizontal, $font-size
	'base':(36px,15px,16px),
	'small':(28px,15px,14px),
	'big':(44px,15px,16px),
);
$bg_colorList:(
	'red': #ff4c48,
	'orange': #ff9800,
	'yellow': #ff6705,
	'green': #2eb252,
	'coffee': #853f3a,
	'blue': #25aaff,
	'purple': #7000C8,
	'black':#000000,
	'white':#ffffff
);
@mixin clearfix() {
  &:before,
  &:after {
    content: " ";
    display: table;
  }
  &:after {
    clear: both;
  }
}
@mixin user-select($select) {
  -webkit-user-select: $select;
     -moz-user-select: $select;
      -ms-user-select: $select;
          user-select: $select;
}
@mixin button-variant($color, $background, $border) {
  color: $color;
  background-color: $background;
  border-color: $border;
}
@mixin button-size($height, $padding-horizontal, $font-size) {
  padding: ($height - 26px - 2) / 2 $padding-horizontal;
  font-size: $font-size;
}
.btn {
  display: inline-block;
  margin-bottom: 0; 
  font-weight: 400;
  text-align: center;
  vertical-align: middle;
  touch-action: manipulation;
  cursor: pointer;
  background-image: none; 
  border: 1px solid transparent;
  white-space: nowrap;
  line-height:26px;
  @include button-variant(#666, #eee, #ccc);
  @include button-size(
      nth(map-get($btn-size-list,'base'),1)
      ,nth(map-get($btn-size-list,'base'),2)
      ,nth(map-get($btn-size-list,'base'),3)
  );
  @include user-select(none);
  &.radius{
    border-radius: $border-radius-base;
  }
  &.reverse{
    @include button-variant(#666, white, #ccc);
  }
  &.disabled,
  &[disabled],
  fieldset[disabled] & {
    cursor: not-allowed;
    opacity:.55;
    box-shadow:none
  }
}
@each $btnSizeName,$btnSizeValue in $btn-size-list{
  @if(#{$btnSizeName}!='base'){
    .btn.#{$btnSizeName}{
      @include button-size(
          nth($btnSizeValue,1),
          nth($btnSizeValue,2),
          nth($btnSizeValue,3)
      );
    }
  }
}
a.btn {
  &.disabled,
  fieldset[disabled] & {
    pointer-events: none; 
  }
}
@each $colorName,$colorValue in $bg_colorList{
  .btn.#{$colorName}{
    @include button-variant(white, $colorValue, $colorValue);
    &.reverse{
      @include button-variant($colorValue, white, $colorValue);
    }
  }
}
.btn.block {
  display: block;
}
.btn.block + .btn.block{
  margin-top: 5px;
}
input[type="submit"],
input[type="reset"],
input[type="button"] {
  &.btn.block {
    width: 100%;
  }
}
//btn-group
.btn-group{
  padding-left:1px;
  @include clearfix();
  .btn{
    position: relative;
    float: left;
    margin-left:-1px;
    &.radius{
      border-radius:0;
      &:first-child{
        border-radius: $border-radius-base 0 0 $border-radius-base;
      }
      &:last-child{
        border-radius: 0 $border-radius-base  $border-radius-base 0;
      }
    }
  }
}
复制代码
<div class="btn red radius"></div>
<div class="btn red reverse radius"></div>
复制代码
default
red
orange
yellow
green
coffee
blue
purple

效果展示

操作举例

现在我们已经可以通过组合的方式来展示各种各样的按钮了,你可以使用任何你需要使用的标签,这里均已DIV举例

<div class="btn">默认</div>
<div class="btn red">红色按钮</div><!--也可以将red替换成blue green等-->
<div class="btn small red">红色小按钮</div><!--也可以将替换成big-->
<div class="btn small red radius">红色圆角小按钮</div>
<div class="btn small red radius reverse">红色翻转圆角小按钮</div>
<div class="btn small red radius reverse block">宽红色翻转圆角小按钮</div>
<div class="btn-group">
    <div class="btn small red radius">第一个</div>
    <div class="btn small red radius">第二个</div>
    <div class="btn small red radius">第三个</div>
</div>
复制代码

后续优化

前文展示的代码只是按钮系统的一部分,实际的代码要比这个复杂的多,包括如下一些没有讲到的内容

  • 按钮和标签以及图标的组合
  • PC端不同状态的切换
  • 表单的组合使用
  • 不同色彩浓度的调整
  • 按需加载:动态生成和加载色彩系统
  • 动态修改:两个页面都使用了红色按钮但要求颜色不同
  • 对方形 药片形 圆形等样式的支持

但是现在的已经可以直接编译使用,如果需要设置其他的尺寸或颜色可以修改默认的配置,编译生成直接使用就可以了。

如果你觉得还有什么功能是你需要但是在这里没提到的可以添加我的微信给我留言,更多文章及直播教程可以关注冰山工作室公众号

关注下面的标签,发现更多相似文章
评论