iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
自我挑戰組

30 天打造 Accessible UI Kit:從 shadcn/ui 到自己的 Design System系列 第 28 篇

Day 28: Legacy 實戰:讓 Legacy 頁面真正用上 CUI

  • 分享至 

  • xImage
  •  

昨天 Day 27,我們終於開始處理 CUI 的另一條輸出路線:CDN Build。

原本只能在 React 專案裡使用的 UI Kit,現在開始有了可以提供給一般 HTML 頁面的 CSS:

dist/
└── cdn/
    ├── cui.css
    └── cui.min.css

我們也重新整理了 CUI 的架構:

                   CUI
                    │
             Design Tokens
                    │
             Component Contract
                    │
        ┌───────────┴───────────┐
        ↓                       ↓
      React                   Legacy
        │                       │
 React Components           HTML + CSS
        │                       │
        └───────────┬───────────┘
                    ↓
              Shared Styling

不過,成功產生 CSS 檔案,只代表建置流程可以運作。

真正重要的是:

這份 CSS 能不能在一個完全沒有 React 的網站裡正常使用?

例如一個已經運作多年的網站,可能有自己的 CSS、JavaScript、表單與資料表格。

我們不可能為了使用一套 UI Kit,就要求整個網站重新開發。

因此今天要實際建立一個傳統 HTML 頁面,模擬既有網站導入 CUI 的情境。

看看我們前面建立的 Design Tokens、Component Contract 和 Accessibility Rules,能不能真正跨越 Framework 的限制。


1. 今天要完成什麼?

今天的目標不是再增加新的 React Component。

而是驗證:

CUI 是否能在沒有 React、TypeScript 與前端建置工具的頁面中正常運作?

預計完成:

  1. 建立 Legacy HTML 測試頁面。
  2. 載入 Day 27 產生的 CDN CSS。
  3. 整合 Button、Input、Alert、Table。
  4. 驗證原本網站的 CSS 不會受到意外影響。
  5. 測試表單語意、鍵盤操作與無障礙。
  6. 討論 Tooltip、Dialog 等互動元件的處理方式。

今天先以一般 HTML、CSS 與少量 Vanilla JavaScript 完成整合。


2. 建立 Legacy 測試環境

首先建立一個測試目錄:

examples/
└── legacy/
    ├── index.html
    ├── legacy.css
    └── legacy.js

其中:

index.html
→ 模擬既有網站的頁面

legacy.css
→ 模擬網站原本的樣式

legacy.js
→ 模擬原本的互動程式

另外,我們會載入 Day 27 的產物:

dist/cdn/cui.min.css

整體結構:

CUI Project
│
├── src/
│   ├── components/
│   └── styles/
│
├── dist/
│   └── cdn/
│       ├── cui.css
│       └── cui.min.css
│
└── examples/
    └── legacy/
        ├── index.html
        ├── legacy.css
        └── legacy.js

這個測試頁面不需要引入 React。

也不需要執行 Vite 才能理解它的元件結構。

它就是一般瀏覽器可以讀取的 HTML。


3. 先建立一個原本就存在的網站

為了驗證 CUI 的相容性,我們先模擬一個有自己樣式的舊網站。

例如:

<!doctype html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">

  <meta
    name="viewport"
    content="width=device-width, initial-scale=1"
  >

  <title>Legacy Web Demo</title>

  <link
    rel="stylesheet"
    href="./legacy.css"
  >
</head>

<body>
  <main class="legacy-container">
    <h1>申請資料管理</h1>

    <p>
      這是一個既有網站的測試頁面。
    </p>

    <button type="button">
      原本的按鈕
    </button>
  </main>
</body>
</html>

再建立:

/* legacy.css */

body {
  margin: 0;
  font-family: Arial, sans-serif;
  background: #f5f5f5;
}

.legacy-container {
  max-width: 960px;
  margin: 40px auto;
  padding: 24px;
}

button {
  padding: 8px 16px;
  border: 1px solid #999;
  border-radius: 4px;
  background: #eee;
}

這裡故意保留:

button {
  ...
}

這種比較廣泛的 Selector。

因為很多既有網站都可能有類似的全域樣式。

我們希望測試:

當 CUI CSS 加進來之後,原本的按鈕會不會突然變成 CUI Button?


4. 載入 CUI CSS

接著在 HTML 裡加入:

<link
  rel="stylesheet"
  href="../../dist/cdn/cui.min.css"
>

完整的載入順序:

<link
  rel="stylesheet"
  href="./legacy.css"
>

<link
  rel="stylesheet"
  href="../../dist/cdn/cui.min.css"
>

這樣 CUI CSS 就能在頁面裡使用。

不過要注意,CSS 載入順序會影響 Cascade。

如果兩份 CSS 都設定了同一個屬性,瀏覽器會依照來源、Importance、Cascade Layer、Specificity 與順序決定最終樣式。

因此不能只認為:

CUI CSS 放在最後面,就一定不會發生衝突。

實際上仍然要檢查 Selector 與 CSS Cascade。


5. 先加入 CUI Button

昨天我們已經建立了 Button 的公開 Contract:

<button
  type="button"
  data-cui-slot="button"
  data-cui-variant="primary"
  data-cui-size="md"
>
  儲存
</button>

這裡最重要的是:

data-cui-slot="button"

它告訴 CUI:

這個元素要使用 CUI Button 的樣式。

而:

data-cui-variant="primary"

代表:

Primary Variant

data-cui-size 則負責尺寸。

這樣 HTML 不需要知道:

Tailwind Utility Classes
CVA
React Props

只需要遵守 CUI 的 Component Contract。


6. 為什麼要使用 data-cui-*?

假設我們直接寫:

button {
  background: var(--cui-primary);
}

就會影響整個頁面的 Button。

但如果改成:

[data-cui-slot="button"] {
  ...
}

就只會套用在明確使用 CUI 的元素。

例如:

<button type="button">
  原本的按鈕
</button>

<button
  type="button"
  data-cui-slot="button"
  data-cui-variant="primary"
  data-cui-size="md"
>
  CUI Button
</button>

預期:

原本的按鈕
→ 維持原有樣式

CUI Button
→ 使用 CUI 樣式

這讓既有網站可以採取漸進式導入。

不需要一次把所有按鈕都替換掉。

不過,原本網站的全域 CSS 仍然可能影響 CUI Button,因此 data-cui-* 是降低衝突的方法,而不是完全隔離 CSS 的保證。


7. 檢查 Button 的樣式衝突

例如 Legacy CSS:

button {
  padding: 8px 16px;
  background: #eee;
}

CUI CSS:

[data-cui-slot="button"] {
  padding: 10px 16px;
  background: var(--cui-primary);
}

在相同來源層級、相同 Importance 的情況下,CUI 的 Attribute Selector 通常具有較高的 Specificity。

因此可以覆蓋一般的元素 Selector。

但如果原本網站有:

.legacy-container button.primary {
  background: red;
}

或者:

button {
  background: red !important;
}

就可能發生其他覆蓋情況。

所以今天不能只看 Button 有沒有出現。

還要打開瀏覽器 DevTools,檢查:

Computed Styles
Matched CSS Rules
Specificity
Overridden Declarations

確認最後套用的是預期的 CUI 樣式。


8. 加入 Input 與 Label

接著來處理表單。

我們在前面已經做過:

Field
├── Label
├── Input
├── Description
└── Error

但 Legacy 不會使用:

<Field>
  <FieldLabel />
  <Input />
</Field>

而是直接寫 HTML。

例如:

<div data-cui-slot="field">
  <label
    for="applicant-name"
    data-cui-slot="field-label"
  >
    申請人姓名
  </label>

  <input
    id="applicant-name"
    name="applicantName"
    type="text"
    data-cui-slot="input"
    required
  >
</div>

這裡保留原生 HTML:

label
input
required

因為這些本來就具有語意與瀏覽器行為。

CUI 只負責補上:

Styling
Component Contract

不應該用 JavaScript 重新模擬原生 Input。


9. 表單的 Accessible Name

這裡值得再次強調。

假設只有:

<input
  type="text"
  placeholder="請輸入姓名"
>

這不是理想的 Label 寫法。

因為 Placeholder 不應該取代真正的 Label。

所以我們使用:

<label for="applicant-name">
  申請人姓名
</label>

<input
  id="applicant-name"
  name="applicantName"
  type="text"
>

這樣可以建立明確的關聯。

而且不需要額外加:

aria-label="申請人姓名"

來重複相同資訊。

因為原生 HTML 已經能完成這件事。

這也是 CUI 一直強調的原則:

Semantic HTML First。


10. 加入 Description

如果欄位需要補充說明,可以使用:

<div data-cui-slot="field">
  <label
    for="contact-email"
    data-cui-slot="field-label"
  >
    電子郵件
  </label>

  <input
    id="contact-email"
    name="email"
    type="email"
    aria-describedby="contact-email-description"
    data-cui-slot="input"
  >

  <p
    id="contact-email-description"
    data-cui-slot="field-description"
  >
    請輸入常用的電子郵件地址。
  </p>
</div>

這裡的:

aria-describedby="contact-email-description"

會將 Input 與說明文字關聯起來。

因此 Screen Reader 使用者在操作欄位時,也有機會取得相關補充資訊。


11. 加入 Validation Error

接著模擬表單驗證失敗。

例如:

<div
  data-cui-slot="field"
  data-invalid="true"
>
  <label
    for="applicant-email"
    data-cui-slot="field-label"
  >
    電子郵件
  </label>

  <input
    id="applicant-email"
    name="email"
    type="email"
    value="abc"
    aria-invalid="true"
    aria-describedby="applicant-email-error"
    data-cui-slot="input"
  >

  <p
    id="applicant-email-error"
    data-cui-slot="field-error"
  >
    請輸入有效的電子郵件地址。
  </p>
</div>

這裡同時有:

Visual State
→ data-invalid

Accessibility State
→ aria-invalid

Error Relationship
→ aria-describedby

不過要注意:

這只是在 HTML 裡展示錯誤狀態。

如果要在實際提交後動態顯示錯誤,仍然需要表單驗證邏輯更新:

Error Message
aria-invalid
aria-describedby

不能只把欄位邊框改成紅色。


12. 加入 Alert

表單通常會有成功或失敗訊息。

例如:

<div
  data-cui-slot="alert"
  data-cui-variant="success"
>
  <p data-cui-slot="alert-title">
    儲存成功
  </p>

  <p data-cui-slot="alert-description">
    申請資料已更新。
  </p>
</div>

這裡使用:

data-cui-variant="success"

對應 CUI 的 Semantic Status。

但這個 Attribute 只負責 CUI 的樣式 Contract。

它不會自動讓 Screen Reader 宣告訊息。

如果訊息是在操作後動態出現,我們還需要考慮:

role="status"

例如:

<div
  role="status"
  data-cui-slot="alert"
  data-cui-variant="success"
>
  申請資料已儲存。
</div>

不過實際使用時,要避免每個 Alert 都無條件設定 Live Region。

因為:

一般靜態資訊

和:

操作後需要宣告的狀態變化

並不完全相同。


13. 加入 Table

接著加入資料列表。

例如:

<table data-cui-slot="table">
  <caption>
    申請資料列表
  </caption>

  <thead data-cui-slot="table-header">
    <tr data-cui-slot="table-row">
      <th scope="col">
        申請人
      </th>

      <th scope="col">
        申請項目
      </th>

      <th scope="col">
        狀態
      </th>
    </tr>
  </thead>

  <tbody data-cui-slot="table-body">
    <tr data-cui-slot="table-row">
      <td>王小明</td>
      <td>資料申請</td>
      <td>
        <span
          data-cui-slot="badge"
          data-cui-variant="success"
        >
          已完成
        </span>
      </td>
    </tr>
  </tbody>
</table>

這裡直接使用:

table
caption
thead
tbody
th
td

而不是用一堆 div 模擬 Table。

因為 HTML Table 本身就有成熟的語意結構。

CUI 的 CSS 只需要讓它符合 Design System 的視覺規範。


14. Table 不只是畫出格線

如果只考慮視覺,我們可能會覺得:

<div class="row">
  <div>申請人</div>
  <div>申請項目</div>
</div>

也可以做出表格。

但真正的 Table 需要讓使用者理解:

Column Header
Row
Cell

因此在適合使用資料表格的情境下,原生 <table> 通常是比較合理的選擇。

例如:

<th scope="col">
  申請人
</th>

這樣就能明確標示欄標題。

這也證明:

CUI 的 Accessibility 不應該只存在於 React Component 裡。

即使換成一般 HTML,語意規範仍然要保留。


15. Responsive Table 怎麼處理?

接著會遇到一個常見問題。

桌面上的 Table:

申請人 | 申請項目 | 狀態 | 操作

可能很正常。

但手機螢幕比較窄。

如果直接讓表格超出畫面,就可能造成水平溢出。

所以可以使用:

<div data-cui-slot="table-container">
  <table data-cui-slot="table">
    ...
  </table>
</div>

搭配:

[data-cui-slot="table-container"] {
  max-width: 100%;
  overflow-x: auto;
}

讓表格在必要時可以水平捲動。

不過要注意,這不是所有 Table 的唯一解法。

如果資料非常複雜,也可能需要重新思考手機版的資訊呈現方式。

今天先以保留原生 Table 語意為優先。


16. 讓 Legacy 頁面真的有互動

目前我們已經有:

Button
Input
Alert
Table

但如果按下:

儲存

完全沒有反應,就還不能算是一個完整的整合示範。

所以接著加入少量 Vanilla JavaScript。

例如:

<form id="application-form">
  <label for="applicant-name">
    申請人姓名
  </label>

  <input
    id="applicant-name"
    name="applicantName"
    data-cui-slot="input"
    required
  >

  <button
    type="submit"
    data-cui-slot="button"
    data-cui-variant="primary"
    data-cui-size="md"
  >
    儲存
  </button>
</form>

<div
  id="save-status"
  role="status"
  aria-atomic="true"
></div>

接著:

const form = document.querySelector("#application-form");
const status = document.querySelector("#save-status");

form.addEventListener("submit", (event) => {
  event.preventDefault();

  if (!form.reportValidity()) {
    return;
  }

  status.textContent = "資料已通過驗證。";
});

這個示範沒有真的把資料送到伺服器。

所以我們不能直接顯示:

儲存成功

而是先用:

資料已通過驗證

確認原生表單行為與 CUI 樣式可以一起運作。


17. JavaScript 不應該破壞原生表單行為

這裡還有一個重要原則。

例如:

<button type="submit">
  儲存
</button>

本身就有原生 Submit Behavior。

我們不需要把它改成:

<div onclick="submitForm()">
  儲存
</div>

因為這樣反而需要額外處理:

Keyboard
Focus
Role
Accessible Name

所以 Legacy 整合時,能使用原生 HTML,就優先使用。

CUI 的目標不是把原生能力全部重新實作一次。


18. Tooltip 可以只靠 CSS 嗎?

接著來看比較麻煩的元件。

Day 23 我們做了 Tooltip。

它需要考慮:

Hover
Focus
Escape
Pointer Movement

如果只用 CSS:

.trigger:hover .tooltip {
  display: block;
}

看起來可以完成基本效果。

但它不一定能完整處理:

Escape Dismiss
Keyboard Focus
Tooltip Relationship
Pointer Hover Persistence

所以對 CUI 來說,不應該把一個只有 Hover 的提示框,直接宣稱成完整的 Accessible Tooltip。

如果只是提供重要說明文字,很多時候直接顯示在頁面上反而更好。

而真正需要 Tooltip 的情境,未來可以由 Vanilla JavaScript Adapter 處理。


19. Dialog 可以使用原生 <dialog>

相比 Tooltip,Dialog 有一個很值得考慮的原生 HTML 元素:

<dialog>

例如:

<button
  type="button"
  id="open-dialog"
  data-cui-slot="button"
  data-cui-variant="outline"
>
  查看說明
</button>

<dialog
  id="help-dialog"
  aria-labelledby="help-dialog-title"
  data-cui-slot="dialog-content"
>
  <h2 id="help-dialog-title">
    使用說明
  </h2>

  <p>
    請確認資料後再送出申請。
  </p>

  <button
    type="button"
    id="close-dialog"
    data-cui-slot="button"
    data-cui-variant="secondary"
  >
    關閉
  </button>
</dialog>

搭配:

const dialog = document.querySelector("#help-dialog");

document
  .querySelector("#open-dialog")
  .addEventListener("click", () => {
    dialog.showModal();
  });

document
  .querySelector("#close-dialog")
  .addEventListener("click", () => {
    dialog.close();
  });

原生 <dialog> 搭配 showModal() 已經具備不少 Modal 基礎能力。

例如:

Top Layer
Background Inertness
Modal Focus Behavior
Escape Close

不過還是要實際確認:

Accessible Name
Initial Focus
Focus Return
Close Behavior

不能只因為用了原生 <dialog>,就認為所有情境都已經處理完成。


20. 互動元件要不要做成 cui.js?

這裡就會碰到架構上的選擇。

我們現在有:

cui.min.css

未來是否需要:

cui.js

讓 Legacy 開發者可以直接使用:

<script src="/assets/cui.js"></script>

我覺得可以,但需要先定義範圍。

例如:

CUI JavaScript Adapter
│
├── Dialog
├── Tooltip
└── Other Interactive Components

不過它不應該只是把 React Component 的 JavaScript 打包後直接丟給 Legacy。

因為 React Component 仍然依賴 React Runtime 與其元件模型。

比較合理的方式是:

Shared Contract
       │
       ├── React
       │    └── Base UI
       │
       └── Legacy
            └── Vanilla JS Adapter

兩邊遵守相同的 Accessibility Requirements,但使用各自適合的實作方式。


21. 不要讓 JavaScript Adapter 變成另一套 UI Kit

這裡有一個風險。

如果我們在 Legacy Adapter 裡重新定義:

Button Variants
Dialog Styles
Tooltip Colors

就很容易變成:

React UI Kit
+
Legacy UI Kit

兩套系統各自發展。

這就偏離 CUI 原本的目標了。

所以 JavaScript Adapter 應該盡量只負責:

Behavior
State
Focus Management
Event Handling

而:

Design Tokens
Visual Styles
Component Contract

仍然來自 CUI 的共用定義。

這樣才能維持 Single Source 的精神。


22. CSS-only 和 Interactive Components 分開處理

經過今天的測試,可以把 Legacy 支援分成兩類。

Component Legacy 使用方式 額外 JavaScript
Button HTML + CUI CSS 一般按鈕不需要
Badge HTML + CUI CSS 不需要
Alert HTML + CUI CSS 靜態訊息不需要
Input HTML + CUI CSS 原生輸入不需要
Table HTML + CUI CSS 靜態表格不需要
Separator HTML + CUI CSS 不需要
Tooltip HTML + CSS + Behavior 通常需要
Dialog 原生 <dialog> + CUI CSS 需要開關控制
Select 原生 <select> + CUI CSS 原生選單不需要

這樣也讓我們知道,Legacy 支援不一定要一開始就把所有互動元件完整移植。

可以先從最常見、最穩定的 HTML 元件開始。


23. 既有網站導入時,最容易遇到什麼問題?

我認為主要有幾個方向。

第一,CSS 衝突。

原本網站可能有:

button {}
input {}
table {}

甚至還有大量使用 !important 的樣式。

所以需要檢查 CUI 的 Selector 是否能維持預期效果。

第二,HTML 結構不同。

React Component 可以控制內部 DOM。

但 Legacy HTML 是由開發者自行撰寫。

如果結構不符合 Contract,樣式或語意就可能不正確。

第三,JavaScript 衝突。

原本網站可能已經有自己的 Event Listener、Dialog 或其他互動程式。

所以不能隨便讓 CUI 自動接管所有元素。

第四,Accessibility 落差。

CUI 提供的 CSS 不會自動修正原本 HTML 的語意問題。

例如:

<div data-cui-slot="button">
  送出
</div>

即使樣式正確,也不等於它具有原生 Button 的操作能力。


24. 建立 Legacy Integration Checklist

今天可以先整理一份檢查清單。

CSS Compatibility

  • [ ] CUI CSS 可以正常載入
  • [ ] Design Tokens 正確生效
  • [ ] 原本網站的元素沒有被意外改變
  • [ ] CUI Button、Input、Alert、Table 樣式正確
  • [ ] Hover、Focus、Disabled State 正常
  • [ ] 沒有不必要的全域 CSS Reset

HTML Semantics

  • [ ] Button 使用原生 <button>
  • [ ] Input 具有正確 Label
  • [ ] Error Message 與欄位正確關聯
  • [ ] Table 使用正確的表格結構
  • [ ] Alert 的語意符合使用情境

Keyboard

  • [ ] Tab 順序合理
  • [ ] Button 可以使用 Enter / Space 操作
  • [ ] Focus Indicator 清楚
  • [ ] Dialog 可以使用鍵盤操作
  • [ ] 沒有 Keyboard Trap

Responsive

  • [ ] 手機版內容沒有意外溢出
  • [ ] Form 在窄螢幕下仍可使用
  • [ ] Table 可以適當捲動
  • [ ] 放大頁面後仍能操作

25. 回頭看 Single Source, Multiple Outputs

經過今天的整合,我們可以重新檢查 CUI 的架構。

                     CUI
                      │
              Shared Contract
                      │
         ┌────────────┼────────────┐
         ↓            ↓            ↓
    Design Tokens   Styles     Accessibility
         │            │         Requirements
         └────────────┼────────────┘
                      │
              ┌───────┴───────┐
              ↓               ↓
            React           Legacy
              │               │
          Components       HTML + CSS
              │               │
           Base UI         Native HTML
              │               │
              └───────┬───────┘
                      ↓
              Consistent Usage

這裡的 Consistent Usage,不是指兩邊的程式碼完全一樣。

而是:

相同的語意原則
相同的 Design Tokens
相同的 Component Contract
相同的 Accessibility Requirements

這才是我們真正希望做到的事情。


26. 今天的測試方式

完成整合後,先執行:

npm run build:cdn

確認 CDN CSS 已經更新。

接著啟動一個靜態檔案伺服器,例如:

npx http-server . -p 8080

再開啟:

http://localhost:8080/examples/legacy/

這樣就能以一般網站的方式載入測試頁面。

比直接使用 file:// 開啟,更接近實際部署情境。

接著測試:

Mouse
Keyboard
Responsive
Screen Reader

並透過 DevTools 確認 CSS 是否正確載入與套用。


27. 最後執行專案檢查

先確認 CDN Build:

npm run build:cdn

再執行:

npm run build
npm run lint
npm run check:contrast

最後:

git status
git diff

今天的 Branch 可以使用:

git switch -c feat/legacy-integration

完成後:

git add .
git diff --staged
git commit -m "feat: add legacy HTML integration example"

這樣我們就有了一個可以持續測試的 Legacy Integration Example。


Day 28 完成

今天我們終於讓 CUI 真正離開 React。

不再只是:

<Button variant="primary">
  儲存
</Button>

而是可以在一般 HTML 裡使用:

<button
  type="button"
  data-cui-slot="button"
  data-cui-variant="primary"
  data-cui-size="md"
>
  儲存
</button>

而且我們不只確認了 CSS 能不能顯示。

也開始檢查:

CSS Compatibility
Semantic HTML
Keyboard Navigation
Responsive Behavior
Accessibility

這次的整合也讓我更確定:

UI Kit 的可重用性,不只是 Component 能不能被 Import,而是它能不能在不同環境裡維持一致的設計與使用規範。

React 有 React 的優勢。

傳統 HTML 也有原生 HTML 的優勢。

CUI 不需要強迫所有環境使用同一種技術。

而是提供一套可以跨環境遵守的 Contract。

這樣即使是沒有 React 的網站,也能逐步使用 CUI 的 Design Tokens、Component Styles 與 Accessibility Guidelines。

當然,目前還不是所有元件都已經具備完整的 Legacy 支援。

尤其是 Tooltip、Dialog 等互動元件,仍然需要進一步設計與驗證。

但至少今天,我們已經完成一個重要的里程碑:

CUI 不再只是 React UI Kit,而是開始具備跨環境使用的能力。


Next:Day 29

到目前為止,CUI 已經有:

React Components
Storybook
Accessibility Testing
CDN CSS
Legacy HTML Example

這些成果主要都是為了讓開發者更容易使用 UI Kit。

但現在還有另一種使用者。

AI。

例如我們在使用 AI Coding Assistant 時,可能會請它:

幫我建立一個表單

幫我新增一個 Dialog

幫我建立資料列表

但 AI 不一定知道:

CUI 有哪些元件?

哪些 Variants 可以使用?

Field Error 應該怎麼處理?

Dialog 的 Focus Management 有哪些要求?

React 和 Legacy 的使用方式有什麼不同?

如果沒有提供明確的規範,AI 很可能會自行產生另一套 Button、Input 或 Dialog。

所以明天,我們要把前面建立的 Component Contract 與 Accessibility Rules,整理成 AI 可以閱讀與遵守的文件。

Day 29:

AI Skill:讓 AI 也能正確使用 CUI。


上一篇
# Day 27:CDN Build:讓 CUI 不只存在於 React
下一篇
Day 29: AI Skill:讓 AI 也能正確使用 CUI
系列文
30 天打造 Accessible UI Kit:從 shadcn/ui 到自己的 Design System 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言