mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-30 18:46:00 +00:00
feat(appearance): show per-size text controls inline with examples
The four size-class sliders (Large/Medium/Normal/Small) are now always visible instead of behind a disclosure, each with a live sample rendered at that size and an example of what it affects (e.g. Normal = place names/descriptions, Small = addresses/labels).
This commit is contained in:
@@ -79,7 +79,6 @@ export default function AppearanceSettingsTab(): React.ReactElement {
|
||||
const tr = (key: string, fallback: string) => t(key) || fallback
|
||||
|
||||
const [cfg, setCfg] = useState<AppearanceConfig>(() => normalizeAppearance(settings.appearance))
|
||||
const [advancedType, setAdvancedType] = useState(false)
|
||||
const persistTimer = useRef<number | undefined>(undefined)
|
||||
|
||||
// Re-sync when settings change elsewhere (e.g. server reconcile / another tab).
|
||||
@@ -285,39 +284,52 @@ export default function AppearanceSettingsTab(): React.ReactElement {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Text size — live preview + global slider + per size-class */}
|
||||
{/* Text size — global, plus an always-visible row per size class with a
|
||||
live sample and an example of what each size affects. */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2 text-content-secondary">
|
||||
{tr('settings.appearance.textSize', 'Text size')}
|
||||
</label>
|
||||
{/* Live preview — these lines use the same size classes the controls drive. */}
|
||||
<div className="rounded-lg border border-edge-secondary px-3 py-2.5 mb-3 space-y-1 overflow-hidden">
|
||||
<div className="text-title font-bold text-content leading-tight">{tr('settings.appearance.preview.large', 'Large heading')}</div>
|
||||
<div className="text-subtitle font-semibold text-content-secondary">{tr('settings.appearance.preview.medium', 'Medium subtitle')}</div>
|
||||
<div className="text-body text-content-secondary">{tr('settings.appearance.preview.normal', 'Normal body text')}</div>
|
||||
<div className="text-caption text-content-faint">{tr('settings.appearance.preview.small', 'Small caption / address')}</div>
|
||||
</div>
|
||||
<SliderRow
|
||||
label={tr('settings.appearance.textSizeAll', 'Everything')}
|
||||
value={cfg.fontScale}
|
||||
onChange={(v) => update({ fontScale: v })}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setAdvancedType((v) => !v)}
|
||||
className="text-xs font-medium text-content-muted hover:text-content"
|
||||
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}
|
||||
>
|
||||
{advancedType ? tr('settings.appearance.hideAdvanced', 'Hide per-size controls') : tr('settings.appearance.perSize', 'Adjust each size separately')}
|
||||
</button>
|
||||
{advancedType && (
|
||||
<div className="space-y-3 pl-1">
|
||||
<SliderRow label={tr('settings.appearance.size.large', 'Large')} value={cfg.typeScale.title} onChange={(v) => update({ typeScale: { ...cfg.typeScale, title: v } })} />
|
||||
<SliderRow label={tr('settings.appearance.size.medium', 'Medium')} value={cfg.typeScale.subtitle} onChange={(v) => update({ typeScale: { ...cfg.typeScale, subtitle: v } })} />
|
||||
<SliderRow label={tr('settings.appearance.size.normal', 'Normal')} value={cfg.typeScale.body} onChange={(v) => update({ typeScale: { ...cfg.typeScale, body: v } })} />
|
||||
<SliderRow label={tr('settings.appearance.size.small', 'Small')} value={cfg.typeScale.caption} onChange={(v) => update({ typeScale: { ...cfg.typeScale, caption: v } })} />
|
||||
<div className="space-y-4 mt-4 pt-4 border-t border-edge-secondary">
|
||||
<SizeRow
|
||||
sampleClass="text-title font-bold"
|
||||
name={tr('settings.appearance.size.large', 'Large')}
|
||||
example={tr('settings.appearance.example.large', 'Headings, big numbers')}
|
||||
sample={tr('settings.appearance.preview.large', 'Large heading')}
|
||||
value={cfg.typeScale.title}
|
||||
onChange={(v) => update({ typeScale: { ...cfg.typeScale, title: v } })}
|
||||
/>
|
||||
<SizeRow
|
||||
sampleClass="text-subtitle font-semibold"
|
||||
name={tr('settings.appearance.size.medium', 'Medium')}
|
||||
example={tr('settings.appearance.example.medium', 'Sub-headings')}
|
||||
sample={tr('settings.appearance.preview.medium', 'Medium subtitle')}
|
||||
value={cfg.typeScale.subtitle}
|
||||
onChange={(v) => update({ typeScale: { ...cfg.typeScale, subtitle: v } })}
|
||||
/>
|
||||
<SizeRow
|
||||
sampleClass="text-body"
|
||||
name={tr('settings.appearance.size.normal', 'Normal')}
|
||||
example={tr('settings.appearance.example.normal', 'Place names, descriptions')}
|
||||
sample={tr('settings.appearance.preview.normal', 'Normal body text')}
|
||||
value={cfg.typeScale.body}
|
||||
onChange={(v) => update({ typeScale: { ...cfg.typeScale, body: v } })}
|
||||
/>
|
||||
<SizeRow
|
||||
sampleClass="text-caption"
|
||||
name={tr('settings.appearance.size.small', 'Small')}
|
||||
example={tr('settings.appearance.example.small', 'Addresses, labels')}
|
||||
sample={tr('settings.appearance.preview.small', 'Small caption / address')}
|
||||
value={cfg.typeScale.caption}
|
||||
onChange={(v) => update({ typeScale: { ...cfg.typeScale, caption: v } })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ── Dashboard widgets ───────────────────────────────────── */}
|
||||
@@ -436,3 +448,28 @@ function SliderRow({ label, value, onChange }: { label: string; value: number; o
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SizeRow({ sampleClass, name, example, sample, value, onChange }: { sampleClass: string; name: string; example: string; sample: string; value: number; onChange: (v: number) => void }) {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-end justify-between gap-3 mb-1.5">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className={`${sampleClass} text-content leading-tight truncate`}>{sample}</div>
|
||||
<div className="text-xs text-content-faint mt-0.5">
|
||||
<span className="font-medium text-content-muted">{name}</span> · {example}
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-xs text-content-muted tabular-nums shrink-0">{Math.round(value * 100)}%</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min={APPEARANCE_SCALE_MIN}
|
||||
max={APPEARANCE_SCALE_MAX}
|
||||
step={0.05}
|
||||
value={value}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
style={{ width: '100%', accentColor: 'var(--accent)', cursor: 'pointer' }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -388,6 +388,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -398,6 +398,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -395,6 +395,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -402,6 +402,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Mittlerer Untertitel',
|
||||
'settings.appearance.preview.normal': 'Normaler Fließtext',
|
||||
'settings.appearance.preview.small': 'Kleine Beschriftung / Adresse',
|
||||
'settings.appearance.example.large': 'Überschriften, große Zahlen',
|
||||
'settings.appearance.example.medium': 'Zwischenüberschriften',
|
||||
'settings.appearance.example.normal': 'Ortsnamen, Beschreibungen',
|
||||
'settings.appearance.example.small': 'Adressen, Labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -396,6 +396,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -401,6 +401,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -406,6 +406,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -407,6 +407,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -400,6 +400,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -399,6 +399,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -399,6 +399,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -376,6 +376,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -391,6 +391,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -399,6 +399,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -400,6 +400,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -401,6 +401,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -396,6 +396,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -396,6 +396,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -400,6 +400,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -380,6 +380,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
@@ -378,6 +378,10 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.preview.medium': 'Medium subtitle',
|
||||
'settings.appearance.preview.normal': 'Normal body text',
|
||||
'settings.appearance.preview.small': 'Small caption / address',
|
||||
'settings.appearance.example.large': 'Headings, big numbers',
|
||||
'settings.appearance.example.medium': 'Sub-headings',
|
||||
'settings.appearance.example.normal': 'Place names, descriptions',
|
||||
'settings.appearance.example.small': 'Addresses, labels',
|
||||
};
|
||||
|
||||
export default settings;
|
||||
|
||||
Reference in New Issue
Block a user