Polar 前端开发指南:从 Orbit Box 设计系统到 monorepo 工作流的完整实战
2026/9/16 21:07:41 网站建设 项目流程

Polar 前端开发指南:从 Orbit Box 设计系统到 monorepo 工作流的完整实战

【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar

导读

本文是 Polar 开源仓库(clients/AGENTS.md)沉淀的Frontend Development Guide的完整展开版。它面向两类读者:一是需要在clients/monorepo 中新增页面、组件或修复缺陷的贡献者,二是想了解"如何用设计令牌驱动的类型安全原语取代 Tailwind 手写类"的 React 前端工程师。读完本文,你将掌握 Polar 客户端的日常命令工作流、以<Box />为核心的 Orbit 设计系统用法(含设计令牌、响应式与伪状态语法、常见布局配方),以及 TanStack Query / Zustand / react-hook-form 的数据与表单模式,并看到这些约定在源码中的落点。


1. 总览:一个以 Next.js 为骨架的前端 monorepo

Polar 的客户端代码集中在clients/目录,技术栈为Next.js + TypeScript + TanStack Query + Tailwind CSS(正在被 Orbit 取代)。整体结构如下(与文档中的 Project Structure 对应):

clients/ ├── adapters/ # 已发布的框架与鉴权适配器(nextjs、nuxt、tanstack-start、better-auth 等) ├── apps/ │ ├── web/ # 主 Next.js 应用 │ │ └── src/ │ │ ├── app/ # App Router 页面 │ │ │ ├── (main)/ # 主导航布局(dashboard、组织页) │ │ │ └── (public)/ # 公开页面 │ │ └── hooks/ # React hooks │ ├── app/ # iOS / Android 应用(Expo / React Native) │ └── orbit/ # Orbit 设计系统的文档与展示站点 ├── packages/ │ ├── ui/ # 共享 UI 组件(含遗留的 Card、Banner) │ ├── client/ # 自动生成的 API 客户端(src/v1.ts 已提交) │ ├── checkout/ # 结账流程包 │ └── orbit/ # Polar 设计系统:组件 + 设计令牌

apps/web/src/app/下的路由分组清晰:(main)/承载 dashboard 与组织页面,(public)/承载公开页面(实际源码中还包括(checkout)/(embed)/等分组,见 apps/web/src/app)。新页面开发时,应放入正确的路由分组,并在apps/web/src/app/(main)/dashboard/的现有布局基础上扩展。


2. 快速命令:Polar 客户端的日常操作手册

所有命令都是clients/下的根脚本(由 clients/package.json 定义),统一用 pnpm 执行:

pnpm dev # 启动开发服务器(默认 http://127.0.0.1:3000) pnpm build # 生产构建 pnpm lint # 全仓库 oxlint 检查 —— 干净时无任何输出 pnpm format:check # oxfmt --check,CI 门禁;覆盖 Markdown。`pnpm exec oxfmt <file>` 可修复 pnpm test # turbo 并行跑 workspace 内测试(12 个包定义了 test) pnpm typecheck # turbo 并行跑 tsc(15 个包定义了 typecheck) pnpm generate # 重新生成 API 客户端(位于 packages/client)

2.1 命令背后的几个"省时间"细节

  • 冷安装很慢是正常的pnpm install会通过prepare钩子执行一次完整的turbo run build --filter='./packages/*'(见 clients/package.json 中的prepare脚本),所以冷安装约需 2.5 分钟而非 1 分钟。turbo.jsontest依赖^build,这是包测试前必须先构建的原因。
  • 务必用--filter缩小范围pnpm test --filter webpnpm typecheck --filter @polar-sh/orbit。不加 filter 的pnpm test会跑满 4 核容器并引发 5 秒的 vitest 超时误报。
  • pnpm test的隐藏依赖:它包含packages/cli(其测试需要bun运行时)、apps/app(jest)以及adapters/nuxt(会构建一个 Nuxt fixture)。
  • 单测不需要后端:单元测试既不需要运行中的后端,也不需要.env.local。apps/web/vitest.config.ts 在env中直接注入NEXT_PUBLIC_API_URLNEXT_PUBLIC_FRONTEND_BASE_URLNEXT_PUBLIC_SANDBOX_FRONTEND_BASE_URLNEXT_PUBLIC_*变量,并引入 StyleX 的 Babel 插件支持测试 JSX。只有test:e2e(Playwright)需要完整可用的环境。
  • pnpm generate的副作用:它会进入 server 的 Python 环境运行scripts.generate_openapi,因此需要server/.jwks.json和 email-renderer 二进制这两个会阻塞 import 的后端产物。日常开发很少需要它,因为生成的 packages/client/src/v1.ts 已经提交到仓库。

2.2 功能完成后清单

每完成一个功能,务必运行pnpm lint,检查你的改动是否引入了新的错误或警告,修复后再算完成。


3. UI 编写铁律:一切新 UI 用<Box />

所有新 UI 必须使用@polar-sh/orbit/Box导出的<Box />编写。

<div>+ Tailwind 类名在布局、间距、颜色、边框、圆角、阴影、flex、grid、position 等视觉关注点上已被弃用。Box 是一个多态(polymorphic)、完全类型安全的基础原语,把 Orbit 设计令牌作为一等 props 使用——没有 className 猜测,没有亮/暗模式样板代码(令牌自动解析),设计系统已定义的东西不允许再用任意值。

严格执行的规则:

  • 新组件一律写 Box,不用<div>做视觉容器。
  • 修改既有 Tailwind 组件时,优先在同一次改动中迁移到 Box,而不是继续堆 Tailwind。
  • 绝不使用裸色值 hex/oklch、裸 px 间距或dark:变体——一律用令牌。
  • Box 上已有类型化 prop(padding、background、radius 等)的属性,绝不退回用className
  • 排版使用@polar-sh/orbit<Text />,而不是 Tailwind 的文本类。
  • Tailwind 仅限三种场景:第三方组件覆盖(className 是唯一 API)、Orbit 尚不支持的临时动画、迁移遗留文件时的临时胶水。

4.<Box />组件深度解析

4.1 它到底做了什么

Box 在构建期把你的类型化 props 编译为StyleX 样式 + 作用域 CSS。从 Box.tsx 源码 可以看到实现要点:

  • 组件内部用useId()生成一个ds...作用域类名;
  • 把传入 props 按BOX_STYLE_PROP_KEYS集合拆分为样式 props 与 DOM props;
  • 样式 props 交给resolveBoxStyles()解析为 StyleX 结果、内联样式与响应式 CSS;
  • 响应式场景会额外渲染一段<style>标签(responsiveCSS);
  • DOM props(如onClickhtmlForaction)原样透传给底层元素。

令牌使用 CSS 的light-dark()函数在亮/暗模式间自动切换,所以一次样式编写,暗色模式免费

4.2display默认是flex

约 90% 的块级元素用法都是 flex,所以裸<Box>就是一行 flex row。spanlabel(内联元素)与li(list-item)会保留原生 display,不破坏语义——这正是源码中NON_FLEX_DEFAULT_ELEMENTS集合(['span', 'label', 'li'])的作用(见 Box.tsx)。传显式display(如display="block"display="grid")可覆盖默认值:

<Box flexDirection="column" gap="m">…</Box> // 已是 flex <Box display="block">…</Box> // 显式退出 flex

4.3 导入方式

import { Box } from '@polar-sh/orbit/Box'

Box深路径导入@polar-sh/orbit/Box),不从包根导出——这与GridTextButton等从@polar-sh/orbit根导出不同(见 packages/orbit/src/index.ts)。

4.4 通过as实现多态

Box 默认渲染<div>,但底层元素可通过as选择,以照顾语义与无障碍。允许的值:

;'div' | 'span' | 'section' | 'article' | 'aside' | 'main' | 'nav' | 'header' | 'footer' | 'form' | 'fieldset' | 'label' | 'ul' | 'ol' | 'li'
<Box as="section" padding="xl">…</Box> <Box as="ul" flexDirection="column" rowGap="s"> <Box as="li">Item</Box> </Box> <Box as="nav" alignItems="center" columnGap="m">…</Box>

所选元素的 DOM props 都有类型并会被转发(如label上的htmlForform上的action、任意元素上的onClick)。


5. 设计令牌:两层级架构与完整取值表

令牌分为两层:原始值层语义层

  • 原始值层在 packages/orbit/src/tokens/value.stylex.ts:定义字面颜色、字号、间距、圆角、阴影、断点、时长与缓动曲线。源码注释明确指出,这是唯一允许出现字面色值与字号的层级,"gray500只是一个颜色,不是 secondary text"。
  • 语义层在 packages/orbit/src/tokens/semantics.stylex.ts:定义带意图的名字(background-primarytext-secondary等),每一个值都引用原始层而非持有字面量,通过light-dark(lightValue, darkValue)成对解析(见 semantics.stylex.ts 源码)。

Box 只接受令牌名,不接受裸值。注意:修改这两份令牌文件需要删除clients/apps/web/.next缓存并重启 dev server(源码注释中明确提示)。

5.1 间距令牌(SpacingToken)

用于 padding、margin、gap。刻度会镜像出负数令牌(-xs-5xl)供 margin 与偏移使用;padding 和 gap 只接受正数令牌(PositiveSpacingToken),因为负数在 CSS 中非法(这一约束在 value.stylex.ts 类型定义中强制):

Token
none0
xs4px
s8px
m12px
l16px
xl24px
2xl32px
3xl48px
4xl64px
5xl96px

5.2 颜色令牌(ColorToken)

用于backgroundColorcolorborderColor。每个令牌自动解析亮/暗两套取值:

Token用途
background-primary页面背景
background-secondary分区/凸起表面
background-card卡片/内嵌面板表面
background-warning警告表面
background-success成功表面
background-danger危险表面
text-primary主要文字
text-secondary次要文字
text-tertiary提示、注释、占位符
text-success成功文字
text-danger危险文字
text-warning警告文字
border-primary默认边框与分隔线
border-secondary微妙/次级分隔线
border-warning警告边框

补充:语义层还定义了background-inversebackground-accenttext-disabledtext-accent等令牌(见 semantics.stylex.ts),可在需要反色、强调或禁用态时直接使用。

5.3 圆角 / 阴影 / 动效 / 断点

圆角(BorderRadiusToken):nones(8)、m(12)、l(16)、xl(32)、full(9999)。

阴影(ShadowToken):nonesmlxl(具体 shadow 定义见 value.stylex.ts)。

动效—— 时长(DurationToken)与缓动(EasingToken):

时长缓动曲线用途
instant0msstandard通用、对称
fast120msdecelerate进入(快 → 稳定)
base200msaccelerate退出(稳定 → 快)
slow320msspring轻微过冲
slower480ms

时长是 CSS 变量,因此动效可全局调节(或为 reduced-motion 归零);缓动是编译期常量(cubic-bezier曲线在 value.stylex.ts 中定义)。

断点(BreakpointKey):sm(640)、md(768)、lg(1024)、xl(1280)。作为响应式 prop 对象的键使用。


6. Prop 参考:完整速查表

每个 prop 都接受单个令牌/值,或一个响应式对象(见下一节)。以下是文档中的完整 prop 清单,可直接作为编码时的速查表。

间距(令牌驱动;括号内为别名):

padding (p), paddingTop (pt), paddingRight (pr), paddingBottom (pb), paddingLeft (pl), paddingHorizontal (px), paddingVertical (py) margin (m), marginTop (mt), marginRight (mr), marginBottom (mb), marginLeft (ml), marginHorizontal (mx), marginVertical (my) // margin 令牌还接受 'auto' 与 // 负令牌:'-xs' … '-5xl' gap (g), rowGap, columnGap

颜色(令牌驱动):backgroundColorcolorborderColor

边框

borderRadius, borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadius // BorderRadiusToken borderWidth, borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth // 数值(px) borderStyle: 'solid' | 'dashed' | 'dotted' | 'none'

阴影boxShadow— ShadowToken。

布局

display: 'flex' | 'grid' | 'block' | 'inline' | 'inline-flex' | 'inline-block' | 'none' | 'contents' // 块级元素默认 'flex'(见 4.2) overflow / overflowX / overflowY: 'hidden' | 'auto' | 'scroll' | 'visible' width, height, minWidth, maxWidth, minHeight, maxHeight: string | number // 数字 → px aspectRatio: string // 如 '16 / 9'

Flex

flex, flexDirection ('row'|'column'|'row-reverse'|'column-reverse'), flexWrap ('wrap'|'nowrap'|'wrap-reverse'), flexGrow, flexShrink, flexBasis, alignItems / alignSelf ('start'|'end'|'center'|'baseline'|'stretch' [+ 'auto' 仅 alignSelf]), justifyContent ('start'|'end'|'center'|'between'|'around'|'evenly'), alignContent ('start'|'end'|'center'|'between'|'around'|'evenly'|'stretch')

Grid

gridTemplateColumns, gridTemplateRows, gridColumn, gridRow, gridAutoFlow ('row'|'column'|'dense'|'row-dense'|'column-dense'), gridAutoColumns, gridAutoRows

Position

position: 'relative'|'absolute'|'fixed'|'sticky'|'static' top, right, bottom, left, inset: SpacingToken | string | number // 令牌可为负('-l') zIndex: number | string

Motion(过渡;配合伪状态 props 实现 hover/focus/active 动画):

transitionProperty: 'none'|'all'|'common'|'colors'|'opacity'|'shadow'|'transform' transitionDuration: DurationToken // 'instant'|'fast'|'base'|'slow'|'slower' transitionTimingFunction (别名: ease): EasingToken // 'standard'|'decelerate'|'accelerate'|'spring' transitionDelay: DurationToken transform: string // 如 'translateY(-2px)'、'scale(1.02)' transformOrigin: string willChange: string

transitionProperty关键字会展开为真实属性列表——colors→ color + background-color + border-color;common→ colors + box-shadow + opacity + transform。未指定transitionProperty时,transitionDuration作用于all

Visual

opacity: number cursor: 'pointer'|'default'|'not-allowed'|'grab'|'grabbing'|'text'|'move'|'wait' pointerEvents: 'none'|'auto' visibility: 'visible'|'hidden' userSelect: 'none'|'text'|'all'|'auto' textAlign: 'left'|'center'|'right'|'justify'

7. 响应式与伪状态:一套语法搞定断点与交互

任何样式 prop 都可以接受一个对象,键为:

  • base:移动优先的默认值(无条件生效);
  • 断点键sm/md/lg/xl:编译为 min-width 媒体查询;
  • 伪状态键hoverfocusactivefocusVisiblefocusWithin:生成作用域化的伪类规则。

伪状态到选择器的映射(:hover:focus:active:focus-visible:focus-within)定义在 packages/orbit/src/utils/resolvers.ts 的PSEUDO_SELECTOR_MAP中,响应式值类型则见 packages/orbit/src/utils/types.ts。

<Box flexDirection={{ base: 'column', md: 'row' }} padding={{ base: 'l', lg: '2xl' }} gridTemplateColumns={{ base: '1fr', md: 'repeat(2, 1fr)', xl: 'repeat(4, 1fr)', }} backgroundColor={{ base: 'background-card', hover: 'background-secondary' }} cursor={{ hover: 'pointer' }} />

可以自由混用——base是无条件值,断点键是最小宽度媒体查询,伪状态键生成作用域伪类规则。


8. 常见布局配方(可直接复制)

垂直堆叠

<Box flexDirection="column" rowGap="l"> … </Box>

水平一行、居中、带间距

<Box alignItems="center" columnGap="m"> … </Box>

卡片表面

<Box borderRadius="l" backgroundColor="background-card" borderWidth={1} borderStyle="solid" borderColor="border-primary" padding="xl" flexDirection="column" rowGap="m" > <Text variant="heading-xs" as="h3"> Title </Text> <Text color="muted">Description</Text> </Box>

可交互卡片(平滑 hover)——伪状态 props 配合 transition,动画是平滑过渡而非跳变:

<Box borderRadius="l" backgroundColor={{ base: 'background-card', hover: 'background-secondary' }} boxShadow={{ base: 's', hover: 'm' }} transform={{ hover: 'translateY(-2px)' }} transitionProperty="common" transitionDuration="fast" ease="decelerate" cursor={{ hover: 'pointer' }} padding="xl" > … </Box>

响应式网格——优先用Grid原语(见第 9 节),它默认display: grid且使用短 prop 名:

<Grid templateColumns={{ base: '1fr', md: 'repeat(2, 1fr)', xl: 'repeat(4, 1fr)' }} gap="l" > {items.map((item) => ( <Card key={item.id} {...item} /> ))} </Grid>

吸顶工具栏

<Box position="sticky" top={0} zIndex={10} backgroundColor="background-primary" borderBottomWidth={1} borderStyle="solid" borderColor="border-primary" paddingHorizontal="xl" paddingVertical="m" alignItems="center" justifyContent="between" > … </Box>

加载骨架屏

<Box height={128} borderRadius="m" backgroundColor="background-card" className="animate-pulse" />

空状态

<Box flexDirection="column" alignItems="center" justifyContent="center" paddingVertical="3xl" rowGap="l" > <Text color="muted">No items found</Text> <Button variant="secondary">Create First Item</Button> </Box>

错误表面

<Box borderRadius="m" backgroundColor="background-warning" borderWidth={1} borderStyle="solid" borderColor="border-warning" padding="l" > <Text>{error.message}</Text> </Box>

9.className/style逃生舱与Grid原语

9.1 逃生舱:允许,但别滥用

Box 接受classNamestyle,用于设计系统之外的东西:动画 keyframes、第三方工具类、CSS Grid template areas 等。不要用它重新实现类型化 prop 已覆盖的内容——这正是这个原语要杜绝的失败模式。如果你发现自己想写className="bg-…"className="p-…",说明你用错了 Box。

当类型化 prop 缺失时:先打开 clients/packages/orbit/src/utils/types.ts 确认——大多数需求都有覆盖。如果确实缺少某个 CSS 属性,优先扩展BoxStyleProps(必要时带令牌支持),而不是走 Tailwind 逃生舱,并在 PR 中说明。

9.2Grid:Box 的 CSS grid 预设

Grid默认display: grid,把 grid 属性以短名称(Chakra 风格)重新暴露;其余所有 Box prop(gappadding、颜色、响应式对象等)都被继承(实现见 packages/orbit/src/components/Grid.tsx,其本质是给 Box 传display={inline ? 'inline-grid' : 'grid'}并映射短 prop 名):

import { Grid } from '@polar-sh/orbit' <Grid templateColumns="repeat(3, 1fr)" gap="m"> … </Grid> // areas + 响应式 <Grid templateAreas={{ base: '"head" "main"', md: '"head head" "nav main"' }} templateColumns={{ base: '1fr', md: '200px 1fr' }} gap="l" />

Prop 名:templateColumnstemplateRowstemplateAreasautoFlowautoRowsautoColumnscolumnrow,以及inline(渲染inline-grid)。

需要子元素显式跨列/放置时用GridItem

import { Grid, GridItem } from '@polar-sh/orbit' ;<Grid templateColumns="repeat(4, 1fr)" gap="m"> <GridItem colSpan={2}>跨两列</GridItem> <GridItem colStart={3} colEnd={5} rowSpan={2}> 显式放置 </GridItem> <GridItem area="sidebar">按模板区域</GridItem> </Grid>

GridItemprops:colSpan/rowSpan(数字或"auto")、colStart/colEnd/rowStart/rowEndarea——全部支持响应式,外加所有 Box prop。

9.3 其他 Orbit 原语

有现成原语时优先使用,不要手搓 Tailwind 组件:

import { Text } from '@polar-sh/orbit' // 排版(variant 驱动) import { Button, Grid } from '@polar-sh/orbit' import { Avatar, SegmentedControl } from '@polar-sh/orbit' import { Alert } from '@polar-sh/orbit' // 着色提示(info/warning/danger/success) import { ButtonGroup } from '@polar-sh/orbit' // 一或两个 primary/ghost 操作

Alert接受variantinfo|warning|danger|success,默认info)、title和可选description;variant 抽象了图标与所有颜色。它还支持loading(图标换成 spinner)、onDismiss(渲染关闭按钮)和actions(一或两个ButtonGroupCTA,渲染在右下角)。ButtonGroup接受最多两个{ text, onClick, loading?, disabled? }组成的actions元组——第一个渲染为 primary 按钮,第二个为 ghost 按钮。

原则:需要完全控制时用Box;当某个用例已有命名原语时优先用它(任何文本节点用Text,操作用Button,网格布局用Grid)。


10. 遗留 Tailwind:已弃用的模式

这些模式遍布旧代码,但新代码中严禁使用

  • <div className="…">做布局/间距/颜色;
  • dark:变体——Orbit 颜色令牌自动解析;
  • 硬编码颜色名,如bg-blue-500text-gray-500dark:bg-polar-800
  • rounded-xlshadow-lgp-4gap-2等——请用 Box props + 令牌。

编辑遗留文件时,把该文件(或紧邻的组件)迁移到 Box,而不是扩大 Tailwind 的使用面。


11. 数据获取与状态管理模式

11.1 TanStack Query:查询模式

import { useQuery } from '@tanstack/react-query' import { api } from '@/utils/api' const useProducts = (organizationId: string) => { return useQuery({ queryKey: ['products', organizationId], queryFn: () => api.products.list({ organizationId }), enabled: !!organizationId, }) } // 组件内使用 const { data: products, isLoading, error } = useProducts(orgId)

api来自@/utils/api,底层是对 packages/client/src/v1.ts 生成的类型安全 API 客户端的封装。enabled: !!organizationId保证在组织 ID 就绪前不发请求。

11.2 变更模式

import { useMutation, useQueryClient } from '@tanstack/react-query' const useCreateProduct = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: (data: ProductCreate) => api.products.create(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['products'] }) }, }) }

变更成功后使相关查询键失效,让列表自动刷新——这是 TanStack Query 缓存一致性的标准做法。

11.3 Zustand 状态管理

import { create } from 'zustand' interface AppState { sidebarOpen: boolean toggleSidebar: () => void } const useAppStore = create<AppState>((set) => ({ sidebarOpen: true, toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), }))

11.4 react-hook-form 表单处理

import { useForm } from 'react-hook-form' const MyForm = () => { const form = useForm({ defaultValues: { name: '', email: '' }, }) return ( <form onSubmit={form.handleSubmit(onSubmit)}> <Input {...form.register('name')} /> {form.formState.errors.name && ( <span className="text-sm text-red-500"> {form.formState.errors.name.message} </span> )} </form> ) }

注意:表单错误提示中的text-sm text-red-500属于遗留 Tailwind 写法,新代码应改用<Text />与颜色令牌。

11.5 常见状态模式:加载 / 空 / 错误

// 加载中 if (isLoading) { return ( <Box height={128} borderRadius="m" backgroundColor="background-card" className="animate-pulse" /> ) } // 空状态 if (!data?.length) { return ( <Box flexDirection="column" alignItems="center" justifyContent="center" paddingVertical="3xl" rowGap="l" textAlign="center" > <Text color="muted">No items found</Text> <Button variant="secondary">Create First Item</Button> </Box> ) } // 错误处理 if (error) { return ( <Box borderRadius="m" backgroundColor="background-warning" borderWidth={1} borderStyle="solid" borderColor="border-warning" padding="l" > <Text>{error.message}</Text> </Box> ) }

12. 导入规范:Orbit 优先,@polar-sh/ui兜底

// Orbit(优先——设计系统原语) import { Box } from '@polar-sh/orbit/Box' import { Text, Button, Avatar, SegmentedControl, Input, TextArea, } from '@polar-sh/orbit' import { DataTable, Select } from '@polar-sh/orbit' // 遗留 @polar-sh/ui(仅在 Orbit 无对应物时使用) import { Card } from '@polar-sh/ui/components/atoms/Card' import { Banner } from '@polar-sh/ui/components/molecules/Banner'

13. 国际化(i18n)约定

翻译文件位于packages/i18n/src/locales/。新增可翻译字符串时,只添加到en.ts(packages/i18n/src/locales/en.ts),不要手动编辑其他语言文件。CI 作业会自动把新增英文串翻译成所有支持语言并提交回分支。推送en.ts的改动后,等 CI 翻译作业完成再拉取分支即可。


14. 源码参考索引

以下路径是深入理解本指南各主题的第一手资料(均为仓库根目录相对路径):

  • Box 组件实现:clients/packages/orbit/src/components/Box.tsx
  • Box prop 类型(BoxStyleProps):clients/packages/orbit/src/utils/types.ts
  • 样式解析器(含伪状态映射):clients/packages/orbit/src/utils/resolvers.ts
  • 原始值令牌(间距/颜色/圆角/阴影/断点/动效):clients/packages/orbit/src/tokens/value.stylex.ts
  • 语义令牌(语义色与排版角色):clients/packages/orbit/src/tokens/semantics.stylex.ts
  • Orbit 桶导出:clients/packages/orbit/src/index.ts
  • Grid/GridItem实现:clients/packages/orbit/src/components/Grid.tsx
  • 遗留 Card:clients/packages/ui/src/components/atoms/Card.tsx
  • 全局样式:clients/apps/web/src/styles/globals.css
  • Dashboard 布局:clients/apps/web/src/app/(main)/dashboard//dashboard/)
  • 生成的 API 客户端:clients/packages/client/src/v1.ts
  • 根脚本定义:clients/package.json

结语

Polar 客户端的这套前端规范,核心思想可以概括为三点:用设计令牌消灭魔法值(裸色值、裸 px、dark:变体一律禁用)、用类型安全原语取代 className 拼接<Box />的多态、响应式与伪状态语法把布局和交互写成可验证的 props)、用 monorepo 工具链保证一致性(turbo 并行、oxlint 门禁、生成式 API 客户端与自动化的 i18n 流水线)。对于任何正在把 Tailwind 项目演进为设计系统驱动的前端团队,这份指南的取舍思路都值得借鉴;对于 Polar 的贡献者,它则是开工前的必读手册。

【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询