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