尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Onyx Opal 设计系统实战:从安装接入到源码构建与 npm 发布

Onyx Opal 设计系统实战:从安装接入到源码构建与 npm 发布 Onyx Opal 设计系统实战从安装接入到源码构建与 npm 发布【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswerOpalonyx-ai/opal是 Onyx 前端web/背后的 TypeScript 组件库与设计系统仓库中对应目录为 web/lib/opal/其定位、结构与用法完整记录在 web/lib/opal/README.md。本文以该文档为主线结合 package.json、tailwind-preset.cjs、scripts/bundle-css.mjs 等源码完整讲解如何安装、初始化、使用、本地联调、构建发布以及设计系统内部的 token、类型与构建机制帮助你在自己的 Next.js/React 项目中复用它或深入理解 Onyx 前端的设计基建。一、Opal 是什么组件库 设计系统的一体化方案Opal 不是单纯的按钮 弹窗组件集合而是一套完整的、与设计 token 深度绑定的设计系统组件层Button、Text、Tag、Tooltip、Popover、Table、Input、Modal、Calendar 等常用 UI 组件以及 Content、Section、SettingsLayouts、RootLayout 等布局组件token 层颜色、圆角、阴影、间距、字体、z-index 等设计变量通过 CSS 自定义属性custom properties暴露主题层Tailwind preset 将 token 映射成text-text-04、rounded-08、shadow-box-01这类开箱即用的工具类发布层作为独立 npm 包发布Onyx 自身也以 monorepo workspace 方式消费它。从仓库结构看见下文目录结构与分层Opal 的源码全部位于web/lib/opal/其中 src/core/ 提供交互原语、src/components/ 提供高层组件、src/layouts/ 提供布局组件src/types.ts 与 src/utils.ts 则承载共享类型与工具函数。二、安装包本体 按需的 peer dependenciesOpal 的运行时依赖非常克制——它自身只有clsx、copy-to-clipboard、tailwind-merge三个直接依赖见 package.json其余能力弹窗、拖拽、表格、表单、Markdown全部通过 peer dependencies 交给消费方按需安装bun add onyx-ai/opal核心原则install whichever the lib actually exercises in your usage——只安装你实际用到的功能对应的依赖。完整清单含 package.json 中声明的版本范围bun add react react-dom next \ radix-ui/react-popover radix-ui/react-separator \ radix-ui/react-slot radix-ui/react-tooltip \ dnd-kit/core dnd-kit/sortable dnd-kit/modifiers dnd-kit/utilities \ tanstack/react-table formik \ react-markdown remark-gfm rehype-sanitize各依赖在库中的典型用途可从源码导出清单反推依赖用途radix-ui/react-*popover/separator/slot/tooltip/dialog/select/tabs无障碍交互原语如 Popover、Tooltip、Modal、Tabs 等组件底层dnd-kit/*拖拽排序Table 列、列表类组件tanstack/react-table数据表格能力见 Table 组件formik表单状态管理表单类输入组件react-markdownremark-gfmrehype-sanitize富文本/Markdown 渲染管线使用 npm 或 pnpm 的环境可把bun add等价替换为对应包管理器的命令React 18 与 19 均被 peerDependencies 接受。三、初始化设置两步接入设计系统3.1 在应用根入口导入样式一次性在应用根入口如 Next.js 的app/layout.tsx引入一次样式文件import onyx-ai/opal/styles.css;该 CSS 文件定义了设计系统所需的全部自定义属性--text-01、--background-neutral-00等正是 Tailwind preset 中各个 token 引用的底层变量。注意它必须是全局只导入一次——这正是 src/root.css 顶部注释强调的用法约定Consumers should import exactly once at the top of the application。3.2 接入 Tailwind preset在tailwind.config.js中挂载 preset 并配置 contentmodule.exports { presets: [require(onyx-ai/opal/tailwind-preset)], content: [ ./src/**/*.{ts,tsx}, ./node_modules/onyx-ai/opal/dist/**/*.{js,mjs}, ], };presets注入设计 token 对应的工具类颜色、圆角、阴影、动画等content中的node_modules/onyx-ai/opal/dist/**/*.{js,mjs}是关键确保 Tailwind 能扫描到 Opal 组件内部使用的类名否则组件样式会因 JIT 裁剪而缺失。3.3 调色板归消费方所有Opal 的 preset 只引用--text-01等 CSS 变量但不定义它们。README 明确说明they live with the consumer so the consumer controls the palette。因此你需要在应用自己的colors.css中定义这些变量或从 Onyx 复制一份。这种preset 与变量分离的设计让每个接入方都能在不动组件代码的前提下定制整套配色——这也是设计系统主题化的标准做法。从 tailwind-preset.cjs 可以看到preset 中所有颜色text-01、background-neutral-*、border-*、action-selection-*、theme-*、status-*、highlight-*等都是var(--xxx)的一一映射消费方只需覆写变量值即可换肤。preset 还额外提供了动画 keyframessubtle-pulse、pulse、fade-in-scale、fade-out-scale、collapsible-down/uptailwind-preset.cjs#L24-L57其中 collapsible 动画基于 Radix 的--radix-collapsible-content-height变量字体族通过var(--font-hanken-grotesk, Hanken Grotesk, sans-serif)解析注释说明这是为了兼容next/font加载的哈希字体名tailwind-preset.cjs#L58-L65圆角刻度--radius-02/04/08/12/16/20及 pill 形态的--radius-round阴影/模糊/z-indexshadow-box-00/01/02、backdrop-blur-01/02/03、z-popover/z-tooltip等。四、基础用法子路径导入与第一个组件Opal 的所有导出都通过子路径subpath imports暴露因此不会打包进用不到的内容。README 给出如下示例import { Button, Text } from onyx-ai/opal/components; import { Content } from onyx-ai/opal/layouts; import SvgPlus from onyx-ai/opal/icons/plus; function MyComponent() { return ( Content icon{SvgPlus} titleHello descriptionWorld sizePresetmain-ui variantsection / ); }从 package.json 的 exports 字段 可以看到每个子路径都有独立的types与import入口ESM 格式并且都指向dist/下的产物。也就是说对外消费的是构建产物而不是源码源码只服务于 Onyx 仓库内部的开发场景见第六节。五、Subpath imports 全景一行一个入口Subpath内容onyx-ai/opal/componentsButton、Text、Tag、Tooltip、Popover、Table 等高层组件onyx-ai/opal/layoutsContent、ContentAction、IllustrationContent、Section 等布局组件onyx-ai/opal/coreInteractive、Hoverable、Disabled 等交互原语onyx-ai/opal/iconsSVG 图标组件onyx-ai/opal/illustrations更大的 SVG 插画组件onyx-ai/opal/logos第三方品牌 Logo见第八节商标说明onyx-ai/opal/hooks共享 hooksuseScreenSize、useFocusOnMount、useOnMount等见 src/hooks/onyx-ai/opal/types共享类型RichStr、IconProps等onyx-ai/opal/utilscn、markdown等工具函数onyx-ai/opal/time时间相关工具onyx-ai/opal/styles.css打包后的组件 CSSonyx-ai/opal/tailwind-preset携带设计 token 的 Tailwind preset5.1 组件家族速览源自源码导出src/components/index.ts 是组件的总出口按模块可归纳为按钮族Button、SelectButton、OpenButton、FilterButton、LineItemButton、SidebarTab、LinkButton、TextButton、CopyButton展示族Text含CompactMarkdown、Tag、Divider、IconContainer、ProgressBar、Code、EndOfList反馈族Tooltip、Popover/PopoverMenu、Modal含BasicModalFooter与useCreateModal/useModalcontext、LoaderIconLoader/OnyxLoader、ShadowDiv输入族InputTypeIn、InputTextArea、InputSelect、InputTags、InputDatePicker、InputTime、PasswordInputTypeIn、Switch、Checkbox数据展示Table配合createTableColumns、Calendar、Pagination、Tabs容器Card、SelectCard、EmptyMessageCard、MessageCard、Spacer。布局侧src/layouts/index.ts则提供Content、ContentAction、IllustrationContent、通用Section、SettingsLayouts、RootLayout含useSidebarState、SidebarLayouts、AuthLayouts、TagList、ToastProvider/toast、ConfirmationModalLayout、PageLoader等。核心原语src/core/index.ts中Interactive是复合组件Simple/Stateless/Stateful/Container/Foldable五种形态配合Hoverable、Disabled构成交互基础。六、类型系统与工具函数源码级细节6.1 共享类型src/types.ts尺寸体系SizeVariants fit | full | xl | lg | md | sm | xs | 2xsContainerSizeVariants排除full/xl容器需要固定高度预设ExtremaSizeVariants只保留fit/full。间距/圆角刻度Spacing直接使用数字N代表N/4rem与 Tailwind 的p-2语义对齐——padding{2}与p-2是同一物理距离types.ts#L82-L94Rounding是闭集0.5 | 1 | 2 | 3 | 4 | 5 | full其中N同样为N/4remfull映射到 pill 形态--radius-roundtypes.ts#L48-L69。语义色ColorTypes涵盖default/muted/success/danger/warning/muted-success/muted-warning/interactiveStatusVariants涵盖default/info/success/warning/pending/error。图标契约IconProps extends SVGPropsSVGSVGElement统一提供size、title、color、classNameIconFunctionComponent用于声明接受图标作为 prop的组件接口。富文本品牌类型RichStrmarkdown()的产物标记内联 Markdown与RichNodesrichNodes()的产物标记故意作为Text子节点的 React 节点主要用于 next-intl 的t.rich(...)富文本场景见 types.ts#L236-L258。品牌类型branded type设计避免了API coloring——组件不需要额外的markdown布尔开关决定权留在调用点。输入状态InputVariants primary | internal | error | disabled | readOnly。6.2 工具函数src/utils.tscn(...)clsxtailwind-merge的组合用于安全拼接与去重 Tailwind 类名markdown(...lines)把多行字符串包装为RichStr每行渲染为独立行richNodes(nodes)/isRichNodes(...)富文本节点包装与类型守卫mergeRefs(...)合并多个 ref 为单个 ref callbackcopyText(text)优先navigator.clipboard回退copy-to-clipboardclickOnKeyDown(onClick)为rolebutton的容器实现 Enter/Space 触发的键盘可访问性并处理了事件冒泡event.target ! event.currentTarget时忽略与长按重复触发event.repeat两个边界情况utils.ts#L79-L92。七、目录结构与构建机制7.1 目录总览README 给出的结构如下与仓库实际一致web/lib/opal/ ├── src/ │ ├── core/ # 低层原语Interactive, Hoverable, Disabled │ ├── components/ # 高层组件Button, Popover, Tooltip, Table, ... │ ├── layouts/ # 布局原语Content, ContentAction, Section, ... │ ├── icons/ # SVG 图标组件 │ ├── illustrations/ # 更大的 SVG 插画 │ ├── logos/ # 品牌 / 产品 Logo │ ├── types.ts # 共享类型RichStr, IconProps, ... │ ├── utils.ts # cn, markdown 等工具 │ ├── shared.ts │ └── root.css # 库自有的设计 token ├── scripts/ │ └── bundle-css.mjs # 将 root.css 各组件 CSS 拼接为 dist/styles.css ├── package.json ├── tsconfig.json # 源码类型检查配置 ├── tsconfig.build.json # 供 tsup 产出 dist/ ├── tsup.config.ts ├── tailwind-preset.cjs └── README.md7.2 Tailwind v4 与reference机制Opal 的样式基于Tailwind v4构建。src/_reference.css 是样式体系的参照文件它import tailwindcss、import tw-animate-css、config ../tailwind-preset.cjs并引入onyx-ai/shared的 tokens 与 typography。每个独立的组件 CSS 通过reference引用它以便apply能解析到自定义 token如rounded-08、text-text-04——因为 Tailwind v4 中每个 CSS 文件由 PostCSS 独立处理。它还定义了一个自定义变体custom-variant no-hover (media (hover: none));用于触屏设备主输入无法 hover场景是 Tailwind v4hover:变体的互补典型用法是opacity-0 group-hover:opacity-100 no-hover:opacity-100让 hover 才出现的控件在移动端保持可见。src/root.css 则依次导入sizes.css、typography.css、z-index.css——其中布局尺寸容器宽度、侧边栏宽度、弹窗宽度等是库自有的见 src/styles/sizes.css如--app-container-md: 54.5rem、--sidebar-width-expanded: 15rem、--block-width-modal-medium: 40rem而设计 token 刻度圆角、间距、字重等来自onyx-ai/shared通过import onyx-ai/shared/tokens.css引入。这种划分把跨平台通用 token与web 专属布局尺寸解耦。7.3 构建tsup CSS 打包器package.json 的构建脚本为bun run build # 等价于 tsup node scripts/bundle-css.mjstsup 负责 JStsup.config.ts以src/components/index.ts、src/layouts/index.ts、src/core/index.ts、src/icons/index.ts、src/illustrations/index.ts、src/logos/index.ts、src/hooks/index.ts、src/time.ts、src/types.ts、src/utils.ts为入口输出 ESMtarget: es2020、生成 d.ts 类型声明React/Radix/dnd-kit 等全部external化用插件丢弃 JS 中的 CSS importCSS 统一走打包后的dist/styles.css并保留use client指令——这是 Next.js App Router 下客户端组件正确运行的前提。bundle-css.mjs 负责 CSSscripts/bundle-css.mjs递归收集src/下所有.css按固定顺序拼接——_reference.css在最前它携带import tailwindcss与configroot.css紧随其后保证 token 先于消费规则定义其余按字母序。同时做三类清理/内联剥离各文件的reference指令合并后上下文统一指令反而无法解析相对路径剥离包内相对import文件已被内联内联onyx-ai/shared/*.css的内容使发布产物自包含、消费方无需安装onyx-ai/shared。最终同时产出dist/styles.css与dist/root.css内容相同前者即 README 中消费方导入的样式入口。八、在 Onyx 仓库内的本地开发Opal 不维护自己的node_modules而是复用/web/node_modules。开发与联调遵循以下约定README 原文要点新增运行时依赖时先在 web/lib/opal/package.json 的peerDependencies声明同时在根 web/package.json 的dependencies加对应版本然后在/web下执行bun install保证 Onyx 前端应用持续可构建Onyx 通过 workspace 消费 Opal根 web/package.json 中的onyx-ai/opal: file:./lib/opal将包指向本地目录开发期间web/通过opal/*TypeScript 路径别名直接解析Opal 源码web/tsconfig.json 中opal/*: [./lib/opal/src/*]改动即时生效无需先跑bun run build需要产出发布产物时在web/lib/opal下执行cd web/lib/opal bun run build # tsup - dist/随后 bundle-css.mjs - dist/styles.css从源码看库内部组件之间也统一使用opal/别名互引如 src/components/index.ts 中的opal/components/...这与外部消费走onyx-ai/opal/...形成两套并行的引用体系仓库内走别名源码仓库外走 npm 产物。九、发布到 npmtag 驱动的自动化发布Opal 的发布完全由 Git tag 驱动通过仓库的Release OpalGitHub Actions 工作流执行。README 描述的关键机制发布采用npm OIDC Trusted Publishers——无需配置NPM_TOKEN并附带签名 provenance 证明推 tag 是唯一触发条件tag 模式必须匹配opal/v*.*.*工作流自动执行构建tsup CSS barrel并运行bun publish --provenance --access public。标准发布流程在 web/lib/opal/package.json 中按语义化版本MAJOR.MINOR.PATCH允许-rc.N预发布后缀递增version提交包含版本号变更与发布相关改动的 PR 并合并在main分支打 tag 并推送git switch main git pull git tag opal/v0.1.1 git push origin opal/v0.1.1在 Actions 页签观察运行验证新版本是否出现在 npm 上onyx-ai/opal。十、代码规范与贡献约定README 的 Conventions 节定义了库内开发的硬性规范直接约束着 src/ 下的组织方式组件目录一律kebab-case如select-button/、open-button/、content-action/每个组件目录包含components.tsx、README.md、需要时提供styles.css、适用时提供PascalName.stories.tsxStorybook 故事文件库内导入一律使用opal/路径别名禁止/类型/接口声明在components.tsx顶部且不加export所有导出统一收口在文件底部的单个export { Foo, type FooProps };块中——src/core/index.ts 中Interactive复合组件的组装方式正是这一约定的体现更广泛的前端规范参见 web/AGENTS.md。十一、第三方商标说明onyx-ai/opal/logos子路径随包发布 Onyx 所集成第三方产品的品牌标志。这些商标归其各自所有者所有Onyx 不对其主张商标权详情见 web/lib/opal/NOTICE.md。结语Opal 的价值在于把设计 token、Tailwind 工具类、React 组件、构建产物四层完整地串联起来消费方只需一次样式导入加一个 preset 挂载即可获得与 Onyx 一致的设计语言与组件能力而对 Onyx 仓库本身opal/*别名与 workspace 机制保证了源码级的热联调体验。理解它的安装流程、子路径体系、_reference.css/bundle-css.mjs的构建链路以及 tag 驱动的发布模式无论你是要在自己的 Next.js 项目里接入 Opal还是想在 Onyx 前端上做二次开发都能直接落地。【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表