Add Switch and Radio atom components

Switch:
- Wraps MUI Switch with FA brand tokens
- Bordered pill track (Figma Style One), brand.500 fill when active
- 4 component tokens: track width/height/borderRadius, thumb size
- Stories include interactive service add-ons demo

Radio:
- Wraps MUI Radio with FA brand tokens
- Brand.500 fill when selected, neutral.400 unchecked
- 2 component tokens: outer size, dot size
- Stories include card selection and payment method patterns

Also:
- Added ColourToggle and Slider to component registry (deferred)
- Updated token registry and session log

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 17:04:37 +11:00
parent b2349d6c78
commit c10a5e4e1c
14 changed files with 529 additions and 2 deletions

View File

@@ -26,6 +26,11 @@
--fa-input-height-sm: 40px; /** Small — compact forms, admin layouts, matches Button medium height */
--fa-input-height-md: 48px; /** Medium (default) — standard forms, matches Button large for alignment */
--fa-input-icon-size-default: 20px; /** 20px — icon size inside input field, matches Figma trailing icon */
--fa-radio-size-default: 20px; /** Default radio size — matches Figma 16px + padding for 44px touch target area */
--fa-radio-dot-size-default: 10px; /** Selected indicator dot — 50% of outer size */
--fa-switch-track-width: 44px; /** Track width — slightly narrower than Figma 52px for better proportion with 44px touch target */
--fa-switch-track-height: 24px; /** Track height */
--fa-switch-thumb-size: 18px; /** Thumb diameter — sits inside the track with 3px inset */
--fa-color-brand-50: #fef9f5; /** Lightest warm tint — warm section backgrounds */
--fa-color-brand-100: #f7ecdf; /** Light warm — hover backgrounds, subtle fills */
--fa-color-brand-200: #ebdac8; /** Warm light — secondary backgrounds, divider tones */
@@ -260,6 +265,7 @@
--fa-input-font-size-default: var(--fa-font-size-base); /** 16px — prevents iOS auto-zoom on focus, matches Figma */
--fa-input-border-radius-default: var(--fa-border-radius-sm); /** 4px — subtle rounding, consistent with Figma design */
--fa-input-gap-default: var(--fa-spacing-2); /** 8px — vertical rhythm between label/input/helper, slightly more generous than Figma's 6px for readability */
--fa-switch-track-border-radius: var(--fa-border-radius-full); /** Pill shape */
--fa-color-text-primary: var(--fa-color-neutral-800); /** Primary text — body content, headings. Cool charcoal (#2C2E35) for comfortable extended reading */
--fa-color-text-secondary: var(--fa-color-neutral-600); /** Secondary text — helper text, descriptions, metadata, less prominent content */
--fa-color-text-tertiary: var(--fa-color-neutral-500); /** Tertiary text — placeholders, timestamps, attribution, meta information */

View File

@@ -72,6 +72,12 @@ export const InputFontSizeDefault = "1rem"; // 16px — prevents iOS auto-zoom o
export const InputBorderRadiusDefault = "4px"; // 4px — subtle rounding, consistent with Figma design
export const InputGapDefault = "8px"; // 8px — vertical rhythm between label/input/helper, slightly more generous than Figma's 6px for readability
export const InputIconSizeDefault = "20px"; // 20px — icon size inside input field, matches Figma trailing icon
export const RadioSizeDefault = "20px"; // Default radio size — matches Figma 16px + padding for 44px touch target area
export const RadioDotSizeDefault = "10px"; // Selected indicator dot — 50% of outer size
export const SwitchTrackWidth = "44px"; // Track width — slightly narrower than Figma 52px for better proportion with 44px touch target
export const SwitchTrackHeight = "24px"; // Track height
export const SwitchTrackBorderRadius = "9999px"; // Pill shape
export const SwitchThumbSize = "18px"; // Thumb diameter — sits inside the track with 3px inset
export const ColorBrand50 = "#fef9f5"; // Lightest warm tint — warm section backgrounds
export const ColorBrand100 = "#f7ecdf"; // Light warm — hover backgrounds, subtle fills
export const ColorBrand200 = "#ebdac8"; // Warm light — secondary backgrounds, divider tones

View File

@@ -636,6 +636,75 @@ export const theme = createTheme({
},
},
},
MuiSwitch: {
styleOverrides: {
root: {
width: parseInt(t.SwitchTrackWidth, 10) + 12,
height: parseInt(t.SwitchTrackHeight, 10) + 12,
padding: 6,
},
switchBase: {
padding: 9,
'&.Mui-checked': {
transform: `translateX(${parseInt(t.SwitchTrackWidth, 10) - parseInt(t.SwitchTrackHeight, 10)}px)`,
color: t.ColorWhite,
'& + .MuiSwitch-track': {
backgroundColor: t.ColorInteractiveDefault,
borderColor: t.ColorInteractiveDefault,
opacity: 1,
},
'&:hover + .MuiSwitch-track': {
backgroundColor: t.ColorInteractiveHover,
},
},
'&.Mui-disabled + .MuiSwitch-track': {
opacity: 0.4,
},
'&:focus-visible + .MuiSwitch-track': {
outline: `2px solid ${t.ColorInteractiveFocus}`,
outlineOffset: '2px',
},
},
thumb: {
width: parseInt(t.SwitchThumbSize, 10),
height: parseInt(t.SwitchThumbSize, 10),
boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
},
track: {
borderRadius: parseInt(t.SwitchTrackBorderRadius, 10),
backgroundColor: t.ColorWhite,
border: `1.5px solid ${t.ColorNeutral400}`,
opacity: 1,
transition: 'background-color 150ms ease-in-out, border-color 150ms ease-in-out',
},
},
},
MuiRadio: {
styleOverrides: {
root: {
color: t.ColorNeutral400,
transition: 'color 150ms ease-in-out',
'&:hover': {
color: t.ColorNeutral600,
backgroundColor: 'transparent',
},
'&.Mui-checked': {
color: t.ColorInteractiveDefault,
'&:hover': {
color: t.ColorInteractiveHover,
},
},
'&:focus-visible': {
outline: `2px solid ${t.ColorInteractiveFocus}`,
outlineOffset: '2px',
borderRadius: '50%',
},
'&.Mui-disabled': {
color: t.ColorNeutral300,
},
},
},
},
},
});