被忽略的Button组件隐藏了这么多

802 阅读5分钟

Button组件设计

Button 组件基本每个应用都会用到,写好一个button也是最基本,那如何来设计一个Button组件,针对不同的业务场景如何设计出符合设计的button,也是每个开发者必备的技能,下面就具体开始一个Button组件的开发

1. 改造原生button 按钮

我们看一下原生的button按钮,样式很丑,肯定是不符合我们的期许的,那我们后续就一点点来改造这个button按钮,当然如果button被其它样式污染,使用了all: revert;来恢复到浏览器默认的样式,接下来我们就一步步来改造button按钮:

1. 禁用按钮样式

浏览器有一部分自带的样式,可能会影响到我们的button按钮,所以针对这部分样式,我们需要将其禁用掉

  • 设置 outline: none; 去除按钮轮廓,详情可参考outline 使用文档
  • 设置 user-select: none; 用户不可以选择文本, 详情可参考 user-select 使用文档
  • 设置 touch-action: manipulation; ,这个属性针对的是移动端,让浏览器只允许进行滚动和持续缩放操作,比如禁用双击缩放页面等,详情可参考 touch-action 使用文档
  • 设置 appearance: none;,这个CSS 属性用于控制 UI 控件的基于操作系统主题的原生外观,通俗点说,就是一个div我想让它展示成button的样子,就设置appearance:button; 达到修改元素的默认的样式,改变元素的外观的效果,不过这个兼容性是有些问题,而且通常我们更喜欢去自定义样式,所以这个设置成node就可以了,详情可参考appearance 使用文档
  • 设置text-transform: none;是因为该属性可以指定将元素的文本大写。它可以用于使文本显示为全大写或全小写,也可单独对每一个单词进行操作,显然这个也不是我们想要的,所以我们设置成none详情可参考text-transform 使用文档
  • 设置 cursor: pointer; 用于设置光标的类型(如果有),在鼠标指针悬停在元素上时显示相应样式,详情可参考文档cursor 使用文档
.button {
    outline: none;
    user-select: none;
    touch-action: manipulation;
    appearance: none;
    text-transform: none;
    cursor: pointer;
}

2. 覆盖按钮样式

经过上列步骤,只是做了优化,样式依然很丑,那下面就需要我们将button默认的样式覆盖掉了,这个可根据设计或是风格去编写样式代码,下列只是案例,大家可自由发挥

.button {
    border: 1px solid #dcdfe6;
    background-color: transparent;
    padding: 10px 20px;
}

3. 重写按钮动作样式

按钮样式被改了,但我们点击按钮,会有一些额外的效果,这个就需要一些伪类样式,下面我们就介绍一下用于按钮的一些伪类,如果来制作我们心仪的按钮效果

  • active伪类,该伪类常用于a标签或是button标签,该伪类匹配被用户激活的元素。它让页面能在浏览器监测到激活时给出反馈。当用鼠标交互时,它代表的是用户按下按键和松开按键之间的时间 详情可参考active 使用文档

在最早我们接触伪类就是a标签,为什么顺序是:link—:visited—:hover—:active,但你尝试写的时候为什么感觉又不太对,如果想详细了解请参考Css章节,在这里会为你介绍

  • hover 伪类,设置鼠标悬停的效果,这个最常用就不赘述了
  • focus伪类,当用户点击或轻触一个元素或使用键盘的 Tab 键选择它时,它会被触发
  • focus-visible伪类,作用就是有效地根据用户的输入方式 (鼠标 vs 键盘) 展示不同形式的焦点,看到这个大家可能有点懵,可以参考我们文章介绍
.button {
    &:active {
        border-color: #0958d9;
    }
    &:hover {
        border-color: #4096ff;
    }
    &:focus {
        border-color: #0958d9;
        outline: none;
    }
    &:focus-visible {
        outline: 4px solid #91caff;
        outline-offset: 2px;
    }
}

4. 效果展示

改到这里基本的button 样式基本就完成了,后续可能会根据设计做一些调整,但最基本的这些样式是基本不会做调整的。可以查看效果

2. 按钮功能改造

按钮的种类很多,又按钮组,又loding按钮,也有圆形的等,而我们的主旨是只注重功能,以方便扩展,那下面我们将按钮进行改造,制作通用的按钮

2.1 添加disabled功能

按钮自带的就又disabled,这里只要设置样式就可以了,但总不能把所有伪类都覆盖一遍吧,这个倒不至于,我们可以通过css的note选择器,防止disabled元素被选中来实现

.button {
    &:not(.button_disabled):active {
        border-color: #0958d9;
    }
    &:not(.button_disabled):hover {
        border-color: #4096ff;
    }
    &:not(.button_disabled):focus {
        border-color: #0958d9;
        outline: none;
    }
    &:not(.button_disabled):focus-visible {
        outline: 4px solid #91caff;
        outline-offset: 2px;
    }
}
.button_disabled {
    opacity: 0.68;
    cursor: not-allowed;
}

效果如下:

2.2 添加文本按钮

文本按钮是不存在边框的,通常把边框去掉就可以了,我们可以通过控制样式就可以轻松实现该功能

.button_text {
    border: 0 solid transparent;
    background-color: transparent;
}

2.3 动态标签按钮

按钮是button来实现的,当然也可以很多场景我们想使用div 或是 a 标签来实现,那如何来动态的来实现该功能呢?首先div 可以添加 role="button" 会告诉屏幕阅读器这个元素是个按钮,但是不提供按钮的功能 可以设置tabindex="0" ,表示元素是可聚焦的,并且可以通过键盘导航来聚焦到该元素

interface ButtonTagNodeType {
    div:{
        role:string,
        tabIndex:number
    },
    button:{
        type:"button" | "submit" | "reset"
    },
    a:{
        rel:string,
        tabIndex:number
    }
}
interface ButtonTagProps {
    tag: "div" | 'a' | 'button',
    children?:ReactNode
}
const TagNode:ButtonTagNodeType = {
    div:{
        role:"button",
        tabIndex: 0
    },
    button:{
        type:"button"
    },
    a:{
        rel:"noopener noreferrer",
        tabIndex: 0
    }
}

const ButtonTag: FC<ButtonTagProps> = function (props) {
    const { tag:Tag,children } = props

    return (
       <Tag className={$style.button} {...TagNode[Tag]}>{children}</Tag>
    )
};



const ButtonTagDemo = function(){
    return (
        <ButtonTag tag="div">div标签</ButtonTag>
        <ButtonTag tag="button">button标签</ButtonTag>
        <ButtonTag tag="a">a标签</ButtonTag>
    )
}