面试问“如何设计可扩展 Button”?我给他看了之后他竖起了大拇指说牛!

前言

面试官:(打着哈欠,眼皮打架)你说你写的组件库拓展性很好,怎么证明呢?

:(自信满满)您直接去我的组件库官网看,这是传统组件库样式,大家都能做:

地址:上图页面地址

:(切换页面)然后这是我用自己写的 @t-headless-ui/react 中的同一个 Button 组件,只是稍微改了下样式。比如你想要像素风格,请看

地址:上图页面地址

(如果您觉得不错,请求 github 一个赞,我一定会拓展所有主流组件到其中的!)

面试官 :(原本昏昏欲睡,突然瞪大眼睛,从凳子上一跃而起)"卧槽!兄弟!你这...这怎么做到的?!"

:(淡定一笑)无它,headless 组件库而已!

面试官:(扶了扶惊掉的下巴)"headless 组件库是什么意思?"

:"简单说就是------我们只提供JavaScript逻辑和功能,样式你爱咋写咋写!甚至连DOM结构都随便你折腾!"

面试官 :(沉默三秒,突然起身握住我的手)"好!我服!明天就来上班!工资你随便开!"

当然,上面只是一个小玩笑,哈哈,今天我们就来介绍一下,如何写一个像素风格的 button 以及为啥我的 headless button 稍微改一下样式,就可以造一个生产级别能用的 button 呢?

一个生产环境要用的 button 需要具备什么样的属性呢?

button 的状态

一个 button 至少需要:

  • 定义 disabled 状态,在 disabled 状态, click 事件是无法生效的
  • 定义 loading 状态,在 loading 状态, click 事件同样是是无法生效的

headless 也就是无样式组件的层面上,做到拦截这两个属性的 click 事件就足够了.

是不是很简单,只是因为 button 的复杂度更多在于定制样式,所以 headless 组件库的 button 很容易实现。

核心代码如下:

javascript 复制代码
  const handleClick: MouseEventHandler = (event): void => {
    if (loading || disabled) {
      // 阻止按钮默认行为,防止在 loading 或 disabled 状态下意外提交表单或触发其他默认事件
      event?.preventDefault?.();
      return;
    }
    onClick?.(event);
  };

好了,接下来我们说样式,样式的难度主要在于状态很多,包括:

  • 各种主题色
    • primary:主色
    • success: 成功色
    • error:失败颜色
    • 。。。 然后每个状态都分为,default 状态,disabled 状态,loading 状态
  • 各种风格
    • 有背景色的风格
    • 只有边框的风格
    • 。。。

然后每个状态都分为,default 状态,disabled 状态,loading 状态

还有需要考虑 focus 上的颜色状态,hover 的颜色状态。

这才是一个 button 真正复杂的点!

如果你想学造组件库,欢迎加入,我们超级可拓展的 headless 组件库交流群(还有包装简历服务哦!)主页

我们现在为了说明这个像素 button 实现的思路,我们化繁为简,假设只有 1 种颜色,1 种风格,如下图:

说明实现思路是如何,大家可以触类旁通!

如何制作出像素效果

从上面图可以看出,最明显的像素想过就是我们死角都是 transparent(透明色),这个明显是 border 实现不了的。那如何做呢?

秘密就是 box-shadow !

我们先举一个小例子,你看看 box-shadow 如何使用,假设我们需要一个下边框,box-shadow 设置为:

css 复制代码
box-shadow: 0 5px black;

其中 0 代表横向的偏移量,我们是 0 ,也就是横轴上什么也不做,5px 代表在竖直方向上向下制造一个 5pxshadow, 然后颜色设置为 black 也就是黑色,请看这个按钮下方多了一条黑色

接着,我们依葫芦画瓢,加上 上面的 shadow

css 复制代码
box-shadow: 0 5px black, 0 -5px black;

效果如下

接着右边我们也加一下:

css 复制代码
box-shadow: 0 5px black, 0 -5px black, 5px 0 black;

是不是有感觉了,shadowborder 最大的不同就是边角不会填充颜色,然后接着,我们加上又边框:

css 复制代码
box-shadow: 0 5px black, 0 -5px black, 5px 0 black, -5px 0 black;

最后我们再润色一下,加一个上阴影,增加立体感,如下图

css 复制代码
box-shadow: 0 5px black, 0 -5px black, 5px 0 black, -5px 0 black, inset 0px 4px rgba(255,255,255,0.21);

其中 inset 的意思是增加内阴影,之前我们都是外阴影,rgba(255,255,255,0.21) 是指白色,然后 0.21 的透明度。

初始化 button

在我们设置 box-shadow 之前,这个 button 背景色,字体这些还没初始化,我们先来补上这个知识

css 复制代码
{
    position: relative;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    user-select: none;
    padding-top: .25rem;
    padding-bottom: .25rem;   
    padding-left: 1.25rem;
    padding-right: 1.25rem;
    background-color: rgb(88,199,192);
    border-width: 0;
    border-radius: 0;
}

好了初始化完毕,我们开始处理各种状态:

hover 状态

hover 状态我们用了一个 filter 函数,增加亮度。

css 复制代码
filter:brightness(1.05)

但是同时注意,在 disabledloading 状态 hover 无效。在 react 一般都要使用 classnames 库,tailwindcss 还要将 classnamestailwindcss-merge 库结合, vue 好像天然对于 css 的条件判断就支持的不错。

类似代码如下(不同的框架和使用的 css 框架不同,代码肯定有不同,这个很简单,大家看得懂意思即可):

javascript 复制代码
{
   'hover:brightness-105': !disabled && !loading
}

'hover:brightness-105' 是 tailwindcss 中的类名, 对于普通的 css 你只要换成你希望的类名,然后引入对应的 css 文件即可(在 css 文件里实现这个 hover 的 css).

active 状态

active 状态,我们只是把内阴影变黑了,给人一种凹凸下去的感觉。 active 下的 box-shadow 变为

css 复制代码
box-shadow: 0px 5px black,0px -5px black,5px 0px black,-5px 0px black,inset 0px 5px #00000038

可以看到只是最后 inset 中的颜色变深了,其它都没有变。

处理 disabled 状态

这个状态其实需要判断,比如 disabled 的时候 cursor ,也就是鼠标显示一个禁止点击的状态,这样有利于告诉用户此时点击无效。

类似的增加对 disabled 状态的处理:

javascript 复制代码
{
    'opacity-50 cursor-not-allowed': disabled, // disabled 时透明度降低,cursor 变为 not-allowed
}

字体更想像素的样子

这个最好是搜索有像素风格的字体样式,我是为了方便,使用系统自带中,类似有像素风格的字体,大家可以参考,兼容macwindows

css 复制代码
{
    font-family: 'ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Lucida_Console','Courier_New',monospace'
}

dark 模式

我使用的是 tailwindcss 天然对 dark 模式可以自定义颜色,国内很多人不使用 taiwindcss 这里就不赘述了

小总结

这里就结束了,这一种状态看起来代码不多,其实状态和颜色多了之后,css 代码会非常多。最后,大家可以看到 headless 组件的好处就是跟样式没有任何耦合,随便用各种 css 框架自己处理,它的好处我会在后面的文章继续介绍,例如 radio 组件,本质上就是单选,我们看到很多组件库都是一个圆形按钮的样式,其实谁规定了必须是这个样式?我可以是任何只要满足单选的样式即可。

所以后续会有很多有趣的主题加入进来,包括button,也会持续更新更重好玩的样式和动画!

最后你到看到这里了,欢迎各位大侠来组件库交流群交流,也希望各位大侠给个github的赞,嘿嘿

相关推荐
消失的旧时光-19432 小时前
Kotlinx.serialization 对多态对象(sealed class )支持更好用
java·服务器·前端
少卿3 小时前
React Compiler 完全指南:自动化性能优化的未来
前端·javascript
广州华水科技3 小时前
水库变形监测推荐:2025年单北斗GNSS变形监测系统TOP5,助力基础设施安全
前端
广州华水科技3 小时前
北斗GNSS变形监测一体机在基础设施安全中的应用与优势
前端
七淮3 小时前
umi4暗黑模式设置
前端
8***B3 小时前
前端路由权限控制,动态路由生成
前端
军军3603 小时前
从图片到点阵:用JavaScript重现复古数码点阵艺术图
前端·javascript
znhy@1233 小时前
Vue基础知识(一)
前端·javascript·vue.js
terminal0073 小时前
浅谈useRef的使用和渲染机制
前端·react.js·面试
我的小月月3 小时前
🔥 手把手教你实现前端邮件预览功能
前端·vue.js