nico-glass-kit
v0.3.2
Published
Apple-style glass (iOS 26) React component kit with tiered rendering
Readme
nico-glass-kit
把苹果风格(iOS 26 质感)的玻璃做成 React 组件——不只是模糊,而是真正的位移折射。
目录
这是什么
一套小组件库,在浏览器里还原 iOS 26 视觉语言中的磨砂玻璃面板。每个表面都是真正的玻璃:backdrop-filter 里跑一张 SVG 位移图,背景会沿着面板的圆角被弯折,而不是简单糊掉。材质参数全部可调——模糊、染色、折射深度、倒角剖面、色散——指针靠近时还会触发弹性形变和边缘高光。
整个库刻意做到零运行时依赖:React 是 peer 依赖,其余都是普通的 TypeScript + CSS。
特性
- 真折射。 背景由按圆角半径生成的符号距离场(SDF)位移贴图驱动,边缘像倒角一样弯折;高画质档还支持色散(RGB 分离)。
- 分档渲染,自动降级。 按浏览器实际能力在
high → medium → low之间选择;不支持backdrop-filter: url()的浏览器拿到一套普通 CSS 背景链,而不是坏掉的表面。 - 逐元素明暗自适应。
overLight="auto"采样每个表面下方实际绘制的像素亮度,单独翻转该元素的文字、染色、边缘与阴影,并带迟滞,不会来回跳变。 GlassLightGroup。 把若干 auto 元素绑成一个识别组,让一整行或整条悬浮栏只做一个明暗判断,而不是逐片闪烁。GlassElasticityGroup。 让多个玻璃表面共享一组指针位置和弹性强度,像锁屏时钟这样的多字形表面可以一起形变;它与GlassLightGroup独立组合。- SSR 安全。 模块顶层不碰
window/document;首次客户端渲染固定为 low 档加深色回退,挂载后再升级。 - 18 个成品组件跑在同一个基元上,接受同一套材质参数。
环境要求
- React 18 及以上。
- 折射需要 Chromium 系浏览器。Firefox 与 Safari 会自动落到 low 档(普通
backdrop-filter背景链)。
安装
npm install nico-glass-kit想跟着仓库而不是发布版本走,可以从 GitHub 安装——仓库里有 prepare 脚本,安装过程会自动构建 dist/:
npm install github:more-nico/nico-glass-kit如果要改这个库本身,就用源码检出,再让应用指向它(包里只发布 dist/):
git clone https://github.com/more-nico/nico-glass-kit.git
cd nico-glass-kit
npm install
npm run build
npm install /path/to/nico-glass-kit快速开始
import { GlassProvider, GlassCard, GlassButton } from 'nico-glass-kit';
import 'nico-glass-kit/style.css';
export function Demo() {
return (
<GlassProvider quality="high" overLight="auto">
<GlassCard cornerRadius={24}>
<GlassButton>新建项目</GlassButton>
</GlassCard>
</GlassProvider>
);
}GlassProvider 负责挂载全局共享的 SVG 滤镜注册表,必须包住所有玻璃组件;样式文件 nico-glass-kit/style.css(设计令牌 + 分层样式)也必须引入。
渲染分档
| 档位 | 实际渲染什么 |
| --- | --- |
| low | 普通 CSS backdrop-filter 链(模糊 / 饱和 / 亮度)。也是首次客户端渲染,以及无法使用 SVG 背景滤镜时的回退。 |
| medium | 经 SVG 滤镜图做一次位移。 |
| high | 色散:RGB 分离,三次位移;并启用指针弹性与悬停提亮。 |
用 provider 或单个表面上的 quality 指定档位,浏览器跑不动时库会沿档位链自动降级。
明暗处理
overLight 接受 true、false 或 'auto':
'auto'(默认)采样每个元素下方的背景并逐元素决定明暗,带节流与迟滞。读不出来的背景(跨域 iframe、非 CORS 图片)退回prefers-color-scheme。true/false固定模式,同时让该元素退出探测。
相邻元素需要保持一致时(一行工具栏、一条底栏、页脚),把它们放进 GlassLightGroup,整条一起翻转。
弹性分组
把需要联动的玻璃表面放进 GlassElasticityGroup,它们会沿组边界共享指针驱动;组边界内的成员间隙也会触发形变。分组的 elasticity(默认 0.2)覆盖成员各自的值,弹簧仍只在 high 档运行。弹性分组和明暗分组相互独立,可以同时使用。
import { GlassElasticityGroup, GlassText } from 'nico-glass-kit';
<GlassElasticityGroup elasticity={0.35}>
<GlassText text="10:09" fontSize={160} />
</GlassElasticityGroup>组件
基元
| 组件 | 说明 |
| --- | --- |
| GlassSurface | 基元组件:分层结构、材质、探测与弹性都在这里。 |
| GlassProvider | 全局默认值 + SVG 滤镜注册表。 |
| GlassLightGroup | 让绑定的多个元素共享一次明暗判断。 |
| GlassElasticityGroup | 让绑定的多个元素共享指针驱动的弹簧。 |
按钮与导航
| 组件 | 说明 |
| --- | --- |
| GlassButton | 胶囊与圆形图标按钮,悬停提亮、按压回弹。 |
| GlassNavBar | 悬浮三段式顶栏(左 / 中 / 右)。 |
| GlassTabBar | 底栏,带滑动激活胶囊。 |
| GlassSegmentedControl | 分段选择器,复用底栏的激活胶囊语言。 |
表单与控件
| 组件 | 说明 |
| --- | --- |
| GlassInput | 文本输入,支持前后缀插槽、尺寸与校验态。 |
| GlassSelect | 玻璃下拉选择,弹层圆角与动效跟随触发器。 |
| GlassSwitch | 开关,状态填充放在内容层。 |
| GlassCheckbox | 勾选框,勾选标记也是玻璃。 |
| GlassSlider | 用原生 range 玻璃化的滑杆,磨砂内嵌轨道。 |
状态与反馈
| 组件 | 说明 |
| --- | --- |
| GlassBadge | 带色调圆点的状态标签。 |
| GlassAvatar | 玻璃边框内的图片或首字头像。 |
| GlassProgress | 复用同一套控件轨道的进度条。 |
| GlassSpinner | 加载环。 |
| GlassText | 字形本身就是玻璃,支持全部 95 个可显示 ASCII 字符。 |
| GlassAlert | 按色调着色的行内提示,可关闭。 |
面板与浮层
| 组件 | 说明 |
| --- | --- |
| GlassCard | 大圆角玻璃面板,内容层可独立滚动。 |
| GlassPill | 悬浮通知胶囊 / Toast,带进入退出动画。 |
| GlassModal | 带动画的弹窗面板,Esc 或点遮罩关闭。 |
| GlassTooltip | 悬停 / 聚焦气泡,四个方向。 |
玻璃文字
每个可见字符都是使用字形遮罩和距离场的 GlassSurface,材质参数与其他表面含义一致,没有文字专属增亮或折射削弱。空格只占位;tabularNums 使用完整十个数字的最大字宽。字体加载和响应式字号变化会更新几何;服务端首屏和 Canvas 失败时保留可读文本。
<GlassText text="01:00" fontSize={132} fontWeight={600} tabularNums />
<GlassText text="B8@%" optics={{ refraction: 1, depth: 8 }} />所有组件共享的参数
| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| quality | 'low' \| 'medium' \| 'high' | 取 provider 的值 | 单个表面的档位覆盖,仍会自动降级。 |
| overLight | boolean \| 'auto' | 'auto' | 该元素的明暗处理方式。 |
| optics | Partial<GlassOptics> | — | 稀疏覆盖,合并到下表的默认值之上。 |
| elasticity | number(0–1) | 0.2 | 指针弹性(high 档);0 为刚性。 |
| highlightIntensity | number | 1 | 边缘高光强度倍数,高光位置跟随指针。 |
| hoverBrightnessBoost | number | 0 | 悬停时额外增加的亮度,GlassButton 设为 0.5。 |
| cornerRadius | number | 20 | 组件级圆角半径。 |
材质参数(optics)
| 键 | 默认值 | 含义 |
| --- | --- | --- |
| blur | 3 | 背景模糊半径(px)。 |
| saturation | 100 | 饱和度百分比。 |
| brightness | 1.1 | 亮度倍数。 |
| tint | light-dark(rgb(255 255 255), rgb(18 20 26)) | 基础染色。 |
| tintStrength | 0.2 | 染色强度,0–1。 |
| refraction | 1 | 折射强度,映射到位移比例。 |
| depth | 8 | 折射带宽度(px)。 |
| curvature | 0.2 | 倒角剖面:0 为桶形,1 为 squircle。 |
| dispersion | 0.1 | 色散强度,仅 high 档生效。 |
Playground
playground/ 里的演示页把组件放在五种内置背景上——极昼、日落、海洋三种动态渐变场景、一面密集文字墙,以及梵高《星月夜》(公有领域)——还可以上传任意图片作背景。右侧面板实时驱动所有材质参数,左下角的 FPS 表用来观察各档位的开销。界面是中文的。
在线演示:nico-glass-kit.vercel.app。
npm run dev # 本地 playground,http://localhost:5173
npm run build:playground # 静态构建到 playground/dist(Vercel 部署的就是它)组件演示
GlassButton —— 悬停提亮、按压回弹、图标按钮与禁用态。
GlassCard —— 指针沿卡片边缘扫过时的边缘高光与弹性位移。
GlassNavBar 与 GlassTabBar —— 悬浮顶栏与底栏叠在滚动信息流上,随内容明暗自动翻转。
GlassInput 与 GlassSelect —— 输入、清除,以及选项高光跟随指针的玻璃下拉。
GlassSwitch、GlassCheckbox、GlassSlider、GlassSegmentedControl —— 选择类控件,所有状态填充都在内容层。
GlassBadge、GlassAvatar、GlassProgress、GlassSpinner —— 状态与反馈类元素。
GlassAlert、GlassTooltip、GlassModal —— 气泡提示、可关闭的提示条与弹窗。
开发
npm run dev # playground 开发服务器
npm run typecheck # tsc --noEmit,覆盖 src/ 与 playground/
npm test # vitest,node 环境,只测纯函数
npm run build # 只打包库(dist/),不打包 playground
npm run build:playground # 打包 playground(playground/dist)测试跑在 node 环境里,所以只覆盖纯函数——位移贴图编码、滤镜图、材质合并、背景探测调度。需要画布或合成器的部分只能在 playground 里人工验证。
说明与限制
- 背景探测是对真实像素的近似:径向渐变取最远角档位,角关键字线性渐变按 45° 对角线近似,
url()背景只在 CORS 干净时逐像素采样;读不出来的背景退回prefers-color-scheme。 iframe、object、embed、video、canvas视为不可知的不透明层:命中它们的采样点会被丢弃,而不是相信元素自身的 CSS 背景。- 含
url()的backdrop-filter链里其它函数会被浏览器丢弃,所以模糊与饱和度都写在 SVG 图里,CSS 里不写。 - 交给滤镜的位移贴图是刻意降采样的(
lensMapRasterScale,默认0.2,范围0.1–0.5):折射边缘会稍软一点,换来明显更低的合成准备开销。
许可
MIT。
playground 内置了一张背景壁纸——梵高《星月夜》(1889),取自 Wikimedia Commons,属于公有领域。
assets/ 里的演示 GIF 是 playground 的录屏,其中的背景插画为演示用途引用,版权归原作者所有,不属于本包的一部分。
