两个文件,直接接入
样式 + 脚本,图标已内置。没有构建、没有 npm、不绑定框架,静态页与后台模板都能用。
样式 + 脚本,图标已内置。没有构建、没有 npm、不绑定框架,静态页与后台模板都能用。
纯色填充、1px 近同色描边、无渐变无投影。层级靠留白与背景差,不靠光影。
事件全部委托、rAF 节流、只跑合成属性;尊重「减弱动态效果」。
颜色、圆角、动效全是 CSS 变量。改三行就能换成你的品牌,深色自动跟随。
样式与脚本两个文件放进项目,图标已内置在脚本里。所有组件都是普通 HTML 类名。
<html lang="zh-CN">
<head>
<link rel="stylesheet" href="sakuno.css">
</head>
<body>
<button class="sk-btn sk-btn--primary">你好,樱乃</button>
<script src="sakuno.js" defer></script>
</body>
</html>
同一份 HTML 在窄屏自动切单列:输入框升到 16px 防止 iOS 聚焦放大,触摸目标补到 32px,表格首列吸附,看板与拖拽列表长按就能拖。断点只有三档,不需要额外配置。
| 断点 | 布局变化 | 典型设备 |
|---|---|---|
| < 640px | 全部单列;表格横滚、看板纵向堆叠、分栏上下排 | 手机 |
| 640–860px | 组件内两列;文档目录折成顶部锚点条 | 平板竖屏 |
| > 860px | 完整双栏,左侧目录常驻 | 平板横屏 / 桌面 |
让 AI 写 Sakuno 页面时,先给它这三样东西:接入说明、整页文档、机器可读索引。全部是纯文本,不需要联网。
组件决策表、颜色 × 变体矩阵、可直接复制的结构、事件与数据属性清单,以及 29 条真实踩坑。
当前文档站全部区块的标题、说明与示例源码,一次复制成 Markdown,直接贴进对话。
站点级索引:文件位置、CDN 地址、关键约束。放在根目录,Agent 抓一个文件就够。
<!-- 两个文件即可接入,静态托管即可 -->
/SKILL.md 接入说明(约 36 KB)
/llms.txt 站点索引
/docs/sakuno.md 整页文档(由文档站生成)
<!-- 页面里也可以直接引用 -->
<link rel="alternate" type="text/markdown" href="/docs/sakuno.md">7 组语义色 × 11 档色阶,由 OKLCH 感知均匀生成;同一档在不同色相上明暗一致。实心底的前景色按对比度配对,淡色底用主色透明度实现,因此明暗两模式共用一套规则。
<div class="sw-grid">
<div class="sw-card" style="background:#1271ea;color:#ffffff">
<b>Aa</b>
<span>primary</span>
<i>#1271ea</i>
</div>
<div class="sw-card" style="background:#ff94da;color:#1f101a">
<b>Aa</b>
<span>secondary</span>
<i>#ff94da</i>
</div>
<div class="sw-card" style="background:#2cc366;color:#0b1a0f">
<b>Aa</b>
<span>success</span>
<i>#2cc366</i>
</div>
<div class="sw-card" style="background:#ffa910;color:#1e1406">
<b>Aa</b>
<span>warning</span>
<i>#ffa910</i>
</div>
<div class="sw-card" style="background:#e00657;color:#ffffff">
<b>Aa</b>
<span>danger</span>
<i>#e00657</i>
</div>
<div class="sw-card" style="background:#00b0d3;color:#04191f">
<b>Aa</b>
<span>info</span>
<i>#00b0d3</i>
</div>
<div class="sw-card" style="background:#70707a;color:#ffffff">
<b>Aa</b>
<span>default</span>
<i>#70707a</i>
</div>
</div><div class="sw-row">
<div class="sw-row__name">primary</div>
<div class="sw-steps">
<div class="sw-step" style="background:#f0f6ff" title="primary-50 #f0f6ff">
<span>50</span>
</div>
<div class="sw-step" style="background:#e2eeff" title="primary-100 #e2eeff">
<span>100</span>
</div>
<div class="sw-step" style="background:#c6ddff" title="primary-200 #c6ddff">
<span>200</span>
</div>
<div class="sw-step" style="background:#a0c6ff" title="primary-300 #a0c6ff">
<span>300</span>
</div>
<div class="sw-step" style="background:#65a3ff" title="primary-400 #65a3ff">
<span>400</span>
</div>
<div class="sw-step" style="background:#2881fb" title="primary-500 #2881fb">
<span>500</span>
</div>
<div class="sw-step" style="background:#0062d4" title="primary-600 #0062d4">
<span>600</span>
</div>
<div class="sw-step" style="background:#004da9" title="primary-700 #004da9">
<span>700</span>
</div>
<div class="sw-step" style="background:#003981" title="primary-800 #003981">
<span>800</span>
</div>
<div class="sw-step" style="background:#00265a" title="primary-900 #00265a">
<span>900</span>
</div>
<div class="sw-step" style="background:#00173d" title="primary-950 #00173d">
<span>950</span>
</div>
</div>
</div>
<div class="sw-row">
<div class="sw-row__name">secondary</div>
<div class="sw-steps">
<div class="sw-step" style="background:#fff1f9" title="secondary-50 #fff1f9">
<span>50</span>
</div>
<div class="sw-step" style="background:#ffe4f4" title="secondary-100 #ffe4f4">
<span>100</span>
</div>
<div class="sw-step" style="background:#ffc9ea" title="secondary-200 #ffc9ea">
<span>200</span>
</div>
<div class="sw-step" style="background:#ffa0dd" title="secondary-300 #ffa0dd">
<span>300</span>
</div>
<div class="sw-step" style="background:#ff53cd" title="secondary-400 #ff53cd">
<span>400</span>
</div>
<div class="sw-step" style="background:#e315b2" title="secondary-500 #e315b2">
<span>500</span>
</div>
<div class="sw-step" style="background:#b5008d" title="secondary-600 #b5008d">
<span>600</span>
</div>
<div class="sw-step" style="background:#900070" title="secondary-700 #900070">
<span>700</span>
</div>
<div class="sw-step" style="background:#6d0054" title="secondary-800 #6d0054">
<span>800</span>
</div>
<div class="sw-step" style="background:#4c0039" title="secondary-900 #4c0039">
<span>900</span>
</div>
<div class="sw-step" style="background:#330025" title="secondary-950 #330025">
<span>950</span>
</div>
</div>
</div>
<div class="sw-row">
<div class="sw-row__name">success</div>
<div class="sw-steps">
<div class="sw-step" style="background:#e1ffe7" title="success-50 #e1ffe7">
<span>50</span>
</div>
<div class="sw-step" style="background:#c0ffce" title="success-100 #c0ffce">
<span>100</span>
</div>
<div class="sw-step" style="background:#73fc9c" title="success-200 #73fc9c">
<span>200</span>
</div>
<div class="sw-step" style="background:#58e485" title="success-300 #58e485">
<span>300</span>
</div>
<div class="sw-step" style="background:#2ac164" title="success-400 #2ac164">
<span>400</span>
</div>
<div class="sw-step" style="background:#00a14d" title="success-500 #00a14d">
<span>500</span>
</div>
<div class="sw-step" style="background:#007e3b" title="success-600 #007e3b">
<span>600</span>
</div>
<div class="sw-step" style="background:#00642d" title="success-700 #00642d">
<span>700</span>
</div>
<div class="sw-step" style="background:#004b20" title="success-800 #004b20">
<span>800</span>
</div>
<div class="sw-step" style="background:#003314" title="success-900 #003314">
<span>900</span>
</div>
<div class="sw-step" style="background:#00210a" title="success-950 #00210a">
<span>950</span>
</div>
</div>
</div>
<div class="sw-row">
<div class="sw-row__name">warning</div>
<div class="sw-steps">
<div class="sw-step" style="background:#fff4e7" title="warning-50 #fff4e7">
<span>50</span>
</div>
<div class="sw-step" style="background:#ffe9cf" title="warning-100 #ffe9cf">
<span>100</span>
</div>
<div class="sw-step" style="background:#ffd39d" title="warning-200 #ffd39d">
<span>200</span>
</div>
<div class="sw-step" style="background:#ffb345" title="warning-300 #ffb345">
<span>300</span>
</div>
<div class="sw-step" style="background:#dd9100" title="warning-400 #dd9100">
<span>400</span>
</div>
<div class="sw-step" style="background:#b67700" title="warning-500 #b67700">
<span>500</span>
</div>
<div class="sw-step" style="background:#8f5d00" title="warning-600 #8f5d00">
<span>600</span>
</div>
<div class="sw-step" style="background:#724800" title="warning-700 #724800">
<span>700</span>
</div>
<div class="sw-step" style="background:#553500" title="warning-800 #553500">
<span>800</span>
</div>
<div class="sw-step" style="background:#3b2300" title="warning-900 #3b2300">
<span>900</span>
</div>
<div class="sw-step" style="background:#261500" title="warning-950 #261500">
<span>950</span>
</div>
</div>
</div>
<div class="sw-row">
<div class="sw-row__name">danger</div>
<div class="sw-steps">
<div class="sw-step" style="background:#fff2f3" title="danger-50 #fff2f3">
<span>50</span>
</div>
<div class="sw-step" style="background:#ffe6e8" title="danger-100 #ffe6e8">
<span>100</span>
</div>
<div class="sw-step" style="background:#ffccd2" title="danger-200 #ffccd2">
<span>200</span>
</div>
<div class="sw-step" style="background:#ffa8b3" title="danger-300 #ffa8b3">
<span>300</span>
</div>
<div class="sw-step" style="background:#ff6986" title="danger-400 #ff6986">
<span>400</span>
</div>
<div class="sw-step" style="background:#ef2663" title="danger-500 #ef2663">
<span>500</span>
</div>
<div class="sw-step" style="background:#c2004a" title="danger-600 #c2004a">
<span>600</span>
</div>
<div class="sw-step" style="background:#9b003a" title="danger-700 #9b003a">
<span>700</span>
</div>
<div class="sw-step" style="background:#75002a" title="danger-800 #75002a">
<span>800</span>
</div>
<div class="sw-step" style="background:#52001b" title="danger-900 #52001b">
<span>900</span>
</div>
<div class="sw-step" style="background:#37000f" title="danger-950 #37000f">
<span>950</span>
</div>
</div>
</div>
<div class="sw-row">
<div class="sw-row__name">info</div>
<div class="sw-steps">
<div class="sw-step" style="background:#e8f9ff" title="info-50 #e8f9ff">
<span>50</span>
</div>
<div class="sw-step" style="background:#d0f4ff" title="info-100 #d0f4ff">
<span>100</span>
</div>
<div class="sw-step" style="background:#9ee9ff" title="info-200 #9ee9ff">
<span>200</span>
</div>
<div class="sw-step" style="background:#38d8ff" title="info-300 #38d8ff">
<span>300</span>
</div>
<div class="sw-step" style="background:#00b5d9" title="info-400 #00b5d9">
<span>400</span>
</div>
<div class="sw-step" style="background:#0095b3" title="info-500 #0095b3">
<span>500</span>
</div>
<div class="sw-step" style="background:#00758d" title="info-600 #00758d">
<span>600</span>
</div>
<div class="sw-step" style="background:#005c70" title="info-700 #005c70">
<span>700</span>
</div>
<div class="sw-step" style="background:#004554" title="info-800 #004554">
<span>800</span>
</div>
<div class="sw-step" style="background:#002f39" title="info-900 #002f39">
<span>900</span>
</div>
<div class="sw-step" style="background:#001d25" title="info-950 #001d25">
<span>950</span>
</div>
</div>
</div>
<div class="sw-row">
<div class="sw-row__name">default</div>
<div class="sw-steps">
<div class="sw-step" style="background:#f5f5ff" title="default-50 #f5f5ff">
<span>50</span>
</div>
<div class="sw-step" style="background:#ececf7" title="default-100 #ececf7">
<span>100</span>
</div>
<div class="sw-step" style="background:#dadae5" title="default-200 #dadae5">
<span>200</span>
</div>
<div class="sw-step" style="background:#c3c3ce" title="default-300 #c3c3ce">
<span>300</span>
</div>
<div class="sw-step" style="background:#a2a2ac" title="default-400 #a2a2ac">
<span>400</span>
</div>
<div class="sw-step" style="background:#85858f" title="default-500 #85858f">
<span>500</span>
</div>
<div class="sw-step" style="background:#686871" title="default-600 #686871">
<span>600</span>
</div>
<div class="sw-step" style="background:#51515b" title="default-700 #51515b">
<span>700</span>
</div>
<div class="sw-step" style="background:#3c3c45" title="default-800 #3c3c45">
<span>800</span>
</div>
<div class="sw-step" style="background:#282830" title="default-900 #282830">
<span>900</span>
</div>
<div class="sw-step" style="background:#191920" title="default-950 #191920">
<span>950</span>
</div>
</div>
</div><div class="surf-row">
<div class="surf surf--bg">--sk-bg<br>页面背景</div>
<div class="surf surf--1">--sk-surface<br>卡片</div>
<div class="surf surf--2">--sk-surface-2<br>柔和填充</div>
<div class="surf surf--3">--sk-surface-3<br>悬停</div>
</div>146 个 Font Awesome Pro 轮廓图标,已内置在 sakuno.js 里,不需要额外的图标脚本。点任意图标即可复制标签。
python3 tools/gen_icons.py 扩充白名单。两种写法等价:<i data-sk-icon="house"></i> 会在运行时替换成
<svg class="sk-i"><use href="#i-house"></use></svg>。颜色继承 currentColor,
大小由 font-size 控制,也可加 sk-i--sm / lg / xl。
图标名不在表里时是空白,不会报错——先查这张表。JS 里拼字符串用 Sakuno.icon.check,
要 DOM 节点用 Sakuno.iconEl('house')。
38px 高、12px 圆角、14px 字、无投影。data-color 选色族,data-variant 选填充方式,两者自由组合。
<button class="sk-btn sk-btn--primary">主要操作</button>
<button class="sk-btn">次要操作</button>
<button class="sk-btn sk-btn--accent">樱粉</button>
<button class="sk-btn sk-btn--soft">柔和</button>
<button class="sk-btn sk-btn--outline">描边</button>
<button class="sk-btn sk-btn--ghost">幽灵</button><button class="sk-btn" data-color="primary" data-variant="solid">primary</button>
<button class="sk-btn" data-color="secondary" data-variant="solid">secondary</button>
<button class="sk-btn" data-color="success" data-variant="solid">success</button>
<button class="sk-btn" data-color="warning" data-variant="solid">warning</button>
<button class="sk-btn" data-color="danger" data-variant="solid">danger</button>
<button class="sk-btn" data-color="info" data-variant="solid">info</button>
<button class="sk-btn" data-color="default" data-variant="solid">default</button><button class="sk-btn" data-variant="solid">solid</button>
<button class="sk-btn" data-variant="bordered">bordered</button>
<button class="sk-btn" data-variant="light">light</button>
<button class="sk-btn" data-variant="flat">flat</button>
<button class="sk-btn" data-variant="ghost">ghost</button><div class="sk-row" style="gap:10px;flex-wrap:wrap">
<button class="sk-btn sk-btn--sm" data-color="primary" data-variant="solid">solid</button>
<button class="sk-btn sk-btn--sm" data-color="primary" data-variant="flat">flat</button>
<button class="sk-btn sk-btn--sm" data-color="primary" data-variant="bordered">bordered</button>
<button class="sk-btn sk-btn--sm" data-color="primary" data-variant="light">light</button>
</div>
<div class="sk-row" style="gap:10px;flex-wrap:wrap">
<button class="sk-btn sk-btn--sm" data-color="secondary" data-variant="solid">solid</button>
<button class="sk-btn sk-btn--sm" data-color="secondary" data-variant="flat">flat</button>
<button class="sk-btn sk-btn--sm" data-color="secondary" data-variant="bordered">bordered</button>
<button class="sk-btn sk-btn--sm" data-color="secondary" data-variant="light">light</button>
</div>
<div class="sk-row" style="gap:10px;flex-wrap:wrap">
<button class="sk-btn sk-btn--sm" data-color="success" data-variant="solid">solid</button>
<button class="sk-btn sk-btn--sm" data-color="success" data-variant="flat">flat</button>
<button class="sk-btn sk-btn--sm" data-color="success" data-variant="bordered">bordered</button>
<button class="sk-btn sk-btn--sm" data-color="success" data-variant="light">light</button>
</div>
<div class="sk-row" style="gap:10px;flex-wrap:wrap">
<button class="sk-btn sk-btn--sm" data-color="danger" data-variant="solid">solid</button>
<button class="sk-btn sk-btn--sm" data-color="danger" data-variant="flat">flat</button>
<button class="sk-btn sk-btn--sm" data-color="danger" data-variant="bordered">bordered</button>
<button class="sk-btn sk-btn--sm" data-color="danger" data-variant="light">light</button>
</div><button class="sk-btn sk-btn--primary sk-btn--xs">极小</button>
<button class="sk-btn sk-btn--primary sk-btn--sm">小</button>
<button class="sk-btn sk-btn--primary ">默认</button>
<button class="sk-btn sk-btn--primary sk-btn--lg">大</button>
<button class="sk-btn sk-btn--primary sk-btn--xl">超大</button><button class="sk-btn sk-btn--primary" data-sk-loading>载入中</button>
<button class="sk-btn" disabled>禁用</button>
<button class="sk-btn sk-btn--primary" id="btnLoadDemo">点击载入</button>
<button class="sk-btn sk-btn--soft sk-btn--block" style="max-width:280px">通栏按钮</button><div class="sk-stack" style="gap:10px">
<a class="sk-link" href="#btn">默认链接,跟随主色</a>
<a class="sk-link sk-link--underline" href="#btn">带下划线的链接</a>
<a class="sk-link" data-color="secondary" href="#btn">樱粉色链接</a>
<a class="sk-link" data-color="danger" href="#btn">危险操作链接</a>
<a class="sk-link sk-link--block" data-color="default" href="#btn">中性链接</a>
</div>同一层级里的互斥选择。选中态只靠一块纯白和 1px 线,不需要投影表达。
<div aria-label="视图" class="sk-segment" role="tablist">
<button aria-selected="true" class="sk-segment__item" role="tab">全部</button>
<button aria-selected="false" class="sk-segment__item" role="tab">进行中</button>
<button aria-selected="false" class="sk-segment__item" role="tab">已完成</button>
</div>
<div aria-label="对齐" class="sk-toggle-group" role="group">
<button aria-label="左对齐" aria-pressed="true" class="sk-toggle">
<i data-sk-icon="align-left"></i>
</button>
<button aria-label="居中" aria-pressed="false" class="sk-toggle">
<i data-sk-icon="align-center"></i>
</button>
<button aria-label="右对齐" aria-pressed="false" class="sk-toggle">
<i data-sk-icon="align-right"></i>
</button>
</div>
<div aria-label="样式" class="sk-toggle-group" role="group">
<button aria-label="加粗" aria-pressed="false" class="sk-toggle">
<i data-sk-icon="bold"></i>
</button>
<button aria-label="斜体" aria-pressed="true" class="sk-toggle">
<i data-sk-icon="italic"></i>
</button>
<button aria-label="下划线" aria-pressed="false" class="sk-toggle">
<i data-sk-icon="underline"></i>
</button>
</div><div aria-label="全宽" class="sk-segment sk-segment--full" role="tablist">
<button aria-selected="true" class="sk-segment__item" role="tab">日视图</button>
<button aria-selected="false" class="sk-segment__item" role="tab">周视图</button>
<button aria-selected="false" class="sk-segment__item" role="tab">月视图</button>
</div>小巧的标签。默认是主色 20% 的淡底加深档文字,可关闭、可带头像。
<span class="sk-chip" data-color="primary">primary</span>
<span class="sk-chip" data-color="secondary">secondary</span>
<span class="sk-chip" data-color="success">success</span>
<span class="sk-chip" data-color="warning">warning</span>
<span class="sk-chip" data-color="danger">danger</span>
<span class="sk-chip" data-color="info">info</span>
<span class="sk-chip" data-color="default">default</span><span class="sk-chip" data-variant="solid">solid</span>
<span class="sk-chip" data-variant="flat">flat</span>
<span class="sk-chip" data-variant="bordered">bordered</span>
<span class="sk-chip" data-variant="light">light</span><span class="sk-chip sk-chip--sm">小</span>
<span class="sk-chip ">默认</span>
<span class="sk-chip sk-chip--lg">大</span><span class="sk-chip" data-color="primary">Sakuno<button class="sk-chip__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-chip" data-color="secondary">樱乃<button class="sk-chip__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-chip" data-color="success">Vue<button class="sk-chip__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-chip" data-color="danger">React<button class="sk-chip__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span><span class="sk-chip" style="height:32px;padding-inline:4px 12px 4px 4px">
<span class="sk-avatar sk-avatar--xs">樱</span>瓜生樱乃</span>
<span class="sk-chip" data-color="secondary" style="height:32px;padding-inline:4px 12px 4px 4px">
<span class="sk-avatar sk-avatar--xs sk-avatar--accent">乃</span>图书委员</span>浅色底 + 同色文字,不使用边框描边,视觉噪音最低。
<span class="sk-badge">默认</span>
<span class="sk-badge sk-badge--primary">Primary</span>
<span class="sk-badge sk-badge--accent sk-badge--pill">樱粉</span>
<span class="sk-badge sk-badge--success">
<i class="sk-dot">
</i>正常</span>
<span class="sk-badge sk-badge--warning">待审核</span>
<span class="sk-badge sk-badge--danger">失败</span>
<span class="sk-badge sk-badge--info">Beta</span>
<span class="sk-badge sk-badge--solid">Solid</span>
<span class="sk-badge sk-badge--outline">描边</span>
<span class="sk-badge sk-badge--success">
<i class="sk-dot sk-dot--pulse">
</i>进行中</span><span class="sk-tag">CSS 变量 <button aria-label="移除" class="sk-tag__close">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-tag sk-tag--primary">零依赖 <button aria-label="移除" class="sk-tag__close">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-tag sk-tag--accent">深色模式 <button aria-label="移除" class="sk-tag__close">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-kbd">⌘</span>
<span class="sk-kbd">K</span>
<span class="sk-count">9</span>
<span class="sk-count">128</span><div class="sk-row" style="gap:22px;align-items:center">
<span class="sk-badged">
<button class="sk-btn">收件箱<span class="sk-badged__badge" data-color="danger">8</span>
</button>
</span>
<span class="sk-badged">
<button class="sk-btn sk-btn--soft">消息<span class="sk-badged__badge" data-color="danger">99+</span>
</button>
</span>
<span class="sk-badged">
<span class="sk-avatar sk-avatar--lg">樱</span>
<span class="sk-badged__badge sk-badged__badge--dot" data-color="success">
</span>
</span>
<span class="sk-badged">
<button class="sk-btn sk-btn--ghost sk-btn--icon" aria-label="通知">
<i data-sk-icon="bell"></i>
<span class="sk-badged__badge sk-badged__badge--dot" data-color="danger">
</span>
</button>
</span>
</div><span class="sk-tag sk-tag--primary">Sakuno<button class="sk-tag__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-tag sk-tag--accent">樱乃<button class="sk-tag__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-tag">纯样式<button class="sk-tag__close" aria-label="移除">
<i data-sk-icon="xmark"></i>
</button>
</span>无图时用淡色块 + 首字,永远不会有裂图。三种色调、五种尺寸、状态点与头像组。
<span class="sk-avatar sk-avatar--xs">樱</span>
<span class="sk-avatar sk-avatar--sm">乃</span>
<span class="sk-avatar">S</span>
<span class="sk-avatar sk-avatar--lg">K</span>
<span class="sk-avatar sk-avatar--xl">A</span>
<span class="sk-avatar sk-avatar--accent">粉</span>
<span class="sk-avatar sk-avatar--neutral">灰</span>
<span class="sk-avatar sk-avatar--square">UI</span>
<span class="sk-avatar">N<span class="sk-avatar__status sk-avatar__status--online">
</span>
</span>
<span class="sk-avatar sk-avatar--accent">B<span class="sk-avatar__status sk-avatar__status--busy">
</span>
</span>
<div class="sk-avatar-group">
<span class="sk-avatar sk-avatar--sm">A</span>
<span class="sk-avatar sk-avatar--sm sk-avatar--accent">B</span>
<span class="sk-avatar sk-avatar--sm sk-avatar--neutral">C</span>
<span class="sk-avatar sk-avatar--sm sk-avatar--neutral" style="font-size:10px">+6</span>
</div><div class="sk-person">
<span class="sk-avatar">樱</span>
<div class="sk-person__meta">
<span class="sk-person__name">瓜生樱乃</span>
<span class="sk-person__desc">图书委员 · 在线</span>
</div>
</div>
<div class="sk-divider sk-divider--v">
</div>
<div class="sk-person">
<span class="sk-avatar sk-avatar--accent">可</span>
<div class="sk-person__meta">
<span class="sk-person__name">九条可怜</span>
<span class="sk-person__desc">2 小时前活跃</span>
</div>
</div>输入框、输入组、文本域、自定义下拉、复选与单选、卡片式选择。焦点态只加一圈 3px 淡色环。
<div class="sk-field">
<label class="sk-label" for="f1">昵称 <span class="sk-label__req">*</span>
</label>
<input class="sk-input" id="f1" placeholder="樱乃" value="Kano"/>
<span class="sk-hint">2–16 个字符,支持中英文。</span>
</div>
<div class="sk-field">
<label class="sk-label" for="f2">邮箱</label>
<div class="sk-input-group">
<span class="sk-input-group__addon">
<i data-sk-icon="envelope"></i>
</span>
<input class="sk-input" id="f2" placeholder="you@example.com"/>
</div>
<span class="sk-error">
<i data-sk-icon="circle-exclamation"></i>
邮箱格式不正确
</span>
</div>
<div class="sk-field">
<label class="sk-label">角色(自定义下拉)</label>
<div class="sk-select" data-sk-select="">
<button aria-expanded="false" class="sk-select__trigger" type="button">
<span class="sk-select__value">魔法少女</span>
<span class="sk-select__chevron">
<i data-sk-icon="chevron-down"></i>
</span>
</button>
<div class="sk-select__panel" hidden="">
<div aria-selected="true" class="sk-select__option" role="option">魔法少女</div>
<div aria-selected="false" class="sk-select__option" role="option">普通高中生</div>
<div aria-selected="false" class="sk-select__option" role="option">图书委员</div>
<div aria-selected="false" class="sk-select__option" role="option">转学生</div>
</div>
</div>
</div>
<div class="sk-field">
<label class="sk-label" for="f4">禁用</label>
<input class="sk-input" disabled="" id="f4" value="不可编辑"/>
</div>
<div class="sk-field" style="grid-column:1/-1">
<label class="sk-label" for="f5">简介</label>
<textarea class="sk-textarea" id="f5" placeholder="写点什么…">
</textarea>
</div><label class="sk-check">
<input checked="" type="checkbox"/>
<span class="sk-check__box">
</span>接受协议</label>
<label class="sk-check">
<input type="checkbox"/>
<span class="sk-check__box">
</span>订阅更新</label>
<label class="sk-check">
<input disabled="" type="checkbox"/>
<span class="sk-check__box">
</span>不可用</label>
<div class="sk-divider sk-divider--v">
</div>
<label class="sk-check sk-check--radio">
<input checked="" name="r1" type="radio"/>
<span class="sk-check__box">
</span>浅色</label>
<label class="sk-check sk-check--radio">
<input name="r1" type="radio"/>
<span class="sk-check__box">
</span>深色</label>
<label class="sk-check sk-check--radio">
<input name="r1" type="radio"/>
<span class="sk-check__box">
</span>跟随系统</label><label class="sk-choice">
<input checked="" class="sk-sr-only" name="plan" type="radio"/>
<span class="sk-choice__box"></span>
<span class="sk-choice__body">
<span class="sk-choice__title">个人版</span>
<span class="sk-choice__desc">¥0 / 月,适合了解框架</span>
</span>
</label>
<label class="sk-choice">
<input class="sk-sr-only" name="plan" type="radio"/>
<span class="sk-choice__box"></span>
<span class="sk-choice__body">
<span class="sk-choice__title">团队版</span>
<span class="sk-choice__desc">¥49 / 月,含协作与历史</span>
</span>
</label>开关是纯色轨道 + 白色圆点;滑块只有一枚浅色 thumb,不加立体高光。
<div class="sk-row" style="gap:22px">
<label class="sk-switch">
<input checked="" type="checkbox"/>
<span class="sk-switch__track">
</span>自动播放</label>
<label class="sk-switch">
<input type="checkbox"/>
<span class="sk-switch__track">
</span>消息通知</label>
<label class="sk-switch sk-switch--sm">
<input checked="" type="checkbox"/>
<span class="sk-switch__track">
</span>小号</label>
<label class="sk-switch">
<input disabled="" type="checkbox"/>
<span class="sk-switch__track">
</span>禁用</label>
</div>
<div class="sk-field">
<div class="sk-between">
<label class="sk-label" for="s1">音量</label>
<span class="sk-badge sk-badge--primary" id="volOut">64</span>
</div>
<input class="sk-slider" data-sk-output="#volOut" id="s1" max="100" min="0" type="range" value="64"/>
</div>
<div class="sk-row" style="gap:20px">
<div class="sk-rating" data-value="4" data-precision="1">
<button aria-label="1 星" class="sk-rating__star is-on" data-sk-rate="">
<i data-sk-icon="star"></i>
</button>
<button aria-label="2 星" class="sk-rating__star is-on" data-sk-rate="">
<i data-sk-icon="star"></i>
</button>
<button aria-label="3 星" class="sk-rating__star is-on" data-sk-rate="">
<i data-sk-icon="star"></i>
</button>
<button aria-label="4 星" class="sk-rating__star is-on" data-sk-rate="">
<i data-sk-icon="star"></i>
</button>
<button aria-label="5 星" class="sk-rating__star" data-sk-rate="">
<i data-sk-icon="star"></i>
</button>
</div>
<span class="sk-rating__value">4.0</span><span class="sk-rating__max">/ 5.0</span>
</div>三种高频「小控件」。验证码自动跳格、退格回退,标签输入支持回车与逗号。
<div class="sk-field">
<label class="sk-label">数量</label>
<div class="sk-stepper" data-sk-stepper="">
<button aria-label="减少" class="sk-stepper__btn" data-sk-step="-1">
<i data-sk-icon="minus"></i>
</button>
<input class="sk-stepper__input" max="99" min="1" value="2"/>
<button aria-label="增加" class="sk-stepper__btn" data-sk-step="1">
<i data-sk-icon="plus"></i>
</button>
</div>
</div>
<div class="sk-field" style="grid-column:span 2">
<label class="sk-label">验证码</label>
<div class="sk-otp">
<input class="sk-otp__cell" inputmode="numeric" maxlength="1"/>
<input class="sk-otp__cell" inputmode="numeric" maxlength="1"/>
<input class="sk-otp__cell" inputmode="numeric" maxlength="1"/>
<input class="sk-otp__cell" inputmode="numeric" maxlength="1"/>
</div>
<span class="sk-hint">输入后自动跳到下一格</span>
</div>
<div class="sk-field" style="grid-column:1/-1">
<label class="sk-label">标签</label>
<div class="sk-tag-input">
<span class="sk-tag sk-tag--primary">CSS<button aria-label="移除" class="sk-tag__close">
<i data-sk-icon="xmark"></i>
</button>
</span>
<span class="sk-tag sk-tag--primary">零依赖<button aria-label="移除" class="sk-tag__close">
<i data-sk-icon="xmark"></i>
</button>
</span>
<input placeholder="回车添加…"/>
</div>
</div>比图形验证码轻,比纯按钮有分量。指针事件实现,触屏可用,成功态整条变绿。
拖到 90% 以上判定通过,成功后可监听 sakuno:verify。
<div class="sk-verify" data-sk-verify data-text="按住滑块,拖到最右侧">
<div class="sk-verify__fill"></div>
<div class="sk-verify__text">拖动滑块完成验证</div>
<div class="sk-verify__thumb"><i data-sk-icon="arrow-right"></i></div>
</div>
<script>
document.addEventListener("sakuno:verify", function (e) {
if (e.detail.ok) console.log("人机校验通过");
});
</script>点击选择或直接拖入文件,虚线框在悬停时变为主色。
<div class="sk-dropzone" data-sk-dropzone="" style="grid-column:1/-1">
<span class="sk-dropzone__icon">
<i data-sk-icon="upload"></i>
</span>
<span class="sk-dropzone__title">拖拽文件到此处,或点击选择</span>
<span class="sk-dropzone__desc">支持 PNG / JPG / SVG,单个不超过 5 MB</span>
<input hidden="" type="file"/>
</div><div class="sk-row" style="gap:8px">
<span class="sk-badge sk-badge--primary">sakuno.css</span>
<span class="sk-badge">49 KB</span>
<div class="sk-progress sk-progress--sm" style="width:120px;--_v:100%">
<div class="sk-progress__bar">
</div>
</div>
</div>一张卡片就是 1px 描边加一块背景。hover 只改变描边颜色和标题色,不位移、不投影。
适合放在已经分层的容器内。
用于推荐位、当前项。
<article class="sk-card sk-card--hover">
<div class="sk-card__header">
<div>
<div class="sk-card__title">基础卡片</div>
<div class="sk-card__desc">描边 + 圆角 16</div>
</div>
</div>
<div class="sk-card__body">层级来自留白与背景差,而不是光影。</div>
<div class="sk-card__footer">
<button class="sk-btn sk-btn--soft sk-btn--sm">查看</button>
</div>
</article>
<article class="sk-card sk-card--soft sk-card--pad">
<div class="sk-card__title">柔和卡片</div>
<div class="sk-card__desc">无描边,用背景区分</div>
<p style="font-size:var(--sk-fs-sm);color:var(--sk-muted);margin-top:8px;line-height:1.7">适合放在已经分层的容器内。</p>
</article>
<article class="sk-card sk-card--accent sk-card--pad">
<div class="sk-card__title">强调卡片</div>
<div class="sk-card__desc">左侧 3px 色条</div>
<p style="font-size:var(--sk-fs-sm);color:var(--sk-muted);margin-top:8px;line-height:1.7">用于推荐位、当前项。</p>
</article>
<article class="sk-card sk-card--row sk-card--hover" style="grid-column:1/-1">
<div class="sk-card__media">
<i data-sk-icon="chart-line"></i>
</div>
<div class="sk-card__body">
<div class="sk-card__title">横向卡片 <span class="sk-badge sk-badge--outline" style="margin-left:4px">--row</span>
</div>
<div class="sk-card__desc">媒体区在左、内容在右,窄屏自动堆叠。</div>
<div class="sk-card__footer" style="padding:10px 0 0">
<span class="sk-avatar sk-avatar--xs">樱</span>
<span class="sk-fs-xs sk-muted">2 小时前</span>
<button class="sk-btn sk-btn--soft sk-btn--xs" style="margin-left:auto">阅读</button>
</div>
</div>
</article><article class="sk-card sk-card--pad" data-color="primary">
<div class="sk-card__title">primary</div>
<div class="sk-card__desc">淡色底 + 同色描边</div>
</article>
<article class="sk-card sk-card--pad" data-color="secondary">
<div class="sk-card__title">secondary</div>
<div class="sk-card__desc">淡色底 + 同色描边</div>
</article>
<article class="sk-card sk-card--pad" data-color="success">
<div class="sk-card__title">success</div>
<div class="sk-card__desc">淡色底 + 同色描边</div>
</article>
<article class="sk-card sk-card--pad" data-color="warning">
<div class="sk-card__title">warning</div>
<div class="sk-card__desc">淡色底 + 同色描边</div>
</article>
<article class="sk-card sk-card--pad" data-color="danger">
<div class="sk-card__title">danger</div>
<div class="sk-card__desc">淡色底 + 同色描边</div>
</article>
<article class="sk-card sk-card--pad" data-color="info">
<div class="sk-card__title">info</div>
<div class="sk-card__desc">淡色底 + 同色描边</div>
</article>数字用等宽数字字形,涨红跌绿遵循国内习惯。柱状图是纯 div,不需要图表库。
<div class="sk-stat">
<span class="sk-stat__label">今日访问</span>
<span class="sk-stat__value">12,480</span>
<span class="sk-stat__trend sk-stat__trend--up">
<i data-sk-icon="arrow-up"></i> 12.4%
</span>
</div>
<div class="sk-stat">
<span class="sk-stat__label">转化率</span>
<span class="sk-stat__value">3.62%</span>
<span class="sk-stat__trend sk-stat__trend--down">
<i data-sk-icon="arrow-down"></i> 0.8%
</span>
</div>
<div class="sk-stat">
<span class="sk-stat__label">近 14 天</span>
<div class="sk-bars" style="margin-top:6px">
<i style="height:30%">
</i>
<i style="height:55%">
</i>
<i style="height:40%">
</i>
<i style="height:70%">
</i>
<i style="height:52%">
</i>
<i style="height:85%">
</i>
<i style="height:64%">
</i>
<i class="is-peak" style="height:100%">
</i>
<i style="height:72%">
</i>
<i style="height:48%">
</i>
<i style="height:60%">
</i>
<i style="height:38%">
</i>
<i style="height:66%">
</i>
<i style="height:44%">
</i>
</div>
</div>
<div class="sk-stat">
<span class="sk-stat__label">樱粉系列</span>
<div class="sk-bars sk-bars--accent" style="margin-top:6px">
<i style="height:44%">
</i>
<i style="height:62%">
</i>
<i class="is-peak" style="height:92%">
</i>
<i style="height:58%">
</i>
<i style="height:74%">
</i>
<i style="height:36%">
</i>
<i style="height:52%">
</i>
<i style="height:28%">
</i>
</div>
</div>点击表头排序,勾选行会出现批量操作条。窄屏横向滚动。
| 角色 | 组件 | 状态 | 更新 | |
|---|---|---|---|---|
| 瓜生樱乃 | Button / Card | 已发布 | 2 分钟前 | |
| 九条可怜 | Modal / Drawer | 审核中 | 1 小时前 | |
| 爱丽丝 | Table | 已发布 | 昨天 | |
| 小路绫 | Toast | 已回滚 | 3 天前 |
<div class="sk-bulkbar" data-sk-bulk="" hidden="">
<span>已选择 <b data-sk-bulk-count="">0</b> 项</span>
<button class="sk-btn sk-btn--sm sk-btn--primary" style="margin-left:auto">批量导出</button>
<button class="sk-btn sk-btn--sm">取消</button>
</div>
<div class="sk-table-wrap">
<table class="sk-table" data-sk-sortable="">
<thead>
<tr>
<th style="width:36px">
<input aria-label="全选" data-sk-check-all="" type="checkbox"/>
</th>
<th data-sort="">角色</th>
<th data-sort="">组件</th>
<th data-sort="">状态</th>
<th data-sort="">更新</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input aria-label="选择" data-sk-row-check="" type="checkbox"/>
</td>
<td>
<strong>瓜生樱乃</strong>
</td>
<td>Button / Card</td>
<td>
<span class="sk-badge sk-badge--success">已发布</span>
</td>
<td>2 分钟前</td>
</tr>
<tr>
<td>
<input aria-label="选择" data-sk-row-check="" type="checkbox"/>
</td>
<td>
<strong>九条可怜</strong>
</td>
<td>Modal / Drawer</td>
<td>
<span class="sk-badge sk-badge--warning">审核中</span>
</td>
<td>1 小时前</td>
</tr>
<tr>
<td>
<input aria-label="选择" data-sk-row-check="" type="checkbox"/>
</td>
<td>
<strong>爱丽丝</strong>
</td>
<td>Table</td>
<td>
<span class="sk-badge sk-badge--success">已发布</span>
</td>
<td>昨天</td>
</tr>
<tr>
<td>
<input aria-label="选择" data-sk-row-check="" type="checkbox"/>
</td>
<td>
<strong>小路绫</strong>
</td>
<td>Toast</td>
<td>
<span class="sk-badge sk-badge--danger">已回滚</span>
</td>
<td>3 天前</td>
</tr>
</tbody>
</table>
</div>设置项、消息、成员名单。描述列表用于详情页的键值展示。
<div class="sk-list">
<div class="sk-list__item sk-list__item--link">
<span class="sk-avatar sk-avatar--sm">新</span>
<div class="sk-list__main">
<div class="sk-list__title">v2.5 已发布</div>
<div class="sk-list__desc">新增柱状图、雷达图、签名板等 8 个组件</div>
</div>
<span class="sk-badge sk-badge--primary">新</span>
</div>
<div class="sk-list__item sk-list__item--link">
<span class="sk-avatar sk-avatar--sm sk-avatar--accent">文</span>
<div class="sk-list__main">
<div class="sk-list__title">主题定制文档已更新</div>
<div class="sk-list__desc">说明如何用 CSS 变量覆盖配色</div>
</div>
<span class="sk-muted sk-fs-xs">2h</span>
</div>
<div class="sk-list__item">
<span class="sk-avatar sk-avatar--sm sk-avatar--neutral">UI</span>
<div class="sk-list__main">
<div class="sk-list__title">无链接的列表项</div>
<div class="sk-list__desc">不带 --link 就没有 hover 反馈</div>
</div>
<i data-sk-icon="chevron-right"></i>
</div>
</div>
<div class="sk-desc-list sk-mt-3">
<dt>框架</dt>
<dd>Sakuno UI v2.5</dd>
<dt>许可</dt>
<dd>MIT</dd>
<dt>浏览器</dt>
<dd>Chrome 111+ / Safari 16.4+</dd>
<dt>体积</dt>
<dd>CSS 26.1 KB + JS 54.0 KB(gzip)</dd>
</div>线性、分段、环形、不定长;骨架屏用透明度呼吸,不闪渐变。
<div class="sk-progress" style="--_v:72%">
<div class="sk-progress__bar">
</div>
</div>
<div class="sk-progress sk-progress--success" style="--_v:100%">
<div class="sk-progress__bar">
</div>
</div>
<div class="sk-progress sk-progress--accent" style="--_v:46%">
<div class="sk-progress__bar">
</div>
</div>
<div class="sk-progress sk-progress--indeterminate">
<div class="sk-progress__bar">
</div>
</div>
<div class="sk-progress-steps">
<i class="is-on">
</i>
<i class="is-on">
</i>
<i class="is-on">
</i>
<i>
</i>
<i>
</i>
</div>
<div class="sk-row" style="gap:18px">
<div class="sk-ring" style="--_v:68">
<span>68%</span>
</div>
<div class="sk-ring" style="--_v:32;--_c:var(--sk-accent)">
<span>32%</span>
</div>
<div class="sk-ring" style="--_v:90;--_c:var(--sk-success);--_size:72px">
<span>90%</span>
</div>
<div class="sk-row" style="gap:16px;margin-left:auto">
<span class="sk-spinner">
</span>
<span class="sk-sakura-spin">
</span>
<span class="sk-dots">
<i>
</i>
<i>
</i>
<i>
</i>
</span>
</div>
</div><div class="sk-card sk-card--pad">
<div class="sk-row" style="gap:9px">
<div class="sk-skeleton sk-skeleton--circle" style="width:32px;height:32px">
</div>
<div style="flex:1">
<div class="sk-skeleton sk-skeleton--text" style="width:60%">
</div>
<div class="sk-skeleton sk-skeleton--text" style="width:38%">
</div>
</div>
</div>
<div class="sk-skeleton sk-skeleton--text sk-mt-3">
</div>
<div class="sk-skeleton sk-skeleton--text">
</div>
<div class="sk-skeleton sk-skeleton--text">
</div>
</div>
<div class="sk-skeleton sk-skeleton--card">
</div>告诉用户「为什么空」,比只说「没有数据」有用得多。
<div class="sk-empty">
<div class="sk-empty__art">
<i data-sk-icon="box-open"></i>
</div>
<div class="sk-empty__title">还没有项目</div>
<div class="sk-empty__desc">创建第一个项目后,这里会显示它的运行状态。</div>
<button class="sk-btn sk-btn--primary sk-btn--sm sk-mt-3">新建项目</button>
</div>
<div class="sk-result sk-result--success" style="padding:24px 16px">
<div class="sk-result__icon">
<i data-sk-icon="check"></i>
</div>
<div class="sk-result__title">提交成功</div>
<div class="sk-result__desc">我们会在 1 个工作日内回复你。</div>
</div>AI 对话、客服、评论回复都能用。左右分边、头像、时间、打字指示器,全部由类名控制。
data-variant 换样式也不用重写类名。<div class="sk-chat">
<!-- 对方 -->
<div class="sk-msg">
<span class="sk-msg__avatar">雪</span>
<div class="sk-msg__main">
<div class="sk-msg__meta">
<span class="sk-msg__name">雪之下</span>
<span class="sk-msg__time">14:20</span>
</div>
<div class="sk-msg__bubble">消息内容,支持 <code>行内代码</code></div>
</div>
</div>
<!-- 自己:加 sk-msg--me 靠右 -->
<div class="sk-msg sk-msg--me">
<span class="sk-msg__avatar sk-msg__avatar--alt">九</span>
<div class="sk-msg__main">
<div class="sk-msg__meta">
<span class="sk-msg__name">九条</span>
<span class="sk-msg__time">14:21</span>
</div>
<div class="sk-msg__bubble">收到。</div>
</div>
</div>
<!-- 正在输入 -->
<div class="sk-msg">
<span class="sk-msg__avatar">雪</span>
<div class="sk-msg__main">
<div class="sk-msg__bubble">
<span class="sk-msg__typing"><i></i><i></i><i></i></span>
</div>
</div>
</div>
</div>把一段 JSON 渲染成可折叠的树,键、字符串、数字、布尔、null 各自配色,直接读配置文件或接口返回。
<!-- 数据既可以写在属性里,也可以作为子文本 -->
<div class="sk-json" data-sk-json='{"name":"sakuno","flat":true}'></div>
<div class="sk-json">
{"name":"sakuno","flat":true}
</div>
<!-- 点击箭头折叠 / 展开,嵌套层级自动缩进 -->胶囊与下划线两种形态。支持 ← → 方向键切换,符合 WAI-ARIA Tabs 模式。
概览面板:切换时只有一次 5px 的上浮淡入。
配置面板:复杂表单适合放在这一层。
日志面板:长内容加 .sk-scroll。
线性风格更贴近内容型页面。
下划线从中间展开。
加 .sk-tabs--line 即可。
<div class="sk-tabs" data-sk-tabs="">
<div aria-label="示例" class="sk-tabs__list" role="tablist">
<button aria-selected="true" class="sk-tabs__tab" data-sk-tab="t1" role="tab">概览</button>
<button aria-selected="false" class="sk-tabs__tab" data-sk-tab="t2" role="tab">配置</button>
<button aria-selected="false" class="sk-tabs__tab" data-sk-tab="t3" role="tab">日志</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="t1">
<p class="sk-lead">概览面板:切换时只有一次 5px 的上浮淡入。</p>
</div>
<div class="sk-tabs__panel" data-sk-panel="t2" hidden="">
<p class="sk-lead">配置面板:复杂表单适合放在这一层。</p>
</div>
<div class="sk-tabs__panel" data-sk-panel="t3" hidden="">
<p class="sk-lead">日志面板:长内容加 <code class="sk-code--inline">.sk-scroll</code>。</p>
</div>
</div>
<div class="sk-tabs sk-tabs--line sk-w-full" data-sk-tabs="" style="margin-top:10px">
<div aria-label="线性示例" class="sk-tabs__list" role="tablist">
<button aria-selected="true" class="sk-tabs__tab" data-sk-tab="l1" role="tab">设计</button>
<button aria-selected="false" class="sk-tabs__tab" data-sk-tab="l2" role="tab">开发</button>
<button aria-selected="false" class="sk-tabs__tab" data-sk-tab="l3" role="tab">发布</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="l1">
<p class="sk-lead">线性风格更贴近内容型页面。</p>
</div>
<div class="sk-tabs__panel" data-sk-panel="l2" hidden="">
<p class="sk-lead">下划线从中间展开。</p>
</div>
<div class="sk-tabs__panel" data-sk-panel="l3" hidden="">
<p class="sk-lead">加 <code class="sk-code--inline">.sk-tabs--line</code> 即可。</p>
</div>
</div><div class="sk-stack" style="gap:22px;width:100%">
<div class="sk-tabs sk-tabs--line" data-sk-tabs>
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="underlined1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="underlined2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="underlined3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="underlined1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="underlined2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="underlined3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
<div class="sk-tabs sk-tabs--solid" data-sk-tabs>
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="solid1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="solid2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="solid3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="solid1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="solid2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="solid3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
<div class="sk-tabs sk-tabs--light" data-sk-tabs>
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="light1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="light2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="light3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="light1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="light2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="light3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
<div class="sk-tabs sk-tabs--bordered" data-sk-tabs>
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="bordered1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="bordered2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="bordered3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="bordered1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="bordered2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="bordered3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
<div class="sk-tabs sk-tabs--pills" data-sk-tabs>
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="pills1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="pills2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="pills3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="pills1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="pills2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="pills3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
</div><div class="sk-stack" style="gap:22px;width:100%">
<div class="sk-tabs sk-tabs--solid" data-sk-tabs data-color="secondary">
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="solid1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="solid2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="solid3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="solid1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="solid2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="solid3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
<div class="sk-tabs sk-tabs--light" data-sk-tabs data-color="success">
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="light1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="light2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="light3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="light1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="light2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="light3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
<div class="sk-tabs sk-tabs--bordered" data-sk-tabs data-color="danger">
<div class="sk-tabs__list">
<button class="sk-tabs__tab" data-sk-tab="bordered1" aria-selected="true">概览</button>
<button class="sk-tabs__tab" data-sk-tab="bordered2">功能</button>
<button class="sk-tabs__tab" data-sk-tab="bordered3">价格</button>
</div>
<div class="sk-tabs__panel" data-sk-panel="bordered1">
<div class="sk-fs-sm sk-text-soft">概览面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="bordered2" hidden>
<div class="sk-fs-sm sk-text-soft">功能面板的内容。</div>
</div>
<div class="sk-tabs__panel" data-sk-panel="bordered3" hidden>
<div class="sk-fs-sm sk-text-soft">价格面板的内容。</div>
</div>
</div>
</div>折叠用 grid-template-rows 平滑过渡,不需要测像素;树形可逐级展开。
@layer sk.components 里,你的选择器天然优先。<div class="sk-accordion" data-sk-accordion="">
<div class="sk-acc__item is-open">
<button aria-expanded="true" class="sk-acc__trigger">为什么要做 Sakuno?
<span class="sk-acc__chevron">
<i data-sk-icon="chevron-down"></i>
</span>
</button>
<div class="sk-acc__panel">
<div class="sk-acc__inner">
<div>因为很多静态页面只需要一点交互,却被迫引入整个前端框架。</div>
</div>
</div>
</div>
<div class="sk-acc__item">
<button aria-expanded="false" class="sk-acc__trigger">可以只用 CSS 吗?
<span class="sk-acc__chevron">
<i data-sk-icon="chevron-down"></i>
</span>
</button>
<div class="sk-acc__panel">
<div class="sk-acc__inner">
<div>可以。纯展示组件不依赖 JS,需要交互时再补 sakuno.js。</div>
</div>
</div>
</div>
<div class="sk-acc__item">
<button aria-expanded="false" class="sk-acc__trigger">会覆盖我的样式吗?
<span class="sk-acc__chevron">
<i data-sk-icon="chevron-down"></i>
</span>
</button>
<div class="sk-acc__panel">
<div class="sk-acc__inner">
<div>不会。组件都在 <code>@layer sk.components</code> 里,你的选择器天然优先。</div>
</div>
</div>
</div>
</div>
<div class="sk-tree">
<div class="sk-tree__node">
<span class="sk-tree__arrow is-open" data-sk-tree-toggle="">
<i data-sk-icon="chevron-right"></i>
</span>
<span class="sk-tree__icon">
<i data-sk-icon="folder"></i>
</span>src</div>
<div class="sk-tree__group">
<div class="sk-tree__node">
<span class="sk-tree__arrow is-open" data-sk-tree-toggle="">
<i data-sk-icon="chevron-right"></i>
</span>
<span class="sk-tree__icon">
<i data-sk-icon="folder"></i>
</span>components</div>
<div class="sk-tree__group">
<div class="sk-tree__node is-active">
<span class="sk-tree__arrow">
</span>
<span class="sk-tree__icon">
<i data-sk-icon="file"></i>
</span>button.css</div>
<div class="sk-tree__node">
<span class="sk-tree__arrow">
</span>
<span class="sk-tree__icon">
<i data-sk-icon="file"></i>
</span>form.css</div>
</div>
<div class="sk-tree__node">
<span class="sk-tree__arrow" data-sk-tree-toggle="">
<i data-sk-icon="chevron-right"></i>
</span>
<span class="sk-tree__icon">
<i data-sk-icon="folder"></i>
</span>utils</div>
<div class="sk-tree__group" hidden="">
<div class="sk-tree__node">
<span class="sk-tree__arrow">
</span>
<span class="sk-tree__icon">
<i data-sk-icon="file"></i>
</span>dom.js</div>
</div>
</div>
<div class="sk-tree__node">
<span class="sk-tree__arrow">
</span>
<span class="sk-tree__icon">
<i data-sk-icon="file"></i>
</span>index.html</div>
</div>导航辅助组件,低调但随时可被找到。
<nav aria-label="面包屑">
<ol class="sk-breadcrumb">
<li>
<a href="#top">首页</a>
</li>
<li>
<a href="#btn">组件</a>
</li>
<li>
<span aria-current="page">按钮</span>
</li>
</ol>
</nav>
<nav aria-label="分页" class="sk-pagination">
<button aria-label="上一页" class="sk-pagination__item">
<i data-sk-icon="chevron-left"></i>
</button>
<button aria-current="page" class="sk-pagination__item">1</button>
<button class="sk-pagination__item">2</button>
<button class="sk-pagination__item">3</button>
<span aria-disabled="true" class="sk-pagination__item">…</span>
<button class="sk-pagination__item">12</button>
<button aria-label="下一页" class="sk-pagination__item">
<i data-sk-icon="chevron-right"></i>
</button>
</nav>流程用步骤,历史用时间线。状态由 is-done / is-active 控制。
<div class="sk-steps">
<div class="sk-step is-done">
<div class="sk-step__dot">
<i data-sk-icon="check"></i>
</div>
<div class="sk-step__label">填写信息</div>
</div>
<div class="sk-step is-done">
<div class="sk-step__dot">
<i data-sk-icon="check"></i>
</div>
<div class="sk-step__label">验证邮箱</div>
</div>
<div class="sk-step is-active">
<div class="sk-step__dot">3</div>
<div class="sk-step__label">设置偏好</div>
</div>
<div class="sk-step">
<div class="sk-step__dot">4</div>
<div class="sk-step__label">完成</div>
</div>
</div>
<div class="sk-timeline sk-w-full">
<div class="sk-timeline__item">
<span class="sk-timeline__dot">
</span>
<div class="sk-timeline__time">2026-06-08 14:20</div>
<div class="sk-timeline__title">发布 v2.0.0</div>
<div class="sk-timeline__desc">全面平面化,移除所有渐变与投影。</div>
</div>
<div class="sk-timeline__item sk-timeline__item--accent">
<span class="sk-timeline__dot">
</span>
<div class="sk-timeline__time">2026-06-05</div>
<div class="sk-timeline__title">新增 28 个组件</div>
<div class="sk-timeline__desc">日历、树、轮播、上传、OTP 等。</div>
</div>
<div class="sk-timeline__item sk-timeline__item--muted">
<span class="sk-timeline__dot">
</span>
<div class="sk-timeline__time">2026-06-01</div>
<div class="sk-timeline__title">项目启动</div>
<div class="sk-timeline__desc">确定藤紫 + 樱粉的双色基调。</div>
</div>
</div>轮播用原生滚动吸附,滚动事件只做节流同步圆点;侧栏与底部导航是布局级组件。
<div class="sk-carousel-wrap" data-sk-carousel data-autoplay="5200">
<div class="sk-carousel" tabindex="0" aria-label="轮播">
<div class="sk-card sk-card--pad">01 · 平面</div>
<div class="sk-card sk-card--pad">02 · 克制</div>
<div class="sk-card sk-card--pad">03 · 温度</div>
</div>
<button class="sk-carousel__nav sk-carousel__nav--prev"
data-sk-carousel-prev aria-label="上一张"><i data-sk-icon="chevron-left"></i></button>
<button class="sk-carousel__nav sk-carousel__nav--next"
data-sk-carousel-next aria-label="下一张"><i data-sk-icon="chevron-right"></i></button>
<div class="sk-carousel-dots">
<i class="is-active"></i><i></i><i></i>
</div>
</div>
<!-- 自动播放交给 data-autoplay(毫秒),悬停暂停 -->
<!-- 抛 sakuno:slide,detail = { index, total } --><nav class="sk-sidebar">
<div class="sk-sidebar__group">工作台</div>
<a class="sk-sidebar__item is-active" href="#carousel">
<i data-sk-icon="table-cells"></i>
概览
</a>
<a class="sk-sidebar__item" href="#carousel">
<i data-sk-icon="chart-line"></i>
分析 <span class="sk-badge sk-badge--primary sk-sidebar__badge">3</span>
</a>
<a class="sk-sidebar__item" href="#carousel">
<i data-sk-icon="gear"></i>
设置
</a>
</nav>
<div style="border:1px solid var(--sk-border);border-radius:var(--sk-r-lg);overflow:hidden;background:var(--sk-surface)">
<div style="height:56px;display:grid;place-items:center;font-size:var(--sk-fs-xs);color:var(--sk-muted);border-bottom:1px solid var(--sk-border)">底部标签栏</div>
<nav class="sk-bottom-nav" style="border-top:0">
<a class="sk-bottom-nav__item is-active" href="#carousel">
<i data-sk-icon="house"></i>
首页
</a>
<a class="sk-bottom-nav__item" href="#carousel">
<i data-sk-icon="magnifying-glass"></i>
发现
</a>
<a class="sk-bottom-nav__item" href="#carousel">
<i data-sk-icon="heart"></i>
收藏
</a>
<a class="sk-bottom-nav__item" href="#carousel">
<i data-sk-icon="user"></i>
我的
</a>
</nav>
</div>打开时锁滚动、焦点落入、Tab 在内部循环、Esc 或点遮罩关闭,关闭后焦点归位。
<button class="sk-btn sk-btn--primary" data-sk-open="#demoModal">打开模态框</button>
<button class="sk-btn" data-sk-open="#demoModalSm">小型确认</button>
<button class="sk-btn" data-sk-open="#demoDrawer">右侧抽屉</button>
<button class="sk-btn" onclick="demoConfirm()">Promise 确认</button>顶部居中、可堆叠、悬停暂停倒计时。一个函数调用就够。
<button class="sk-btn sk-btn--soft" onclick="Sakuno.toast.success('保存成功', {title:'已完成'})">成功</button>
<button class="sk-btn sk-btn--soft" onclick="Sakuno.toast.error('网络异常,请重试')">错误</button>
<button class="sk-btn sk-btn--soft" onclick="Sakuno.toast.warning('磁盘空间不足 10%')">警告</button>
<button class="sk-btn sk-btn--soft" onclick="Sakuno.toast.info('正在同步 3 个文件…', {duration:0})">常驻</button>Sakuno.toast.success('保存成功', { title: '已完成' });
按 ⌘ K 或 Ctrl K。支持过滤与 ↑ ↓ 选择。
<button class="sk-btn sk-btn--primary" onclick="openCmd()">
<i data-sk-icon="magnifying-glass"></i>
打开命令面板
</button>
<span class="sk-hint">Esc 关闭 · ↑↓ 选择 · Enter 执行</span>组合框在本地过滤,不依赖网络;区间滑块用两个原生 range 叠放,键盘与触摸都可用;密码强度只是启发式提示,真正的校验永远在服务端。
<div class="sk-field sk-combo" data-sk-combo>
<label class="sk-label">技术栈</label>
<div class="sk-combo__control">
<input class="sk-input sk-combo__input" placeholder="搜索框架…" aria-expanded="false">
<i data-sk-icon="chevron-down"></i>
</div>
<div class="sk-combo__panel" hidden>
<button class="sk-combo__opt" data-value="react"><span class="sk-combo__label">React</span></button>
<button class="sk-combo__opt" data-value="vue"><span class="sk-combo__label">Vue</span></button>
<div class="sk-combo__empty" hidden>没有匹配项</div>
</div>
<input type="hidden" name="stack">
</div>
<!-- 级联:选项用 JSON 描述,叶子节点回填 -->
<div class="sk-field sk-cascader" data-sk-cascader
data-options='[{"label":"浙江","children":[{"label":"杭州","children":[{"label":"西湖区"}]}]}]'>
<button class="sk-select__trigger sk-cascader__trigger" aria-expanded="false">
<span class="sk-select__value sk-cascader__value" data-placeholder="选择地区">选择地区</span>
</button>
<div class="sk-cascader__panel" hidden></div>
</div><div class="sk-range" data-sk-range data-prefix="¥">
<div class="sk-range__track">
<i class="sk-range__rail"></i>
<i class="sk-range__fill"></i>
<input type="range" min="0" max="1000" step="10" value="200" data-sk-range-min>
<input type="range" min="0" max="1000" step="10" value="760" data-sk-range-max>
</div>
<output class="sk-range__out">¥200 – ¥760</output>
</div>
<!-- 密码:强度条 + 显隐切换 -->
<div class="sk-field sk-pw" data-sk-password>
<label class="sk-label" for="pw">设置密码</label>
<div class="sk-input-group">
<input class="sk-input" id="pw" type="password" autocomplete="new-password">
<span class="sk-input-group__addon sk-input-group__addon--btn"
data-sk-pw-toggle role="button" tabindex="0" aria-label="显示密码">…</span>
</div>
<div class="sk-pw__meter" data-score="0"><i></i><i></i><i></i><i></i></div>
<div class="sk-pw__hint" data-score="0">建议 8 位以上</div>
</div>颜色选择提供了固定色板与十六进制兜底;时间列用原生滚动,不需要虚拟列表;树选择把层级压平成一列,避免用户在小面板里来回钻。
<div class="sk-color" data-sk-color data-value="#1271ea">
<button class="sk-color__trigger" type="button">
<span class="sk-color__chip"></span>
<span class="sk-color__hex">#1271EA</span>
<i data-sk-icon="chevron-down" style="font-size:11px;color:var(--sk-muted)"></i>
</button>
<div class="sk-color__panel" hidden>
<div class="sk-color__grid">
<button class="sk-color__sw" type="button" data-color="#1271ea" style="background:#1271ea;color:#1271ea"></button>
<button class="sk-color__sw" type="button" data-color="#ff94da" style="background:#ff94da;color:#ff94da"></button>
<button class="sk-color__sw" type="button" data-color="#2cc366" style="background:#2cc366;color:#2cc366"></button>
<button class="sk-color__sw" type="button" data-color="#ffa910" style="background:#ffa910;color:#ffa910"></button>
<button class="sk-color__sw" type="button" data-color="#e00657" style="background:#e00657;color:#e00657"></button>
<button class="sk-color__sw" type="button" data-color="#00b0d3" style="background:#00b0d3;color:#00b0d3"></button>
<button class="sk-color__sw" type="button" data-color="#8b5cf6" style="background:#8b5cf6;color:#8b5cf6"></button>
<button class="sk-color__sw" type="button" data-color="#70707a" style="background:#70707a;color:#70707a"></button>
</div>
<div class="sk-color__foot">
<input class="sk-input" aria-label="十六进制颜色" value="#1271EA"/>
<button class="sk-btn sk-btn--sm" type="button">确定</button>
</div>
</div>
</div><div class="sk-time" data-sk-time data-value="09:30" data-step="5" style="width:160px">
<button class="sk-select__trigger" type="button" aria-expanded="false" style="width:100%">
<i data-sk-icon="clock" style="font-size:13px;color:var(--sk-muted)"></i>
<span class="sk-select__value">09:30</span>
<i data-sk-icon="chevron-down" style="font-size:11px;color:var(--sk-muted);margin-left:auto"></i>
</button>
<div class="sk-time__panel" hidden>
<div class="sk-time__col sk-time__col--h"></div>
<div class="sk-time__col sk-time__col--m"></div>
</div>
<input type="hidden" name="time" value="09:30"/>
</div><div class="sk-treeselect sk-select" data-sk-treeselect style="width:220px">
<button class="sk-select__trigger" type="button" aria-expanded="false">
<i data-sk-icon="folder-tree" style="font-size:13px;color:var(--sk-muted)"></i>
<span class="sk-select__value">选择目录</span>
<i data-sk-icon="chevron-down" style="font-size:11px;color:var(--sk-muted);margin-left:auto"></i>
</button>
<div class="sk-treeselect__panel" hidden>
<button class="sk-treeselect__opt" type="button" data-value="src" data-label="src">
<i data-sk-icon="folder"></i>src
</button>
<button class="sk-treeselect__opt" type="button" data-value="comp" data-label="src / components" style="padding-left:22px">
<i data-sk-icon="folder"></i>components
</button>
<button class="sk-treeselect__opt" type="button" data-value="btn" data-label="src / components / button.css" style="padding-left:36px">
<i data-sk-icon="file"></i>button.css
</button>
<button class="sk-treeselect__opt" type="button" data-value="docs" data-label="docs">
<i data-sk-icon="folder"></i>docs
</button>
</div>
<input type="hidden" name="path"/>
</div><div class="sk-transfer" data-sk-transfer>
<div class="sk-transfer__panel">
<div class="sk-transfer__head">
<i data-sk-icon="list" style="font-size:11px"></i>待选
<span class="sk-transfer__count">0</span>
</div>
<div class="sk-transfer__list">
<div class="sk-transfer__item" data-value="btn"><span>按钮 Button</span></div>
<div class="sk-transfer__item" data-value="tabs"><span>标签页 Tabs</span></div>
<div class="sk-transfer__item" data-value="modal"><span>模态框 Modal</span></div>
<div class="sk-transfer__item" data-value="table"><span>表格 Table</span></div>
</div>
</div>
<div class="sk-transfer__mid">
<button class="sk-btn sk-btn--soft sk-btn--icon sk-btn--sm" data-sk-tf="right" type="button" aria-label="添加">
<i data-sk-icon="chevron-right"></i>
</button>
<button class="sk-btn sk-btn--soft sk-btn--icon sk-btn--sm" data-sk-tf="left" type="button" aria-label="移除">
<i data-sk-icon="chevron-left"></i>
</button>
</div>
<div class="sk-transfer__panel">
<div class="sk-transfer__head">
<i data-sk-icon="check" style="font-size:11px"></i>已选
<span class="sk-transfer__count">0</span>
</div>
<div class="sk-transfer__list"></div>
</div>
</div>热力格用五档纯色表达强度;文件列表把进度收进 3px 细线;数据表工具条只做组合,不引入新的表格抽象。
<div class="sk-heat" data-sk-heatmap data-cols="26"
data-values="0,1,2,4,0,3,1,…">
<div class="sk-heat__grid"></div>
<div class="sk-heat__legend">
少 <i data-lv="0"></i><i data-lv="1"></i><i data-lv="2"></i><i data-lv="3"></i><i data-lv="4"></i> 多
</div>
</div><div class="sk-files">
<div class="sk-file">
<span class="sk-file__icon" data-type="pdf">PDF</span>
<div class="sk-file__body">
<div class="sk-file__name">guide.pdf</div>
<div class="sk-file__meta">2.4 MB · 正在上传 64%</div>
<div class="sk-file__bar"><i style="width:64%"></i></div>
</div>
<button class="sk-file__x" aria-label="移除">…</button>
</div>
<div class="sk-file is-done">…</div>
</div><div class="sk-card sk-card--plain">
<div class="sk-toolbar">
<span class="sk-toolbar__title">成员</span>
<input class="sk-input" type="search" placeholder="搜索…"
data-sk-search-live="#members">
<div class="sk-toolbar__grow"></div>
<div class="sk-select" data-sk-select>…</div>
</div>
<div class="sk-table-wrap">
<table class="sk-table" id="members">…</table>
</div>
<div class="sk-pager">
<button class="sk-pager__btn" aria-label="上一页" disabled><i data-sk-icon="chevron-left"></i></button>
<button class="sk-pager__btn" aria-current="page">1</button>
<button class="sk-pager__btn" aria-label="下一页"><i data-sk-icon="chevron-right"></i></button>
<span class="sk-pager__info">共 24 条</span>
</div>
</div>点缩略图放大,支持同组左右切换、Esc 关闭。水印用 canvas 生成平铺纹理,不占 DOM。
水印铺在内容上层,不挡点击,主题切换会自动重绘。
<!-- 灯箱:同组自动支持左右切换 -->
<button class="sk-thumb" data-sk-lightbox="gallery"
data-full="assets/sakuno.webp" data-cap="说明文字">
<img src="assets/sakuno.webp" alt="缩略图"/>
</button>
<!-- 水印:文字与间距都能配 -->
<div class="sk-watermark" data-text="Sakuno · 内部资料"
data-size="13" data-gap="60">
<p>正文内容</p>
</div>
<script>
Sakuno.lightbox("assets/sakuno.webp", "标题");
</script>图表也是平面的:只用纯色描边和留白,不加渐变和投影。进度用 pathLength="100" 归一化,改数值不需要算弧长。
<div class="sk-gauge" style="--_v:72">
<svg viewBox="0 0 148 96" aria-hidden="true">
<path class="sk-gauge__track" d="M14 84a60 60 0 0 1 120 0"/>
<path class="sk-gauge__bar" pathLength="100" d="M14 84a60 60 0 0 1 120 0"/>
</svg>
<div class="sk-gauge__val">72<span class="sk-gauge__unit">%</span></div>
<div class="sk-gauge__meta">本月目标完成度</div>
</div><div class="sk-row" style="gap:28px;align-items:flex-end">
<div class="sk-gauge" style="--_v:34;--_c:var(--sk-success)">
<svg viewBox="0 0 148 96" aria-hidden="true">
<path class="sk-gauge__track" d="M14 84a60 60 0 0 1 120 0"/>
<path class="sk-gauge__bar" pathLength="100" d="M14 84a60 60 0 0 1 120 0"/>
</svg>
<div class="sk-gauge__val">34<span class="sk-gauge__unit">%</span></div>
<div class="sk-gauge__meta">CPU</div>
</div>
<div class="sk-gauge" style="--_v:86;--_c:var(--sk-danger)">
<svg viewBox="0 0 148 96" aria-hidden="true">
<path class="sk-gauge__track" d="M14 84a60 60 0 0 1 120 0"/>
<path class="sk-gauge__bar" pathLength="100" d="M14 84a60 60 0 0 1 120 0"/>
</svg>
<div class="sk-gauge__val">86<span class="sk-gauge__unit">%</span></div>
<div class="sk-gauge__meta">磁盘</div>
</div>
</div><div class="sk-donut">
<div class="sk-donut__ring" role="img" aria-label="组件类型占比">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="44" stroke="var(--sk-surface-3)"/>
<circle cx="50" cy="50" r="44" stroke="var(--sk-primary)" data-value="46"/>
<circle cx="50" cy="50" r="44" stroke="var(--sk-secondary)" data-value="27"/>
<circle cx="50" cy="50" r="44" stroke="var(--sk-warning)" data-value="18"/>
<circle cx="50" cy="50" r="44" stroke="var(--sk-success)" data-value="9"/>
</svg>
<div class="sk-donut__center"><b>62</b><span>组件</span></div>
</div>
<div class="sk-donut__legend">
<div><i style="background:var(--sk-primary)"></i>表单<b>46%</b></div>
<div><i style="background:var(--sk-secondary)"></i>展示<b>27%</b></div>
<div><i style="background:var(--sk-warning)"></i>导航<b>18%</b></div>
<div><i style="background:var(--sk-success)"></i>反馈<b>9%</b></div>
</div>
</div><div class="sk-card sk-card--pad" style="width:260px">
<div class="sk-stat__label">日活</div>
<div class="sk-row" style="justify-content:space-between;align-items:baseline;margin-top:2px">
<b style="font-size:var(--sk-fs-2xl);font-variant-numeric:tabular-nums">8,412</b>
<span class="sk-badge sk-badge--success">
<i data-sk-icon="arrow-trend-up" style="font-size:10px"></i>12.4%
</span>
</div>
<div class="sk-spark" data-sk-spark data-values="32,38,30,46,42,58,64" style="width:100%;margin-top:6px">
<svg aria-hidden="true" style="width:100%"></svg>
</div>
<div class="sk-hint sk-mt-2">近 7 天</div>
</div>数据展示的第二批组件:柱状图与雷达图由 data- 属性驱动、入场自动生长;甘特图用纯 CSS 变量定位任务条;数字滚动在滚进视野时播放一次。
<div class="sk-barchart" data-sk-barchart data-values="26,54,38,72,45,90,63" data-labels="周一,周二,周三,周四,周五,周六,周日" data-highlight="5" data-unit=" 单"></div>data-highlight 指定强调某一天,其余柱子变淡;数值与悬浮提示自动生成。
<div style="display:flex;gap:36px;flex-wrap:wrap">
<div><div class="sk-countup" data-sk-countup="12864" style="font-size:32px;color:var(--sk-text)">0</div><div class="sk-hint">累计用户</div></div>
<div><div class="sk-countup" data-sk-countup="98.6" data-suffix="%" data-duration="1600" style="font-size:32px;color:var(--sk-text)">0</div><div class="sk-hint">好评率</div></div>
<div><div class="sk-countup" data-sk-countup="2350" data-suffix=" 条" style="font-size:32px;color:var(--sk-text)">0</div><div class="sk-hint">本周评论</div></div>
</div><div class="sk-ratebars" data-sk-ratebars="" data-values="128,42,18,9,5" data-total="202" data-avg="4.6"></div><div class="sk-gantt" style="--n:10">
<div class="sk-gantt__row"><span class="sk-gantt__name">需求评审</span><div class="sk-gantt__lane"><div class="sk-gantt__grid"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><span class="sk-gantt__task" style="--s:0;--w:2">5.6 – 5.7</span></div></div>
<div class="sk-gantt__row"><span class="sk-gantt__name">视觉稿</span><div class="sk-gantt__lane"><div class="sk-gantt__grid"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><span class="sk-gantt__task" style="--s:1;--w:3">5.7 – 5.10</span></div></div>
<div class="sk-gantt__row"><span class="sk-gantt__name">前端开发</span><div class="sk-gantt__lane"><div class="sk-gantt__grid"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><span class="sk-gantt__task" style="--s:3;--w:4">5.9 – 5.13</span></div></div>
<div class="sk-gantt__row"><span class="sk-gantt__name">联调测试</span><div class="sk-gantt__lane"><div class="sk-gantt__grid"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><span class="sk-gantt__task sk-gantt__task--muted" style="--s:6;--w:3">5.12 – 5.15</span></div></div>
<div class="sk-gantt__row"><span class="sk-gantt__name">灰度发布</span><div class="sk-gantt__lane"><div class="sk-gantt__grid"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><span class="sk-gantt__task sk-gantt__task--ok" style="--s:8;--w:2">5.14 – 5.15</span></div></div>
<div class="sk-gantt__axis"><span></span><div class="sk-gantt__axis-labels"><span>5.6</span><span>5.7</span><span>5.8</span><span>5.9</span><span>5.10</span><span>5.11</span><span>5.12</span><span>5.13</span><span>5.14</span><span>5.15</span></div></div>
</div><div class="sk-radar" data-sk-radar="" data-labels="玩法,画面,剧情,音乐,社区,性能" data-values="92,88,76,84,70,95"></div>折线图用纯 SVG 绘制,支持多系列、面积填充、坐标轴与网格;悬停或触摸会出现游标和各系列数值,点图例可隐藏系列。漏斗图按数值递减宽度,右侧标注相邻两级的转化率。两者都不依赖图表库。
<div class="sk-linechart" data-sk-linechart data-area
data-title="近 7 日活跃" data-sub="单位:人次"
data-labels="周一,周二,周三,周四,周五,周六,周日"
data-series="访问量:320,410,385,520,470,610,580|下单量:86,112,98,140,131,176,168|咨询量#ff94da:42,55,48,63,59,72,70"></div><div class="sk-linechart" data-sk-linechart
data-title="构建耗时" data-sub="秒"
data-labels="v2.1,v2.2,v2.3,v2.4,v2.5"
data-series="CSS:12.1,12.4,12.0,12.6,12.9|JS:18.4,19.1,18.8,19.6,20.2|总量:30.5,31.5,30.8,32.2,33.1"></div><div class="sk-funnel" data-sk-funnel
data-labels="访问页面,加入购物车,提交订单,完成支付"
data-values="12480,6320,2810,1960"></div>data-series 用 | 分隔系列,每个系列写作 名称:值,值,…;名称后跟 #颜色 可指定颜色,缺省时按语义色轮换。加 data-area 开启面积填充。
媒体处理三件套:对比滑块用 clip-path 切分两层图片,支持拖拽与左右方向键;瀑布流是纯 CSS 多列布局;签名板基于 canvas,导出为 PNG dataURL。
原图
滤镜后
<div class="sk-compare" data-sk-compare="">
<img src="assets/sakuno.webp" style="filter:grayscale(1) brightness(1.04)" alt="原图"/>
<img class="sk-compare__after" src="assets/sakuno.webp" alt="滤镜后"/>
<span class="sk-compare__tag sk-compare__tag--before">原图</span>
<span class="sk-compare__tag sk-compare__tag--after">滤镜后</span>
<div class="sk-compare__bar"></div>
<div class="sk-compare__grip"><i data-sk-icon="arrow-right-arrow-left" style="width:14px;height:14px"></i></div>
</div><div class="sk-masonry">
<div class="sk-card"><div class="sk-card__body"><div class="sk-card__title">水墨樱花</div><div class="sk-card__desc">用平面色块表现花瓣飘落的层次,主色只出现一次。</div></div></div>
<div class="sk-card"><div class="sk-card__body"><div class="sk-card__title">夜行电车站</div><div class="sk-card__desc">低饱和的蓝绿配一点暖灯,画面安静但不冷。留白比例参考了杂志排版的三分法。</div></div></div>
<div class="sk-card"><div class="sk-card__body"><div class="sk-card__title">便利店黄昏</div><div class="sk-card__desc">暖色灯箱是画面的唯一光源。</div></div></div>
<div class="sk-card"><div class="sk-card__body"><div class="sk-card__title">天台远眺</div><div class="sk-card__desc">把地平线压到画面下三分之一,天空用大面积纯色留出呼吸感,云的边缘只用两三个色阶做分层。</div></div></div>
<div class="sk-card"><div class="sk-card__body"><div class="sk-card__title">雨后小巷</div><div class="sk-card__desc">湿路面的反光用一两个亮色块点出。</div></div></div>
</div><div class="sk-sign" data-sk-sign="" id="signBox" data-sk-sign-clear="signClear" data-sk-sign-export="signExport" style="height:200px">
<div class="sk-sign__hint"><i data-sk-icon="pen" style="width:18px;height:18px"></i> 在这里写下名字</div>
</div>
<div class="sk-sign__tools">
<button class="sk-btn sk-btn--sm" id="signClear" type="button"><i data-sk-icon="trash-can" style="width:13px;height:13px"></i>清空</button>
<button class="sk-btn sk-btn--primary sk-btn--sm" id="signExport" type="button"><i data-sk-icon="download" style="width:13px;height:13px"></i>导出 PNG</button>
</div>导出按钮会触发 sakuno:sign 事件,携带 detail.dataUrl(PNG base64),可以上传或写回表单。
这几个组件只做一件事:排序返回顺序,分栏返回比例,通知返回未读数,引导返回完成状态。没有额外的状态管理。
<div class="sk-sortable" data-sk-sortable>
<div class="sk-sortable__item" data-id="design" draggable="true">
<span class="sk-sortable__grip"><i data-sk-icon="grip-dots"></i></span>设计评审
<span class="sk-sortable__idx">01</span>
</div>
…
</div>
<!-- 排序完成后抛 sakuno:sort,detail.order 是 data-id 数组 --><div class="sk-split" data-sk-split style="--_split:38%">
<div class="sk-split__pane">左栏</div>
<div class="sk-split__handle" data-sk-split-handle></div>
<div class="sk-split__pane">右栏</div>
</div><!-- 通知:复用 dropdown 的开关逻辑 -->
<div class="sk-dropdown sk-dropdown--end" data-sk-notify>
<button class="sk-btn sk-btn--ghost sk-btn--icon" data-sk-toggle aria-label="通知">
<i data-sk-icon="bell"></i><span class="sk-badged">
<span class="sk-badge sk-badge--danger sk-badge--pill" data-sk-notify-badge>3</span>
</span>
</button>
<div class="sk-notify" hidden>
<div class="sk-notify__head">通知<button data-sk-notify-read>全部已读</button></div>
<div class="sk-notify__list">
<div class="sk-notify__item is-unread">…</div>
</div>
</div>
</div>
<!-- 三态主题 -->
<div class="sk-theme-switch">
<button data-sk-theme-set="light" aria-pressed="true"><i data-sk-icon="sun"></i></button>
<button data-sk-theme-set="dark" aria-pressed="false"><i data-sk-icon="moon"></i></button>
<button data-sk-theme-set="auto" aria-pressed="false"><i data-sk-icon="desktop"></i></button>
</div>
<!-- 引导 -->
<script>
Sakuno.tour([
{ el: "#field-plus", title: "表单进阶", text: "Combobox 与级联都在本地过滤。" },
{ el: "#data-plus", title: "数据展示", text: "热力图与数据表工具条。" }
]);
</script>移动端优先的浮层形态,以及三个减少页面跳转的小交互:就地确认、就地修改、就地复制。
<div class="sk-dropdown" data-sk-popconfirm style="display:inline-block">
<button class="sk-btn sk-btn--danger sk-btn--soft" data-sk-toggle type="button" aria-expanded="false">
<i data-sk-icon="trash"></i>删除文件
</button>
<div class="sk-popconfirm" hidden>
<div class="sk-popconfirm__text">确定删除 sakuno.css?</div>
<div class="sk-popconfirm__desc">删除后无法恢复。</div>
<div class="sk-popconfirm__act">
<button class="sk-btn sk-btn--sm sk-btn--ghost" data-sk-pc-act="cancel" type="button">取消</button>
<button class="sk-btn sk-btn--sm sk-btn--danger" data-sk-pc-act="ok" type="button">删除</button>
</div>
</div>
</div><button class="sk-btn" data-sk-open="#demoSheet" type="button">
<i data-sk-icon="bars"></i>打开底部面板
</button>
<div class="sk-overlay sk-overlay--sheet" id="demoSheet" hidden>
<div class="sk-sheet" role="dialog" aria-modal="true" aria-labelledby="sheetTitle">
<div class="sk-sheet__grab"></div>
<div class="sk-sheet__head" id="sheetTitle">
分享到
<button class="sk-modal__close" data-sk-dismiss type="button" aria-label="关闭">
<i data-sk-icon="xmark"></i>
</button>
</div>
<div class="sk-sheet__body">
往下拖动把手可以关闭面板;内容超长时只有中间区域滚动,头尾固定。
</div>
<div class="sk-sheet__foot">
<button class="sk-btn sk-btn--ghost" data-sk-dismiss type="button">取消</button>
<button class="sk-btn sk-btn--primary" data-sk-dismiss type="button">分享</button>
</div>
</div>
</div><div class="sk-card sk-card--pad" style="width:100%">
<div class="sk-stat__label">项目名</div>
<div class="sk-row" style="margin-top:4px">
<div class="sk-editable" data-sk-editable>
<span class="sk-editable__text">Sakuno UI</span>
<i data-sk-icon="pen" class="sk-editable__pen"></i>
</div>
</div>
<div class="sk-hint sk-mt-2">点一下就能改,回车保存,Esc 还原。</div>
</div><div class="sk-stack" style="gap:10px;width:100%;max-width:380px">
<div class="sk-copyfield" data-copy-field>
<span class="sk-copyfield__text" data-value="https://ui.sakuno.vip">https://ui.sakuno.vip</span>
<button class="sk-btn sk-btn--ghost sk-btn--sm" data-sk-copy type="button" aria-label="复制链接">
<i data-sk-icon="copy"></i>
</button>
</div>
<div class="sk-copyfield" data-copy-field>
<span class="sk-copyfield__text" data-value="npm i sakuno-ui">npm i sakuno-ui</span>
<button class="sk-btn sk-btn--ghost sk-btn--sm" data-sk-copy type="button" aria-label="复制命令">
<i data-sk-icon="copy"></i>
</button>
</div>
</div><div class="sk-marquee" style="--_speed:22s">
<span class="sk-marquee__tag"><i data-sk-icon="bullhorn" style="font-size:10px"></i>公告</span>
<div class="sk-marquee__track">
<div class="sk-marquee__inner">
<span class="sk-marquee__set">v2.5 新增折线图、漏斗、Diff、终端、多选与虚拟列表 6 个组件。</span>
<span class="sk-marquee__set">v2.5 新增折线图、漏斗、Diff、终端、多选与虚拟列表 6 个组件。</span>
</div>
</div>
</div><div class="sk-comments">
<div class="sk-comment">
<span class="sk-avatar sk-avatar--sm sk-avatar--accent">乃</span>
<div class="sk-comment__main">
<div class="sk-comment__head">
<span class="sk-comment__name">瓜生樱乃</span>
<span class="sk-comment__time">12 分钟前</span>
</div>
<div class="sk-comment__body">把圆角从 10 调到 12,整体气质立刻软下来了。</div>
<div class="sk-comment__actions">
<button type="button"><i data-sk-icon="reply"></i>回复</button>
<button type="button"><i data-sk-icon="heart"></i>12</button>
</div>
<div class="sk-comment__replies">
<div class="sk-comment">
<span class="sk-avatar sk-avatar--xs">K</span>
<div class="sk-comment__main">
<div class="sk-comment__head">
<span class="sk-comment__name">Kano</span>
<span class="sk-comment__time">8 分钟前</span>
</div>
<div class="sk-comment__body">同意,1px 描边也比阴影干净。</div>
</div>
</div>
</div>
</div>
</div>
<div class="sk-comment">
<span class="sk-avatar sk-avatar--sm">九</span>
<div class="sk-comment__main">
<div class="sk-comment__head">
<span class="sk-comment__name">九条可怜</span>
<span class="sk-comment__time">半小时前</span>
</div>
<div class="sk-comment__body">深色模式下的描边也很稳,没有发灰。</div>
</div>
</div>
</div>看板用原生拖拽实现,卡片跟手插入;价格卡带推荐角标与倒计时,营销页可以直接拼。
移动端长按卡片即可拖动换列;松手后抛 sakuno:board,计数自动更新。
<div class="sk-board">
<div class="sk-board__col" data-col="todo">
<div class="sk-board__head">待处理<span class="sk-board__count">2</span></div>
<div class="sk-board__card" draggable="true" data-id="t1">
<b>整理色板</b><span>77 色阶</span>
</div>
</div>
<div class="sk-board__col" data-col="doing">…</div>
<div class="sk-board__col" data-col="done">…</div>
</div>
<script>
document.addEventListener("sakuno:board", function (e) {
console.log(e.detail.card, "→", e.detail.column);
});
</script>自己玩、写 demo 够用。
多人维护一套设计语言。
定制主题与私有部署。
限时优惠还剩
紧凑模式
<div class="sk-plan sk-plan--featured">
<span class="sk-plan__badge">推荐</span>
<span class="sk-plan__name">团队版</span>
<div class="sk-plan__price">¥49<small>/ 月</small></div>
<p class="sk-plan__desc">多人维护一套设计语言。</p>
<ul class="sk-plan__list">
<li><i data-sk-icon="check"></i>全部组件与图标</li>
<li class="is-off"><i data-sk-icon="xmark"></i>私有部署</li>
</ul>
<button class="sk-btn sk-btn--primary sk-btn--sm">升级</button>
</div>
<!-- 倒计时:给截止时间,或给剩余秒数 -->
<div class="sk-countdown" data-sk-countdown
data-deadline="2026-12-31T23:59:59"></div>
<div class="sk-countdown sk-countdown--sm"
data-sk-countdown data-seconds="3600"></div>五种语气用纯色淡底表达,不加描边;横幅用于页面级的单行通知。
<div class="sk-alert sk-alert--primary">
<span class="sk-alert__icon">
<i data-sk-icon="circle-info"></i>
</span>
<div class="sk-alert__content">
<div class="sk-alert__title">信息</div>所有组件都支持深色模式。</div>
</div>
<div class="sk-alert sk-alert--success">
<span class="sk-alert__icon">
<i data-sk-icon="check"></i>
</span>
<div class="sk-alert__content">
<div class="sk-alert__title">保存成功</div>配置已同步到 3 个节点。</div>
</div>
<div class="sk-alert sk-alert--warning">
<span class="sk-alert__icon">
<i data-sk-icon="triangle-exclamation"></i>
</span>
<div class="sk-alert__content">
<div class="sk-alert__title">注意</div>该操作不可撤销。</div>
</div>
<div class="sk-alert sk-alert--danger">
<span class="sk-alert__icon">
<i data-sk-icon="circle-xmark"></i>
</span>
<div class="sk-alert__content">
<div class="sk-alert__title">连接失败</div>请检查网络后重试。</div>
<button aria-label="关闭" class="sk-alert__close">
<i data-sk-icon="xmark"></i>
</button>
</div>
<div class="sk-alert sk-alert--neutral sk-alert--line">
<span class="sk-alert__icon">
<i data-sk-icon="circle-info"></i>
</span>
<div class="sk-alert__content">描边变体:中性语气,用于补充说明。</div>
</div><div class="sk-banner sk-w-full">
<i data-sk-icon="sparkles"></i>
<div class="sk-banner__body">v2.5 已发布:图表与开发者组件 + 6 个新组件</div>
<button class="sk-btn sk-btn--xs">查看更新</button>
</div>面向开发者页面的四件套:Diff 展示行级增删与统计,终端带标题栏和逐行打字动画,多选下拉支持搜索与键盘操作,虚拟列表在万级数据下只渲染可视区。
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,6 +1,7 @@
import { defineConfig } from 'vite'
export default defineConfig({
- base: '/old/',
+ base: '/sakuno/',
+ build: { target: 'es2018' },
server: { port: 5173 },
- plugins: []
+ plugins: [svgSprite()]
})
<div class="sk-diff" data-sk-diff data-title="vite.config.js">
<pre class="sk-diff__src">
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,6 +1,7 @@
import { defineConfig } from 'vite'
export default defineConfig({
- base: '/old/',
+ base: '/sakuno/',
+ build: { target: 'es2018' },
server: { port: 5173 },
- plugins: []
+ plugins: [svgSprite()]
})
</pre>
</div>$ npm i sakuno ✓ 已安装 0 个依赖 $ sakuno build ✓ 输出 dist/index.html ✓ 输出 dist/sakuno.min.css ✓ 输出 dist/sakuno.min.js # 零依赖,直接打开 index.html 即可
<div class="sk-terminal" data-sk-terminal data-title="sakuno — bash">
<pre class="sk-terminal__src">$ npm i sakuno
✓ 已安装 0 个依赖
$ sakuno build
✓ 输出 dist/index.html
✓ 输出 dist/sakuno.min.css
✓ 输出 dist/sakuno.min.js
# 零依赖,直接打开 index.html 即可</pre>
</div><div class="sk-multiselect" data-sk-multiselect
data-placeholder="选择参与成员"
data-options="樱乃,千夏,结衣,凛,奏,阳菜,诗织,美咲"
data-value="樱乃,结衣"
data-name="members"></div><div class="sk-vlist" data-sk-vlist
data-count="10000" data-item-height="46"
data-title="任务列表 · 10,000 条"></div>Diff 的源码放在隐藏的 .sk-diff__src 里,行首 + / - 决定增删;「仅变更」可折叠上下文行。终端源码放在 .sk-terminal__src,$ 开头是命令,✓ 成功、! 警告、# 注释。多选下拉在值变化时派发 sakuno:change;虚拟列表派发 sakuno:vlist-select,也可用 Sakuno.virtualList(el, { items, rowHeight, render }) 编程式接入。
日历可切换月份与选择日期,加 data-range 变成区间选择;代码块带复制按钮;键帽用于说明快捷键。
<div class="sk-calendar" data-sk-calendar data-range data-value="2026-06-08">
<div class="sk-calendar__head">
<div class="sk-calendar__title"></div>
<div class="sk-calendar__nav">
<button class="sk-calendar__today" data-sk-cal-today type="button">今天</button>
<button aria-label="上个月" data-sk-cal-prev><i data-sk-icon="chevron-left"></i></button>
<button aria-label="下个月" data-sk-cal-next><i data-sk-icon="chevron-right"></i></button>
</div>
</div>
<div class="sk-calendar__week"><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span><span>日</span></div>
<div class="sk-calendar__grid"></div>
<div class="sk-calendar__range"></div>
</div>// 初始化(脚本会自动执行) Sakuno.init(); Sakuno.theme.toggle();
简洁不是极简主义。把不必要的东西去掉,让核心目的发光。 — Apple 设计原则
<div class="sk-calendar" data-sk-calendar="" data-value="2026-06-08">
<div class="sk-calendar__head">
<div class="sk-calendar__title">
</div>
<div class="sk-calendar__nav">
<button aria-label="上个月" data-sk-cal-prev="">
<i data-sk-icon="chevron-left"></i>
</button>
<button aria-label="下个月" data-sk-cal-next="">
<i data-sk-icon="chevron-right"></i>
</button>
</div>
</div>
<div class="sk-calendar__week">
<span>一</span>
<span>二</span>
<span>三</span>
<span>四</span>
<span>五</span>
<span>六</span>
<span>日</span>
</div>
<div class="sk-calendar__grid">
</div>
</div>
<div class="sk-stack">
<div class="sk-code">
<button class="sk-code__copy" data-sk-copy="">复制</button>
<pre>
<span class="c">// 初始化(脚本会自动执行)</span>
<span class="t">Sakuno</span>.<span class="a">init</span>();
<span class="t">Sakuno</span>.<span class="a">theme</span>.<span class="a">toggle</span>();</pre>
</div>
<div class="sk-row" style="gap:5px">
<span class="sk-kbd">⌘</span>
<span class="sk-kbd">K</span>
<span class="sk-kbd" style="margin-left:8px">Esc</span>
<span class="sk-kbd">↵</span>
<span class="sk-kbd">⇧</span>
<span class="sk-kbd">Tab</span>
</div>
<blockquote class="sk-quote">
简洁不是极简主义。把不必要的东西去掉,让核心目的发光。
<cite>— Apple 设计原则</cite>
</blockquote>
</div>.sk-divided 自动生成<div class="sk-divider">
</div>
<div class="sk-divider sk-divider--text">或者</div>
<div class="sk-divided" style="display:flex;flex-direction:column">
<div class="sk-fs-sm sk-text-soft">分段一:分隔由 <code class="sk-code--inline">.sk-divided</code> 自动生成</div>
<div class="sk-fs-sm sk-text-soft">分段二:不用手写每一条 hr</div>
<div class="sk-fs-sm sk-text-soft">分段三:间距与线宽全局统一</div>
</div>