From 3cdfe37c6ff5000e3795ea28f8d12999ba1df873 Mon Sep 17 00:00:00 2001 From: Salastil Date: Thu, 8 Oct 2026 13:50:21 -0400 Subject: [PATCH] Apply the design tokens across the stylesheet; one class name per thing (#282) Colours, shadows, layers, transition durations, radii, font sizes and weights now come from theme.css instead of literals. Timestamps and reply previews use the quieter text colour. The status dot in the join dialogs and the voice panel's mic bars no longer share class names with the avatar's presence dot and the voice recorder's level bar. Dead rules removed; icons are drawn at their own optical size. A test fails if the stylesheet uses an undefined custom property. Co-Authored-By: Claude Sonnet 5.5 --- scripts/ui-shots.mjs | 34 + src/renderer/src/app.css | 595 ++++++++---------- .../src/components/AddToConversation.tsx | 2 +- src/renderer/src/components/Icon.tsx | 5 +- src/renderer/src/components/JoinPanel.tsx | 2 +- src/renderer/src/components/VoicePanel.tsx | 4 +- src/renderer/src/styles.test.ts | 17 + 7 files changed, 329 insertions(+), 330 deletions(-) create mode 100644 src/renderer/src/styles.test.ts diff --git a/scripts/ui-shots.mjs b/scripts/ui-shots.mjs index 4239161..330b965 100644 --- a/scripts/ui-shots.mjs +++ b/scripts/ui-shots.mjs @@ -271,6 +271,40 @@ const SCENES = [ await sleep(600) } }, + { + // The date fields appear only with "A range" chosen; they are what had no border colour (#268). + name: 'modal-export-range', + setup: async (p) => { + await openChannel(p, '#general') + await p.evaluate(click('.header-nameplate')) + await sleep(300) + await p.evaluate(click('.context-menu-item', 'Export')) + await sleep(500) + await p.evaluate(`[...document.querySelectorAll('.reason-prompt label')].find((l) => l.innerText.includes('A range'))?.click()`) + await sleep(400) + } + }, + { + // The pointer over a channel and over a member: how visible a hover is. + name: 'irc-hover-rows', + setup: async (p) => { + await openChannel(p, '#general') + const c = await centre(p, '.buffer-row', 'dev') + if (c) await mouse(p, 'mouseMoved', c.x, c.y) + await sleep(300) + }, + clip: { x: 0, y: 44, width: 420, height: 300 } + }, + { + name: 'irc-hover-member', + setup: async (p) => { + await openChannel(p, '#general') + const c = await centre(p, '.nick-row', 'carol') + if (c) await mouse(p, 'mouseMoved', c.x, c.y) + await sleep(300) + }, + clip: { x: 860, y: 44, width: 420, height: 300 } + }, { name: 'toasts', setup: async (p) => { diff --git a/src/renderer/src/app.css b/src/renderer/src/app.css index 3ea49cb..4fd7c06 100644 --- a/src/renderer/src/app.css +++ b/src/renderer/src/app.css @@ -42,7 +42,7 @@ .titlebar-title { font-size: var(--font-size-large); - font-weight: 500; + font-weight: var(--weight-medium); } .titlebar-status { @@ -51,7 +51,7 @@ gap: var(--spacing-xxs); margin-left: var(--spacing-s); padding: 2px var(--spacing-s); - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--surface-container-highest); color: var(--warning); font-size: var(--font-size-small); @@ -145,7 +145,7 @@ tools ran out of room too and spilled leftwards over the name. */ flex: 1 1 0; min-width: 0; - font-weight: 500; + font-weight: var(--weight-medium); } .main-body { @@ -196,64 +196,6 @@ padding: var(--spacing-m); } -.bufferlist-footer { - display: flex; - flex: none; - padding: var(--spacing-xs); - gap: var(--spacing-xs); -} - -.footer-button { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: var(--spacing-xs); - padding: 6px; - border-radius: var(--corner-radius-small); - color: var(--surface-variant-text); - font-size: var(--font-size-small); -} - -.footer-button:hover { - background: var(--primary-hover-light); - color: var(--surface-text); -} - -.account-header, -.group-header { - display: flex; - align-items: center; - padding: 0 var(--spacing-xs); - margin-top: var(--spacing-xs); -} - -.account-header-main, -.group-header-main { - flex: 1; - min-width: 0; - display: flex; - align-items: center; - gap: var(--spacing-xs); - padding: 4px var(--spacing-xs); - border-radius: var(--corner-radius-small); - color: var(--surface-variant-text); - font-size: var(--font-size-small); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.03em; -} - -.account-header-main:hover, -.group-header-main:hover { - background: var(--surface-hover); - color: var(--surface-text); -} - -.guild-group .group-header { - padding-left: var(--spacing-m); -} - .connection-dot { width: 6px; height: 6px; @@ -291,7 +233,7 @@ .buffer-row.highlight .buffer-name { color: var(--surface-text); - font-weight: 600; + font-weight: var(--weight-semibold); } .buffer-name { @@ -317,7 +259,7 @@ display: flex; align-items: center; justify-content: center; - font-weight: 600; + font-weight: var(--weight-semibold); color: var(--primary-text); } @@ -334,10 +276,10 @@ flex: none; min-width: 18px; padding: 0 5px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--surface-container-highest); color: var(--surface-text); - font-size: 11px; + font-size: var(--font-size-xs); line-height: 18px; text-align: center; } @@ -345,7 +287,7 @@ .unread-badge.highlight { background: var(--primary); color: var(--primary-text); - font-weight: 600; + font-weight: var(--weight-semibold); } @@ -356,7 +298,7 @@ panel it used to live in is usually closed when one does. */ .verification-dialog { position: fixed; - z-index: 70; + z-index: var(--z-notice); right: var(--spacing-m); bottom: var(--spacing-m); width: min(360px, calc(100vw - var(--spacing-m) * 2)); @@ -367,7 +309,7 @@ background: var(--surface-container-high); border: 1px solid var(--outline-strong); border-radius: var(--corner-radius); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-2); } .verification-head { @@ -383,7 +325,7 @@ viewport, a panel can be as wide as it needs while the row stays narrow. */ .header-popover { position: fixed; - z-index: 60; + z-index: var(--z-popover); display: flex; flex-direction: column; gap: var(--spacing-xs); @@ -393,7 +335,7 @@ background: var(--surface-container-high); border: 1px solid var(--outline-strong); border-radius: var(--corner-radius); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); + box-shadow: var(--shadow-2); } .popover-field { @@ -434,7 +376,7 @@ .search-scope .link-button.active { color: var(--primary); - font-weight: 600; + font-weight: var(--weight-semibold); } /* Inside a panel the results are the panel, so they drop the floating shell @@ -570,10 +512,10 @@ position: relative; flex: none; padding: 0 var(--spacing-xs); - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--success); - color: #fff; - font-weight: 700; + color: var(--on-accent); + font-weight: var(--weight-bold); } .poll-option.winner { @@ -594,7 +536,7 @@ border: 1px solid var(--outline-light); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35); + box-shadow: var(--shadow-2); overflow: hidden; } @@ -610,7 +552,7 @@ } .poll-title { - font-weight: 600; + font-weight: var(--weight-semibold); } .poll-buttons { @@ -650,7 +592,7 @@ inset: 0 auto 0 0; background: var(--primary); opacity: 0.28; - transition: width 0.3s ease; + transition: width var(--motion) ease; } /* The one you picked, which is worth seeing at a glance in a list of rows @@ -666,7 +608,7 @@ .poll-label, .poll-count { position: relative; - font-weight: 600; + font-weight: var(--weight-semibold); } .poll-label { @@ -691,7 +633,7 @@ display: block; height: 100%; background: var(--success); - transition: width 0.5s linear; + transition: width var(--motion-slow) linear; } /* Folded away: still running, still one click from being answered. */ @@ -721,13 +663,13 @@ .live-badge { flex: none; padding: 0 5px; - border-radius: 999px; + border-radius: var(--radius-pill); /* The same solid red as the hang-up button rather than --error, which is a pale pink for text and reads as decorative when used as a fill. */ - background: #c9403c; - color: #fff; + background: var(--danger); + color: var(--on-accent); font-size: 9px; - font-weight: 700; + font-weight: var(--weight-bold); letter-spacing: 0.05em; line-height: 18px; text-align: center; @@ -818,7 +760,7 @@ border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container); - box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35); + box-shadow: var(--shadow-2); } .command-picker-head { @@ -865,14 +807,14 @@ } .command-name { - font-weight: 500; + font-weight: var(--weight-medium); } /* The arguments are not the name: same line, quieter, so the name still reads as the thing being chosen. */ .command-usage { color: var(--surface-variant-text); - font-weight: 400; + font-weight: var(--weight-regular); } .command-source { @@ -916,10 +858,10 @@ -webkit-app-region: no-drag; right: var(--spacing-m); bottom: 90px; - z-index: 40; + z-index: var(--z-raised); width: 340px; margin: 0; - box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45); + box-shadow: var(--shadow-2); } /* @@ -1083,7 +1025,7 @@ button.video-tile { padding: 1px 6px; border-radius: var(--corner-radius-small); background: rgba(0, 0, 0, 0.55); - color: #fff; + color: var(--on-accent); } .call-stage-controls { @@ -1117,14 +1059,14 @@ button.video-tile { padding: var(--spacing-m); border-radius: var(--corner-radius); background: var(--surface-container); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-2); } .screen-picker-head { display: flex; align-items: center; gap: var(--spacing-s); - font-weight: 500; + font-weight: var(--weight-medium); } .screen-picker-head .icon-button { @@ -1158,7 +1100,7 @@ button.video-tile { width: 100%; aspect-ratio: 16 / 9; object-fit: contain; - background: #000; + background: var(--media-backdrop); border-radius: var(--corner-radius-small); } @@ -1330,7 +1272,7 @@ button.video-tile { padding: 5px; border-radius: var(--corner-radius-small); background: var(--surface-container-highest); - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4); + box-shadow: var(--shadow-1); } .message-row { @@ -1390,7 +1332,7 @@ button.video-tile { animation: none; transform: rotate(-90deg); stroke-linecap: butt; - transition: stroke-dasharray 150ms linear; + transition: stroke-dasharray var(--motion-fast) linear; } @keyframes upload-spin { @@ -1419,7 +1361,7 @@ button.video-tile { } .message-row.system .message-body { - color: var(--surface-variant-text); + color: var(--text-muted); font-style: italic; } @@ -1485,7 +1427,7 @@ button.video-tile { become a paragraph of text on a wide window. */ max-width: min(72%, 34rem); padding: var(--spacing-xs) var(--spacing-s); - border-radius: 18px; + border-radius: var(--radius-bubble); background: var(--bubble-them); color: var(--bubble-them-text); } @@ -1516,13 +1458,13 @@ button.video-tile { } .message-row.bubbles .message-from { - font-size: 12px; + font-size: var(--font-size-small); } .message-row.bubbles .bubble-time { display: block; margin-top: 2px; - font-size: 10px; + font-size: var(--font-size-xxs); text-align: right; font-variant-numeric: tabular-nums; /* Against a filled bubble rather than the page, so the muted grey used @@ -1575,7 +1517,7 @@ button.video-tile { width: 42px; text-align: right; font-variant-numeric: tabular-nums; - font-size: 11px; + font-size: var(--font-size-xs); } .message-avatar { @@ -1601,8 +1543,8 @@ button.video-tile { align-items: center; justify-content: center; color: #101418; - font-weight: 600; - font-size: 13px; + font-weight: var(--weight-semibold); + font-size: var(--font-size-compact); } .message-content { @@ -1619,7 +1561,7 @@ button.video-tile { } .message-from { - font-weight: 600; + font-weight: var(--weight-semibold); } /* Comfy and bubble layouts give the byline its own line; the compact log @@ -1630,7 +1572,7 @@ button.video-tile { } .edited-tag { - font-size: 11px; + font-size: var(--font-size-xs); } .reply-preview { @@ -1648,7 +1590,7 @@ button.video-tile { } .reply-from { - font-weight: 600; + font-weight: var(--weight-semibold); flex: none; } @@ -1666,8 +1608,8 @@ button.video-tile { gap: var(--spacing-s); margin: var(--spacing-s) var(--spacing-m); color: var(--error); - font-size: 11px; - font-weight: 600; + font-size: var(--font-size-xs); + font-weight: var(--weight-semibold); text-transform: uppercase; } @@ -1698,7 +1640,7 @@ button.video-tile { background: var(--surface-container-highest); color: var(--surface-text); font-size: var(--font-size-small); - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4); + box-shadow: var(--shadow-1); } .jump-to-present:hover { @@ -1862,7 +1804,7 @@ img.composer-emoji { padding: var(--spacing-s) var(--spacing-s) var(--spacing-xxs); text-transform: uppercase; letter-spacing: 0.03em; - font-weight: 500; + font-weight: var(--weight-medium); } .nicklist-empty { @@ -1926,7 +1868,7 @@ img.composer-emoji { .panel-heading { margin: 0 0 var(--spacing-s); font-size: var(--font-size-medium); - font-weight: 600; + font-weight: var(--weight-semibold); color: var(--surface-variant-text); } @@ -2085,13 +2027,13 @@ img.composer-emoji { .context-menu { position: fixed; - z-index: 1000; + z-index: var(--z-menu); min-width: 170px; padding: var(--spacing-xs); border-radius: var(--corner-radius); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-2); } .context-menu-item { @@ -2141,7 +2083,7 @@ img.composer-emoji { border-radius: var(--corner-radius); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6); + box-shadow: var(--shadow-2); } .leave-confirm-head { @@ -2149,7 +2091,7 @@ img.composer-emoji { align-items: center; gap: var(--spacing-s); font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); margin-bottom: var(--spacing-s); } @@ -2196,12 +2138,12 @@ img.composer-emoji { border-radius: var(--corner-radius); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6); + box-shadow: var(--shadow-2); } .membership-rules-head { font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); margin-bottom: var(--spacing-s); } @@ -2226,7 +2168,7 @@ img.composer-emoji { border-radius: var(--corner-radius); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6); + box-shadow: var(--shadow-2); } .drop-confirm-head { @@ -2234,7 +2176,7 @@ img.composer-emoji { align-items: center; gap: var(--spacing-s); font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); margin-bottom: var(--spacing-m); } @@ -2294,7 +2236,7 @@ img.composer-emoji { left: 0; right: 0; bottom: 0; - z-index: 60; + z-index: var(--z-popover); display: flex; flex-direction: column; align-items: center; @@ -2302,9 +2244,9 @@ img.composer-emoji { gap: var(--spacing-s); pointer-events: none; background: rgba(0, 0, 0, 0.45); - color: #fff; + color: var(--on-accent); font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); outline: 2px dashed var(--primary); outline-offset: -10px; } @@ -2312,8 +2254,8 @@ img.composer-emoji { /* The one button in the app that destroys something. Not --error, which is a pale text colour and reads as decorative when used as a fill. */ .button.danger { - background: #c9403c; - color: #fff; + background: var(--danger); + color: var(--on-accent); } /* --- mentions ---------------------------------------------------------- */ @@ -2369,7 +2311,7 @@ img.composer-emoji { } .mention-from { - font-weight: 600; + font-weight: var(--weight-semibold); } /* Pushed to the far edge: the when is the least of the three things on this @@ -2407,11 +2349,11 @@ img.composer-emoji { pointer-events: none; min-width: 15px; padding: 0 3px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--primary); color: var(--primary-text); - font-size: 10px; - font-weight: 600; + font-size: var(--font-size-xxs); + font-weight: var(--weight-semibold); line-height: 15px; text-align: center; } @@ -2442,7 +2384,7 @@ img.composer-emoji { /* Inside the panel the rows sit against its own padding, not the sidebar's. */ .mentions-panel .mention-row { padding: var(--spacing-xs) var(--spacing-s); - border-radius: var(--spacing-xs); + border-radius: var(--radius-xs); } .mentions-panel .mention-row:hover { @@ -2481,7 +2423,7 @@ img.composer-emoji { flex-direction: column-reverse; gap: var(--spacing-s); /* Above the toasts, which are the only other floating thing this low. */ - z-index: 2100; + z-index: var(--z-call); max-width: 360px; } @@ -2493,7 +2435,7 @@ img.composer-emoji { border-radius: var(--corner-radius); background: var(--surface-container-highest); border: 1px solid var(--outline-medium); - box-shadow: 0 6px 22px rgba(0, 0, 0, 0.55); + box-shadow: var(--shadow-2); /* Breathes in time with the ringtone, so the panel reads as ringing rather than as one more notification that happens to have buttons. */ animation: call-pulse 2.6s ease-in-out infinite; @@ -2525,7 +2467,7 @@ img.composer-emoji { } .incoming-call-name { - font-weight: 600; + font-weight: var(--weight-semibold); } /* Green to answer and red to hang up, which is the one piece of visual @@ -2539,7 +2481,7 @@ img.composer-emoji { height: 36px; flex: none; border-radius: 50%; - color: #fff; + color: var(--on-accent); } .call-answer { @@ -2549,7 +2491,7 @@ img.composer-emoji { /* Not --error: that token is a pale pink meant for text on a dark surface, and a hang-up button filled with it reads as decorative. */ .call-decline { - background: #c9403c; + background: var(--danger); } .call-answer:hover, @@ -2564,7 +2506,7 @@ img.composer-emoji { display: flex; flex-direction: column; gap: var(--spacing-xs); - z-index: 2000; + z-index: var(--z-toast); max-width: 420px; } @@ -2576,7 +2518,7 @@ img.composer-emoji { border-radius: var(--corner-radius); background: var(--surface-container-highest); border: 1px solid var(--outline-medium); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45); + box-shadow: var(--shadow-2); font-size: var(--font-size-small); } @@ -2694,7 +2636,7 @@ img.smilie { .rich-embed-title { display: block; - font-weight: 600; + font-weight: var(--weight-semibold); margin-bottom: 2px; } @@ -2703,7 +2645,7 @@ img.smilie { } .rich-embed-author { - font-weight: 600; + font-weight: var(--weight-semibold); margin-bottom: 2px; } @@ -2778,7 +2720,7 @@ img.smilie { width: min(420px, 100%); aspect-ratio: 16 / 9; border-radius: var(--corner-radius-small); - background: #000; + background: var(--media-backdrop); } /* The player, at the thumbnail's width and YouTube's shape. */ @@ -2788,7 +2730,7 @@ img.smilie { aspect-ratio: 16 / 9; border: none; border-radius: var(--corner-radius-small); - background: #000; + background: var(--media-backdrop); } .media-embed.youtube.playing { @@ -2806,10 +2748,10 @@ img.smilie { border: none; border-radius: var(--corner-radius-small); background: rgba(0, 0, 0, 0.6); - color: #fff; + color: var(--on-accent); cursor: pointer; opacity: 0; - transition: opacity 120ms; + transition: opacity var(--motion-fast); } .media-embed.youtube:hover .youtube-outside, @@ -2823,7 +2765,7 @@ img.smilie { display: flex; align-items: center; justify-content: center; - color: #fff; + color: var(--on-accent); text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7); } @@ -2846,7 +2788,7 @@ img.smilie { align-items: center; gap: var(--spacing-xxs); padding: 1px var(--spacing-s); - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--surface-container-high); border: 1px solid transparent; line-height: 1.5; @@ -2893,7 +2835,7 @@ img.smilie { border-radius: var(--corner-radius-small); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); + box-shadow: var(--shadow-1); z-index: 5; } @@ -2918,9 +2860,9 @@ img.smilie { justify-content: center; width: 26px; height: 24px; - border-radius: 4px; + border-radius: var(--radius-xs); color: var(--surface-variant-text); - font-size: 14px; + font-size: var(--font-size-medium); } .toolbar-button:hover { @@ -2932,7 +2874,7 @@ img.smilie { .emoji-picker { position: fixed; - z-index: 1500; + z-index: var(--z-picker); width: min(420px, calc(100vw - 8px)); /* A fixed height, so the rail down the side always has the room the list has, as Discord's does. */ @@ -2945,7 +2887,7 @@ img.smilie { border-radius: var(--corner-radius); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55); + box-shadow: var(--shadow-2); } .emoji-scroll { @@ -2957,7 +2899,7 @@ img.smilie { padding: var(--spacing-s) 2px var(--spacing-xxs); text-transform: uppercase; letter-spacing: 0.03em; - font-weight: 500; + font-weight: var(--weight-medium); } .emoji-grid { @@ -2987,7 +2929,7 @@ img.smilie { justify-content: center; height: 34px; border-radius: var(--corner-radius-small); - font-size: 20px; + font-size: var(--font-size-xlarge); line-height: 1; } @@ -3028,7 +2970,7 @@ img.smilie { .emoji-cell.emoji-token { overflow: hidden; padding: 0 2px; - font-size: 10px; + font-size: var(--font-size-xxs); line-height: 1.1; word-break: break-all; } @@ -3088,11 +3030,7 @@ img.smilie { .settings-rail-item.active { background: var(--primary-container); - font-weight: 500; -} - -.settings-rail-item.soon { - color: var(--surface-variant-text); + font-weight: var(--weight-medium); } .settings-content { @@ -3112,7 +3050,7 @@ img.smilie { .settings-section-title { margin: 0 0 var(--spacing-xxs); font-size: var(--font-size-medium); - font-weight: 600; + font-weight: var(--weight-semibold); color: var(--surface-variant-text); } @@ -3177,7 +3115,7 @@ img.smilie { border-radius: 10px; background: var(--surface-container-high); cursor: pointer; - transition: background-color 120ms ease, border-color 120ms ease; + transition: background-color var(--motion-fast) ease, border-color var(--motion-fast) ease; } .switch.on { @@ -3193,7 +3131,7 @@ img.smilie { height: 14px; border-radius: 50%; background: var(--surface-text); - transition: transform 120ms ease; + transition: transform var(--motion-fast) ease; } .switch.on .switch-knob { @@ -3252,7 +3190,7 @@ img.smilie { .setting-segmented button.active { background: var(--primary); color: var(--primary-text); - font-weight: 500; + font-weight: var(--weight-medium); } .button-row { @@ -3384,7 +3322,7 @@ img.smilie { } .invite-name { - font-weight: 600; + font-weight: var(--weight-semibold); } .invite-actions { @@ -3430,7 +3368,7 @@ img.smilie { } .join-friends-header .setting-text { - font-weight: 600; + font-weight: var(--weight-semibold); } .tab { @@ -3440,7 +3378,7 @@ img.smilie { .tab.active { color: var(--primary); - font-weight: 500; + font-weight: var(--weight-medium); } .friend-row { @@ -3495,7 +3433,7 @@ img.smilie { font-size: var(--font-size-small); } -.presence-dot { +.status-dot { width: 8px; height: 8px; border-radius: 50%; @@ -3503,15 +3441,15 @@ img.smilie { background: var(--outline); } -.presence-dot.online { +.status-dot.online { background: var(--success); } -.presence-dot.idle { +.status-dot.idle { background: var(--warning); } -.presence-dot.dnd { +.status-dot.dnd { background: var(--error); } @@ -3708,7 +3646,7 @@ img.smilie { display: block; height: 100%; background: var(--primary); - transition: width 120ms linear; + transition: width var(--motion-fast) linear; } /* --- a voice message ------------------------------------------------- */ @@ -3727,7 +3665,7 @@ img.smilie { background: var(--surface-container-high); /* A large radius rather than the small one the media cards use: this is a control, and the shape is most of what says so at a glance. */ - border-radius: 999px; + border-radius: var(--radius-pill); width: max-content; max-width: 100%; } @@ -3786,7 +3724,7 @@ img.smilie { border-radius: 2px; background: var(--outline-strong); align-self: center; - transition: background-color 90ms linear; + transition: background-color var(--motion-fast) linear; } /* How far in the listener is: the played part changes colour and keeps its @@ -3813,7 +3751,7 @@ img.smilie { .voice-speed { padding: 2px 7px; border: none; - border-radius: 999px; + border-radius: var(--radius-pill); font: inherit; font-size: var(--font-size-small); font-variant-numeric: tabular-nums; @@ -3857,7 +3795,7 @@ img.smilie { background: var(--surface-container-highest); border: 1px solid var(--outline); border-radius: var(--corner-radius-small); - box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35); + box-shadow: var(--shadow-2); z-index: 3; } @@ -4008,7 +3946,7 @@ img.smilie { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .forward-picker-list { @@ -4055,14 +3993,14 @@ img.smilie { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .discord-modal-head { display: flex; align-items: center; gap: var(--spacing-s); - font-weight: 500; + font-weight: var(--weight-medium); } .discord-modal-field { @@ -4086,14 +4024,14 @@ img.smilie { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .reason-prompt-head { display: flex; align-items: center; gap: var(--spacing-s); - font-weight: 500; + font-weight: var(--weight-medium); } .reason-prompt-box { @@ -4127,7 +4065,7 @@ img.smilie { left: 0; right: 0; bottom: 0; - z-index: 200; + z-index: var(--z-lightbox); /* Belt and braces: nothing in the viewer should ever be draggable. */ -webkit-app-region: no-drag; background: rgba(0, 0, 0, 0.85); @@ -4259,7 +4197,7 @@ video.lightbox-media { border-radius: 0 3px 3px 0; background: var(--surface-text); opacity: 0; - transition: height 120ms ease, opacity 120ms ease; + transition: height var(--motion-fast) ease, opacity var(--motion-fast) ease; } .rail-pill.active { @@ -4283,7 +4221,7 @@ video.lightbox-media { color: var(--surface-variant-text); /* Squircle-to-circle on hover and selection, the way Discord's does. */ border-radius: 34%; - transition: border-radius 120ms ease, background 120ms ease, color 120ms ease; + transition: border-radius var(--motion-fast) ease, background var(--motion-fast) ease, color var(--motion-fast) ease; } .rail-tile:hover .rail-face, @@ -4308,7 +4246,7 @@ video.lightbox-media { .rail-initials { font-size: var(--font-size-medium); - font-weight: 600; + font-weight: var(--weight-semibold); letter-spacing: 0.02em; } @@ -4322,7 +4260,7 @@ video.lightbox-media { width: 100%; height: 100%; border-radius: inherit; - color: #ffffff; + color: var(--on-accent); } /* Which service a tile belongs to, in the corner. Small and quiet - it @@ -4366,12 +4304,12 @@ video.lightbox-media { min-width: 18px; height: 18px; padding: 0 5px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--surface-container-highest); color: var(--surface-text); border: 2px solid var(--surface-container-lowest); - font-size: 11px; - font-weight: 600; + font-size: var(--font-size-xs); + font-weight: var(--weight-semibold); line-height: 14px; text-align: center; } @@ -4401,7 +4339,7 @@ video.lightbox-media { .group-title-name { flex: 1; min-width: 0; - font-weight: 600; + font-weight: var(--weight-semibold); } /* A brand mark kept in its own colours. Unlike a guild icon it is a logo @@ -4573,7 +4511,7 @@ video.lightbox-media { .render-error h2 { font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); } .render-error-detail { @@ -4607,7 +4545,7 @@ video.lightbox-media { width: 100%; max-height: 340px; border-radius: var(--corner-radius); - background: #000; + background: var(--media-backdrop); } .video-expand { @@ -4623,7 +4561,7 @@ video.lightbox-media { background: rgba(0, 0, 0, 0.55); border: none; border-radius: var(--corner-radius-small); - color: #fff; + color: var(--on-accent); opacity: 0; } @@ -4645,8 +4583,8 @@ video.lightbox-media { color: var(--surface-variant-text); text-transform: uppercase; letter-spacing: 0.06em; - font-size: 11px; - font-weight: 600; + font-size: var(--font-size-xs); + font-weight: var(--weight-semibold); } .category-head:hover { @@ -4684,7 +4622,7 @@ video.lightbox-media { .category-count { margin-left: auto; - font-weight: 400; + font-weight: var(--weight-regular); } .category-empty { @@ -4723,8 +4661,8 @@ video.lightbox-media { border-radius: var(--corner-radius-small); color: var(--surface-text); font-family: inherit; - font-size: 11px; - font-weight: 600; + font-size: var(--font-size-xs); + font-weight: var(--weight-semibold); letter-spacing: 0.06em; text-transform: uppercase; outline: none; @@ -4736,7 +4674,7 @@ video.lightbox-media { padding: var(--spacing-s) var(--spacing-s) var(--spacing-xxs); text-transform: uppercase; letter-spacing: 0.06em; - font-size: 11px; + font-size: var(--font-size-xs); } .voice-channel { @@ -4788,7 +4726,7 @@ video.lightbox-media { .voice-you { margin-left: auto; - font-size: 11px; + font-size: var(--font-size-xs); } /* The reason an account is not connected, under its name. Constrained rather @@ -4863,7 +4801,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-2); } /* Says what the button will actually do, which differs between a group and a @@ -5023,7 +4961,7 @@ video.lightbox-media { .channel-browser-name { flex: none; max-width: 40%; - font-weight: 500; + font-weight: var(--weight-medium); } /* Right-aligned and tabular so the counts line up as a column somebody can @@ -5052,7 +4990,7 @@ video.lightbox-media { background: var(--surface-container-highest); color: var(--surface-variant-text); font-size: 9px; - font-weight: 700; + font-weight: var(--weight-bold); letter-spacing: 0.04em; vertical-align: middle; } @@ -5146,7 +5084,7 @@ video.lightbox-media { position: absolute; top: calc(100% + var(--spacing-xs)); right: 0; - z-index: 40; + z-index: var(--z-raised); width: 380px; max-height: 420px; overflow-y: auto; @@ -5154,7 +5092,7 @@ video.lightbox-media { background: var(--surface-container-high); border: 1px solid var(--outline-strong); border-radius: var(--corner-radius); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); + box-shadow: var(--shadow-2); } .search-results-head, @@ -5170,7 +5108,7 @@ video.lightbox-media { padding: var(--spacing-xs) var(--spacing-s); background: none; border: none; - border-radius: var(--spacing-xs); + border-radius: var(--radius-xs); text-align: left; color: var(--surface-text); } @@ -5186,7 +5124,7 @@ video.lightbox-media { } .search-result-from { - font-weight: 600; + font-weight: var(--weight-semibold); } .search-result-body { @@ -5197,7 +5135,7 @@ video.lightbox-media { wall of similar rows. */ .message-row.found { background: var(--primary-pressed); - transition: background 0.4s ease; + transition: background var(--motion-slow) ease; } /* --- voice panel ------------------------------------------------------ */ @@ -5224,13 +5162,13 @@ video.lightbox-media { flex: 1; min-width: 0; color: var(--success); - font-weight: 600; + font-weight: var(--weight-semibold); font-size: var(--font-size-small); } /* Rising bars, so a glance says whether the microphone is hearing anything rather than only that a connection exists. */ -.voice-meter { +.voice-level { display: flex; align-items: flex-end; gap: 2px; @@ -5238,23 +5176,23 @@ video.lightbox-media { flex: none; } -.voice-meter-bar { +.voice-level-bar { width: 3px; background: var(--outline); border-radius: 1px; } -.voice-meter-bar:nth-child(1) { +.voice-level-bar:nth-child(1) { height: 4px; } -.voice-meter-bar:nth-child(2) { +.voice-level-bar:nth-child(2) { height: 8px; } -.voice-meter-bar:nth-child(3) { +.voice-level-bar:nth-child(3) { height: 12px; } -.voice-meter-bar.lit { +.voice-level-bar.lit { background: var(--success); } @@ -5281,11 +5219,11 @@ video.lightbox-media { .modal-scrim { position: fixed; inset: 0; - z-index: 60; + z-index: var(--z-dialog); display: flex; align-items: center; justify-content: center; - background: rgba(0, 0, 0, 0.6); + background: var(--scrim); } .modal { @@ -5298,7 +5236,7 @@ video.lightbox-media { background: var(--surface-container); border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .modal-head { @@ -5309,7 +5247,7 @@ video.lightbox-media { .modal-head h2 { font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); } .folder-field { @@ -5335,7 +5273,7 @@ video.lightbox-media { height: 34px; border: 2px solid transparent; border-radius: var(--corner-radius-small); - color: #fff; + color: var(--on-accent); } .swatch.chosen { @@ -5353,7 +5291,7 @@ video.lightbox-media { color: var(--primary-text); border: none; border-radius: var(--corner-radius-small); - font-weight: 600; + font-weight: var(--weight-semibold); } /* --- rail folders ------------------------------------------------------ */ @@ -5463,7 +5401,7 @@ video.lightbox-media { padding: var(--spacing-xxs); background: none; border: none; - border-radius: var(--spacing-xs); + border-radius: var(--radius-xs); text-align: left; color: inherit; } @@ -5483,7 +5421,7 @@ video.lightbox-media { padding: 0; background: none; border: none; - border-radius: var(--spacing-xs); + border-radius: var(--radius-xs); color: var(--surface-variant-text); } @@ -5505,7 +5443,7 @@ video.lightbox-media { } .user-name { - font-weight: 500; + font-weight: var(--weight-medium); color: var(--surface-text); } @@ -5521,7 +5459,7 @@ video.lightbox-media { display: flex; flex-direction: column-reverse; gap: var(--spacing-s); - z-index: 2100; + z-index: var(--z-call); width: 340px; max-width: calc(100vw - var(--spacing-xl)); } @@ -5534,7 +5472,7 @@ video.lightbox-media { border-radius: var(--corner-radius); background: var(--surface-container-highest); border: 1px solid var(--outline-medium); - box-shadow: 0 6px 22px rgba(0, 0, 0, 0.55); + box-shadow: var(--shadow-2); } .transfer-head { @@ -5583,7 +5521,7 @@ video.lightbox-media { .transfer-bar-fill { height: 100%; background: var(--primary); - transition: width 0.2s linear; + transition: width var(--motion) linear; } .transfer-panel p { @@ -5697,12 +5635,12 @@ video.lightbox-media { min-width: 17px; height: 17px; padding: 0 4px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--primary); color: var(--primary-text); border: 2px solid var(--surface-container-lowest); - font-size: 10px; - font-weight: 600; + font-size: var(--font-size-xxs); + font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; } @@ -5779,7 +5717,7 @@ video.lightbox-media { width: 9px; height: 9px; margin: 0 4px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--primary); flex: none; } @@ -5794,7 +5732,7 @@ video.lightbox-media { border-radius: 6px; background: var(--surface-container-lowest); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 13px; + font-size: var(--font-size-compact); letter-spacing: 0.06em; line-height: 1.7; overflow-wrap: anywhere; @@ -5851,8 +5789,8 @@ video.lightbox-media { } .emoji-jump-initial { - font-size: 12px; - font-weight: 600; + font-size: var(--font-size-small); + font-weight: var(--weight-semibold); } /* Same reasoning as the tab: shown, plainly unusable, and the title says @@ -5910,7 +5848,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius) var(--corner-radius) 0 0; background: var(--surface-container-high); - box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.45); + box-shadow: var(--shadow-up); overflow: hidden; } @@ -6045,7 +5983,7 @@ video.lightbox-media { gap: 3px; margin-left: var(--spacing-xs); padding: 0 6px; - border-radius: 999px; + border-radius: var(--radius-pill); border: 1px solid var(--outline-medium); color: var(--surface-variant-text); font-style: normal; @@ -6083,7 +6021,7 @@ video.lightbox-media { gap: 3px; margin-left: var(--spacing-xs); padding: 0 6px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--whisper-tint); color: var(--whisper); font-style: normal; @@ -6189,7 +6127,7 @@ video.lightbox-media { window - it opens from the right-hand gutter, so it grows leftwards. */ .reader-list { position: fixed; - z-index: 40; + z-index: var(--z-raised); min-width: 180px; max-width: 260px; max-height: 320px; @@ -6201,7 +6139,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-2); } .reader-list-head { @@ -6242,7 +6180,7 @@ video.lightbox-media { .thread-title { flex: 1; - font-weight: 600; + font-weight: var(--weight-semibold); } .thread-body { @@ -6325,7 +6263,7 @@ video.lightbox-media { background: var(--surface-container); border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); overflow: hidden; } @@ -6555,7 +6493,7 @@ video.lightbox-media { background: var(--surface-container); border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .profile-head { @@ -6573,7 +6511,7 @@ video.lightbox-media { .profile-name { font-size: var(--font-size-large); - font-weight: 600; + font-weight: var(--weight-semibold); } .profile-roles { @@ -6681,8 +6619,8 @@ video.lightbox-media { width: 34px; height: 34px; border-radius: var(--corner-radius); - color: #fff; - font-weight: 600; + color: var(--on-accent); + font-weight: var(--weight-semibold); overflow: hidden; } @@ -6736,7 +6674,7 @@ video.lightbox-media { .account-group-name { flex: 1; - font-weight: 600; + font-weight: var(--weight-semibold); } /* --- tagging someone ----------------------------------------------------- @@ -6751,7 +6689,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.35); + box-shadow: var(--shadow-up); } .mention-option { @@ -6901,7 +6839,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); overflow-y: auto; } @@ -7063,7 +7001,7 @@ video.lightbox-media { .room-search-suggested-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--spacing-xxs) var(--spacing-m); max-height: 180px; overflow-y: auto; } .room-search-suggestion { display: flex; flex-direction: column; align-items: flex-start; text-align: left; background: none; border: 0; padding: var(--spacing-xxs) 0; color: inherit; cursor: pointer; font: inherit; } .room-search-suggestion:hover .room-search-suggestion-name { text-decoration: underline; } -.room-search-suggestion-name { font-weight: 600; } +.room-search-suggestion-name { font-weight: var(--weight-semibold); } /* A room being looked into, in the pane a conversation would fill. */ .peek-view { flex: 1; min-height: 0; display: flex; flex-direction: column; } @@ -7083,7 +7021,7 @@ video.lightbox-media { .stage { position: relative; flex: none; - background: #000; + background: var(--media-backdrop); color: #f2f3f5; overflow: hidden; user-select: none; @@ -7099,7 +7037,7 @@ video.lightbox-media { .stage-window-root { position: fixed; inset: 0; - background: #000; + background: var(--media-backdrop); } .stage.collapsed { @@ -7147,12 +7085,12 @@ video.lightbox-media { display: flex; align-items: center; gap: var(--spacing-xs); - font-weight: 600; + font-weight: var(--weight-semibold); } .stage-subtitle { - font-weight: 400; - font-size: 12px; + font-weight: var(--weight-regular); + font-size: var(--font-size-small); color: #b5bac1; margin-left: var(--spacing-xs); } @@ -7176,8 +7114,8 @@ video.lightbox-media { } .stage-head-button:hover { - background: rgba(255, 255, 255, 0.12); - color: #fff; + background: var(--overlay-control); + color: var(--on-accent); } .stage-area { @@ -7190,7 +7128,7 @@ video.lightbox-media { } .stage-empty { - color: #949ba4; + color: var(--text-muted); } .stage-grid { @@ -7229,7 +7167,7 @@ video.lightbox-media { justify-content: center; cursor: pointer; outline: none; - transition: box-shadow 0.12s ease; + transition: box-shadow var(--motion-fast) ease; } .stage-tile.strip { @@ -7242,12 +7180,12 @@ video.lightbox-media { /* Who is talking: the green ring Discord draws. */ .stage-tile.speaking { - box-shadow: inset 0 0 0 3px #23a55a; + box-shadow: inset 0 0 0 3px var(--speaking); } .stage-tile-face .avatar-fallback, .stage-tile-face img { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35); + box-shadow: var(--shadow-2); } .stage-picture { @@ -7256,7 +7194,7 @@ video.lightbox-media { width: 100%; height: 100%; object-fit: contain; - background: #000; + background: var(--media-backdrop); } .stage-tile-name { @@ -7269,22 +7207,22 @@ video.lightbox-media { gap: 4px; max-width: calc(100% - 16px); padding: 2px 8px; - border-radius: 4px; + border-radius: var(--radius-xs); background: rgba(0, 0, 0, 0.6); - color: #fff; - font-size: 13px; - font-weight: 500; + color: var(--on-accent); + font-size: var(--font-size-compact); + font-weight: var(--weight-medium); } .stage-tile.strip .stage-tile-name { left: 4px; bottom: 4px; - font-size: 11px; + font-size: var(--font-size-xs); padding: 1px 6px; } .stage-tile-state { - color: #f23f43; + color: var(--danger-text); } .stage-tile-live { @@ -7293,11 +7231,11 @@ video.lightbox-media { left: 8px; z-index: 1; padding: 1px 6px; - border-radius: 4px; - background: #da373c; - color: #fff; - font-size: 11px; - font-weight: 700; + border-radius: var(--radius-xs); + background: var(--danger); + color: var(--on-accent); + font-size: var(--font-size-xs); + font-weight: var(--weight-bold); letter-spacing: 0.04em; } @@ -7306,11 +7244,11 @@ video.lightbox-media { z-index: 1; padding: 8px 16px; border: 0; - border-radius: 4px; + border-radius: var(--radius-xs); background: rgba(0, 0, 0, 0.72); - color: #fff; + color: var(--on-accent); font: inherit; - font-weight: 600; + font-weight: var(--weight-semibold); cursor: pointer; } @@ -7331,7 +7269,7 @@ video.lightbox-media { border: 0; border-radius: 50%; background: rgba(0, 0, 0, 0.6); - color: #fff; + color: var(--on-accent); cursor: pointer; } @@ -7367,7 +7305,7 @@ video.lightbox-media { background: #2b2d31; color: #f2f3f5; cursor: pointer; - transition: background 0.12s ease; + transition: background var(--motion-fast) ease; } .stage-button.compact { @@ -7388,16 +7326,16 @@ video.lightbox-media { /* Something switched off that normally is on: a muted microphone. */ .stage-button.off { background: #f2f3f5; - color: #da373c; + color: var(--danger); } .stage-button.danger { - background: #da373c; - color: #fff; + background: var(--danger); + color: var(--on-accent); } .stage-button.danger:hover { - background: #a12828; + background: var(--danger-hover); } .stage-resize { @@ -7411,7 +7349,7 @@ video.lightbox-media { } .stage-resize:hover { - background: rgba(255, 255, 255, 0.12); + background: var(--overlay-control); } /* The line left above the conversation while the call is in its own window. */ @@ -7427,7 +7365,7 @@ video.lightbox-media { /* Something a call button asks before it acts, above the button. */ .stage-popover { position: fixed; - z-index: 1000; + z-index: var(--z-menu); transform: translateX(-50%); } @@ -7442,7 +7380,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .go-live-row { @@ -7508,8 +7446,8 @@ video.lightbox-media { padding: 2px 7px; border-radius: 10px; background: rgba(0, 0, 0, 0.6); - color: #fff; - font-size: 11px; + color: var(--on-accent); + font-size: var(--font-size-xs); font-variant-numeric: tabular-nums; pointer-events: none; } @@ -7573,7 +7511,7 @@ video.lightbox-media { background: transparent; color: var(--surface-variant-text, inherit); font: inherit; - font-weight: 600; + font-weight: var(--weight-semibold); cursor: pointer; } @@ -7652,7 +7590,7 @@ video.lightbox-media { color: var(--surface-variant-text, inherit); cursor: pointer; overflow: hidden; - transition: border-radius 0.12s; + transition: border-radius var(--motion-fast); } .emoji-rail-item:hover, @@ -7693,7 +7631,7 @@ video.lightbox-media { display: flex; flex-direction: column; min-width: 0; - font-weight: 600; + font-weight: var(--weight-semibold); } /* A small panel above a call button: the call's volume, the soundboard. */ @@ -7702,7 +7640,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-3); } .call-volume-panel { @@ -7775,7 +7713,7 @@ video.lightbox-media { } .soundboard-emoji { - font-size: 16px; + font-size: var(--font-size-large); line-height: 1; } @@ -7811,7 +7749,7 @@ video.lightbox-media { margin: 0; border: none; border-radius: 0; - background: #000; + background: var(--media-backdrop); } .call-stage.in-window .call-stage-title { @@ -7871,7 +7809,7 @@ video.lightbox-media { border: 1px solid var(--outline-medium); border-radius: var(--corner-radius); background: var(--surface-container-high); - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55); + box-shadow: var(--shadow-3); overflow: hidden; } @@ -7883,8 +7821,8 @@ video.lightbox-media { } .events-pane-count { - font-size: 16px; - font-weight: 600; + font-size: var(--font-size-large); + font-weight: var(--weight-semibold); } .events-pane-sep { @@ -7923,7 +7861,7 @@ video.lightbox-media { } .event-card.live { - border-color: #3ba55d; + border-color: var(--live); } .event-card-image { @@ -7944,13 +7882,13 @@ video.lightbox-media { display: flex; align-items: center; gap: 8px; - font-size: 13px; - font-weight: 600; + font-size: var(--font-size-compact); + font-weight: var(--weight-semibold); color: var(--primary); } .event-card.live .event-card-when { - color: #3ba55d; + color: var(--live); } .event-card-count { @@ -7966,7 +7904,7 @@ video.lightbox-media { .event-card-name { font-size: 18px; - font-weight: 600; + font-weight: var(--weight-semibold); } .event-card-desc { @@ -7998,7 +7936,7 @@ video.lightbox-media { background: var(--surface-container-highest); color: var(--surface-text, inherit); font: inherit; - font-weight: 500; + font-weight: var(--weight-medium); cursor: pointer; } @@ -8040,8 +7978,8 @@ video.lightbox-media { } .event-create-title { - font-size: 16px; - font-weight: 600; + font-size: var(--font-size-large); + font-weight: var(--weight-semibold); } .event-where { @@ -8070,7 +8008,7 @@ video.lightbox-media { } .event-where-label { - font-weight: 600; + font-weight: var(--weight-semibold); } .event-field { @@ -8102,7 +8040,7 @@ video.lightbox-media { /* "Invite friends to event": over the events pane. */ .share-event-backdrop { - z-index: 1600; + z-index: var(--z-sheet); } .share-event { @@ -8139,7 +8077,7 @@ video.lightbox-media { .share-event-section { padding: 10px 0 4px; - font-weight: 600; + font-weight: var(--weight-semibold); } .share-row { @@ -8159,7 +8097,7 @@ video.lightbox-media { flex: 1; flex-direction: column; min-width: 0; - font-weight: 600; + font-weight: var(--weight-semibold); } .share-event-foot { @@ -8172,7 +8110,7 @@ video.lightbox-media { } .share-event-foot-title { - font-weight: 600; + font-weight: var(--weight-semibold); } .share-event-link { @@ -8214,13 +8152,13 @@ video.lightbox-media { width: 8px; height: 8px; border-radius: 50%; - background: #3ba55d; + background: var(--live); } .live-event-label { - color: #3ba55d; - font-weight: 600; - font-size: 13px; + color: var(--live); + font-weight: var(--weight-semibold); + font-size: var(--font-size-compact); } .live-event-name { @@ -8229,8 +8167,8 @@ video.lightbox-media { background: none; color: var(--surface-text, inherit); font: inherit; - font-size: 16px; - font-weight: 700; + font-size: var(--font-size-large); + font-weight: var(--weight-bold); text-align: left; cursor: pointer; } @@ -8247,15 +8185,15 @@ video.lightbox-media { padding: 8px; border: 0; border-radius: var(--corner-radius-small); - background: #248046; - color: #fff; + background: var(--go); + color: var(--on-accent); font: inherit; - font-weight: 600; + font-weight: var(--weight-semibold); cursor: pointer; } .live-event-join:hover:not(:disabled) { - background: #1a6334; + background: var(--go-hover); } .live-event-join:disabled { @@ -8315,7 +8253,7 @@ video.lightbox-media { } .service-banner-title { - font-weight: 600; + font-weight: var(--weight-semibold); } .service-banner-detail { @@ -8356,7 +8294,7 @@ video.lightbox-media { /* An account's routing: three positions on one track, the chosen one under a sliding thumb (#257). */ .route-level-head { - font-weight: 500; + font-weight: var(--weight-medium); } .route-level { @@ -8365,7 +8303,7 @@ video.lightbox-media { grid-template-columns: repeat(3, 1fr); margin-top: var(--spacing-xs); padding: 3px; - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--surface-container-high); } @@ -8375,10 +8313,10 @@ video.lightbox-media { bottom: 3px; left: 3px; width: calc((100% - 6px) / 3); - border-radius: 999px; + border-radius: var(--radius-pill); background: var(--primary-container); transform: translateX(calc(var(--route-index, 0) * 100%)); - transition: transform 160ms ease; + transition: transform var(--motion-fast) ease; } .route-level-option { @@ -8386,7 +8324,7 @@ video.lightbox-media { z-index: 1; padding: 5px 8px; border: none; - border-radius: 999px; + border-radius: var(--radius-pill); background: none; color: var(--surface-variant-text); font: inherit; @@ -8396,7 +8334,7 @@ video.lightbox-media { .route-level-option.on { color: var(--primary-text); - font-weight: 500; + font-weight: var(--weight-medium); } .route-level-desc { @@ -8428,7 +8366,7 @@ video.lightbox-media { flex-direction: column; gap: var(--spacing-s); padding: var(--spacing-s) var(--spacing-m); - border: 1px solid #c9403c; + border: 1px solid var(--danger); border-radius: var(--corner-radius-small); } @@ -8468,7 +8406,7 @@ video.lightbox-media { /* --- formatting in the message box ------------------------------------- */ /* The box shows a format as it will look. */ -.composer-input [data-fmt='bold'] { font-weight: 700; } +.composer-input [data-fmt='bold'] { font-weight: var(--weight-bold); } .composer-input [data-fmt='italic'] { font-style: italic; } .composer-input [data-fmt='underline'] { text-decoration: underline; } .composer-input [data-fmt='strike'] { text-decoration: line-through; } @@ -8491,7 +8429,7 @@ video.lightbox-media { /* The strip over selected text. Centred on the selection, above it. */ .format-bar { position: fixed; - z-index: 1000; + z-index: var(--z-menu); transform: translate(-50%, calc(-100% - 8px)); display: flex; flex-wrap: wrap; @@ -8501,7 +8439,7 @@ video.lightbox-media { border-radius: var(--corner-radius); background: var(--surface-container-high); border: 1px solid var(--outline-medium); - box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5); + box-shadow: var(--shadow-2); } .format-group { @@ -8546,8 +8484,15 @@ video.lightbox-media { justify-content: center; padding: 0; border: 1px solid var(--outline-medium); - border-radius: 4px; + border-radius: var(--radius-xs); cursor: pointer; color: var(--surface-variant-text); background: none; } + +/* Timestamps and reply previews are there, and quieter than what was said. The + second class is what lets this beat `.muted`, which they also carry. */ +.message-time.muted, +.reply-preview.muted { + color: var(--text-muted); +} diff --git a/src/renderer/src/components/AddToConversation.tsx b/src/renderer/src/components/AddToConversation.tsx index 8ef6159..fdde782 100644 --- a/src/renderer/src/components/AddToConversation.tsx +++ b/src/renderer/src/components/AddToConversation.tsx @@ -158,7 +158,7 @@ export function AddToConversation({ buffer }: { buffer: BufferEntry }): JSX.Elem )} {f.globalName || f.username} - + ))} diff --git a/src/renderer/src/components/Icon.tsx b/src/renderer/src/components/Icon.tsx index 5327105..6f5e95c 100644 --- a/src/renderer/src/components/Icon.tsx +++ b/src/renderer/src/components/Icon.tsx @@ -20,7 +20,10 @@ export function Icon({ name, size = 20, color, fill, className, style }: Props): width: size, height: size, color, - ...(fill ? { fontVariationSettings: "'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24" } : {}), + // The glyph is drawn for its size: the font's optical-size axis runs + // 20-48, and small ones are given a little more weight, or they read + // as hairlines next to text. + fontVariationSettings: `'FILL' ${fill ? 1 : 0}, 'wght' ${size <= 14 ? 500 : 400}, 'GRAD' 0, 'opsz' ${Math.min(48, Math.max(20, size))}`, ...style }} > diff --git a/src/renderer/src/components/JoinPanel.tsx b/src/renderer/src/components/JoinPanel.tsx index ff9a764..706f639 100644 --- a/src/renderer/src/components/JoinPanel.tsx +++ b/src/renderer/src/components/JoinPanel.tsx @@ -886,7 +886,7 @@ function DiscordJoin({ account }: { account: Account }): JSX.Element { )} {f.globalName || f.username} {f.username} - + ))} diff --git a/src/renderer/src/components/VoicePanel.tsx b/src/renderer/src/components/VoicePanel.tsx index 3b774b0..9a7b3ba 100644 --- a/src/renderer/src/components/VoicePanel.tsx +++ b/src/renderer/src/components/VoicePanel.tsx @@ -57,9 +57,9 @@ export function VoicePanel(): JSX.Element | null {
- + {[0, 1, 2].map((i) => ( - + ))} Voice Connected diff --git a/src/renderer/src/styles.test.ts b/src/renderer/src/styles.test.ts new file mode 100644 index 0000000..ac4a59a --- /dev/null +++ b/src/renderer/src/styles.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest' +import { readFileSync } from 'node:fs' +import { join } from 'node:path' + +const css = ['theme.css', 'app.css'].map((f) => readFileSync(join(__dirname, f), 'utf8')).join('\n') + +describe('the stylesheets', () => { + it('only use custom properties that something defines, or that carry a fallback', () => { + const defined = new Set([...css.matchAll(/(--[a-z0-9-]+)\s*:/gi)].map((m) => m[1])) + // `var(--x)` with no comma is a use with no fallback. + const used = [...css.matchAll(/var\((--[a-z0-9-]+)\s*\)/gi)].map((m) => m[1]) + const missing = [...new Set(used.filter((name) => !defined.has(name)))] + // An undefined property draws a border in the text colour and text in + // whatever it inherited - which is how seven of them went unnoticed (#268). + expect(missing).toEqual([]) + }) +})