Communitygithub.com

OpenLabs-so/oa-design

Open Analytics 的設計語言,濃縮在單一檔案中,專為 AI 代理程式與人類設計師打造。

oa-design 是什麼?

此技能定義了 Open Analytics (OA) 產品完整的設計語言系統,並以單一檔案的形式呈現,方便 AI 代理程式(如 Claude Code、Cursor)直接讀取和應用。它包含了從色彩、間距、字體到動畫等所有設計 Token,並明確制定了 10 條核心設計規則,例如「單一墨水色,所有灰色由此衍生」、「使用連續曲率的方圓角(Squircle)」以及「700 無襯線字重上限」等。此技能支援 Light 與 Dark 雙主題,並詳細說明了版面佈局、按鈕、下拉選單、模態框、Skeleton 加載狀態等元件的具體實作方式。開發者只需將此檔案放入專案或 AI 代理的上下文,即可要求生成「OA 風格」的使用者介面。

相容平台✓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. 狀態用膠囊條呈現,事件自行結束。 持續存在的狀態渲染為不可關閉的條帶,其存在時間與狀態持續時間完全一致;一次性結果則以自動消失的提示訊息呈現。
  9. 文案也是設計的一部分。 使用一般句子的書寫格式;按鈕說明將執行的動作;錯誤訊息說明原因與解決方法,不帶指責語氣。
  10. 品質底線,始終如一。 聚焦環、role="status"、aria-hidden 裝飾、prefers-reduced-motion,以及無水平頁面捲動。

2. Token

淺色模式

:root {
  --ink: #292929;

  --background: #f6f6f6;     /* 盤面之間的灰色舞台 */
  --card: #ffffff;
  --popover: #ffffff;

  /* 墨水色衍生的中性色:百分比本身就是系統 */
  --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;
}

需要更明顯的分隔線?提高墨水色百分比(產品僅使用 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 檔案保留在儲存庫中)。
  • Geist Mono 用於 code, kbd, samp, pre、按鍵和程式碼片段。
  • 字重上限即為品牌識別:任何元素都不應比 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);
/* 浮動 Chrome */ 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. 版面配置

盤面。 區塊是各自獨立的面板;頁面背景在它們之間顯示,此即為唯一的邊界。垂直間距寬敞(行銷用 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 防止內容頂到視口邊緣。

網格:grid gap-3,自適應欄數(統計數字使用 grid-cols-2 sm:grid-cols-4)。卡片共享固定的內容高度,內部可捲動;網格基準線永不中斷。

響應式:以 390px 和 1280px 為設計基準。水平成對的元素在 sm: 斷點以下會垂直堆疊,且操作項在最後。寬內容在自己的 overflow-x-auto 容器內捲動;頁面永遠不會水平捲動。

設定頁面:窄標籤導覽列 + 由區段面板組成的內容欄;每個區段以一個標題區塊(標題 + 一行灰色說明文字,選擇性在右側放置一個操作)開頭。透過 ?tab=<id> 實現深層連結標籤。


4. 元件

方圓角表面

透過 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; }
}

迷你卡片

外部方圓角: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; 懸浮: 強調色底色, 箭頭向右滑動 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 高,懸浮時使用全出血的強調色底色,數值使用 tabular-nums。

按鈕

膠囊形,font-medium,按下時有物理回饋:active:translate-y-px active:scale-[0.98]。

  • 主要:強調色藍色,帶有斜角(背景/邊框顏色向深靛藍混合,頂部 inset 0 1px 0 rgba(255,255,255,0.22),底部深色內陰影);懸浮時變亮,趨向純粹的主要色。
  • 次要:扁平 --secondary,透明邊框,懸浮時混合 5% 墨水色。主要工作按鈕。
  • 幽靈:僅顯示淡出文字。危險:紅色填滿,僅在確認步驟使用。
  • 尺寸 h-9 / h-8 / h-7;應用程式 Chrome 元件大多使用小尺寸。
  • 載入狀態保留標籤,並添加一個 14px 的環形轉輪(border-2 border-current border-t-transparent);寬度永不跳動。
  • focus-visible:ring-[3px] 使用 --ring/50。永不移除。

下拉面板(選單、下拉選單、切換器)

觸發膠囊按鈕 → 浮動面板(浮動陰影、邊框、白色、rounded-2xl+),以 PANEL (550/38) 彈簧動畫搭配小幅 y/偏移縮放進入,更快速地退出。32px 高的 rounded-lg 選項,帶有強調色底色懸浮效果;啟用狀態的高亮顯示是一個共享的移動元素(使用 layoutId 或寬高彈簧),永不逐項重新繪製。點擊外部或按 Escape 鍵關閉。

模態框

背景遮罩淡入約 0.15 秒;面板以 POP (400/26) 彈簧動畫從 opacity 0, scale .96 進入,以 380/28 退出。多步驟對話框保持一個面板,其高度會根據每個步驟的測量大小(使用 LAYOUT 550/40 + ResizeObserver)進行動畫調整,同時步驟內容滑入(x: ±12px,popLayout,0.16 秒 easeOut 淡出)。頁尾:h-12 條帶,返回(次要 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="…次要膠囊按鈕…">檢查安裝</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 秒後自動消失。

提示訊息

持續時間低於 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。應用程式 Chrome 元件中沒有動畫持續時間超過 0.2 秒;如果感覺太慢,請降低阻尼,而不是延長時間。

標誌性動畫:透過測量高度進行編排(一個恆定面板,步驟內容滑入其中)、移動的高亮顯示(一個共享的啟用元素,永不逐項重新繪製)、進入/退出不對稱(退出總是更快)、懸浮效果僅改變顏色(加上最多 2px 的箭頭滑動;卡片永不浮起或縮放),以及每個畫面最多一個微小的環境循環。

規則:對所有卸載的元素使用 AnimatePresence;尊重 prefers-reduced-motion(展開效果收縮為可見,循環動畫停止);永遠不對未測量的版面進行動畫處理;永遠不要在渲染期間讀取 wall clock(應訂閱以分鐘為桶的 ticking store)。


6. 著陸頁

使用相同的 Token,但…

[為翻譯而截斷]

相關技能