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 <[email protected]>
This commit is contained in:
@@ -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) => {
|
||||
|
||||
+270
-325
File diff suppressed because it is too large
Load Diff
@@ -158,7 +158,7 @@ export function AddToConversation({ buffer }: { buffer: BufferEntry }): JSX.Elem
|
||||
</span>
|
||||
)}
|
||||
<span className="ellipsis">{f.globalName || f.username}</span>
|
||||
<span className={`presence-dot ${f.status || 'offline'}`} />
|
||||
<span className={`status-dot ${f.status || 'offline'}`} />
|
||||
</button>
|
||||
))}
|
||||
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -886,7 +886,7 @@ function DiscordJoin({ account }: { account: Account }): JSX.Element {
|
||||
)}
|
||||
<span className="ellipsis">{f.globalName || f.username}</span>
|
||||
<span className="small muted ellipsis">{f.username}</span>
|
||||
<span className={`presence-dot ${f.status || 'offline'}`} />
|
||||
<span className={`status-dot ${f.status || 'offline'}`} />
|
||||
</button>
|
||||
))}
|
||||
|
||||
|
||||
@@ -57,9 +57,9 @@ export function VoicePanel(): JSX.Element | null {
|
||||
<div className="voice-panel">
|
||||
<div className="voice-panel-head">
|
||||
<span className="voice-panel-state">
|
||||
<span className="voice-meter" aria-hidden>
|
||||
<span className="voice-level" aria-hidden>
|
||||
{[0, 1, 2].map((i) => (
|
||||
<span key={i} className={`voice-meter-bar${i < bars ? ' lit' : ''}`} />
|
||||
<span key={i} className={`voice-level-bar${i < bars ? ' lit' : ''}`} />
|
||||
))}
|
||||
</span>
|
||||
Voice Connected
|
||||
|
||||
@@ -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([])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user