按钮与状态 触发动作、告诉用户「在跑」

FlowButton flowbtn

悬停时圆形色块自中心扩散铺满,左箭头滑入、右箭头滑出,文字右移让位,胶囊收成圆角矩形。把鼠标移上去看。

用法
<link rel="stylesheet" href="ui-kit.css">
<script src="ui-kit.js"></script>

<!-- 加 data-flow,页面加载后自动升级 -->
<button data-flow>刷新数据</button>

// 或者用 JS 新建 / 升级已有按钮
const b = UIKit.flowButton("刷新数据", { onClick: run });
UIKit.upgradeFlow(document.querySelector("#btnSave"));
UIKit.flowText(b, "已刷新");   // 改文字不能用 textContent=

LoadingState loadst

3×3 像素网格 + 流光标签 + 等宽实时计时。适合 10 秒以上的长任务:转圈只说明「在跑」,这个还回答「跑了多久」。

Drive
Dots
Orbit
用法
<span id="status"></span>

const h = UIKit.busy("#status", "刷新实际数据", "Drive");  // 开始,计时自动走
// ...await fetch(...)
h.stop("已刷新(3 组)");        // 结束:停表 + 写终态文字
// 等价写法:UIKit.idle("#status", "已刷新(3 组)")

// 三种波形:Drive 方块波前 / Dots 圆点波前 / Orbit 绕周长的彗星
// 长任务(分钟级)用 Orbit,高频短任务用 Dots

筛选与选择 都是持有值的控件,看板工具栏三件套

SlideTabs slidetabs

滑块在标签之间移动:悬停先滑过去「预演」,点击才真正切换,移出容器自动归位。做切片器 / 分段控件 / 视图切换。方向键也能切。

全部
含禁用项 · 铺不下时可横滑
用法
<div id="slicer"></div>

const tabs = UIKit.slideTabs("#slicer", {
  items: ["全部", "洗地机", "吸尘器"],
  value: "全部",
  onChange: (value, index) => render(value)
});
tabs.value();               // 当前值
tabs.setValue("吸尘器");     // 程序化赋值,不触发 onChange
tabs.setItems(list, "全部"); // 换一批选项

<!-- 或纯标签声明,自动升级;加 class="fill" 铺满一行等分 -->
<div data-tabs="日,周,月,季,年" data-value="月"></div>
el.addEventListener("uik:change", e => e.detail.value);

SelectBox selectbox

下拉框(持有值的那种):选中项打勾并高亮,选项 ≥12 个自动出搜索框,首字母连打可跳转。第二个是由原生 <select> 升级来的——没 JS 时它就是个能用的下拉框,升级后仍能正常提交表单。

en · cn
用法
<!-- 推荐:原生 select 加 data-select,渐进增强 -->
<select data-select name="lang">
  <option value="zh-CN" data-icon="🇨🇳" selected>简体中文</option>
  <option value="en"    data-icon="🇺🇸">English</option>
</select>
// 选中值会写回原生 select 并派发原生 change,表单照常提交

// 或纯 JS
const sb = UIKit.selectBox("#lang", {
  items: [
    { value: "en",    label: "English",  icon: "🇺🇸" },
    { value: "zh-CN", label: "简体中文", icon: "🇨🇳" }
  ],
  value: "en",
  search: "auto",          // true / false / "auto"(≥12 项时出搜索框)
  onChange: (v, item) => setLang(v)
});
sb.value();                // 当前值
sb.setValue("zh-CN");      // 程序化赋值,不触发 onChange
sb.setItems(list, "en");   // 换一批选项

DateFilter datefilter

日期筛选:日期区间(双月并排)与单日两种模式,左侧快捷区间(本月、近 30 天、本季度…)。方向键可逐日移动,PageUp/Down 翻月。

未选
用法
<button id="range">时间范围</button>

const df = UIKit.dateFilter("#range", {
  mode: "range",                    // 或 "single"
  value: ["2026-01-01", "2026-03-31"],
  min: "2020-01-01", max: "2026-12-31",
  onChange: (v) => reload(v[0], v[1])   // v 是 ["YYYY-MM-DD","YYYY-MM-DD"],清空时为 null
});
df.value();                 // 当前值
df.setValue(["2026-04-01","2026-06-30"]);   // 不触发 onChange

<!-- 或纯标签声明,用 uik:change 取值 -->
<button data-date-filter="range" data-value="2026-01-01,2026-03-31">时间范围</button>

// 自定义快捷区间 / 禁用某些日子
presets: [{ label: "本财年", get: () => [new Date(2026,3,1), new Date(2027,2,31)] }],
disabledDate: (d) => d.getDay() === 0        // 周日不可选

菜单与导航 点了就走 / 点了就执行,不留值

DropMenu dropmenu

毛玻璃下拉菜单:面板模糊淡入 + 缩放,条目逐条错峰滑入,箭头翻转 180°。面板挂在 body 下、fixed 定位,不会被工具栏的 overflow 裁掉;放不下会自动翻边。要保存选中值请用 SelectBox。

未选
用法
<button id="btnOps">操作</button>

const m = UIKit.dropMenu("#btnOps", {
  items: [
    { label: "编辑", icon: PEN,  onClick: edit },
    { label: "复制", icon: COPY, onClick: dup },
    "-",                                        // 分隔线
    { label: "删除", icon: BIN,  onClick: del, danger: true }
  ],
  align: "end",          // 右对齐;side:"top" 可指定向上弹
  onSelect: (item, i) => console.log(item.value)
});
m.setItems(list); m.setLabel("更多操作"); m.close();

<!-- 或纯标签声明(无图标),用 uik:select 取值 -->
<button data-menu="按日,按周,按月" data-align="end">粒度</button>
btn.addEventListener("uik:select", e => e.detail.label);

MenuVertical menuvert

竖排大字导航:悬停时箭头自左侧滑入、文字右移让位并变色,可选斜切。动效全在 CSS 的 :hover / :focus-visible 上,用 Tab 键也能看到。

用法
<!-- 渐进增强写法(推荐):链接留在 HTML 里 -->
<nav data-menu-vertical data-color="#ff6900" data-skew="-8">
  <a href="/about">关于</a>
  <a href="/work">作品</a>
</nav>

// 或用 JS 给数据
UIKit.menuVertical("#nav", {
  items: [{ label: "关于", href: "/about" }, { label: "作品", href: "/work" }],
  color: "#ff6900",
  skew: -8,           // 悬停斜切角(度),0 = 不斜
  current: "auto"     // 按 location 自动标当前项
});

/* 尺寸只看两个变量,改字号箭头与让位距离自动跟上 */
.menuvert{ --mv-size:48px; --mv-arrow:1.1em; }

Dock dock

macOS 式放大坞:鼠标靠近时图标按距离连续放大,弹簧收敛,悬停冒出标签气泡。方向键也能走,聚焦项同样会放大。

用法
<div id="dock"></div>

UIKit.dock("#dock", {
  items: [
    { icon: SVG_HOME, label: "总览", value: "overview", active: true },
    { icon: "📈",     label: "同比", value: "yoy" },     // svg 字符串 / Element / emoji 都行
    { icon: SVG_SET,  label: "设置", href: "/settings" }
  ],
  size: 44, magnify: 78, distance: 150,          // 静止边长 / 最大边长 / 影响半径
  spring: { mass: .1, stiffness: 150, damping: 12 },
  onSelect: (item, i) => go(item.value)
});
UIKit.dockOf("#dock").setActive("yoy");

<!-- 或纯标签声明:子元素内容当图标,data-label 当标签 -->
<div data-dock data-size="40" data-magnify="66">
  <button data-label="总览" data-active>📊</button>
  <a href="/set" data-label="设置">⚙️</a>
</div>

流程图 自成一套板岩配色,不吃 --uik-accent

FlowDiagram flowdia

系统图 / 流程图:泳道 + 按语义分色的节点 + 正交连线。把鼠标移到节点上看聚焦。位置由你给的 (lane, col) 决定,它不做自动布局。
五套皮:slate 提炼自 archify(MIT), flat / terminal / blueprint / notion 提炼自 fireworks-tech-graph(MIT)的风格指南 1/2/3/4。换皮只改令牌,走线与交互不动。

slate · archify
用法
<div id="arch"></div>

UIKit.flowDiagram("#arch", {
  lanes: [
    { id: "edge", title: "接入层" },
    { id: "svc",  title: "服务层" },
    { id: "data", title: "数据层" }
  ],
  nodes: [
    { id: "web",  label: "Web 控制台", sub: "React",  type: "frontend", lane: "edge", col: 0 },
    { id: "api",  label: "API 网关",   sub: "Kong",   type: "backend",  lane: "edge", col: 2, tag: "LB" },
    { id: "calc", label: "测算服务",   sub: "Python", type: "backend",  lane: "svc",  col: 1 },
    { id: "pg",   label: "PostgreSQL", sub: "主库",   type: "database", lane: "data", col: 1 },
    { id: "mq",   label: "任务队列",   sub: "Redis",  type: "queue",    lane: "svc",  col: 3 }
  ],
  edges: [
    { from: "web",  to: "api",  label: "HTTPS" },
    { from: "api",  to: "calc", label: "gRPC", type: "emphasis" },
    { from: "calc", to: "pg",   label: "SQL",  type: "dashed" },
    { from: "calc", to: "mq" },
    { from: "mq",   to: "calc", label: "回调" }            // 回头线自动绕道
  ],
  onSelect: (n) => console.log(n.id)
});

// 节点 type 共 7 种,决定配色与图例:
// frontend 青 / backend 绿 / database 紫 / cloud 黄 / security 粉 / queue 橙 / external 灰
// 同一泳道内叠多行用 row: 1;拿静态图去贴文档用 handle.svg()

// 换皮:slate(默认)/ flat / terminal / blueprint / notion
UIKit.flowDiagram("#arch", { preset: "blueprint", ...spec });
UIKit.flowDiagramOf("#arch").setPreset("notion");   // 也可以随时换

使用方法

按项目形态三选一。

1. 普通网页
dist/ui-kit.css + dist/ui-kit.js 两个文件拷进项目,link/script 引入。

<link rel="stylesheet" href="ui-kit.css">
<script src="ui-kit.js"></script>

2. 单文件 HTML(离线分发、聊天里发来发去的那种)
把两个文件的内容直接内联进 <style><script>,没有外链、没有字体、没有图标包。

3. React / Next.js
react/ 下的原件(各组件依赖不同:Tailwind、 lucide-reactframer-motion, 个别还要 shadcn 的 ui/button),别用这里的原生版。

改配色
每个颜色都写成 var(--uik-xxx, 默认值),覆盖令牌即可全局换色; 暗色跟随系统,也可给根元素加 data-uik-theme="dark" 手动切。

新增组件
components/<名字>/ 下放同名 css/js,把目录名加进 build.pyCOMPONENTS,跑一次 python build.py,再到本页加卡片与左侧目录项。详见 README.md。