HKChat · V3

HKChat Style Guide

Every piece of the HKChat interface, and when to use it.

1 · Start

How to read this

You do not need to be a designer or a developer to use this page. If you are making anything that carries the HKChat brand, this tells you which pieces to use.

Every entry has four parts
One sentenceWhat the thing is.
LookThe real component, exported straight from Figma.
StatusEvery state it can be in.
UsageWhen to use it, and when not to.
Three words you will see
TokenA saved setting. Instead of "purple" we say primary/default. Change it once, everything follows.
ComponentA reusable piece, like a button.
StateA version of a piece. Resting, pressed, switched off.
Where the truth lives Design: Figma 港話通HKChat V2.1, page ⚠️V2R2, section 📤 STYLE GUIDE EXPORT.
Code: Storybook — the source of truth for anything shipping.
If this page and Storybook disagree, Storybook is right.
Sections 6 and 7 are exploration Input bar, Agent button and Widget are not settled. They are here so the team can argue about them, not so anyone can build from them. Everything else is agreed.

2 · Foundation

Colour

Colour carries meaning. It is never decoration.

Brand

primary/defaultthe HKChat purple
primary/pressedwhile held down
primary/containersoft purple fill
secondary/defaultAI actions
tertiary/defaultrare, informational

Text and icons

foreground/defaultmain text
foreground/mutedcaptions, timestamps
foreground/subtleplaceholders, decoration
foreground/lighttext on colour

Feedback

feedback/successit worked
feedback/errorit failed, or it destroys

Each has an -on partner for text placed on top. Always use the partner.

Backgrounds

surface/defaultthe page
container/defaultthings on the page
container/mutedquieter fill
container/subtlequieter still

The full spectrum

157 raw colours behind the named ones. Never use these directly — they carry no meaning and do not change with dark mode.

Use the named token, never a raw colour code. Green means success. Red means error or danger — nothing else.
Never invent a shade because one looks nicer. Never use colour as the only signal.

Text

Six styles, in three languages — 繁體, 简体 and English.

Look
Six styles × three languages. The grey line above each sample is the style name.
TC
TC/H1 · 22 Bold

港話通 · 香港人的 AI 助手

TC/H2 · 16 Medium

港話通 · 香港人的 AI 助手

TC/Body · 14 Regular

港話通 · 香港人的 AI 助手

TC/Label · 14 Bold/SemiBold

港話通 · 香港人的 AI 助手

TC/Caption · 12 Regular

港話通 · 香港人的 AI 助手

TC/XS · 8 Regular

港話通 · 香港人的 AI 助手

SC
SC/H1 · 22 Bold

港话通 · 香港人的 AI 助手

SC/H2 · 16 Medium

港话通 · 香港人的 AI 助手

SC/Body · 14 Regular

港话通 · 香港人的 AI 助手

SC/Label · 14 Bold/SemiBold

港话通 · 香港人的 AI 助手

SC/Caption · 12 Regular

港话通 · 香港人的 AI 助手

SC/XS · 8 Regular

港话通 · 香港人的 AI 助手

ENG
ENG/H1 · 22 Bold

The quick brown fox

ENG/H2 · 16 Medium

The quick brown fox

ENG/Body · 14 Regular

The quick brown fox

ENG/Label · 14 Bold/SemiBold

The quick brown fox

ENG/Caption · 12 Regular

The quick brown fox

ENG/XS · 8 Regular

The quick brown fox

Status
StyleSizeUse for
H122px BoldPage titles. One per screen.
H216px MediumSection headings.
Body14px RegularReading text. Large buttons.
Label14px Bold / SemiBoldEmphasis at body size.
Caption12px RegularLabels, timestamps, small buttons.
XS8px RegularTiny metadata only.

Each exists three times — TC · SC · ENG. Pick the one matching the language on screen.

These are the mobile baseline, not fixed numbers One control scales the whole ramp — two steps down, two up, on a 1.125 ratio. The same factor serves the user's font-size setting and larger screens.
Floor: never render below ~11px. Touch targets and spacing do not scale with the font.
Use Caption 12px by default. Reach for Label before a bigger size.
Never a size not in the table. No 13px, no 15px, no 18px.

Shape

Three corner roundings. That is the whole system.

Look
8
16
999
A real card
all three roundings at once
科技財經
card 16 · tags 8 · button 999
Status
RoundingGoes on
radius/sm 8Small things inside other things — tags, tabs, agent buttons, SnackToast.
radius/md 16Things that hold other things — cards, inputs, sheets, widgets.
radius/full 999Buttons and anything pill shaped.
Never a number that is not 8, 16 or 999. If you see one, it is a bug.

Space

Gaps come from a fixed ladder, and everything is a multiple of 4.

Look
space/1
4px · icon to its label
space/2
8px · inside small buttons
space/3
12px · between related items
space/4
16px · card padding, screen edge margin, and the gap between two buttons
space/6
24px · between groups
space/8
32px · between sections
space/12
48px · big breaks
space/24
96px · very rare, empty states

Fullsize single & double button spec

What matters here is the margin from the screen edge and the gap between buttons — not the button's own width.

Look
Two buttons sit 8px apart. What changes across sizes is the edge margin — 16, 24, 96 — never the button.
Mobile 390 · edge margin 16 — Single
今晚七點同小明食飯,加入日程
HKChat · AI Insight
分享對話
Mobile 390 · edge margin 16 — Double
今晚七點同小明食飯,加入日程
HKChat · AI Insight
取消分享對話
Tablet 768 · edge margin 24 — Single
今晚七點同小明食飯,加入日程
HKChat · AI Insight
分享對話
Tablet 768 · edge margin 24 — Double
今晚七點同小明食飯,加入日程
HKChat · AI Insight
取消分享對話
Expanded 1200 · edge margin 96 — Single
今晚七點同小明食飯,加入日程
HKChat · AI Insight
分享對話
Expanded 1200 · edge margin 96 — Double
今晚七點同小明食飯,加入日程
HKChat · AI Insight
取消分享對話
Status
ScreenWidthEdge marginGap between two
Mobile390 → 76716 each side16
Tablet768 → 1199column caps, centred16
Desktop1200 and upcolumn caps, centred16
Use a 16px margin from the screen edge, and 16px between two side-by-side buttons. Equal widths — split the column in half minus the gap.
Never let a single button keep stretching on a wide screen. Above tablet the column stops and centres. Never mix a small and a large button in one group.

Icon size

Five icon sizes, each tied to where the icon sits.

Look
16
20
24
32
40
Status
TokenSizeWhere
icon/xs16pxinside small buttons
icon/sm20pxinside large buttons, social actions
icon/md24pxicon buttons, bottom menu
icon/lg32pxempty states
icon/xl40pxvery rare, feature moments
Use the line style, never the filled style — except when filled is the on state, like a liked heart.

Elevation

How far something floats above the page.

Look
flat
on the page
raised
lifted
overlay
floating
Look — glow
glow/brand
widgets only
Not a height. A purple glow is decoration. It never means something floats.
Look — all styles from Figma
Elevation

Two heights. One glow. Shadow + surface always travel together.

raised0 2 8 · 8%
raised-dark0 2 8 · 40%
overlay0 8 24 -4 · 12%
overlay-dark0 8 24 -4 · 55%
glow/brand0 10 30 -10 · purple
glow/brand-glassglow + glass 12
SURFACE PAIRING — never mixraised   needs a token: surface/raised   light #FFFFFF · dark #16182Boverlay   needs a token: surface/overlay   light #FFFFFF · dark #201C30glow/brand is DECORATION, not height. Widgets only.Glass = container/glass fill (70%) + effect/glass-blur (12). Both halves, or it is not glass.

PENDING — surface/raised and surface/overlay do not exist yet. One token cannot be a different token per mode, so these two must be created before the pairing above can be applied. Awaiting Bon.

Status
LevelUse forNeeds
FlatAlmost everything.a border — invisible without one
RaisedChat input, cards that move.elevation/raised
OverlaySheets, menus, dialogs, toasts.elevation/overlay
GlowWidgets only. Decoration, not height.glow/brand
In dark mode the shadow nearly vanishes — that is normal A black shadow on a near-black page has almost no contrast. In dark mode the background gets lighter as things rise. Switch this page to dark and watch the three boxes above.
Use a shadow only when something really floats.
Never use a shadow to group things — use a border or space. Never make a shadow lighter in dark mode.

3 · Action

Button graph

Every button is a weight crossed with a content type — pick one from each.

Look
Primary · Secondary · Danger, each as label only, icon + label, icon only. The dashed square is the 44×44 tap area — it is transparent, only the 28×28 circle is drawn.
語音語音語音語音語音語音
The rule: the container decides how loud it is. The content slot decides what goes inside.
Never two Primary buttons in the same area.

Primary Button

The main action on a screen — filled, so it is impossible to miss.

Look — sizes and states
Small · Large · Default · Pressed (primary/pressed) · Disabled (35%)
語音諮詢我的AI語音語音語音
Look — both sizes side by side
Small 30 tall · pad 6/8 · gap 2 · icon 16 · 12px — Large 52 tall · pad 16/24 · gap 8 · icon 20 · 14px
語音語音諮詢我的AI諮詢我的AI
Status
StateWhat it means
DefaultResting.
PressedWhile a finger or mouse is down.
DisabledCannot be used right now.
HoverMouse over it. Built in code, not inside Figma.
FocusReached by keyboard. Built in code, not inside Figma.
Use one per area. Size follows what it sits next to, not importance — Small inside cards and rows, Large for screen-level actions.
Never two on one screen area. Width hugs its own text.

Secondary Button

A supporting action — outlined, so it sits quietly beside the primary one.

Look
Same five states, 1.5px outline instead of a fill
語音諮詢我的AI語音語音語音
Status

Same five states as Primary — Default, Pressed, Disabled drawn in Figma; Hover and Focus built in code, not inside Figma.

Use beside a Primary button, usually for the backwards action — cancel, back, not now.
Never alone for the main action. If it is the main action, it should be Primary.
About the Danger button Danger is the third weight, in red, for actions that destroy something — delete, unsubscribe, log out.

It has nowhere to live yet. A Danger button only makes sense where there is a confirm step, and HKChat has no Dialog component. Until there is one, do not place a Danger button anywhere.

Error and Danger use the same red but face opposite directions in time: Error reports what already happened. Danger warns about what is about to.

Icon & text button

A button whose whole label is its picture — used in headers, toolbars and rows that repeat.

Look
Menu · New conversation · Close · Share · More · Search · Attach, then Pressed and Disabled. Attach is plus-circle, never a paperclip.
Status
RuleValue
StatesDefault · Pressed · Disabled. Hover and Focus in code, not inside Figma.
Tap area44 × 44 minimum — bigger than the picture you can see
Picture24px, line style
Hidden namerequired, always — real text, never a code key
Use only when all three are true: the picture is universally understood; it lives in fixed furniture; a word would be noise because it repeats.
Never for the main action. Never for delete, unsubscribe or log out. Never for a picture you had to think about.
Every icon button carries a hidden name A blind user hears it read aloud. Real translated text — 選單, never common.more.

Tab

A row of filters — press one and the content below changes.

Look
Default and Selected
全部全部
Status
StateLook
Defaultwhite fill, grey border, regular weight
Selectedsoft purple fill, purple border, bold

34px tall · rounding 8 · 12px text.

Use when the user picks one thing and the page changes. Always exactly one selected.
Never leave none selected. If it does not filter anything, it is a Tag.

Social

The like, comment, share and save actions on a post.

Look
like 3.2k · like 12 · like 0, all resting. The last row is the same bar switched on — filled and purple.
Status
Typedefaultpressed
heart · 讚line, greyfilled, purple
comment · 評論line, greyfilled, purple
share · 分享line, greyfilled, purple
star · 收藏line, greyfilled, purple
Use in a row under a post. Filled means switched on.
Never purple for a resting social action. Purple here means on, not important.

4 · Navigation

Bottom Menu

The bar at the bottom of the app — four places you can go, always the same four.

Look — all four, both states
All four, resting then selected. Below — the bar as it ships, with exactly one selected.
Status
StateLook
Defaultgrey, line icon
Selectedpurple, filled icon

Selected changes two things — colour and icon fill. Colour alone is not enough for colour-blind users.

Use for the four top-level places. Exactly one always selected.
Never a fifth item. Never an action here — this bar goes places, it does not do things.

Hamburger menu

The panel that slides in when you press ☰ — it holds past conversations, search and settings.

Look — full screen
Left — the panel, 336 × 852. Right — open, over the page.
9:41
港話通
新對話

如何幫我找出過去三年立法會

如何幫我找出過去三年立法會

今天

颱風預測資訊

三日前

如何幫我找出過去三年立法會

過去七天

如何幫我找出過去三年立法會

Bboxxxyun
299 積分
9:41
港話通
新對話

如何幫我找出過去三年立法會

如何幫我找出過去三年立法會

今天

颱風預測資訊

三日前

如何幫我找出過去三年立法會

過去七天

如何幫我找出過去三年立法會

Bboxxxyun
299 積分
Status
StateBehaviour
ClosedOff screen. Only the ☰ button is visible.
OpeningSlides in from the left. The page behind dims.
OpenSits at overlay height. The page behind stays visible but is not pressable.
ClosingPress ☰ again, press the dimmed area, or swipe left.
Use for things people need occasionally — past conversations, search, settings, account.
Never a main action in here. Never open it over another overlay.

5 · Information

SnackToast

A short message confirming something just happened — it fades on its own.

Look
Default · Success · Error, small then large
操作通知操作通知操作通知操作通知操作通知操作通知
Status
StatusMeans
DefaultSomething happened. Neutral.
SuccessIt worked.
ErrorIt failed.
Plus: an optional icon, and at most one action such as 重試.
Use only when all three are true — it can disappear, it does not block, missing it loses nothing.
Timing: 4 seconds alone, 6–8 with an action. One at a time.
Never for form errors, decisions, or anything over two lines. Never auto-hide an error needing a retry. Never two actions.

User Pill

Shows who someone is — a small round picture and their name.

Look
One colour per account, so the same person is always the same colour
BboxxxyunAalexMMmaya_kJjordy22Ssam_x
Status
No statesThis is information, not a control. You cannot press it.
Picture26px round. Use the person's photo if they have one.
No photoThe first letter of the account name, white, on a colour.
ColourFrom the brand set, so the same person is always the same colour.
Name12px Medium, 4px after the picture.
Use anywhere you show who wrote or did something.
Never make it look pressable. If pressing opens a profile, that is a different component and needs a 44×44 tap area.

Tag

A small label describing what something is — you cannot press it.

Look
The five categories. Bon's export frame is deliberately empty — Tag has no instances by design, so these come straight from the component.
科技財經體育娛樂健康
Status
No statesPurely informative. It describes. It does not respond.
Size22px tall · rounding 8 · 12px Medium

Tag and Tab share the same rounding. Tell them apart by height — Tag 22px, Tab 34px — and by colour.

Use to label what a post or card is about.
Never make a Tag look pressable or give it a selected state. If people can press it, it is a Tab.

6–7 · Exploration

Not settled. Do not build from this. Input bar, Agent button and Widget are still being worked out. They are here so the team can argue about them. Ask Bon before using anything in this part.

Input bar Exploration

The box you type into. Five variants — and they combine.

Look — every variant
Text status

如何在香港续领特区护照

想搵旺角邊間蛋撻?定睇紅隧交通? 想搵旺角邊間蛋撻?定睇紅隧交通?想搵旺角邊間蛋撻?定睇紅

想搵旺角邊間蛋撻?定睇紅隧交通? 想搵旺角邊間蛋撻?定睇紅隧交通?想搵旺角邊間蛋撻?定睇紅想搵旺角邊間蛋撻?定睇紅隧交通? 想搵旺角邊間蛋撻?定睇紅隧交通?想搵旺角邊間蛋撻?定睇紅

Record status

一起參與討論吧!

語音
取消
00:03
取消
識別中...
Attachment status

一起參與討論吧!

語音
IMG_0524JPG 144.5KB

想搵旺角邊間蛋撻?定睇紅隧交通?

語音
Guide status
深度思考
英漢語言結構對思維方式的影響 →成人第二語言學習中「語感」形成的機制 →研究方言在年輕機制 →中國消費品機制 →

想搵旺角邊間蛋撻?定睇紅隧交通?

語音
Agent status
深度思考

一起參與討論吧!

語音
Status — five variants
GroupVariantWhat it is
Generaltexttyping, including when the text overflows
recordingvoice input running
attachmenta file has been added
Situationalguiding questionssuggested prompts sit above the box
agent selectedan agent is active, so the box changes
They cross over — this is the hard part These are not five separate boxes. One box can be several at once: overflowing text, with an attachment, with guiding questions above, with an agent selected.
That combination is what still needs deciding. The five variants are a starting point for the argument, not a finished set.

Agent button Exploration

Picks which AI skill answers you.

Look — all agents, both states
Every agent, resting then selected. Selected is a muted fill with purple bold text — the word never goes away.
問得好徵文深度思考巴士到站翻譯選校港話通幫手回應今日動態
問得好徵文深度思考巴士到站翻譯選校港話通幫手回應今日動態
Status
StateLook
Defaultsurface fill, grey border, muted text
Selectedmuted fill, purple bold text

36px tall · rounding 8 · 12px text.

Use above the chat input so people can pick a skill before typing. One selected at a time, or none.
Never remove the word — these are never icon-only. Nobody can guess an agent from a picture.

Widget Exploration

A block the AI builds to show one task as structured information you can check, or an action, instead of a paragraph you have to read.

What makes something a widget — three tests
1 · One taskIt does a single thing. Two things means two widgets.
2 · StructuredIts content is fields, not prose. Title, time, destination — things you can scan and verify.
3 · Worth showingThere is something to check, decide or watch. Nothing to check? Use plain text.
Look — one example
Bon's add-to-calendar flow — ONE example of a widget, not the universal rule.
1 · The ask
今晚七點同小明食飯,加入日程
10:42 AM

一起參與討論吧!

語音
2 · The widget
加入日曆
標題與 Peter 晚餐
時間下午 7:00 — 下午 9:00
日曆程式IOS日曆
取消匯入日曆
今晚七點同小明食飯,加入日程
10:42 AM

一起參與討論吧!

語音
3 · The explanation
加入日曆
標題與 Peter 晚餐
時間下午 7:00 — 下午 9:00
日曆程式IOS日曆
取消匯入日曆
今晚七點同小明食飯,加入日程
10:42 AM

收到!撳「匯入日曆」就可以加入你嘅日曆。或者喺聊天輸入框話我知你想改啲咩!

10:42 AM

一起參與討論吧!

語音
4 · The receipt
今晚七點同小明食飯,加入日程
10:42 AM
與 Peter 晚餐今天 · 下午 7:00 – 9:00已加入 IOS日曆
10:42 AM

一起參與討論吧!

語音

ONE example of a conversational widget. The pattern here is not the rule — other widgets will behave differently.

Five principles
1 · Two actions, maximumOne primary, one optional secondary, at the bottom. More than two means it should not be a widget.
2 · No second screen insideNo tabs, no drill-in, no deeper navigation. If it needs a second view, it is two widgets or a whole page.
3 · No scrolling insideIt grows to fit its content, up to the screen height. It never gets its own scrollbar.
4 · Never repeat the appNo search box, no second text field, no keyboard. The app already has those. A widget that duplicates them competes with the thing it lives inside.
5 · Bigger means more, not largerA taller widget shows more rows — not the same rows at a bigger size.
Where it can sit — placement
banner
chat
chat
input
full-width strip
top
chat
chat
input
pulls down from the top
chat
chat
bottom
input
slides up from the bottom
chat
inline
chat
input
sits in the conversation
Today HKChat ships inline only. The other three exist in code and are not agreed for design.
How big — size
0.25
0.5
0.75
There is no full screen — the conversation must stay visible.
Do — one widget on screen at a time · coexists with the conversation, never replaces it · at most two actions · height fits its content.

Don't — no editing inside a widget, changes happen by talking · no tabs, no drill-in, no second screen · never repeat what the app already does · never blank, if there is nothing to show use text.
Still open Which placements are allowed beyond inline · whether non-conversational widgets follow the same rules · the full state list.
Where these rules come from OpenAI Apps SDK UI guidelines · Apple widget principles · HKChat decisions 5 & 11 August.
One deliberate difference: OpenAI allows inline editing inside a card. HKChat does not.

8 · Interactive

Hover

One rule covers every component — no component gets its own hover design.

Look — hover these
A card
Cards get 4%, not 8% — they are big, so less goes further. A card may also lift to raised on hover.
Same rule on all four. Press Tab to see focus.
Status
StateWhat happensWhen
Hoverthe element's own text colour over itself at 8% · 4% on large cardsmouse only
Pressedthe same film at 12%mouse and finger
Focus2px purple ring, 2px offsetkeyboard only

White text gives a white film. Black text gives a black film. Works in dark mode by itself.

Use a 120ms fade. Cards may lift to raised on hover. Buttons should not move.
Never design hover per component. Never change text colour on hover. Never use hover as the only way to reveal something.
The mobile trap Phones have no mouse, but fire hover on tap — and it sticks. The fix is one line: @media (hover: hover), meaning "only if there is a real mouse."

HKChat Style Guide · Version 3
Every component above is exported from Figma 港話通HKChat V2.1⚠️V2R2📤 STYLE GUIDE EXPORT.
Code: Storybook. Sections 6 and 7 are exploration. 18 August 2026.