极简按钮

button · minimal · 灵感来源:Refactoring UI(层次靠排版与留白,不靠材质)

试试 hover / 按下 / disabled / 加载

复用卡

Flat Minimal Button · 极简按钮 · 按钮 / Minimal

规格 · 术语提示词(给 AI 用)

做一个 Flat Minimal Button(极简按钮):
- 实底 + 一条发丝边线 + 一档软阴影,没有模糊与浮起
- 层次靠排版与留白,hover 只加一层 5% 墨色
- sm / md / lg 三档尺寸 + pill 变体 + disabled
- 与玻璃按钮同一份 props,按压统一缩放 0.97
用这些 CSS 变量:--color-divider --color-surface --color-text --color-text-muted --color-text-subtle --dur-fast --ease-standard --glass-border --glass-recess --shadow-sm --text-base --text-xs
灵感参考:Refactoring UI(层次靠排版与留白,不靠材质)

参数(CSS 变量)

--color-divider --color-surface --color-text --color-text-muted --color-text-subtle --dur-fast --ease-standard --glass-border --glass-recess --shadow-sm --text-base --text-xs

必缺状态

焦点 / 按压 / 禁用

整页场景

(尚未接进整页场景)

安装(一条链接)

参考(本站可核对)

调研笔记

极简按钮

  • 与玻璃按钮同一份 props(variant / size / loading / disabled + 原生属性)——风格只改视觉,不改调用方式
  • 没有模糊、没有浮起、没有双阴影:--color-surface 实底 + 一条 --color-divider 发丝边 + --shadow-sm
  • hover 只加一层 5% 墨色(--glass-recess),不放大、不上浮;按压仍是全站统一的 0.97(.press-feedback)
  • 圆角用 rounded-md,切换极简配方时会跟着配方收小(4px)

对比玻璃按钮:同一位置少了 backdrop-blur 与 --glass-inset,所以它在任何背景上都一样清楚——这是极简轴要的确定性。

实现

button/minimal
import { type ButtonHTMLAttributes, type ReactNode } from 'react';
import { Spinner } from '~/components/Feedback';

type Variant = 'default' | 'pill';
type Size = 'sm' | 'md' | 'lg';

interface MinimalButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  children: ReactNode;
  variant?: Variant;
  size?: Size;
  /** 加载中:转圈 + 禁用 + aria-busy(宽度不变,内容变淡) */
  loading?: boolean;
}

const sizeClasses: Record<Size, string> = {
  sm: 'px-4 py-1.5 text-sm',
  md: 'px-6 py-2.5 text-base',
  lg: 'px-8 py-3.5 text-lg',
};

const variantClasses: Record<Variant, string> = {
  default: 'rounded-md',
  pill: 'rounded-full',
};

/**
 * 极简按钮:与玻璃按钮同一份 props,只换视觉——
 * 没有模糊、没有浮起、没有双阴影;层次靠一条发丝边线与一层 5% 墨色。
 */
export function MinimalButton({
  children,
  variant = 'default',
  size = 'md',
  loading = false,
  className = '',
  ...rest
}: MinimalButtonProps) {
  const spinnerSize = size === 'lg' ? 16 : size === 'md' ? 14 : 12;
  return (
    <button
      {...rest}
      aria-busy={loading || undefined}
      disabled={loading || rest.disabled}
      className={`
        relative inline-flex items-center justify-center
        ${sizeClasses[size]}
        ${variantClasses[variant]}
        font-medium
        text-[var(--color-text)]
        bg-[var(--color-surface)]
        border border-[var(--color-divider)]
        shadow-[var(--shadow-sm)]
        press-feedback
        transition-[background-color,border-color,color,opacity] duration-[var(--dur-fast)] ease-[var(--ease-standard)]
        hover:bg-[var(--glass-recess)]
        disabled:opacity-40 disabled:cursor-not-allowed
        cursor-pointer
        ${className}
      `}
    >
      {loading && <Spinner size={spinnerSize} className="mr-2 align-[-0.125em]" label="加载中" />}
      <span className={loading ? 'opacity-60' : ''}>{children}</span>
    </button>
  );
}

元信息

标识
button/minimal
状态
done
最后提交
2026-10-31
风格轴
minimal
依赖 token
--color-divider --color-surface --color-text --color-text-muted --color-text-subtle --dur-fast --ease-standard --glass-border --glass-recess --shadow-sm --text-base --text-xs
在工作台打开 源码与 notes 都在 src/components/button/minimal/