fix(poll): render vote buttons with folds tokens (N4)

Poll answer buttons referenced undefined CSS vars (--accent-cyan,
--accent-cyan-dim, --accent-cyan-border, --border-color) plus hardcoded
rgba()/#fff and raw rem font sizes, so they rendered unstyled on every
non-TDS theme (invisible borders, no selected/progress state).

Replace all colors with always-defined folds tokens (Primary.* for the
selected/indicator state, SurfaceVariant.* for the resting surface +
progress fill), size/spacing/radii with config.* tokens, and the
checkbox/radio glyphs + percentage/label text with folds <Text>. The
progress-bar-behind-text affordance is preserved (folds Button has no
equivalent), now theme-reactive. Merged the duplicate checkbox/radio
indicator spans into one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 04:02:13 -04:00
parent 23649d85b0
commit caf6318a5d
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { Box, Text } from 'folds'; import { Box, color, config, Text, toRem } from 'folds';
import { RelationsEvent } from 'matrix-js-sdk/lib/models/relations'; import { RelationsEvent } from 'matrix-js-sdk/lib/models/relations';
import { RoomEvent } from 'matrix-js-sdk'; import { RoomEvent } from 'matrix-js-sdk';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
@@ -175,7 +175,7 @@ export function PollContent({
if (!poll) { if (!poll) {
return ( return (
<Text style={{ opacity: 0.6 }}> <Text priority="300">
<i>Poll (unreadable format)</i> <i>Poll (unreadable format)</i>
</Text> </Text>
); );
@@ -244,21 +244,20 @@ export function PollContent({
gap="200" gap="200"
style={{ maxWidth: '340px', paddingTop: '2px', paddingBottom: '4px' }} style={{ maxWidth: '340px', paddingTop: '2px', paddingBottom: '4px' }}
> >
<Box <Text
alignItems="Center" as="div"
gap="100" size="T200"
priority="300"
data-poll-content-label data-poll-content-label
style={{ style={{
fontSize: '0.68rem',
fontWeight: 700, fontWeight: 700,
letterSpacing: '0.12em', letterSpacing: '0.12em',
textTransform: 'uppercase', textTransform: 'uppercase',
opacity: 0.55, marginBottom: config.space.S100,
marginBottom: '2px',
}} }}
> >
{`◉ Poll · ${isMultiple ? 'Multiple choice' : 'Single choice'}`} {`◉ Poll · ${isMultiple ? 'Multiple choice' : 'Single choice'}`}
</Box> </Text>
<Text size="T400" style={{ fontWeight: 600 }}> <Text size="T400" style={{ fontWeight: 600 }}>
{questionText} {questionText}
</Text> </Text>
@@ -280,18 +279,19 @@ export function PollContent({
data-selected={selected} data-selected={selected}
onClick={canVote ? () => handleVote(id) : undefined} onClick={canVote ? () => handleVote(id) : undefined}
style={{ style={{
padding: '7px 12px', padding: `${config.space.S200} ${config.space.S300}`,
borderRadius: '8px', borderRadius: config.radii.R300,
background: selected ? 'var(--accent-cyan-dim)' : 'rgba(255,255,255,0.04)', background: selected ? color.Primary.Container : color.SurfaceVariant.Container,
border: `1.5px solid ${selected ? 'var(--accent-cyan)' : 'var(--border-color)'}`, border: `${config.borderWidth.B300} solid ${
fontSize: '0.88rem', selected ? color.Primary.Main : color.SurfaceVariant.ContainerLine
}`,
lineHeight: 1.4, lineHeight: 1.4,
textAlign: 'left', textAlign: 'left',
cursor: canVote ? 'pointer' : 'default', cursor: canVote ? 'pointer' : 'default',
color: 'inherit', color: 'inherit',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: '4px', gap: config.space.S100,
width: '100%', width: '100%',
position: 'relative', position: 'relative',
overflow: 'hidden', overflow: 'hidden',
@@ -306,58 +306,59 @@ export function PollContent({
inset: 0, inset: 0,
right: 'auto', right: 'auto',
width: `${pct}%`, width: `${pct}%`,
background: selected ? 'var(--accent-cyan-dim)' : 'rgba(255,255,255,0.03)', background: selected
? color.Primary.ContainerActive
: color.SurfaceVariant.ContainerActive,
pointerEvents: 'none', pointerEvents: 'none',
transition: 'width 0.3s ease', transition: 'width 0.3s ease',
}} }}
/> />
)} )}
<span <span
style={{ display: 'flex', alignItems: 'center', gap: '8px', position: 'relative' }} style={{
display: 'flex',
alignItems: 'center',
gap: config.space.S200,
position: 'relative',
}}
> >
{isMultiple && ( <span
<span style={{
style={{ flexShrink: 0,
flexShrink: 0, width: toRem(14),
width: '14px', height: toRem(14),
height: '14px', border: `${config.borderWidth.B300} solid ${
border: `1.5px solid ${selected ? 'var(--accent-cyan)' : 'var(--accent-cyan-border)'}`, selected ? color.Primary.Main : color.Primary.ContainerLine
borderRadius: '3px', }`,
background: selected ? 'var(--accent-cyan)' : 'none', borderRadius: isMultiple ? config.radii.R300 : config.radii.Pill,
display: 'flex', background: selected ? color.Primary.Main : 'transparent',
alignItems: 'center', display: 'flex',
justifyContent: 'center', alignItems: 'center',
fontSize: '10px', justifyContent: 'center',
color: '#fff', color: color.Primary.OnMain,
transition: 'all 0.15s', transition: 'all 0.15s',
}} }}
> >
{selected ? '✓' : ''} {selected && isMultiple ? (
</span> <Text as="span" size="T200" style={{ lineHeight: 1 }}>
)}
{!isMultiple && ( </Text>
<span ) : null}
style={{ </span>
flexShrink: 0, <Text as="span" size="T300" style={{ flexGrow: 1 }}>
width: '14px', {text}
height: '14px', </Text>
border: `1.5px solid ${selected ? 'var(--accent-cyan)' : 'var(--accent-cyan-border)'}`,
borderRadius: '50%',
background: selected ? 'var(--accent-cyan)' : 'none',
transition: 'all 0.15s',
}}
/>
)}
<span style={{ flexGrow: 1 }}>{text}</span>
{total > 0 && ( {total > 0 && (
<span style={{ opacity: 0.55, fontSize: '0.78rem', flexShrink: 0 }}>{pct}%</span> <Text as="span" size="T200" priority="300" style={{ flexShrink: 0 }}>
{pct}%
</Text>
)} )}
</span> </span>
</button> </button>
); );
})} })}
</Box> </Box>
<Text size="T200" style={{ opacity: 0.5, marginTop: '2px' }}> <Text size="T200" priority="300" style={{ marginTop: '2px' }}>
<i> <i>
{total > 0 ? `${total} vote${total === 1 ? '' : 's'} · ` : ''} {total > 0 ? `${total} vote${total === 1 ? '' : 's'} · ` : ''}
{canVote {canVote