ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Supabase packages/ui 组件库:基于 Radix、shadcn 与 OKLCH 语义色体系的前端复用实战

Supabase packages/ui 组件库:基于 Radix、shadcn 与 OKLCH 语义色体系的前端复用实战 Supabase packages/ui 组件库基于 Radix、shadcn 与 OKLCH 语义色体系的前端复用实战【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase导读packages/ui/README.md 介绍了 Supabase 官方共享 React 组件库packages/ui它建立在 Radix UI 与 shadcn/ui 之上以 Tailwind CSS 为唯一样式方案被 Supabase 旗下各应用dashboard、docs、www 等统一消费。本文将完整梳理该组件库的导入方式、工具函数、_Shadcn_后缀迁移约定并深入 semantic.css 的“主题输入变量 → OKLCH 派生语义色”体系让读者既能直接用起ui包也能理解并定制 Supabase 风格的明暗主题。组件库定位与技术底座packages/ui是 monorepo 中被 pnpm-workspace.yaml 统一管理的 workspace 包包名ui见 packages/ui/package.json入口与类型声明均指向./index.tsx。README 明确指出它的三条技术支柱Radix UI primitives无头、可访问的交互原语负责 Dialog、Select、Dropdown、Tabs、Tooltip 等复杂交互的行为与键盘可访问性shadcn/ui提供可复制进代码库的组件风格与语义化配色约定Tailwind CSS唯一的样式方案。从依赖清单packages/ui/package.json可以进一步印证直接依赖radix-ui、class-variance-authority、clsx、tailwind-merge、tailwindcss并围绕业务需要扩展了cmdk命令面板、framer-motion动效、input-otp验证码输入、react-hook-form表单、recharts图表、sonnerToast、vaul抽屉、lucide-react图标等。包本身license: MIT、sideEffects: false利于 tree-shaking脚本内置typechecktsc --noEmit、test/test:ciVitest与generate-tailwind-classes同时通过preinstall: npx only-allow pnpm强制使用 pnpm。使用方式从ui别名导入在 pnpm workspace 内应用只需要把ui加入依赖即可按包别名导入无需关心组件内部的相对路径import { Badge, Button, Input } from ui入口文件 packages/ui/index.tsx 是“一张完整的导出台账”可据此快速了解能拿到哪些能力通用组件Button、Menu、NavMenu、SidePanel、Loading、LogoLoader、AnimatedCounter、TreeView、StatusIcon、SuccessCheck、ShadowScrollArea、KeyboardShortcut等HTML 语义化辅助Heading、getAnchor、removeAnchor、highlightSelectedNavItem文档页锚点导航场景shadcn 组件badge、button、card、dialog、dropdown-menu、select、tabs、table、tooltip、sidebar、chart、form、calendar等一整套覆盖表单、浮层、导航与图表图标IconDiscord、IconGitHubSolid、IconTwitterX、IconYoutubeSolid等品牌/社区图标Hooks 与工具use-mobile、KeyboardShortcut与src/lib下的工具函数。_Shadcn_后缀新旧组件并行的迁移约定README 中有一条重要约定部分组件带有_Shadcn_后缀例如Button_Shadcn_它们应当被优先使用目前正处于“逐步替换旧组件”的迁移过程中。从 packages/ui/index.tsx 可以看到这种并行的影子export { Button as Button_Shadcn_ } from ./src/components/shadcn/ui/button也就是说直接export *会与新组件重名的旧实现存在命名冲突因此新 shadcn 版Button以别名Button_Shadcn_显式导出。开发者在新代码中应优先使用带_Shadcn_后缀的实现从而跟随组件库的演进方向。工具函数cn、mergeDeep 与 clipboard组件之外ui还导出一组高频工具函数packages/ui/src/lib/utils/index.ts// deep object merge (used for themes) import { clipboard, cn, mergeDeep } from ui // clsx tailwind-merge注释中三者分别对应cnclsxtailwind-merge的组合封装用于条件拼接并自动去重冲突的 Tailwind class实现见 packages/ui/src/lib/utils/cn.ts。它是 shadcn 生态的标准“万能拼接器”组件内部大量用于组合语义化 classmergeDeep深度合并对象README 明确指出它服务于主题场景——主题需要以浅层覆盖的方式叠加输入变量而保留其余派生值见 packages/ui/src/lib/utils/mergeDeep.tsclipboardcopy-to-clipboard 助手。其实现 packages/ui/src/lib/utils/clipboard.ts 对浏览器差异做了防御Safari 锁死 Clipboard API需要ClipboardItemnavigator.clipboard.write、Firefox 的ClipboardItem位于dom.events.asyncClipboard.clipboardItem偏好开关之后因此分别回退到navigator.clipboard.writeText失败时通过sonner的toast.error提示“Unable to copy to clipboard”。样式约定一Tailwind-only 与语义化优先README 把样式约定总结为“只能 Tailwind、优先语义化”这也是编写新组件时的硬性规范Tailwind only——不使用内联style不引入 CSS Modules优先使用 shadcn 语义配对而不是写死颜色bg-card text-card-foregroundbg-muted text-muted-foregroundbg-tertiary text-tertiary-foreground形如text-foreground-light、border-default的旧工具类仅是兼容别名compatibility aliases不应用在新代码中。“为什么旧类名还在工作”的答案藏在 packages/ui/build/css/source/compat.css 中这份文件把所有旧 token 映射到新的语义系统例如--foreground-light: var(--muted-foreground)、--foreground-lighter / --foreground-muted: var(--tertiary-foreground)、--border-default: var(--border)、--border-strong: var(--input)。文件头注释说明其使命让未迁移的调用点继续解析一旦消费者全部迁移完毕即删除对应条目文件清空后整体移除。它必须被放在 semantic.css 之后导入确保旧名最终覆盖解析到新变量。样式约定二控制面表面角色Control Surface Roles表单控件承载着“输入”与“选择”两类相反的视觉语言README 推荐使用专门的语义角色类而不是自创填充色bg-field下沉式文本输入位Input、Textarea、filled MultiSelectbg-control-raised上浮式选择器面板Select、空态 MultiSelectborder-control-hover共享的交互边框CSS 侧--control是--control-raised的别名旧bg-control仍指 accent 水洗wash别名。这些变量的推导见 packages/ui/build/css/source/semantic.css 中--field/--control-raised/--control的定义--field用带透明度的黑色实现“始终下沉”oklch(0 0 0 / var(--field-alpha))--control-raised则用极淡的白色“上浮”oklch(1 0 0 / ...)。其中--field-alpha是一个主题输入同一绝对透明度在浅色面板上很明显、在深色面板上近乎不可见因此深色主题必须调得更强——dark.css 将其设为0.12注释明确说明默认0.015在深色面板上会“消失”浅色主题沿用默认。样式约定三主题输入变量与 OKLCH 派生色体系README 中分量最重的是主题变量约定。它描述了输入/派生两层模型主题只需覆盖少量输入其余语义色全部在 semantic.css 中用OKLCH推导。核心输入如下CSS 变量作用semantic.css:root默认themes/dark.cssthemes/light.css--hue中性色与品牌共用色相159159159--surface-hue中性面/文字/边框色相var(--hue)沿用34冷暖分离--primary-hue品牌色相--primary的来源var(--hue)沿用沿用--chroma中性色饱和度0.0160.0050--surface基准表面明度0.17镜像深色0.190.995--foreground-lightness前景文本明度0.950.950.1--contrast全局对比度旋钮0.50.50.53--muted-foreground-levelmuted 文本在层级中的位置0.80.80.65--tertiary-foreground-leveltertiary 文本层级0.650.650.5色相分离表面与品牌解耦semantic.css 顶部注释还原了设计演进过去单一--hue同时驱动中性灰阶和品牌色导致二者永远无法分开。如今拆成--surface-hue中性 ramp 的色相与--primary-hue构建--primary的品牌色相二者都默认回落到--hueSupabase 绿在 OKLCH 下约159°。也就是说主题若只设置--hue表面与品牌保持联动、行为与旧版一致覆盖其中任意一个即可让二者“分道扬镳”。light.css 正是这样做的——--surface-hue: 34让浅色界面呈现暖灰表面而品牌仍是绿色。对比度旋钮--contrast的数值说明README 文字描述中写道“--contrast: 1是基线支持范围0.75到1.25”这应理解为面向主题作者的推荐表达区间。需要指出的是本仓库当前实际源码的取数不同semantic.css 的默认值与其注释一致——--contrast是取值0到1的全局对比度旋钮“舒适基线”是0.50使边框与强调色在 muted 表面上几乎不可见1推至全强度内置主题中 dark.css 设为0.5、light.css 设为0.53。进一步地--contrast会派生出--contrast-delta在0.5处为零、-1/1分别为两端以及--contrast-text、--contrast-border对0..1取平方使大部分取值落在高强度区等中间变量用于统一调节边框、输入框与前景文本的对比强度。实际定制主题时请以 semantic.css 当前实现为准。表面抬升模型各层级表面--card、--popover、--secondary不是独立写死的颜色而是以--background为基准、按“--elevation-step × 抬升比”推高明度得到的。--elevation-step带符号深色下--tone-span前景明度 − 表面明度为正--elevation-step随之为正越高越亮light.css 将--surface压到0.995近白但非纯白给抬升留出余量并显式覆盖--elevation-step: 0.024使浅色下高层级也向白色增亮——从而在两种模式下统一为“更高 更亮”。--muted/--accent/--tertiary则以透明前景叠加层--muted-alpha/--accent-alpha/--tertiary-alpha的方式实现叠加层 Alpha 与同抬升比下的明度差对齐深色模式下增亮、浅色模式下压暗从而在任何表面之上都能正确合成。状态色刻意不被中性 ramp 派生--warning琥珀75°附近、--destructive红25°附近、--info紫288°这些表达性颜色遵循独立规则它们锚定各自色相峰值附近的固定目标明度共享常量--expressive-chroma: 0.14。这个 chroma 刻意不随中性--chroma走——即使品牌被调成近灰阶状态色仍须保持可读。状态色相还会按--status-hue-pull默认0.15向品牌--primary-hue轻微“靠拢”以获得和谐再用clamp()锁回各自感知类别warning 永远读作琥珀、destructive 永远读作红。其上文字色--warning-foreground等通过clamp(0.12, (0.62 − l) × 100, 0.99)在明度约0.62处做硬翻转亮底深字、暗底白字。品牌 stepped numeric scales 属于主题侧一个容易误读的细节--primary不是brand。semantic.css 中--primary是“品牌派生的强调色”明亮的 Supabase 绿模式无关、不随--chroma脱饱和而brand工具类theme.css 的--color-brand映射的是主题文件里各自的--brand-default阶梯值属于主行为二者解耦。README 未展开说明的这类“数值阶梯”从源码可推断如下--brand-*、--warning-*、--destructive-*、--secondary-*、--secondary-default等带数字后缀的阶梯变量不在 semantic.css 派生而是作为“每主题字面量”写进 dark.css 与 light.css再在packages/config/css/theme.css中映射为工具类见 semantic.css 头注释。例如深色主题的--brand-default: 153.1deg 60.2% 52.7%浅色主题的--destructive-default: 10.2deg 77.9% 53.9%。主题 CSS 文件的组成与内置主题主题相关样式集中在 packages/ui/build/cssbuild/css/ ├── source/ │ ├── compat.css # 旧 token → 语义 token 的兼容别名最后导入 │ ├── global.css # 中性色阶、间距/尺寸/圆角/阴影等基础 token │ └── semantic.css # 核心输入变量 → OKLCH 派生语义色 └── themes/ ├── light.css # [data-themelight] / .light ├── dark.css # [data-themedark] / .dark ├── classic-dark.css # 经典深色主题 └── faux-classic-dark.css # 仿经典深色主题三个文件的分工即“派生链”的物理分层semantic.css描述输入→派生模型主题文件只覆盖输入compat.css兜底存量调用。暗色模式是应用默认外观semantic.css 的:root默认值即镜像深色主题浅色模式通过[data-themelight]/.light选择器覆盖--surface、--surface-hue、--chroma、--foreground-lightness、--contrast与各*-lightness状态锚点实现。除默认明暗两套外还有经典深色与仿经典深色变体供特定页面沿用旧观感。Tailwind 配置归属root 负责、包内只留 IntelliSense 桩README 特意提醒了一个易踩的坑真正的 Tailwind 配置由 workspace 根目录拥有packages/ui内的文件只是给编辑器 IntelliSense 用的桩。实际情况印证了这一点——包内 packages/ui/tailwind.config.css 的内容只有一行注释和import config/tailwind.config.css;而 shadcn 约定文件 packages/ui/components.json 也把 Tailwind 配置指向./../config/tailwind-shadcn.config.js、CSS 指向./build/css/source/global.css组件别名定位到src/components/shadcn。因此修改配色与工具类时应在 root 层或packages/config的 Tailwind 配置中操作而不是改动包内桩文件。落地建议在应用内使用与迁移结合上述全部约定为需要接入该组件库的应用或新组件给出可执行清单通过 pnpm workspace 引入ui依赖import { Badge, Button, Input } from ui交互复杂的场景可继续从src/components/shadcn/ui/*中选取并参与二次封装新代码一律 Tailwind 工具类优先 shadcn 语义配对bg-card text-card-foreground、bg-muted text-muted-foreground、bg-tertiary text-tertiary-foreground不要写死颜色也不要新引用text-foreground-light、border-default等兼容别名表单控件表面使用bg-field/bg-control-raised/border-control-hover表达“下沉输入 / 上浮选择”的角色差异涉及新组件时优先使用_Shadcn_后缀的版本配合组件库持续推进的新旧替换自定义主题只需覆盖输入变量色相、--chroma、--surface、--foreground-lightness、--contrast、muted/tertiary 层级其余语义色由 OKLCH 自动推导需要极端中性化或冷暖表面分离时可参考 light.css 的--surface-hue与--chroma: 0组合想验证自己的工具类组合是否合法可运行pnpm --filter ui typechecktsc 类型检查与pnpm --filter ui testVitest 测试。参考文件导航组件库说明与样式约定packages/ui/README.md包元信息与脚本packages/ui/package.json导出台账组件 / 图标 / hookspackages/ui/index.tsxshadcn 别名配置packages/ui/components.json语义色核心模型packages/ui/build/css/source/semantic.css兼容别名层packages/ui/build/css/source/compat.css基础设计 tokenpackages/ui/build/css/source/global.css内置明暗主题themes/light.css、themes/dark.css工具函数cn.ts、mergeDeep.ts、clipboard.ts【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表