Communitygithub.com

OpenLabs-so/oa-design

以单个文件形式呈现的 [Open Analytics](https://getopen.so) 设计语言。

oa-design 是什么?

该技能将 Open Analytics 的设计语言浓缩为单个可移植文件,专为 AI 代理及其人类指挥者编写。它提供了完整的设计令牌系统,包括十六进制颜色代码、弹簧常量、圆角半径和缓动函数,可直接粘贴到代理的上下文中或作为规则文件添加到仓库中。用户可以通过“以 OA 风格”的指令,让代理按照统一的设计规范生成屏幕。技能本身包含了十项核心规则(例如单色中性系统、连续曲率圆角、唯一弹簧族等),以及完整的亮色和暗色主题令牌、布局指南、组件样式(如 squircles、迷你卡片、按钮、模态框、通知等)和运动曲线,确保整个产品界面的视觉和交互一致性。它同时支持 Claude Code、Cursor 和 Codex 等 AI 代理平台,并提供了在仓库中作为代理技能或在 CLI 中作为 npm 包的高级使用方式。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/OpenLabs-so/oa-design/tree/HEAD

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

DESIGN-SKILL.md

Open Analytics 的设计语言,一个文件搞定, 专为 AI 代理及其人类指挥者编写。这里的每个值都已实际应用于产品中:十六进制颜色代码、弹簧常量、圆角半径、缓动函数。将此文件粘贴到代理的上下文中(或作为规则文件放入你的仓库),然后要求生成“以 OA 风格”的屏幕。

这是专为粘贴而编译的单文件版本。同样的设计语言也作为适当的代理技能(skills/oa-design/:包含完整类型检查源代码的每个组件配方)和 CLI 工具(npx getopen-design add <component>)存在于该仓库中。对于 Claude Code 和其他能识别技能的代理,优先使用这些版本。

用一句话概括它的外观:白色表面,连续曲率圆角,安放在宁静的灰色舞台上,用单一墨色绘制,由单个弹簧驱动。


1. 十条规则

  1. 一种墨色,一切由此派生。 整个中性系统由一种颜色以固定百分比混合到透明度中生成。永远不要挑选第二种灰色。
  2. 两个图层,间隙即页面。 表面由一个白色框架容纳一个嵌入式灰色内嵌组成;区块是板块,它们之间的页面背景是唯一的分隔线。不要使用水平分割线。
  3. 圆角矩形用于表面,药丸用于操作。 卡片使用连续曲率圆角;所有非卡片的可点击元素都是药丸形状。
  4. 一个弹簧族。 七个命名的弹簧覆盖整个产品。不要发明第八个。
  5. Chrome 绝不等待。 布局和标题立即渲染;只有数据会从像素匹配的骨架中交换出来,通过模糊效果而非弹跳效果呈现。
  6. 字重最高为 500。 任何地方都不使用粗体。层级通过字号、颜色和间距来体现。
  7. 一种强调色,用在一个地方。 一种蓝色专用于主要操作和主要图表线条;语义颜色是文本色调,而非填充色。
  8. 状态用药丸表示,事件自行结束。 持续性条件渲染不可关闭的条状组件,其寿命与状态持续的时间完全一致;一次性结果则使用 Toast,它们会自行消失。
  9. 文案是设计的一部分。 使用句子大小写;按钮说明会发生什么;错误信息说明原因和解决方法,不指责。
  10. 始终保证质量底线。 焦点环、role="status"、aria-hidden 装饰、prefers-reduced-motion,以及无水平页面滚动。

2. 令牌 (Tokens)

亮色模式

:root {
  --ink: #292929;

  --background: #f6f6f6;     /* 板块之间的灰色舞台 */
  --card: #ffffff;
  --popover: #ffffff;

  /* ink 派生的中性色:百分比本身就是系统 */
  --border: color-mix(in srgb, var(--ink) 12%, transparent);
  --input:  color-mix(in srgb, var(--ink) 14%, transparent);
  --accent: color-mix(in srgb, var(--ink) 5%, transparent);  /* 悬停水洗色 */
  --muted:  color-mix(in srgb, var(--ink) 5%, transparent);
  --muted-foreground: #6d6d6d;
  --foreground: var(--ink);

  --primary: #305dde;             /* 唯一的强调色 */
  --primary-foreground: #ffffff;
  --ring: #3ba6f1;

  --secondary: #e9e9e9;           /* 平面灰色控件,无边框 */
  --secondary-foreground: var(--ink);

  /* 语义色,作为亮色模式下的文本色调(Tailwind 级别,700 行) */
  --destructive: var(--color-red-500);
  --destructive-foreground: var(--color-red-700);
  --success-foreground: var(--color-emerald-700);
  --warning-foreground: var(--color-amber-700);
  --info-foreground: var(--color-blue-700);

  --chart-1: #296FF0;             /* 主要图表线条 */
  --radius: 0.625rem;
}

需要更强的分隔线?提高 ink 百分比(产品仅使用 4、5、10、12、14)。语义色用于文字和 8px 圆点,从不用于面板填充。

暗色模式

相同的配方,重新配色,带有两个系统性的调整:线条百分比提高 2 点,语义文本从 700 行移至 400 行。

.dark {
  --ink: #ededed;
  --background: #191919;
  --card: #212121;
  --popover: #242424;
  --border: color-mix(in srgb, var(--ink) 14%, transparent);
  --input:  color-mix(in srgb, var(--ink) 16%, transparent);
  --muted-foreground: #969696;
  --secondary: #2e2e2e;
  --primary: #296FF0;
  --destructive-foreground: var(--color-red-400);
  --success-foreground: var(--color-emerald-400);
}

圆角半径

一个基础值,乘以倍数;绝不使用特制的半径:

--radius: 0.625rem;  /* sm ×0.6, md ×0.8, lg ×1, xl ×1.4,
                        2xl ×1.8, 3xl ×2.2, 4xl ×2.6 */

rounded-full 保留给药丸形状。圆角矩形表面带有自己更大的半径(请参阅第 4 节)。

排版

  • Inter Tight,可变字体,字重 300 到 500,自托管(字体 CDN 会导致构建失败;将 woff2 文件保存在仓库中)。
  • code, kbd, samp, pre 等元素、键盘快捷键和代码片段使用 Geist Mono。
  • 字重上限即身份标识:没有任何东西渲染得比 500 更重。层级通过字号、text-foreground/80 对比 text-muted-foreground 以及间距来体现。
  • 标题:font-medium tracking-tight;营销标题添加 text-wrap: balance。
  • 数据:任何在列中或就地更新的数字使用 tabular-nums。
  • 默认 UI 文本为 14px;次要文本为 12px,配合 leading-5/leading-6。

阴影

层级是二元的:

/* 静止卡片 */    box-shadow: 0 1px 2px rgba(0,0,0,0.06);
/* 浮动界面 */ box-shadow: 0 1px 2px rgba(0,0,0,0.08),
                                  0 8px 24px rgba(0,0,0,0.08);

背景规范

body 为 白色(过度滚动会显示 body 的背景;灰色从白色屏幕边缘露出来看起来像另一个页面)。需要灰色舞台的屏幕会自行绘制背景。根元素设置 overflow-x: clip。


3. 布局

板块 (Plates)。区块是自包含的面板;它们之间显示的页面背景是唯一的边界。垂直间距慷慨(营销页面 gap-12 sm:gap-20,应用内 gap-6),侧边内边距更紧凑(px-4 sm:px-6)。同级元素由父元素的 gap 隔开,绝不使用每个子元素的 margin。区块之间不使用 <hr> 或 border-b。

按作用域设定宽度:营销页面 76rem (6.5xl),应用站点页面 max-w-6xl,列表/选择器屏幕 max-w-5xl,阅读表面约 65ch。居中,mx-auto。

应用页面结构:

固定头部 (h-14) → 容器 pt-14
  可选的浮动药丸(固定定位 top-16,居中)
  <main class="px-4 pb-36 pt-8 sm:px-6 bg-white">
    标题行:h1 text-xl font-medium tracking-tight + 右侧一个控件
    可选的通知条
    板块:flex flex-col gap-6

标题行恰好包含一个控件。pb-36 确保内容不接触视口边缘。

网格 (Grids):grid gap-3,响应式列(统计数据使用 grid-cols-2 sm:grid-cols-4)。卡片共享固定的内容高度并在内部滚动;网格的基线永远不会被打断。

响应式:在 390px 和 1280px 宽度下设计。水平配对的元素在 sm: 断点下堆叠,操作项放在最后。宽内容在其自己的 overflow-x-auto 容器中滚动;页面永远不会横向滚动。

设置页面:窄选项卡导轨 + 由区块面板组成的内容列;每个区块以一个标题块开始(标题 + 一行灰色文字,右侧可选的一个操作)。通过 ?tab=<id> 深度链接到选项卡。


4. 组件

圆角矩形表面 (Squircle Surface)

通过 CSS shape() 裁剪路径实现连续曲率圆角,由两个变量参数化,并在支持 corner-shape: squircle 的地方使用该属性:

.squircle {
  border-radius: 26px;
  --card-clip-radius: 14px;
  --card-clip-handle: 2.25px;
  clip-path: shape(
    from var(--card-clip-radius) 0px,
    line to calc(100% - var(--card-clip-radius)) 0px,
    curve to 100% var(--card-clip-radius)
      with calc(100% - var(--card-clip-handle)) 0px / 100% var(--card-clip-handle),
    line to 100% calc(100% - var(--card-clip-radius)),
    curve to calc(100% - var(--card-clip-radius)) 100%
      with 100% calc(100% - var(--card-clip-handle)) / calc(100% - var(--card-clip-handle)) 100%,
    line to var(--card-clip-radius) 100%,
    curve to 0px calc(100% - var(--card-clip-radius))
      with var(--card-clip-handle) 100% / 0px calc(100% - var(--card-clip-handle)),
    line to 0px var(--card-clip-radius),
    curve to var(--card-clip-radius) 0px
      with 0px var(--card-clip-handle) / var(--card-clip-handle) 0px,
    close
  );
  corner-shape: squircle;
}
@media (min-width: 640px) {
  .squircle { border-radius: 50px; --card-clip-radius: 20px; --card-clip-handle: 3px; }
}

迷你卡片 (Mini Card)

外层圆角矩形:bg-card, border, p-1, 静止阴影
├── 头部条 (pb-2 pl-3.5 pr-2 pt-1.5):
│     16px 哑光图标 + text-sm font-medium text-foreground/80 标题
│     可选的标签,位于标题中心线上
│     “查看更多 ›” (text-xs muted; 悬停: accent wash, 箭头滑动 2px)
└── 内层圆角矩形:bg #f6f6f6, border, rounded-[22px] sm:rounded-[44px],
      固定高度 (h-44 = 五行 32px 的行), overflow hidden

卡片绝不对不对数据增长,更长的列表在内部滚动。溢出内容在右下角显示一个 24px 的 V 形图标标签,在 y:[0,2.5,0] 之间浮动(1.4 秒 easeInOut 循环),在滚动到底部时淡出(0.18s,缩放 0.7)。行:32px 高,全幅 accent 水洗色悬停效果,数值使用 tabular-nums。

按钮

药丸形状,font-medium,物理按压效果:active:translate-y-px active:scale-[0.98]。

  • primary:强调色蓝色,带有斜面效果(背景/边框色向深靛蓝混合,顶部 inset 0 1px 0 rgba(255,255,255,0.22),底部深色内阴影);悬停时变亮,趋向纯蓝色。
  • secondary:平面 --secondary,透明边框,悬停时混合 5% 的 ink。是最常用的主力按钮。
  • ghost:仅显示哑光文本。destructive:红色填充,仅在确认步骤使用。
  • 尺寸 h-9 / h-8 / h-7;应用界面主要使用小尺寸。
  • 加载状态保持标签文本,并添加一个 14px 的环形旋转器 (border-2 border-current border-t-transparent);宽度绝不跳动。
  • focus-visible:ring-[3px],颜色为 --ring/50。绝不移除。

下拉面板(菜单、选择器、切换器)

触发药丸 → 浮动面板(浮动阴影,边框,白色,rounded-2xl+),以 PANEL (550/38) 弹簧进入,带有小的 y/缩放偏移,更快的退出。32px 高的 rounded-lg 项目,带有 accent 水洗色悬停效果;活动高亮是一个共享元素,会移动(使用 layoutId 或宽度/高度弹簧),绝不会逐项重绘。点击外部或按 Escape 键关闭。

模态框

背景遮罩淡入约 0.15 秒;面板以 POP (400/26) 弹簧从 opacity 0, scale .96 进入,以 380/28 退出。多步骤对话框保持一个面板,其高度通过 LAYOUT (550/40) 弹簧和 ResizeObserver 动画到每一步的测量大小,同时步骤之间滑动切换(x: ±12px,popLayout,0.16s easeOut 淡出)。底部栏:h-12 条,返回按钮(secondary xs)+ 主要 xs 按钮,右对齐。

选项卡栏

活动药丸是一个固定窗口;标签在其后面滑动。进入的标签从移动方向以其自身宽度的 110% 进入;退出的标签向相反方向退出固定 130px(当药丸朝向更长的单词增长时,百分比会不足)。药丸的宽度/高度跟随 LAYOUT 弹簧上测量的标签。全程保持完全不透明度;通过遮罩来实现效果。

骨架屏

  1. Chrome 绝不等待;只有数据区域是骨架屏。
  2. 像素匹配:一个条形位于与其所代表文本相同的行高插槽内(flex h-6 items-center 包裹一个 h-3 条形);内容交换时不移动任何东西。
  3. 数据通过焦点效果进入:一次性的动画,从 opacity ~0.4 + blur(4px) 到清晰。一个动画,而不是过渡(内容挂载时已经是最终状态;过渡没有前一个值)。之后保持 filter: blur(0)。

条形:rounded bg-muted-foreground/15,缓慢的背景位置扫描(2 秒线性无限循环,-1s 起始偏移,使条形不同步)。

通知、空状态、浮动药丸

通知条 位于其解释的内容上方:

<div class="flex flex-col gap-2 rounded-2xl border border-border bg-card
            px-4 py-3 sm:flex-row sm:items-center sm:justify-between"
     role="status">
  <p class="text-sm leading-6 text-muted-foreground">
    <span class="font-medium text-foreground">暂无事件。</span>
    如果代码片段已启用,请浏览您的网站,我们将在几秒钟内捕获第一个页面浏览量。
  </p>
  <button class="…secondary pill…">检查安装</button>
</div>

一个粗体声明,一行灰色文字,一个次要操作;在 sm: 下堆叠;其寿命与状态持续的时间完全一致;不可关闭。

浮动药丸 用于应用范围内的持续性条件:固定定位 top-16,居中,rounded-full bg-card py-1.5 pl-3 pr-4 text-xs font-medium,浮动阴影,8px 状态圆点(紧急时脉冲),以 BANNER (400/30) 弹簧从 y:-8 进入,退出 0.15 秒。整个药丸链接到解决状态的位置。状态药丸从不自行关闭;事件药丸在大约 6 秒后消失。

Toast

持续时间为 0.35 秒,缓动函数 cubic-bezier(0.5, 1, 0.89, 1):

  • 成功:脉冲 scale 1 → 1.025 → 0.99 → 1 (0.32 秒)。
  • 错误:抖动 x 0 → -3 → 3 → -3 → 0 px (0.28 秒)。

交替使用奇/偶关键帧副本,以便重复时重新开始。


5. 动效

名称值用途
PANEL550 / 38下拉菜单、菜单、面板、切换
LAYOUT550 / 40测量后的高度/宽度、滑动标签
POP400 / 26模态框进入
POP_EXIT380 / 28模态框退出
BANNER400 / 30浮动药丸、页面横幅
FLICK900 / 50图标微观移动
CHART300 / 25-30图表工具提示、十字准线

微观淡入淡出:0.1 秒到 0.18 秒 easeOut。应用界面中没有动画持续时间超过 0.2 秒;如果感觉慢,降低阻尼,不要延长持续时间。

标志性动效:基于测量高度的编排(一个不变的面板,步骤在其间滑动)、移动的高亮(一个共享的活跃元素,绝不逐项重绘)、进入/退出不对称(退出总是更快)、悬停仅为颜色变化(加上最多 2px 的箭头滑动;卡片从不抬起或缩放),以及每屏最多一个微小的环境循环。

规则:对所有卸载的元素使用 AnimatePresence;尊重 prefers-reduced-motion(揭示内容折叠为可见,循环停止);绝不动画未经测量的布局;绝不在渲染期间读取系统时钟(订阅按分钟桶更新的时钟存储)。


6. 着陆页

相同的令牌,略……

……[内容已截断,等待翻译]

相关技能