Initial commit: FA 2.0 Design System foundation
Token pipeline (Style Dictionary v4, DTCG format): - Primitive tokens: colour palettes (brand, sage, neutral, feedback), typography (3 font families, 21-variant type scale), spacing (4px grid), border radius, shadows, opacity - Semantic tokens: text, surface, border, interactive, feedback colours; typography roles; layout spacing - Component tokens: Button (4 sizes), Input (2 sizes) - Generated outputs: CSS custom properties, JS ES6 module, flat JSON Atoms (3 components): - Button: contained/soft/outlined/text × primary/secondary, 4 sizes, loading state, underline for text variant - Typography: 21 variants across display/heading/body/label/caption/overline, maxLines truncation - Input: external label, helper text, error/success validation, start/end icons, required indicator, 2 sizes, multiline support Infrastructure: - MUI v5 theme with full token mapping - Storybook 8 with autodocs - Claude Code agents and skills for token/component workflows - Design system documentation and cross-session memory Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
568
src/theme/index.ts
Normal file
568
src/theme/index.ts
Normal file
@@ -0,0 +1,568 @@
|
||||
/**
|
||||
* FA Design System — MUI Theme
|
||||
*
|
||||
* Maps design tokens to MUI's theme structure. All values come from the
|
||||
* token pipeline: Token JSON → Style Dictionary → generated/tokens.js → here.
|
||||
*
|
||||
* To update colours/typography/spacing: edit tokens/ JSON, run `npm run build:tokens`,
|
||||
* then update references here if token names changed.
|
||||
*/
|
||||
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import type { CSSProperties } from 'react';
|
||||
import * as t from './generated/tokens.js';
|
||||
|
||||
/* ---------- Types ---------- */
|
||||
|
||||
type StyleWithMedia = CSSProperties & {
|
||||
[key: `@media ${string}`]: CSSProperties;
|
||||
};
|
||||
|
||||
/* ---------- Custom typography variant declarations ---------- */
|
||||
|
||||
declare module '@mui/material/styles' {
|
||||
interface TypographyVariants {
|
||||
displayHero: StyleWithMedia;
|
||||
display1: StyleWithMedia;
|
||||
display2: StyleWithMedia;
|
||||
display3: StyleWithMedia;
|
||||
displaySm: StyleWithMedia;
|
||||
bodyLg: CSSProperties;
|
||||
bodyXs: CSSProperties;
|
||||
labelLg: CSSProperties;
|
||||
label: CSSProperties;
|
||||
labelSm: CSSProperties;
|
||||
captionSm: CSSProperties;
|
||||
overlineSm: CSSProperties;
|
||||
}
|
||||
interface TypographyVariantsOptions {
|
||||
displayHero?: StyleWithMedia;
|
||||
display1?: StyleWithMedia;
|
||||
display2?: StyleWithMedia;
|
||||
display3?: StyleWithMedia;
|
||||
displaySm?: StyleWithMedia;
|
||||
bodyLg?: CSSProperties;
|
||||
bodyXs?: CSSProperties;
|
||||
labelLg?: CSSProperties;
|
||||
label?: CSSProperties;
|
||||
labelSm?: CSSProperties;
|
||||
captionSm?: CSSProperties;
|
||||
overlineSm?: CSSProperties;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@mui/material/Typography' {
|
||||
interface TypographyPropsVariantOverrides {
|
||||
displayHero: true;
|
||||
display1: true;
|
||||
display2: true;
|
||||
display3: true;
|
||||
displaySm: true;
|
||||
bodyLg: true;
|
||||
bodyXs: true;
|
||||
labelLg: true;
|
||||
label: true;
|
||||
labelSm: true;
|
||||
captionSm: true;
|
||||
overlineSm: true;
|
||||
// Disable old aliases
|
||||
display: false;
|
||||
bodyLarge: false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Custom Button declarations ---------- */
|
||||
|
||||
declare module '@mui/material/Button' {
|
||||
interface ButtonPropsSizeOverrides {
|
||||
xs: true;
|
||||
}
|
||||
interface ButtonPropsVariantOverrides {
|
||||
soft: true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Theme ---------- */
|
||||
|
||||
const MOBILE = '@media (max-width:600px)';
|
||||
|
||||
export const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: t.ColorInteractiveDefault,
|
||||
dark: t.ColorInteractiveActive,
|
||||
light: t.ColorBrand400,
|
||||
contrastText: t.ColorTextInverse,
|
||||
},
|
||||
secondary: {
|
||||
main: t.ColorNeutral600,
|
||||
dark: t.ColorNeutral700,
|
||||
light: t.ColorNeutral300,
|
||||
contrastText: t.ColorTextInverse,
|
||||
},
|
||||
error: {
|
||||
main: t.ColorFeedbackError,
|
||||
light: t.ColorRed400,
|
||||
dark: t.ColorRed800,
|
||||
contrastText: t.ColorTextInverse,
|
||||
},
|
||||
warning: {
|
||||
main: t.ColorFeedbackWarning,
|
||||
light: t.ColorAmber400,
|
||||
dark: t.ColorAmber800,
|
||||
contrastText: t.ColorTextInverse,
|
||||
},
|
||||
success: {
|
||||
main: t.ColorFeedbackSuccess,
|
||||
light: t.ColorGreen400,
|
||||
dark: t.ColorGreen800,
|
||||
contrastText: t.ColorTextInverse,
|
||||
},
|
||||
info: {
|
||||
main: t.ColorFeedbackInfo,
|
||||
light: t.ColorBlue400,
|
||||
dark: t.ColorBlue800,
|
||||
contrastText: t.ColorTextInverse,
|
||||
},
|
||||
text: {
|
||||
primary: t.ColorTextPrimary,
|
||||
secondary: t.ColorTextSecondary,
|
||||
disabled: t.ColorTextDisabled,
|
||||
},
|
||||
background: {
|
||||
default: t.ColorSurfaceDefault,
|
||||
paper: t.ColorSurfaceRaised,
|
||||
},
|
||||
divider: t.ColorBorderDefault,
|
||||
action: {
|
||||
disabled: t.ColorTextDisabled,
|
||||
disabledBackground: t.ColorInteractiveDisabled,
|
||||
},
|
||||
},
|
||||
|
||||
typography: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
|
||||
/* ── Display (Noto Serif SC, Regular 400) ── */
|
||||
displayHero: {
|
||||
fontFamily: t.FontFamilyDisplay,
|
||||
fontSize: t.TypographyDisplayHeroFontSize,
|
||||
fontWeight: t.TypographyDisplayHeroFontWeight,
|
||||
lineHeight: t.TypographyDisplayHeroLineHeight,
|
||||
letterSpacing: t.TypographyDisplayHeroLetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyDisplayHeroFontSizeMobile },
|
||||
},
|
||||
display1: {
|
||||
fontFamily: t.FontFamilyDisplay,
|
||||
fontSize: t.TypographyDisplay1FontSize,
|
||||
fontWeight: t.TypographyDisplay1FontWeight,
|
||||
lineHeight: t.TypographyDisplay1LineHeight,
|
||||
letterSpacing: t.TypographyDisplay1LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyDisplay1FontSizeMobile },
|
||||
},
|
||||
display2: {
|
||||
fontFamily: t.FontFamilyDisplay,
|
||||
fontSize: t.TypographyDisplay2FontSize,
|
||||
fontWeight: t.TypographyDisplay2FontWeight,
|
||||
lineHeight: t.TypographyDisplay2LineHeight,
|
||||
letterSpacing: t.TypographyDisplay2LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyDisplay2FontSizeMobile },
|
||||
},
|
||||
display3: {
|
||||
fontFamily: t.FontFamilyDisplay,
|
||||
fontSize: t.TypographyDisplay3FontSize,
|
||||
fontWeight: t.TypographyDisplay3FontWeight,
|
||||
lineHeight: t.TypographyDisplay3LineHeight,
|
||||
letterSpacing: t.TypographyDisplay3LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyDisplay3FontSizeMobile },
|
||||
},
|
||||
displaySm: {
|
||||
fontFamily: t.FontFamilyDisplay,
|
||||
fontSize: t.TypographyDisplaySmFontSize,
|
||||
fontWeight: t.TypographyDisplaySmFontWeight,
|
||||
lineHeight: t.TypographyDisplaySmLineHeight,
|
||||
letterSpacing: t.TypographyDisplaySmLetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyDisplaySmFontSizeMobile },
|
||||
},
|
||||
|
||||
/* ── Headings (Montserrat, Bold 700) ── */
|
||||
h1: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyH1FontSize,
|
||||
fontWeight: t.TypographyH1FontWeight,
|
||||
lineHeight: t.TypographyH1LineHeight,
|
||||
letterSpacing: t.TypographyH1LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyH1FontSizeMobile },
|
||||
},
|
||||
h2: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyH2FontSize,
|
||||
fontWeight: t.TypographyH2FontWeight,
|
||||
lineHeight: t.TypographyH2LineHeight,
|
||||
letterSpacing: t.TypographyH2LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyH2FontSizeMobile },
|
||||
},
|
||||
h3: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyH3FontSize,
|
||||
fontWeight: t.TypographyH3FontWeight,
|
||||
lineHeight: t.TypographyH3LineHeight,
|
||||
letterSpacing: t.TypographyH3LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyH3FontSizeMobile },
|
||||
},
|
||||
h4: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyH4FontSize,
|
||||
fontWeight: t.TypographyH4FontWeight,
|
||||
lineHeight: t.TypographyH4LineHeight,
|
||||
letterSpacing: t.TypographyH4LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyH4FontSizeMobile },
|
||||
},
|
||||
h5: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyH5FontSize,
|
||||
fontWeight: t.TypographyH5FontWeight,
|
||||
lineHeight: t.TypographyH5LineHeight,
|
||||
letterSpacing: t.TypographyH5LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyH5FontSizeMobile },
|
||||
},
|
||||
h6: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyH6FontSize,
|
||||
fontWeight: t.TypographyH6FontWeight,
|
||||
lineHeight: t.TypographyH6LineHeight,
|
||||
letterSpacing: t.TypographyH6LetterSpacing,
|
||||
[MOBILE]: { fontSize: t.TypographyH6FontSizeMobile },
|
||||
},
|
||||
|
||||
/* ── Body (Montserrat, Medium 500) ── */
|
||||
bodyLg: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyBodyLgFontSize,
|
||||
fontWeight: t.TypographyBodyLgFontWeight,
|
||||
lineHeight: t.TypographyBodyLgLineHeight,
|
||||
letterSpacing: t.TypographyBodyLgLetterSpacing,
|
||||
},
|
||||
body1: {
|
||||
fontSize: t.TypographyBodyFontSize,
|
||||
fontWeight: t.TypographyBodyFontWeight,
|
||||
lineHeight: t.TypographyBodyLineHeight,
|
||||
letterSpacing: t.TypographyBodyLetterSpacing,
|
||||
},
|
||||
body2: {
|
||||
fontSize: t.TypographyBodySmFontSize,
|
||||
fontWeight: t.TypographyBodySmFontWeight,
|
||||
lineHeight: t.TypographyBodySmLineHeight,
|
||||
letterSpacing: t.TypographyBodySmLetterSpacing,
|
||||
},
|
||||
bodyXs: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyBodyXsFontSize,
|
||||
fontWeight: t.TypographyBodyXsFontWeight,
|
||||
lineHeight: t.TypographyBodyXsLineHeight,
|
||||
letterSpacing: t.TypographyBodyXsLetterSpacing,
|
||||
},
|
||||
|
||||
/* ── Subtitle (maps to body variants for MUI compat) ── */
|
||||
subtitle1: {
|
||||
fontSize: t.TypographyBodyLgFontSize,
|
||||
fontWeight: t.TypographyBodyLgFontWeight,
|
||||
lineHeight: t.TypographyBodyLgLineHeight,
|
||||
},
|
||||
subtitle2: {
|
||||
fontSize: t.TypographyLabelFontSize,
|
||||
fontWeight: t.TypographyLabelFontWeight,
|
||||
lineHeight: t.TypographyLabelLineHeight,
|
||||
letterSpacing: t.TypographyLabelLetterSpacing,
|
||||
},
|
||||
|
||||
/* ── Label (Montserrat, Medium 500) ── */
|
||||
labelLg: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyLabelLgFontSize,
|
||||
fontWeight: t.TypographyLabelLgFontWeight,
|
||||
lineHeight: t.TypographyLabelLgLineHeight,
|
||||
letterSpacing: t.TypographyLabelLgLetterSpacing,
|
||||
},
|
||||
label: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyLabelFontSize,
|
||||
fontWeight: t.TypographyLabelFontWeight,
|
||||
lineHeight: t.TypographyLabelLineHeight,
|
||||
letterSpacing: t.TypographyLabelLetterSpacing,
|
||||
},
|
||||
labelSm: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyLabelSmFontSize,
|
||||
fontWeight: t.TypographyLabelSmFontWeight,
|
||||
lineHeight: t.TypographyLabelSmLineHeight,
|
||||
letterSpacing: t.TypographyLabelSmLetterSpacing,
|
||||
},
|
||||
|
||||
/* ── Caption (Montserrat, Regular 400) ── */
|
||||
caption: {
|
||||
fontSize: t.TypographyCaptionFontSize,
|
||||
fontWeight: t.TypographyCaptionFontWeight,
|
||||
lineHeight: t.TypographyCaptionLineHeight,
|
||||
letterSpacing: t.TypographyCaptionLetterSpacing,
|
||||
},
|
||||
captionSm: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyCaptionSmFontSize,
|
||||
fontWeight: t.TypographyCaptionSmFontWeight,
|
||||
lineHeight: t.TypographyCaptionSmLineHeight,
|
||||
letterSpacing: t.TypographyCaptionSmLetterSpacing,
|
||||
},
|
||||
|
||||
/* ── Overline (Montserrat, SemiBold 600, uppercase) ── */
|
||||
overline: {
|
||||
fontSize: t.TypographyOverlineFontSize,
|
||||
fontWeight: t.TypographyOverlineFontWeight,
|
||||
lineHeight: t.TypographyOverlineLineHeight,
|
||||
letterSpacing: t.TypographyOverlineLetterSpacing,
|
||||
textTransform: 'uppercase' as const,
|
||||
},
|
||||
overlineSm: {
|
||||
fontFamily: t.FontFamilyBody,
|
||||
fontSize: t.TypographyOverlineSmFontSize,
|
||||
fontWeight: t.TypographyOverlineSmFontWeight,
|
||||
lineHeight: t.TypographyOverlineSmLineHeight,
|
||||
letterSpacing: t.TypographyOverlineSmLetterSpacing,
|
||||
textTransform: 'uppercase' as const,
|
||||
},
|
||||
|
||||
/* ── Button text ── */
|
||||
button: {
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.02em',
|
||||
textTransform: 'none' as const,
|
||||
},
|
||||
},
|
||||
|
||||
spacing: 4,
|
||||
|
||||
shape: {
|
||||
borderRadius: parseInt(t.BorderRadiusMd, 10),
|
||||
},
|
||||
|
||||
components: {
|
||||
MuiButton: {
|
||||
defaultProps: {
|
||||
disableElevation: true,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: parseInt(t.ButtonBorderRadiusDefault, 10),
|
||||
textTransform: 'none' as const,
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.02em',
|
||||
transition: 'background-color 150ms ease-in-out, border-color 150ms ease-in-out, box-shadow 150ms ease-in-out',
|
||||
'&:focus-visible': {
|
||||
outline: `2px solid ${t.ColorInteractiveFocus}`,
|
||||
outlineOffset: '2px',
|
||||
},
|
||||
},
|
||||
sizeSmall: {
|
||||
minHeight: parseInt(t.ButtonHeightSm, 10),
|
||||
padding: `${t.ButtonPaddingYSm} ${t.ButtonPaddingXSm}`,
|
||||
fontSize: t.ButtonFontSizeSm,
|
||||
'& .MuiButton-startIcon': { marginRight: t.ButtonIconGapSm },
|
||||
'& .MuiButton-endIcon': { marginLeft: t.ButtonIconGapSm },
|
||||
'& .MuiButton-startIcon > *:nth-of-type(1), & .MuiButton-endIcon > *:nth-of-type(1)': {
|
||||
fontSize: t.ButtonIconSizeSm,
|
||||
},
|
||||
},
|
||||
sizeMedium: {
|
||||
minHeight: parseInt(t.ButtonHeightMd, 10),
|
||||
padding: `${t.ButtonPaddingYMd} ${t.ButtonPaddingXMd}`,
|
||||
fontSize: t.ButtonFontSizeMd,
|
||||
'& .MuiButton-startIcon': { marginRight: t.ButtonIconGapMd },
|
||||
'& .MuiButton-endIcon': { marginLeft: t.ButtonIconGapMd },
|
||||
'& .MuiButton-startIcon > *:nth-of-type(1), & .MuiButton-endIcon > *:nth-of-type(1)': {
|
||||
fontSize: t.ButtonIconSizeMd,
|
||||
},
|
||||
},
|
||||
sizeLarge: {
|
||||
minHeight: parseInt(t.ButtonHeightLg, 10),
|
||||
padding: `${t.ButtonPaddingYLg} ${t.ButtonPaddingXLg}`,
|
||||
fontSize: t.ButtonFontSizeLg,
|
||||
'& .MuiButton-startIcon': { marginRight: t.ButtonIconGapLg },
|
||||
'& .MuiButton-endIcon': { marginLeft: t.ButtonIconGapLg },
|
||||
'& .MuiButton-startIcon > *:nth-of-type(1), & .MuiButton-endIcon > *:nth-of-type(1)': {
|
||||
fontSize: t.ButtonIconSizeLg,
|
||||
},
|
||||
},
|
||||
containedPrimary: {
|
||||
'&:hover': { backgroundColor: t.ColorInteractiveHover },
|
||||
'&:active': { backgroundColor: t.ColorInteractiveActive },
|
||||
},
|
||||
containedSecondary: {
|
||||
'&:hover': { backgroundColor: t.ColorNeutral700 },
|
||||
},
|
||||
outlinedPrimary: {
|
||||
borderColor: t.ColorInteractiveDefault,
|
||||
color: t.ColorInteractiveDefault,
|
||||
'&:hover': {
|
||||
borderColor: t.ColorInteractiveHover,
|
||||
color: t.ColorInteractiveHover,
|
||||
backgroundColor: t.ColorBrand100,
|
||||
},
|
||||
'&:active': {
|
||||
borderColor: t.ColorInteractiveActive,
|
||||
color: t.ColorInteractiveActive,
|
||||
},
|
||||
},
|
||||
outlinedSecondary: {
|
||||
borderColor: t.ColorNeutral400,
|
||||
color: t.ColorNeutral600,
|
||||
'&:hover': {
|
||||
borderColor: t.ColorNeutral600,
|
||||
color: t.ColorNeutral700,
|
||||
backgroundColor: t.ColorNeutral200,
|
||||
},
|
||||
},
|
||||
textPrimary: {
|
||||
color: t.ColorInteractiveDefault,
|
||||
'&:hover': {
|
||||
color: t.ColorInteractiveHover,
|
||||
backgroundColor: t.ColorBrand100,
|
||||
},
|
||||
'&:active': {
|
||||
color: t.ColorInteractiveActive,
|
||||
},
|
||||
},
|
||||
textSecondary: {
|
||||
color: t.ColorNeutral600,
|
||||
'&:hover': {
|
||||
color: t.ColorNeutral700,
|
||||
backgroundColor: t.ColorNeutral200,
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
props: { size: 'xs' as const },
|
||||
style: {
|
||||
minHeight: parseInt(t.ButtonHeightXs, 10),
|
||||
padding: `${t.ButtonPaddingYXs} ${t.ButtonPaddingXXs}`,
|
||||
fontSize: t.ButtonFontSizeXs,
|
||||
'& .MuiButton-startIcon': { marginRight: t.ButtonIconGapXs },
|
||||
'& .MuiButton-endIcon': { marginLeft: t.ButtonIconGapXs },
|
||||
'& .MuiButton-startIcon > *:nth-of-type(1), & .MuiButton-endIcon > *:nth-of-type(1)': {
|
||||
fontSize: t.ButtonIconSizeXs,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
props: { variant: 'soft' as const, color: 'primary' as const },
|
||||
style: {
|
||||
backgroundColor: t.ColorBrand200,
|
||||
color: t.ColorInteractiveHover,
|
||||
'&:hover': { backgroundColor: t.ColorBrand300 },
|
||||
'&:active': { backgroundColor: t.ColorBrand400 },
|
||||
},
|
||||
},
|
||||
{
|
||||
props: { variant: 'soft' as const, color: 'secondary' as const },
|
||||
style: {
|
||||
backgroundColor: t.ColorNeutral200,
|
||||
color: t.ColorNeutral700,
|
||||
'&:hover': { backgroundColor: t.ColorNeutral300 },
|
||||
'&:active': { backgroundColor: t.ColorNeutral400, color: t.ColorNeutral700 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
MuiCard: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: parseInt(t.BorderRadiusMd, 10),
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiOutlinedInput: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: parseInt(t.InputBorderRadiusDefault, 10),
|
||||
transition: 'box-shadow 150ms ease-in-out',
|
||||
// Default (medium) height
|
||||
minHeight: parseInt(t.InputHeightMd, 10),
|
||||
// Small size
|
||||
'&.MuiInputBase-sizeSmall': {
|
||||
minHeight: parseInt(t.InputHeightSm, 10),
|
||||
},
|
||||
// Default border
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: t.ColorNeutral300,
|
||||
transition: 'border-color 150ms ease-in-out',
|
||||
},
|
||||
// Hover — darker border (skip when focused, error, or disabled)
|
||||
'&:hover:not(.Mui-focused):not(.Mui-error):not(.Mui-disabled) .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: t.ColorNeutral400,
|
||||
},
|
||||
// Focus — brand gold border + double ring (white gap + brand ring)
|
||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: t.ColorBrand500,
|
||||
borderWidth: '1px',
|
||||
},
|
||||
'&.Mui-focused': {
|
||||
boxShadow: `0 0 0 3px ${t.ColorWhite}, 0 0 0 5px ${t.ColorBrand500}`,
|
||||
},
|
||||
// Error border
|
||||
'&.Mui-error .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: t.ColorFeedbackError,
|
||||
borderWidth: '1px',
|
||||
},
|
||||
// Error + focused — error-coloured ring
|
||||
'&.Mui-error.Mui-focused': {
|
||||
boxShadow: `0 0 0 3px ${t.ColorWhite}, 0 0 0 5px ${t.ColorFeedbackError}`,
|
||||
},
|
||||
// Disabled — muted background
|
||||
'&.Mui-disabled': {
|
||||
backgroundColor: t.ColorNeutral200,
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: t.ColorNeutral300,
|
||||
},
|
||||
},
|
||||
// Adornment icon sizing
|
||||
'& .MuiInputAdornment-root': {
|
||||
color: t.ColorNeutral400,
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: t.InputIconSizeDefault,
|
||||
},
|
||||
},
|
||||
},
|
||||
input: {
|
||||
padding: `${t.InputPaddingYMd} ${t.InputPaddingXDefault}`,
|
||||
fontSize: t.InputFontSizeDefault,
|
||||
color: t.ColorTextPrimary,
|
||||
'&::placeholder': {
|
||||
color: t.ColorNeutral400,
|
||||
opacity: 1,
|
||||
},
|
||||
// Small size padding
|
||||
'.MuiInputBase-sizeSmall &': {
|
||||
padding: `${t.InputPaddingYSm} ${t.InputPaddingXDefault}`,
|
||||
},
|
||||
},
|
||||
notchedOutline: {
|
||||
// Reset top offset — MUI defaults to -5px for floating label space.
|
||||
// We use external labels, so the border should be flush with the input.
|
||||
top: 0,
|
||||
// Hide the notch legend — we use external labels
|
||||
'& legend': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
multiline: {
|
||||
padding: 0,
|
||||
'& .MuiOutlinedInput-input': {
|
||||
padding: `${t.InputPaddingYMd} ${t.InputPaddingXDefault}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
Reference in New Issue
Block a user