dddk — Theming(CSS 變數客製)

不同網站要不同 UI 風格。dddk 全部視覺元素用 CSS 變數控制,企業在自己 site CSS 就能完全 override。

設計原則

  1. 每個視覺值都是變數 — 沒有 hardcode 顏色 / 邊角 / 字型
  2. 預設值合理 — 用戶不設定也好看
  3. 支援亮 / 暗主題 — 透過 [data-theme="dark"] 切換
  4. 不污染 host — 所有 selector 用 [data-dddk-ui] 範圍化
  5. Shadow DOM 可選 — 想徹底隔離可以開(trade-off:難 inspect)

完整變數清單

Colors(基礎色)

:root {
  --dddk-accent: #8B7355;              /* 主色:highlight / button / link */
  --dddk-accent-hover: #7A6347;        /* 主色 hover */
  --dddk-accent-active: #6A5337;       /* 主色 active */

  --dddk-bg: rgba(255, 255, 255, 0.96); /* 字幕條、palette、A2UI 底色 */
  --dddk-bg-elevated: #ffffff;          /* 卡片底 */
  --dddk-bg-overlay: rgba(0, 0, 0, 0.5); /* modal backdrop */

  --dddk-text: #1a1a1a;
  --dddk-text-muted: #6b6b6b;
  --dddk-text-on-accent: #ffffff;

  --dddk-border: rgba(0, 0, 0, 0.1);
  --dddk-border-focus: var(--dddk-accent);

  --dddk-success: #00aa66;
  --dddk-warning: #ff9900;
  --dddk-error: #cc3333;
  --dddk-info: #3388dd;
}

Spacing & sizing

:root {
  --dddk-radius: 12px;
  --dddk-radius-sm: 6px;
  --dddk-radius-lg: 16px;

  --dddk-spacing-xs: 4px;
  --dddk-spacing-sm: 8px;
  --dddk-spacing-md: 12px;
  --dddk-spacing-lg: 16px;
  --dddk-spacing-xl: 24px;

  --dddk-bar-max-width: 640px;
  --dddk-palette-width: 720px;
  --dddk-palette-max-height: 80vh;
  --dddk-modal-max-width: 560px;
  --dddk-dock-width: 360px;
}

Typography

:root {
  --dddk-font: system-ui, -apple-system, 'Segoe UI', sans-serif;
  --dddk-font-mono: ui-monospace, 'SF Mono', Menlo, monospace;

  --dddk-font-size-sm: 13px;
  --dddk-font-size-md: 14px;
  --dddk-font-size-lg: 16px;
  --dddk-font-size-xl: 20px;

  --dddk-line-height: 1.5;
  --dddk-font-weight-normal: 400;
  --dddk-font-weight-medium: 500;
  --dddk-font-weight-bold: 600;
}

Shadow & effects

:root {
  --dddk-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
  --dddk-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
  --dddk-shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.15);

  --dddk-blur: 12px;  /* backdrop-filter blur amount */
}

z-index 層級

:root {
  --dddk-z-overlay: 9000;
  --dddk-z-bar: 9500;
  --dddk-z-pointer: 9800;
  --dddk-z-palette: 9900;
  --dddk-z-modal: 10000;
  --dddk-z-toast: 10100;
}

Animation

:root {
  --dddk-transition-fast: 80ms ease-out;
  --dddk-transition-md: 200ms ease-out;
  --dddk-transition-slow: 400ms ease-out;
}

暗主題

[data-theme="dark"] {
  --dddk-accent: #d4a574;
  --dddk-bg: rgba(28, 28, 30, 0.96);
  --dddk-bg-elevated: #2c2c2e;
  --dddk-text: #ffffff;
  --dddk-text-muted: #98989d;
  --dddk-border: rgba(255, 255, 255, 0.1);
  --dddk-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
}

或用 prefers-color-scheme:

@media (prefers-color-scheme: dark) {
  :root { /* 同上 */ }
}

自動跟 host 取色

如果 host 已有自己的 design token:

:root {
  --dddk-accent: var(--brand-primary, #8B7355);
  --dddk-bg: var(--surface-1, rgba(255, 255, 255, 0.96));
  --dddk-text: var(--text-primary, #1a1a1a);
  --dddk-radius: var(--card-radius, 12px);
  --dddk-font: var(--font-sans, system-ui);
}

完整客製範例

某客戶要 brutalist 風格(方角、黑邊、亮黃):

:root {
  --dddk-accent: #ffeb3b;
  --dddk-bg: #ffffff;
  --dddk-text: #000000;
  --dddk-border: #000000;
  --dddk-radius: 0;          /* 全方角 */
  --dddk-radius-sm: 0;
  --dddk-radius-lg: 0;
  --dddk-shadow-md: 4px 4px 0 #000;  /* 立體陰影 */
  --dddk-font: 'Space Grotesk', sans-serif;
}

[data-dddk-ui="bar"],
[data-dddk-ui="palette"] {
  border: 2px solid var(--dddk-border);
}

DOM 範圍化

所有 dddk render 出來的元素都有 [data-dddk-ui] attribute:

<div data-dddk-ui="bar">           <!-- 字幕條 -->
<div data-dddk-ui="palette">       <!-- palette 容器 -->
<div data-dddk-ui="a2ui-surface"> <!-- A2UI modal -->
<div data-dddk-ui="spotter-ring"> <!-- Spotter 環 -->

要對某 UI 特別做事 → selector 用 [data-dddk-ui="bar"]

[data-dddk-ui="bar"] {
  /* 只改字幕條樣式 */
}

Shadow DOM 隔離(opt-in)

new DotDotDuck({
  isolation: 'shadow',  // 預設 'none'
});

開了 Shadow DOM:

  • ✅ host CSS 完全不會影響 dddk
  • ✅ dddk CSS 完全不會洩漏到 host
  • ❌ inspector 不好點
  • ❌ 部分 keyboard event 行為微妙
  • ❌ A2UI 自訂 catalog 元件要注意 shadowDOM 邊界

預設不開,因為 99% 場景用 CSS 變數就夠。

不同 placement 各自的客製

A2UI placement = dock 想要不同樣式:

[data-dddk-ui="a2ui-surface"][data-dddk-placement="dock"] {
  --dddk-radius: var(--dddk-radius-sm);
  width: var(--dddk-dock-width);
  height: 100vh;
  right: 0;
}

i18n 字串

不算主題,但同一概念 — 透過 config 注入:

new DotDotDuck({
  locale: 'zh-TW',
  i18n: {
    'zh-TW': {
      'voice.listening': '聽取中 — 鬆開結束',
      'voice.processing': '處理中...',
      'palette.placeholder': '搜尋指令或頁面內容',
      'subtitle.hint.accept': 'space 同意',
      'subtitle.hint.reject': '雙擊 space 拒絕',
      // ...
    },
  },
});

內建 en + zh-TW,其他語言 host 補。

字體載入

dddk 不夾帶字型。host 自己 load:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
  :root { --dddk-font: 'Inter', system-ui, sans-serif; }
</style>

測試 checklist

主題改完,host 應該檢查:

  • 亮 / 暗主題切換正常
  • 字幕條在各種背景下對比夠
  • palette 在不同畫面尺寸正常(mobile / tablet / desktop)
  • A2UI modal backdrop 不擋住 host 的 sticky 元素
  • z-index 沒跟 host 的 modal 打架(必要時調 --dddk-z-*
  • Spotter ring 在所有顏色背景下都看得到
  • 字體變數沒造成 layout shift