Linyao Design System

主題

Linyao Design System 以語意變數定義主題。所有主題共用相同的元件 CSS;亮色、深色或自訂主題只重新指定角色變數。

載入樣式

在應用程式入口匯入一次:

import "@linyao.tw/ui/styles.css";

不得從 @linyao.tw/ui/dist/**src/** 或內部 CSS 路徑匯入。公開樣式入口只有兩個:必要的 @linyao.tw/ui/styles.css,以及可選的 @linyao.tw/ui/fonts.css

字型

styles.css 不載入任何遠端資源。品牌字型是獨立的可選入口:

import "@linyao.tw/ui/fonts.css"; // 選用
import "@linyao.tw/ui/styles.css";

fonts.css 會向 font.emtech.ccfonts.gstatic.com 發出三個跨來源請求。正式環境建議自行代管相同字型,並覆寫 --font-family-sans--font-family-serif--font-family-mono 指向自己的 @font-face;設計變數本身已帶有系統字型備援,未載入 fonts.css 時元件仍可正常呈現。

不匯入 fonts.css 的理由通常是:Content Security Policy 未允許這兩個網域、離線或內網環境無法連線,或需要避免 CSS @import 造成的串行阻塞。

選擇主題

亮色是 :root 預設,也可明確指定:

<html data-lyds-theme="light"></html>

深色:

<html data-lyds-theme="dark"></html>

主題也可套在子樹:

<main data-lyds-theme="light">
	<Preview />
	<aside data-lyds-theme="dark">
		<DarkPreview />
	</aside>
</main>

一般應用程式應將主題設在文件根元素。SelectComboboxMenuTooltipDialogDatePicker 等浮層元件預設透過 portal 呈現在 body,會依 portal 目的節點的祖先元素取得設計變數。若主題只套在子樹,portal 容器也必須位於相同主題範圍。

每個主題範圍同時設定對應的 color-scheme,使瀏覽器原生控制項與 Linyao Design System 表面一致。

主題狀態

主題狀態由應用程式管理。Linyao Design System 不讀取系統偏好、不寫入 localStorage,也不建立全域 React context。

type LydsTheme = "light" | "dark";

export function AppTheme({ theme, children }: React.PropsWithChildren<{ theme: LydsTheme }>) {
	document.documentElement.dataset.lydsTheme = theme;
	return children;
}

需要跟隨系統時,應用程式可使用 matchMedia("(prefers-color-scheme: dark)")。需要保存設定時,由應用程式選擇 cookie、伺服器設定或 localStorage。

首次繪製

SSR 的伺服器標記與用戶端第一次 render 必須使用相同主題。若只能在瀏覽器決定,可由應用程式在 hydration 前設定 document.documentElement.dataset.lydsTheme。Linyao Design System 不注入此程式,因為儲存方式、CSP、nonce 與系統偏好都屬於產品設定。

淺色主題

Limestone
├─ Background/Main       暖白主背景
├─ Background/Secondary  清單與相鄰表面
├─ Background/Elevated   浮動表面
├─ Background/Inset      群組控制項表面
└─ Background/Sunken     次要表面

Charcoal
├─ Text/Title 與 Text/Main
├─ 圖示
└─ 低對比分隔線

Vermilion
├─ primary action
├─ 選取/勾選
└─ 必要的狀態強調

表面層級主要透過明度、選取底板與結構性分隔線表達。陰影只用於浮動元件或浮層元件。Vermilion 上的文字使用 OnAccent 語意值。

深色主題

  • Background/Main 約為 oklch(0.18 0.008 75) 的暖色近黑。
  • Background/SecondaryBackground/Elevated 逐層提高明度。
  • Background/InsetSunken 保留群組層級,不使用單一純黑背景。
  • Text/Main 使用 Limestone/暖色中性色系。
  • Vermilion 保留主要操作與選取角色,不加入 neon glow。
  • 細線與陰影依深色主題重新指定,不直接反轉亮色主題值。

深色色盤、語意設定與動態效果是 Linyao Design System 的設計,不是 Modulor 公開 Figma 元件庫的原始內容。

自訂主題

覆寫語意變數

使用 class 或 data attribute 覆寫語意角色:

.operations-theme {
	--background-main: oklch(88% 0.018 82);
	--background-secondary: oklch(92% 0.014 82);
	--text-main: oklch(35% 0.01 82);
	--control-primary: var(--palette-signal-600);
	--control-primary-hover: var(--palette-signal-700);
	--control-primary-pressed: var(--palette-signal-800);
	--control-on-primary: var(--palette-warm-25);
	--focus-ring: oklch(48% 0.17 32);
}
<section className="operations-theme" data-lyds-theme="light">
	<AccountSettings />
</section>

完整自訂主題必須覆寫語意角色,不能只修改 --palette-vermilion。元件不直接使用色盤變數。

使用語意角色

建議:

.billing-surface {
	--control-primary: var(--billing-action);
}

不建議:

.billing-surface .lyds-button[data-variant="primary"] {
	background: #e04010;
}

直接覆寫 .lyds-* 會略過 hover、按下、停用、深色主題與未來內部變更。若現有語意角色無法完成合理的自訂,應先提出設計變數調整,不要分支修改元件 CSS。

版面調整

classNamestyle 用於應用程式控制的位置、寬度與 grid/flex 關係:

<DatePicker className="checkout-delivery-date" label="交付日期" />
.checkout-delivery-date {
	inline-size: min(100%, 24rem);
}

不要以 style 傳入元件原始色值。需要從 React 動態傳入設計變數時,使用 CSS custom property 與可檢查的型別包裝。

主題驗證

每個主題都必須檢查:

  • 內文、標題、次要、停用與連結文字;
  • primary、secondary、quiet、danger 操作;
  • 輸入元件預設/hover/焦點/無效/唯讀/停用;
  • 選取/勾選控制項;
  • popup、dialog、drawer、bottom sheet、backdrop;
  • Toast/狀態表面;
  • 日期選取/範圍/今天/不可用狀態;
  • focus-visible、鍵盤順序與焦點返回;
  • 繁體中文與英文混合的長文字;
  • 200% zoom 與窄螢幕;
  • prefers-reduced-motion: reduce
  • 產品需要列印時的 print colors。

修改主題不應造成版面位移。語意值改變時,元件尺寸、互動目標與文字換行應保持一致。

對比責任

內建主題以 WCAG 2.2 AA 為目標。使用者覆寫主題後,必須重新驗證:

  • 前景色與合成後背景;
  • 焦點指示器與控制項/相鄰表面;
  • 非文字控制項邊界;
  • hover/按下/選取狀態;
  • 停用狀態的可讀性。

品牌原色不保證符合對比。Limestone、Charcoal 與 white 都不適合直接作為 Vermilion 上的一般尺寸文字。品牌強調表面應使用 --text-on-accent;需要近白前景的主要控制項,應同時使用較深的 --control-primary--control-on-primary