CodeGym /課程 /ChatGPT Apps /視覺設計:主題、色彩、字體排印、間距與現成元件庫

視覺設計:主題、色彩、字體排印、間距與現成元件庫

ChatGPT Apps
等級 8 , 課堂 3
開放

1. 背景:你的 App 是 ChatGPT 家中的客人

在開始畫按鈕、挑字體之前,先接受一個現實:使用者不是打開「你的網站」,而是待在 ChatGPT 中。ChatGPT 已經有自己的:

  • 配色方案,
  • 字體與尺寸,
  • 間距與元件編排。

你的小工具會顯示在這個環境裡,多數情況下是在 iframe 中。重要結論:視覺上 App 應該像是 ChatGPT 介面的自然延伸,而不是一個從 2008 年拖來的廣告橫幅。

OpenAI 的官方指引正是如此:不要破壞系統的顏色與字體,只加入適度的品牌重點,並遵循平台的基本字體排印與間距網格。

實務上這意味著三件事。

第一,背景、基礎文字色、標準字體排印——這些都應該繼承自 ChatGPT 或系統變數,而不是「我就是藝術家、我想怎麼看就怎麼看」。

第二,如果你想要「自己的風格」,它應該集中在強調處:主要按鈕、徽章、突顯狀態。不要使用彩虹背景或自訂的 Comic Sans 字體——即使你內心很想這麼做。

第三,同一個 App 的 inline 與 fullscreen 模式在視覺上應該屬於同一個世界:相同的 CTA 顏色、相同的卡片圓角與間距、相同的字體排印。使用者不該在從 inline 切換到 fullscreen 時,感覺像換了一個產品。

接著依層次來看:顏色與主題、字體排印、間距與網格,然後談 Tailwindshadcn/ui 如何幫你把這一切組裝起來。

Insight

ChatGPT 的 sandbox 不只限制你小工具的功能,也會套用它自己的樣式。

首先 — 這是 HTML 的頂層標籤

網站原始版:

<html lang="ru">

在沙盒中:

<html lang="en-US" data-theme="light" class="light" style="--safe-area-inset-top: 0px; --safe-area-inset-bottom: 0px; --safe-area-inset-left: 0px; --safe-area-inset-right: 0px;">

其次 — 這是一組系統提供的 CSS 樣式,讓你的小工具更像 ChatGPT:

<style>
  html,body,#root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;padding:0}
  html,body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif!important}
  button,input,textarea,select{font-family:inherit}
  html{background-color:#fff}
  html.dark{background-color:#212121}
  html.mobileSkybridge.dark{background-color:#000}
  @supports (font: -apple-system-body){html.mobileSkybridge{font:-apple-system-body}}
</style>

務必記得這點——可以少遇到驚喜(踩雷)。

2. 主題與色彩:同時活在淺色與深色宇宙

淺色與深色主題

ChatGPT 介面已支援淺色與深色主題。你的小工具會被放在其中之一,且使用者可以隨時切換。因此,任何硬編碼的純白或純黑背景,都是潛在地雷。

想像一個把背景畫成白色、文字畫成黑色的小工具。在淺色主題下還算能看;在深色主題下就像拿探照燈照眼睛。相反地,黑色背景在淺色主題下也不好看。這就是為什麼官方建議不要硬編碼顏色,而是依賴主題/宿主的變數。

在 Apps SDK 的環境中,通常會提供 API 或 CSS 變數來表示當前主題。文件裡可能會出現像 window.openai.theme 以及使用 ChatGPT 的標準 CSS 變數。此外還有 prefers-color-schemeTailwinddark: 工具類別可用。

核心想法是:你的小工具應該能自動依宿主主題調整以下項目:

  • 卡片背景(比基礎背景稍亮/稍暗),
  • 文字顏色(足夠對比),
  • 邊框、陰影與 hover 狀態。

使用 Tailwind 的簡單主題包裝範例:

// components/AppShell.tsx
export function AppShell({ children }: { children: React.ReactNode }) {
  return (
    <div className="bg-background text-foreground">
      {/* bg-background/text-foreground 由主題覆寫 */}
      {children}
    </div>
  );
}

這裡的 bg-backgroundtext-foreground 並非 Tailwind 預設類別,而是你的設計系統(例如 shadcn/ui)對應到 CSS 變數的別名,並進一步綁定到 ChatGPT 的淺色/深色主題。

系統色 vs. 品牌重點色

OpenAI 說得相當直接:ChatGPT 的系統色不能改。基礎文字、標準聊天面板、背景——都應維持平台的共同色彩。你的舞台在小工具內部的重點:CTA 按鈕(call to action——主要行為)、徽章、小型元素。

在 GiftGenius 的實作中,這代表:

  • 禮物卡片的背景接近系統背景,
  • 文字使用系統預設色,就像聊天中的文字一樣,
  • GiftGenius 的品牌色用在主要的「選擇禮物」按鈕上,也可能用在折扣徽章。

可以想像如下表:

元素 建議 避免
小工具背景 繼承 ChatGPT 放上鮮明的品牌漸層
主要文字 繼承系統色 把文字弄成彩色/或灰到看不清
主要 CTA 按鈕 使用品牌重點色 在上面畫「彩虹」與 5 種顏色
次要按鈕/連結 接近系統連結樣式 做得和 CTA 一樣搶眼
陰影/邊框 柔和、極簡 厚重的螢光描邊

簡短的 Tailwind 主色設定範例:

// styles/globals.css(片段)
:root {
  --gift-accent: 222 84% 56%; /* hsl */
}

.dark {
  --gift-accent: 222 84% 64%; /* dark 模式下稍微更亮 */
}
// components/GiftButton.tsx
export function GiftButton({ children }: { children: React.ReactNode }) {
  return (
    <button className="rounded-md bg-[hsl(var(--gift-accent))] px-4 py-2 text-sm font-medium text-white hover:opacity-90">
      {children}
    </button>
  );
}

你不動整個小工具的背景,只把品牌色用在主要的 CTA 按鈕上,乾淨俐落。

對比度與 WCAG:務實為先

即使你不打算去考 WCAG,也有個簡單準則:文字必須可讀。字越小,對比越要高。無障礙課程建議主要文字的前景與背景對比維持在約 4.5:1 以上。這裡不細談標準細節;我們要的實用準則是——確保文字與背景有足夠對比。

實務上:

  • 不要為了「優雅」而用淺灰文字搭淺灰背景;
  • 避免在深色主題用近乎黑的背景上放深灰文字;
  • 至少憑直覺檢查:如果你得瞇眼,使用者也會痛苦。

可以給自己一個原則:任何次要文字(標註、提示)依然要可讀,只是比主要內容更低調一些(顏色與大小),而不是「幾乎看不見」。

3. 字體排印:系統字體、層級與一點常識

以系統字體取代「自家」字型

官方指引建議使用平台的系統字體,例如 SF Pro、Roboto 及其替代,不要塞自家 webfont。原因不只效能,還因為你的 App 應該像介面的原生元素。

在 Next.js 應用中,最簡單的方式是讓小工具中的一切繼承基礎的系統字體堆疊。Tailwind 通常已經設好 font-sans。如果你想更明確:

// app/layout.tsx(片段)
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body className="font-sans antialiased">
        {children}
      </body>
    </html>
  );
}

不需要透過 Google Fonts 連三個字型家族。對教學用的 GiftGenius 來說,嚴謹的系統字體會比像是 Lobster 這種字型更顯得俐落。

層級與字級

我們只需要幾個層級:區塊標題、副標/關鍵參數、主體文字與附註。

以 GiftGenius 的 inline 卡片為例,可以約定這樣的層級:

角色 Tailwind 類別 範例
卡片標題
text-base font-semibold
禮物名稱
關鍵參數
text-sm font-medium
價格或類別
說明
text-sm text-muted-foreground
簡短描述
附註/小字
text-xs text-muted-foreground
配送、商店

卡片小元件:

// components/GiftCard.tsx
type GiftCardProps = {
  title: string;
  price: string;
  description: string;
};

export function GiftCard({ title, price, description }: GiftCardProps) {
  return (
    <div className="rounded-lg border bg-card p-4">
      <h3 className="text-base font-semibold">{title}</h3>
      <p className="mt-1 text-sm font-medium text-emerald-600">{price}</p>
      <p className="mt-2 text-sm text-muted-foreground">{description}</p>
    </div>
  );
}

這裡:

  • 沒有巨大 H1;
  • 資訊緊湊;
  • 層級透過字級與字重清楚表達。

對齊與每行字數

聊天介面通常較窄,尤其是 inline。因此不必在複雜字體排印上糾結:靠左對齊、每行約 40–60 個字就很舒服。

實用習慣:

  • 不要把卡片中的長文置中——較難閱讀;
  • 不要全部大寫;
  • 不要把基礎文字小於 14 px(在 Tailwind 是 text-sm),除非有非常充足的理由。

拿不準時請記住:讀者很可能是坐在地鐵、用手機且已經疲憊的人,而不是你眼前的 27 吋完美螢幕。

4. 間距、密度與網格

如果顏色與字體是「顏料」,那間距就是空氣。沒有它們,再乾淨的卡片也會變成一團糟。

OpenAI 的建議強調:元素不要「黏在一起」,間距與圓角最好取自設計系統或 UI 框架(Tailwindshadcn/ui 等),並盡量避免水平捲動。

讓版面「會呼吸」的原則

最簡單的做法:使用一致的間距刻度(例如 4 px 或 8 px 為步進),不要每次自己發明尺寸。Tailwind 已內建:p-2p-3p-4gap-3 等。

GiftGenius 的 inline 禮物清單小網格:

// components/GiftListInline.tsx
export function GiftListInline({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex flex-col gap-3">
      {children}
    </div>
  );
}

每張卡片以 gap-3 分隔,內部有自己的 p-4,這樣就足以避免清單變成長長「一張床單」。

欄位數:inline 與 fullscreen

Apps SDK 的 UX 建議中,inline 小工具維持 1–2 欄卡片;fullscreen 則可在足夠寬度下使用 2–3 欄。

原因很簡單:在聊天中寬度受限,尤其行動裝置上,兩欄已經接近可讀性的臨界點;fullscreen 就能利用幾乎整個螢幕,內容可以更緊湊。

大致示意:

flowchart LR
  subgraph Inline
    A[1 欄
窄螢幕] B[2 欄
在桌機上] end subgraph Fullscreen C[2 欄
主要情境] D[3 欄
適用於網格/目錄] end

在 Tailwind 中實作 GiftGenius:

// components/GiftGrid.tsx
export function GiftGrid({ fullscreen, children }: { fullscreen?: boolean; children: React.ReactNode }) {
  const base = fullscreen ? "grid-cols-2 md:grid-cols-3" : "grid-cols-1 sm:grid-cols-2";

  return (
    <div className={`grid gap-4 ${base}`}>
      {children}
    </div>
  );
}

在 inline 模式下,手機是一欄、較寬的螢幕給兩欄;在 fullscreen 下,視寬度給 2–3 欄。

避免水平捲動

聊天天性是垂直的。使用者習慣往下捲,而不是往旁邊捲。因此:

  • 盡量讓表格與卡片在容器寬度內排好;
  • 不要對位於彈性容器內的元素設定像 width: 600px; 這種固定寬度;
  • 使用 max-w-fulloverflow-x-auto 當作「最後手段」,而不是預設。

對 GiftGenius 的卡片來說,設定 w-full,把「一列能放幾個」交由網格決定會更方便。

5. 在 ChatGPT 容器內的響應式

在一般前端你擁有完整的 viewport 控制;在 ChatGPT 中則受限:你的小工具被放在聊天容器中,容器有自己的尺寸與規則。Apps SDK 提供幾個有用的橋樑:最大高度、安全區域(safe area)、裝置類型等。

maxHeight 與垂直限制

在 inline 模式下,ChatGPT 可能限制小工具高度,以免它「吃掉」整個畫面。像 useMaxHeight() 這類 hook 可以讓你知道目前能佔用的高度,並在需要的地方加上內部捲動。

偽代碼:

// 偽代碼,非真實 API:
const maxHeight = useMaxHeight();

return (
  <div style={{ maxHeight, overflowY: "auto" }}>
    <GiftGrid>{/* ... */}</GiftGrid>
  </div>
);

如此可以避免小工具頂到螢幕下緣,導致聊天訊息「被擠到過去的某個角落」。

safeArea 與行動裝置

在行動裝置上,上下可能有鏡頭缺口、狀態列、系統面板。Apps SDK 允許取得 safeArea,並調整間距,避免內容被手機的「瀏海」遮住。

在 CSS 層級可以加上額外 padding:

// 偽代碼
const { top, bottom } = useSafeArea(); // 假設回傳 { top: 8, bottom: 16 }

return (
  <div style={{ paddingTop: top, paddingBottom: bottom }}>
    {/* 內容 */}
  </div>
);

這堂課更重要的是原則:小工具必須尊重最大高度與安全區域,否則 UX 會瞬間變成「再多捲三下才看得到按鈕」。

6. Tailwind 與 shadcn/ui:別重新發明按鈕

現在要用純 CSS 手寫完整 UI 幾乎是硬核運動。在 ChatGPT Apps 的脈絡下,拿現成的元件庫並依平台要求調整會輕鬆許多。本課程以 Tailwindshadcn/ui 作為基礎堆疊。

Tailwind 是間距與色彩的字典

Tailwind 提供一組很好用的工具類別:

  • 間距(p-4gap-3),
  • 尺寸(text-smtext-base),
  • 顏色(text-muted-foregroundbg-card),而在 shadcn/ui 等系統中,這些已經綁定到主題的 CSS 變數。

這與 ChatGPT 的要求完美契合:

  • 你不會臨時編造間距;
  • 一致地設定文字大小;
  • 不會破壞系統色,而是使用事先協調好的代幣。

shadcn/ui 作為一組俐落的元件

shadcn/ui(與同類庫)提供現成的 CardButtonInputTabs 等,並已設定為使用 Tailwind 主題。這大幅加速了乾淨、極簡介面的拼裝,特別適合 GiftGenius 的卡片。

使用 shadcn/ui 的 GiftCard 範例:

// components/GiftCardShadcn.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";

type GiftCardProps = {
  title: string;
  price: string;
  description: string;
};

export function GiftCardShadcn(props: GiftCardProps) {
  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">{props.title}</CardTitle>
      </CardHeader>
      <CardContent className="space-y-2">
        <p className="text-sm font-medium text-emerald-600">{props.price}</p>
        <p className="text-sm text-muted-foreground">{props.description}</p>
        <Button className="mt-2">選擇禮物</Button>
      </CardContent>
    </Card>
  );
}

重點不在 shadcn 本身,而在這些原則:

  • 標題不浮誇;
  • 說明文字可讀;
  • 按鈕遵循共同設計系統,而不是「走自己的路」。

針對 ChatGPT 的調整

在真實專案中,你可以把調色盤調成更貼近 ChatGPT 的極簡風格:淺色背景、柔和陰影、恰到好處的圓角。課程建議直接依賴既有的設計系統,而不是打造另一個宇宙。

簡單做法:

  • 採用 shadcn/ui 作為基底;
  • 保留系統字體;
  • primaryaccent 代幣中設定一到兩個品牌色;
  • 確保 inline 與 fullscreen 都使用同一組代幣。

如此便能用最少的力氣取得一致的視覺核心。

7. GiftGenius 的視覺語言:把一切整合起來

讓我們系統化整理一下,對於假想的 GiftGenius,可以稱之為「視覺語言」的內容。

第一,配色。背景與文字繼承 ChatGPT;重點色低調但明顯,應用在 CTA 按鈕,可能也用在折扣徽章。深色主題下,重點色稍微更亮以維持對比。

第二,字體排印。基礎系統字體,text-sm 作為主要文字,text-base 作為卡片標題。斜體與全大寫很少用,且只在必要時使用。fullscreen 向導中的標題再往上調一級,但仍不需要誇張的 text-4xl

第三,間距與網格。inline 模式下,禮物清單為一到兩欄,使用 gap-3/gap-4,每張卡片 p-4。fullscreen 模式下為 2–3 欄,向導各步驟間的表單與按鈕間距要充足。主要情境不使用水平捲動。

GiftGenius 的畫面小示意:

graph TD
  A[Inline:禮物清單] --> B[GiftCard
色彩/字體/CTA] A --> C[GiftGrid 1-2 欄] D[Fullscreen:選擇精靈] --> E[步驟 1
表單] D --> F[步驟 2
篩選/範圍] D --> G[步驟 3
確認] B --> H[GiftButton
品牌重點色]

第四,與宿主環境的相容性。所有元素在切換淺色/深色主題時行為合理,尊重 maxHeight,不被 safe-area 遮住。顏色不與 ChatGPT 衝突,而 CTA 按鈕在各處外觀一致,讓使用者靠肌肉記憶就知道該點哪裡。

這樣的一套決策,已足以把你的應用拿去展示給不只工程師,還有真實使用者或 PM——討論焦點不再只剩下「這邊是 MCP,那邊是 Agents SDK」。

8. 無障礙(Accessibility Guidelines,WCAG AA)

在第 2.3 節談文字與背景對比時,我們已提過 WCAG。當時重點是實用準則——不要犧牲可讀性。現在把視角放寬:對看不見畫面的使用者,以及 ChatGPT 的語音模式來說,同一個介面是什麼樣子。

WCAG AA 是國際標準 WCAG(Web Content Accessibility Guidelines) 中的一個無障礙等級,說明如何讓網站與介面能被不同視覺、動作能力、認知特徵的使用者所使用。

WCAG AA 的主旨是把介面從「理論上可用」變成真正可用。它涵蓋數十條直接影響互動品質的要求,包括我們提過的文字與背景對比約 4.5:1,以及點擊區域大小、焦點狀態、表單錯誤處理等。

另一個層面是支援無障礙技術,包含螢幕閱讀器(screen reader)。AA 等級要求正確語意:標題要是標題、清單要是清單、按鈕要是按鈕,互動元件要有正確的角色與替代文字。如此一來,使用 VoiceOver、TalkBack 或 NVDA 的使用者才能完整理解結構與意義。

Screen reader(螢幕閱讀器)

Screen reader(螢幕閱讀器)是一種程式,會把內容唸出來並/或結構化螢幕內容,讓視覺受限的人能使用電腦、手機或網頁應用。

但 screen reader 並不只是「把文字唸出來」的軟體。它是一整套與介面互動的系統,會把網站或應用的視覺呈現轉換成可被理解的語音結構化導覽。

ChatGPT、screen reader 與 WCAG AA

如果你的小工具遵循 WCAG AA 的語意標註(正確的角色、標題、按鈕標籤),它就不只讓 screen reader 能理解,也讓 ChatGPT 的語音模式更好用。使用者用聲音和 ChatGPT 對話,而模型可依相同的語意結構「虛擬地」做跟人一樣的事:找到對的介面元素、按按鈕、開連結,等等。

依據 ChatGPT Store 的要求,支援 WCAG AA強制性的。每個小工具與每個 tool 都要有高品質、具體的描述,並以符合 WCAG AA 的語意來編排:正確的語意、可讀的標籤、可預期的狀態。

因此,WCAG AA 不是一個「只給特別需求者的額外功能」,而是基本的設計原則,好讓 ChatGPT Apps 能完整地與你的應用互動——包含使用者以語音模式交流時。

關於 voice-UX(語音體驗)、語音對話與文字對話的差異,以及 ChatGPT Store 的要求,我們會在本模組的其他課與 App 發佈模組再談。但一切都建立在你剛看到的基礎上:語音模式 = 多模態 + 無障礙(WCAG AA + 螢幕閱讀器)

9. ChatGPT App 視覺設計常見錯誤

錯誤 №1:硬編碼白/黑背景與文字顏色。
開發者把背景畫成白、文字畫成黑,完全沒考慮深色主題。淺色主題或許還行,深色主題就像探照燈,整個 UX 會壞掉。更好的做法是使用系統色與宿主主題(CSS 變數、prefers-color-scheme 或 Apps SDK 的 API),而自家顏色只用於重點。

錯誤 №2:過度激進的品牌化。
出現鮮豔漸層背景、自訂字體、花俏邊框。小工具開始看起來像促銷橫幅,而不是 ChatGPT 介面的一部分。指引剛好相反:極簡、「原生」的外觀,只在關鍵元素(例如主要按鈕)中使用克制的品牌色。

錯誤 №3:沒有字體排印的層級。
所有文字同一大小與字重,或反過來——在小卡片上塞三個層級的標題,還全大寫。使用者搞不清什麼是重點:名稱、價格,還是描述。更好的方式是事先約定 3–4 個層級,並一致使用:標題、關鍵參數、主體文字、附註。

錯誤 №4:元素擠在一起,沒有間距。
卡片彼此緊貼、文字緊挨邊界、按鈕緊靠文字。桌機上或許還能忍,手機上就變成雜訊。建議使用統一的間距刻度(例如 Tailwind 的 p-4gap-3),別省空氣。

錯誤 №5:在 inline 模式硬塞 4–5 欄。
開發者腦中還停留在電商頁面,於是在聊天中擺出四欄狹長卡片。寬螢幕上也許勉強,手機上根本不可讀,還多了水平捲動。inline 小工具通常一到兩欄就夠;第三欄留給 fullscreen 吧。

錯誤 №6:忽略高度限制與 safe-area。
小工具畫出巨大的清單,沒有內部捲動,也不理會 maxHeight,結果按鈕「沉到螢幕底下」。或元素被行動裝置的缺口遮住。應該使用最大高度與安全區域的資訊,妥善分配內部高度與間距。

錯誤 №7:inline 與 fullscreen 的按鈕與卡片樣式不一致。
inline 的按鈕是綠色且圓角,fullscreen 的按鈕卻是藍色且直角。使用者會失去「是一個產品」的感覺。應該把按鈕與卡片的基礎樣式抽到共同的元件/主題中,在所有模式都重用。

錯誤 №8:「作者風」字體與裝飾花招。
載入沉重的 webfont「為了更美」,破壞了與 ChatGPT 的視覺一致性,有時還拖累效能。平台建議使用系統字體與俐落的字體排印。如果真的想展現設計品味——不如在圖示與 microcopy 上用功,而不是發動字體革命。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION