Desktop Material

Show emojis in dialogs and message boxes

A persisted, per-profile switch on Settings → Appearance that puts one decorative emoji beside a dialog's title. Turn it off and the same dialog says exactly the same words with no emoji: nothing is reworded, nothing is removed, and no control changes what it does.

The decoration is decoration and the implementation treats it as such. It is hidden from assistive technology, it is rendered outside the element the dialog is named by, and it never enters a button, an action label, a field label, or any other control text.

Behavior

Aspect Behavior
Default On (ShowDialogEmojiDefault = true)
Storage show-dialog-emoji in local storage, via the shared getBoolean/setBoolean helpers
Scope Every dialog and message box the app renders, including the MD3 surfaces
Live A dialog already on screen gains or loses its decoration immediately
Accessible name Byte-identical with the setting on or off
Control text Never decorated, in any state
Network None. The glyphs are literal characters in the source

Where a decoration comes from

A dialog asks for the kind of situation it represents, never for a glyph:

<Dialog id="delete-branch" type="warning" emojiDecoration="destructive" …>

app/src/lib/dialog-emoji.ts owns the single mapping from DialogDecorationKind to an emoji, so switching the setting off removes every decoration in the app from one place, and changing which glyph a destructive confirmation carries is a one-line edit that no call site has to know about.

The kinds are information, question, warning, error, destructive, success, progress, security, account, repository, branch, commit, sync, search, settings, update, terminal, agent, export, file and celebration.

A dialog that names no kind still gets one: Dialog derives it from the existing type prop — error → error, warning → warning, everything else → information. The contract asks every dialog to carry a decoration, not a hand-picked few, so the default covers the whole application and an explicit emojiDecoration is only needed where a more relevant kind exists.

Where the glyph is rendered

DialogEmoji (app/src/ui/lib/dialog-emoji.tsx) renders

<span class="dialog-emoji" aria-hidden="true" role="presentation">🧨</span>

as a sibling of the title element, never a child of it. That placement is the whole safety property. The legacy DialogHeader puts it before the <h1> that aria-labelledby points at; Md3ComposeDialog, Md3RegexBuilderDialog and Md3DestructiveGate put it before their own title element for the same reason. An emoji moved inside the labelled element would become part of what a screen reader announces while looking completely correct on screen.

When the setting is off, or when no kind is named, the component returns null so the surrounding flex layout collapses rather than reserving an empty slot.

Staying current while a dialog is open

useShowDialogEmoji subscribes to the desktop-material-dialog-emoji-changed window event that setShowDialogEmoji raises, and to the cross-window storage event (including the key === null clear-everything case). A dialog that is already open therefore updates in place; no restart and no reopen.

Configuration

The switch lives on Settings → Appearance, in its own Dialogs and message boxes section, and is not hidden by School mode — a decorative glyph beside a dialog title is not one of the presentation features that mode suppresses, and hiding the control would leave a user unable to turn off something they can plainly see.

Surface Entry
Settings Appearance → Dialogs and message boxes
Settings search appearance-dialog-emoji
Command palette palette:set-dialog-emoji, rendered as a live switch
Teleport target settingsDialogEmoji[data-teleport-target="settings-dialog-emoji"]

Explanation and default provenance

The full explanation sits behind progressive disclosure (<details>), so the row stays a row. Under the switch, a provenance line states plainly where the current value came from and names the real value rather than the word "default":

getShowDialogEmojiProvenance() distinguishes the two by asking whether the key exists at all, so a deliberate false never reads as an unset default.

Languages and tone

All three language modes (English, Cantonese, bilingual) carry real copy, and the explanation is a funny-level family — dialogEmoji.explanation.plain, .light, .playful, .maximum — styled independently per language by the two playfulness sliders. The label, the boundary note and the provenance line are single fixed strings in both languages: what the switch is called, where emoji are forbidden, and whether the value was actually chosen are facts a reader acts on, not voice.

Failure modes

Situation Behavior
No local storage (a non-renderer context) Reads the shipped default; writes are skipped; the change event is still raised where a window exists
Unparseable stored value ("yes please") Falls back to the shipped default rather than guessing
Unknown decoration kind at runtime resolveDialogDecoration returns null; the dialog renders undecorated
A dialog with no title No header is rendered, so no decoration is rendered either
The change event never arrives The value is re-read when the subscription lands, so a change between first render and subscription is not lost

Security considerations

Verification

app/test/unit/dialog-emoji-test.tsx, 26 tests:

Every guard-shaped assertion above was verified by breaking the thing it guards and watching the suite go red: the decoration moved inside the labelled <h1>, aria-hidden removed, an emoji added to a translated control label, pointer-events: none removed, the em size replaced with pixels, the palette entry renamed, the settings-search entry renamed, English left in a Cantonese slot, and the provenance check hard-coded to stored.

Suggested articles


喺對話框同訊息框顯示 emoji

設定 → 外觀入面一個會記住嘅開關,開咗就會喺對話框標題側邊加一粒裝飾用嘅 emoji。閂咗,同一個對話框會用完全一樣嘅字,只係冇咗粒 emoji:字句唔會改,內容唔會 少,控制項嘅行為亦都完全唔變。

粒 emoji 純粹係裝飾,實作亦都當佢係裝飾:佢對輔助技術隱藏、渲染喺對話框命名元素之 外,亦都唔會入到按鈕、動作名、欄位標籤或者任何控制項文字。

行為

項目 行為
預設 開(ShowDialogEmojiDefault = true
儲存 local storage 嘅 show-dialog-emoji,用共用嘅 getBooleansetBoolean
範圍 app 渲染嘅每個對話框同訊息框,包括 MD3 介面
即時 已經開咗嘅對話框會即刻加返或者除返粒 emoji
無障礙名稱 開同閂都完全一樣,一個 byte 都唔差
控制項文字 任何狀態下都唔會有 emoji
網絡 冇。啲字元直接寫喺原始碼入面

一個對話框係要求「呢個係邊種情況」,唔係要求某粒 emoji;由 app/src/lib/dialog-emoji.ts 一個地方負責由 DialogDecorationKind 對應到 emoji, 所以閂咗個設定就一次過清走全 app 嘅裝飾。冇指明種類嘅對話框亦都有:Dialog 會用返 本身嘅 type 推導(errorwarning,其餘一律 information)。

DialogEmoji 會渲染一個 aria-hidden<span class="dialog-emoji">,而且一定 係標題元素嘅兄弟節點,唔會係佢嘅仔。呢個位置就係成個安全性所在:擺入標題入面 睇落完全正常,但螢幕閱讀器會讀埋粒 emoji。

設定

開關喺設定 → 外觀嘅「對話框同訊息框」一節,School mode 唔會收埋佢。完整說明擺 喺 <details> 漸進式披露入面,開關下面嘅來源句會照直講清楚而家個值係喺呢部電腦記 錄過,定係用緊出廠設定,而且會講返真正嘅值,唔會淨係寫「預設」。

三種語言模式都有真正嘅文案,說明本身係搞笑程度家族(.plain.light.playful.maximum),由兩支滑桿各自控制語氣;標籤、界線說明同來源句就係固定 字串,因為嗰啲係用家要照住做嘅事實,唔係語氣。

失敗情況

冇 local storage 就用返出廠值;讀唔明嘅值會退返出廠值而唔會亂估;未知種類會渲染成 冇裝飾;冇 title 嘅對話框根本冇 header,自然亦冇裝飾。

保安考量

呢個功能淨係一個關於顯示嘅布林值:唔會讀寫任何密碼、權杖、路徑或者用戶內容,冇任何 網絡請求,而渲染出嚟嘅 emoji 永遠嚟自固定目錄,唔會由用家輸入嘅字串衍生。

驗證

app/test/unit/dialog-emoji-test.tsx 一共 26 個測試,涵蓋設定往返同來源、目錄完整 性、無障礙名稱一個 byte 都唔差嘅比對(普通對話框、撰寫對話框、regex 產生器、破壞性 閘門)、設定介面同三種語言、以及版面契約。每一個守衛式斷言都刻意整壞過一次、確認會 變紅,然後先還原。