Palette
Palette (팔레트)
컴포넌트의 색상을 브랜드에 맞게 수정할 수 있게 해 주는 팔레트에 대해 알아봅니다. 컬러 토큰, 기본 색상, 커스텀 색상 추가 방법을 정리했어요.
출처: 문서
본문
팔레트를 사용하면 브랜드에 맞게 컴포넌트의 색상을 수정할 수 있어요.
컬러 토큰 (Color tokens)
팔레트 색상은 네 개의 토큰으로 표현됩니다.
main: 색상의 기본 shadelight:main보다 밝은 shadedark:main보다 어두운 shadecontrastText:main과 대비되도록 의도된 텍스트 색상
Material UI의 기본 테마가 primary 색상 토큰을 어떻게 정의하는지 보여드릴게요.
const primary = {
main: '#1976d2',
light: '#42a5f5',
dark: '#1565c0',
contrastText: '#fff',
};
Material Design 컬러 시스템에 대한 자세한 내용은 Color 문서를 참고하세요.
기본 색상 (Default colors)
테마는 다음과 같은 기본 팔레트 색상을 노출합니다.(theme.palette.*에서 접근할 수 있어요.)
primary- 기본 인터페이스 요소용.secondary- 보조 인터페이스 요소용.error- 사용자가 인지해야 하는 요소용.warning- 잠재적으로 위험한 동작이나 중요한 메시지용.info- 중립적인 정보를 강조하기 위한 용도.success- 사용자가 트리거한 동작의 성공적인 완료를 나타내기 위한 용도.
색상 사용과 가이드라인에 대한 자세한 내용은 Material Design의 Color System을 참고하세요.
값 (Values)
기본 팔레트 값을 테마 탐색기로 살펴보거나, 이 페이지에서 개발자 도구 콘솔을 열어(window.theme.palette) 확인할 수 있어요.
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import { useTheme, rgbToHex, styled } from '@mui/material/styles';
const Group = styled(Typography)(({ theme }) => ({
marginTop: theme.spacing(3),
}));
const Color = styled(Grid)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
'& div:first-of-type': {
width: theme.spacing(6),
height: theme.spacing(6),
marginRight: theme.spacing(1),
borderRadius: theme.shape.borderRadius,
boxShadow: 'inset 0 2px 4px 0 rgba(0, 0, 0, .06)',
},
}));
export default function Intentions() {
const theme = useTheme();
const item = (color, name) => (
<Color size={{ xs: 12, sm: 6, md: 4 }}>
<div style={{ backgroundColor: color }} />
<div>
<Typography variant="body2">{name}</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
{rgbToHex(color)}
</Typography>
</div>
</Color>
);
return (
<Box sx={{ width: '100%' }}>
<Group gutterBottom>Primary</Group>
<Grid container spacing={2}>
{item(theme.palette.primary.light, 'palette.primary.light')}
{item(theme.palette.primary.main, 'palette.primary.main')}
{item(theme.palette.primary.dark, 'palette.primary.dark')}
</Grid>
<Group gutterBottom>Secondary</Group>
<Grid container spacing={2}>
{item(theme.palette.secondary.light, 'palette.secondary.light')}
{item(theme.palette.secondary.main, 'palette.secondary.main')}
{item(theme.palette.secondary.dark, 'palette.secondary.dark')}
</Grid>
<Group gutterBottom>Error</Group>
<Grid container spacing={2}>
{item(theme.palette.error.light, 'palette.error.light')}
{item(theme.palette.error.main, 'palette.error.main')}
{item(theme.palette.error.dark, 'palette.error.dark')}
</Grid>
<Group gutterBottom>Warning</Group>
<Grid container spacing={2}>
{item(theme.palette.warning.light, 'palette.warning.light')}
{item(theme.palette.warning.main, 'palette.warning.main')}
{item(theme.palette.warning.dark, 'palette.warning.dark')}
</Grid>
<Group gutterBottom>Info</Group>
<Grid container spacing={2}>
{item(theme.palette.info.light, 'palette.info.light')}
{item(theme.palette.info.main, 'palette.info.main')}
{item(theme.palette.info.dark, 'palette.info.dark')}
</Grid>
<Group gutterBottom>Success</Group>
<Grid container spacing={2}>
{item(theme.palette.success.light, 'palette.success.light')}
{item(theme.palette.success.main, 'palette.success.main')}
{item(theme.palette.success.dark, 'palette.success.dark')}
</Grid>
</Box>
);
}
기본 팔레트는 secondary 팔레트 색상에는 A 접두사(A200 등)가 붙은 shade를, 다른 팔레트 색상에는 접두사가 없는 shade를 사용합니다.
커스터마이즈 (Customization)
테마의 일부로 팔레트 객체를 포함시키면 기본 팔레트 값을 오버라이드할 수 있어요. 다음 중 어떤 것이든:
팔레트 색상 객체가 제공되면 기본값을 대체하게 됩니다.
이는 색상 객체를 사용하거나 색상을 직접 제공하는 두 가지 방법으로 할 수 있어요.
색상 객체 사용 (Using a color object)
팔레트 색상을 커스터마이즈하는 가장 직접적인 방법은 아래와 같이 하나 이상의 color object를 import해 적용하는 것입니다.
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { lime, purple } from '@mui/material/colors';
import Button from '@mui/material/Button';
const theme = createTheme({
palette: {
primary: lime,
secondary: purple,
},
});
export default function UsingColorObject() {
return (
<ThemeProvider theme={theme}>
<Button variant="contained">Primary</Button>
<Button variant="contained" color="secondary" sx={{ ml: 2 }}>
Secondary
</Button>
</ThemeProvider>
);
}
색상을 직접 제공 (Providing the colors directly)
각 색상을 직접 수정하려면 하나 이상의 컬러 토큰을 가진 객체를 제공하면 됩니다. main 토큰만 필수이고, light, dark, contrastText는 선택입니다. 제공하지 않으면 값이 자동으로 계산됩니다.
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: '#FF5733',
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
},
secondary: {
main: '#E0C2FF',
light: '#F5EBFF',
// dark: will be calculated from palette.secondary.main,
contrastText: '#47008F',
},
},
});
import { createTheme, ThemeProvider } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import { unstable_capitalize as capitalize } from '@mui/utils';
import Typography from '@mui/material/Typography';
const theme = createTheme({
palette: {
primary: {
main: '#FF5733',
// light: will be calculated from palette.primary.main,
// dark: will be calculated from palette.primary.main,
// contrastText: will be calculated to contrast with palette.primary.main
},
secondary: {
main: '#E0C2FF',
light: '#F5EBFF',
// dark: will be calculated from palette.secondary.main,
contrastText: '#47008F',
},
},
});
function ColorShowcase({ color }: { color: 'primary' | 'secondary' }) {
return (
<Stack sx={{ gap: 2, alignItems: 'center' }}>
<Button variant="contained" color={color}>
{capitalize(color)}
</Button>
<Stack direction="row" sx={{ gap: 1 }}>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">light</Typography>
<Box sx={{ bgcolor: `${color}.light`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">main</Typography>
<Box sx={{ bgcolor: `${color}.main`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">dark</Typography>
<Box sx={{ bgcolor: `${color}.dark`, width: 40, height: 20 }} />
</Stack>
</Stack>
</Stack>
);
}
export default function ManuallyProvidePaletteColor() {
return (
<ThemeProvider theme={theme}>
<Stack direction="row" sx={{ gap: 8 }}>
<ColorShowcase color="primary" />
<ColorShowcase color="secondary" />
</Stack>
</ThemeProvider>
);
}
대비 임계값 (Contrast threshold)
contrastText 토큰은 contrastThreshold 값을 사용해 계산되며, 배경과 텍스트 사이의 대비를 최대화합니다.
대비 임계값이 높을수록 배경색이 밝은 것으로 간주되는 지점이 높아지고, 따라서 어두운 contrastText가 주어집니다. 대비 임계값은 비선형 곡선을 따르며, 기본값은 3으로 최소 대비 비율 3:1을 나타냅니다.
import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles';
import Button from '@mui/material/Button';
import { Stack } from '@mui/system';
const defaultContrastThresholdTheme = createTheme({});
const highContrastThresholdTheme = createTheme({
palette: {
contrastThreshold: 4.5,
},
});
function ContrastShowcase(props: { title: string }) {
const { title } = props;
const theme = useTheme();
return (
<Stack sx={{ gap: 1, alignItems: 'center' }}>
<span>
<b>{title}</b>
</span>
<span>{theme.palette.contrastThreshold}:1</span>
<Stack direction="row" sx={{ gap: 1 }}>
<Button variant="contained" color="warning">
Warning
</Button>
</Stack>
</Stack>
);
}
export default function ContrastThreshold() {
return (
<Stack direction="row" sx={{ gap: 4 }}>
<ThemeProvider theme={defaultContrastThresholdTheme}>
<ContrastShowcase title="Default contrast threshold" />
</ThemeProvider>
<ThemeProvider theme={highContrastThresholdTheme}>
<ContrastShowcase title="Higher contrast threshold" />
</ThemeProvider>
</Stack>
);
}
음영 오프셋 (Tonal offset)
light와 dark 토큰은 tonalOffset 값을 사용해 계산되며, main 색상의 휘도를 이동시킵니다. 음영 오프셋 값이 높을수록 light 토큰은 더 밝아지고 dark 토큰은 더 어두워집니다.
:::warning 이것은 커스텀 색상을 사용할 때만 적용됩니다—기본값에는 아무 효과가 없어요. :::
예를 들어 음영 오프셋 기본값 0.2는 휘도를 대략 두 단계만큼 이동시킵니다. 따라서 main 토큰이 blue[500]라면 light 토큰은 blue[300], dark는 blue[700]이 됩니다.
음영 오프셋 값은 0과 1 사이의 숫자(light와 dark 토큰 모두에 적용)이거나, light와 dark 키를 지정한 객체일 수 있습니다.
import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles';
import { blue } from '@mui/material/colors';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
const defaultTonalOffsetTheme = createTheme({
palette: {
primary: {
main: blue[500],
},
},
});
const higherTonalOffsetTheme = createTheme({
palette: {
primary: {
main: blue[500],
},
tonalOffset: 0.5,
},
});
const asymmetricTonalOffsetTheme = createTheme({
palette: {
primary: {
main: blue[500],
},
tonalOffset: {
light: 0.1,
dark: 0.9,
},
},
});
function ColorShowcase({ title, color }: { title: string; color: string }) {
const {
palette: { tonalOffset },
} = useTheme();
let caption;
if (typeof tonalOffset === 'number') {
caption = tonalOffset;
} else {
caption = `{ light: ${tonalOffset.light}, dark: ${tonalOffset.dark} }`;
}
return (
<Stack sx={{ gap: 1, alignItems: 'center' }}>
<span>
<b>{title}</b>
</span>
<span>{caption}</span>
<Stack direction="row" sx={{ gap: 1 }}>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">light</Typography>
<Box sx={{ bgcolor: `${color}.light`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">main</Typography>
<Box sx={{ bgcolor: `${color}.main`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">dark</Typography>
<Box sx={{ bgcolor: `${color}.dark`, width: 40, height: 20 }} />
</Stack>
</Stack>
</Stack>
);
}
export default function TonalOffset() {
return (
<Stack direction={{ xs: 'column', sm: 'row' }} sx={{ gap: 8 }}>
<ThemeProvider theme={defaultTonalOffsetTheme}>
<ColorShowcase title="Default tonal offset" color="primary" />
</ThemeProvider>
<ThemeProvider theme={higherTonalOffsetTheme}>
<ColorShowcase title="Higher tonal offset" color="primary" />
</ThemeProvider>
<ThemeProvider theme={asymmetricTonalOffsetTheme}>
<ColorShowcase title="Asymmetric tonal offset" color="primary" />
</ThemeProvider>
</Stack>
);
}
커스텀 색상 (Custom colors)
:::warning 기본 색상과 달리, 커스텀 색상의 토큰은 자동으로 계산되지 않습니다. :::
커스텀 색상을 추가하려면 토큰을 직접 제공하거나 augmentColor 유틸리티로 생성해야 합니다.
토큰 직접 제공 (Provide tokens manually)
가장 간단한 접근 방식은 main, light, dark, contrastText 모든 토큰을 직접 정의하는 것입니다.
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
ochre: {
main: '#E3D026',
light: '#E9DB5D',
dark: '#A29415',
contrastText: '#242105',
},
},
});
import { createTheme, ThemeProvider } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
// Augment the palette to include an ochre color
declare module '@mui/material/styles' {
interface Palette {
ochre: Palette['primary'];
}
interface PaletteOptions {
ochre?: PaletteOptions['primary'];
}
}
// Update the Button's color options to include an ochre option
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
ochre: true;
}
}
const theme = createTheme({
palette: {
ochre: {
main: '#E3D026',
light: '#E9DB5D',
dark: '#A29415',
contrastText: '#242105',
},
},
});
export default function ManuallyProvideCustomColor() {
return (
<ThemeProvider theme={theme}>
<Stack sx={{ gap: 2, alignItems: 'center' }}>
<Button variant="contained" color="ochre">
Ochre
</Button>
<Stack direction="row" sx={{ gap: 1 }}>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">light</Typography>
<Box sx={{ bgcolor: 'ochre.light', width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">main</Typography>
<Box sx={{ bgcolor: 'ochre.main', width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">dark</Typography>
<Box sx={{ bgcolor: 'ochre.dark', width: 40, height: 20 }} />
</Stack>
</Stack>
</Stack>
</ThemeProvider>
);
}
:::info
지원되는 색상 형식은 hex, rgb, hsl입니다. oklch 같은 다른 형식을 사용하려면 native color 기능을 활성화해야 해요.
:::
색상을 조작해야 한다면 @mui/material/styles가 도움이 되는 유틸리티 세트를 제공합니다. 다음 예시는 alpha()와 getContrastRatio() 유틸리티를 사용해 투명도로 토큰을 정의합니다.
import { createTheme, alpha, getContrastRatio } from '@mui/material/styles';
const violetBase = '#7F00FF';
const violetMain = alpha(violetBase, 0.7);
const theme = createTheme({
palette: {
violet: {
main: violetMain,
light: alpha(violetBase, 0.5),
dark: alpha(violetBase, 0.9),
contrastText: getContrastRatio(violetMain, '#fff') > 4.5 ? '#fff' : '#111',
},
},
});
import {
createTheme,
ThemeProvider,
alpha,
getContrastRatio,
} from '@mui/material/styles';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
// Augment the palette to include a violet color
declare module '@mui/material/styles' {
interface Palette {
violet: Palette['primary'];
}
interface PaletteOptions {
violet?: PaletteOptions['primary'];
}
}
// Update the Button's color options to include a violet option
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
violet: true;
}
}
const violetBase = '#7F00FF';
const violetMain = alpha(violetBase, 0.7);
const theme = createTheme({
palette: {
violet: {
main: violetMain,
light: alpha(violetBase, 0.5),
dark: alpha(violetBase, 0.9),
contrastText: getContrastRatio(violetMain, '#fff') > 4.5 ? '#fff' : '#111',
},
},
});
export default function UsingStylesUtils() {
return (
<ThemeProvider theme={theme}>
<Stack sx={{ gap: 2, alignItems: 'center' }}>
<Button variant="contained" color="violet">
Violet
</Button>
<Stack direction="row" sx={{ gap: 1 }}>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">light</Typography>
<Box sx={{ bgcolor: 'violet.light', width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">main</Typography>
<Box sx={{ bgcolor: 'violet.main', width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">dark</Typography>
<Box sx={{ bgcolor: 'violet.dark', width: 40, height: 20 }} />
</Stack>
</Stack>
</Stack>
</ThemeProvider>
);
}
augmentColor 유틸리티로 토큰 생성 (Generate tokens using augmentColor utility)
또는 팔레트의 augmentColor 유틸리티를 사용해 light, dark, contrastText 토큰을 생성할 수 있어요. 이 유틸리티는 기본 팔레트 색상에 사용되는 것과 같은 함수입니다. 이를 위해서는 테마를 두 단계로 만들고, 나머지가 기반이 될 main 토큰을 제공해야 합니다.
import { createTheme } from '@mui/material/styles';
let theme = createTheme({
// Theme customization goes here as usual, including tonalOffset and/or
// contrastThreshold as the augmentColor() function relies on these
});
theme = createTheme(theme, {
// Custom colors created with augmentColor go here
palette: {
salmon: theme.palette.augmentColor({
color: {
main: '#FF5733',
},
name: 'salmon',
}),
},
});
import { createTheme, ThemeProvider } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
// Augment the palette to include a salmon color
declare module '@mui/material/styles' {
interface Palette {
salmon: Palette['primary'];
}
interface PaletteOptions {
salmon?: PaletteOptions['primary'];
}
}
// Update the Button's color options to include a salmon option
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
salmon: true;
}
}
let theme = createTheme({
// Theme customization goes here as usual, including tonalOffset and/or
// contrastThreshold as the augmentColor() function relies on these
});
theme = createTheme(theme, {
// Custom colors created with augmentColor go here
palette: {
salmon: theme.palette.augmentColor({
color: {
main: '#FF5733',
},
name: 'salmon',
}),
},
});
export default function UsingAugmentColor() {
return (
<ThemeProvider theme={theme}>
<Stack sx={{ gap: 2, alignItems: 'center' }}>
<Button variant="contained" color="salmon">
Salmon
</Button>
<Stack direction="row" sx={{ gap: 1 }}>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">light</Typography>
<Box sx={{ bgcolor: 'salmon.light', width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">main</Typography>
<Box sx={{ bgcolor: 'salmon.main', width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">dark</Typography>
<Box sx={{ bgcolor: 'salmon.dark', width: 40, height: 20 }} />
</Stack>
</Stack>
</Stack>
</ThemeProvider>
);
}
대비 임계값과 음영 오프셋 값은 이 유틸리티로 정의한 색상에도 적용됩니다.
컴포넌트에서 사용하기 (Using in components)
커스텀 색상을 추가한 후에는 기본 팔레트 색상과 똑같이 컴포넌트에서 사용할 수 있어요.
<Button color="custom">
TypeScript
TypeScript를 사용한다면 커스텀 색상에 대해 module augmentation을 사용해야 합니다.
팔레트에 커스텀 색상을 추가하려면 Palette와 PaletteOptions 인터페이스에 추가해야 합니다.
declare module '@mui/material/styles' {
interface Palette {
custom: Palette['primary'];
}
interface PaletteOptions {
custom?: PaletteOptions['primary'];
}
}
컴포넌트의 color prop에 커스텀 색상을 사용하려면 컴포넌트의 PropsColorOverrides 인터페이스에 추가해야 합니다. 아래 예시는 Button 컴포넌트로 하는 방법을 보여줍니다.
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
custom: true;
}
}
컬러 토큰 추가 (Adding color tokens)
새 color token을 추가하려면 색상의 객체에 다음과 같이 포함시키면 됩니다.
import { createTheme } from '@mui/material/styles';
import { blue } from '@mui/material/colors';
const theme = createTheme({
palette: {
primary: {
light: blue[300],
main: blue[500],
dark: blue[700],
darker: blue[900],
},
},
});
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { blue } from '@mui/material/colors';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
declare module '@mui/material/styles' {
interface PaletteColor {
darker?: string;
}
interface SimplePaletteColorOptions {
darker?: string;
}
}
const theme = createTheme({
palette: {
primary: {
light: blue[300],
main: blue[500],
dark: blue[700],
darker: blue[900],
},
},
});
export default function AddingColorTokens() {
return (
<ThemeProvider theme={theme}>
<Stack direction="row" sx={{ gap: 1 }}>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">light</Typography>
<Box sx={{ bgcolor: `primary.light`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">main</Typography>
<Box sx={{ bgcolor: `primary.main`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">dark</Typography>
<Box sx={{ bgcolor: `primary.dark`, width: 40, height: 20 }} />
</Stack>
<Stack sx={{ alignItems: 'center' }}>
<Typography variant="body2">darker</Typography>
<Box sx={{ bgcolor: `primary.darker`, width: 40, height: 20 }} />
</Stack>
</Stack>
</ThemeProvider>
);
}
TypeScript
TypeScript를 사용한다면 새 컬러 토큰을 PaletteColor와 SimplePaletteColorOptions 인터페이스에 추가하기 위해 module augmentation을 사용해야 합니다.
declare module '@mui/material/styles' {
interface PaletteColor {
darker?: string;
}
interface SimplePaletteColorOptions {
darker?: string;
}
}
팔레트 밖의 색상 (Non-palette colors)
theme.palette 밖에 색상을 추가하는 방법은 Theming—Custom variables를 참고하세요.
접근성 (Accessibility)
WCAG 2.2 Success Criterion 1.4.3에 정의된 최소 4.5:1 대비를 충족하려면, 대비 임계값이 4.5인 커스텀 테마를 다음과 같이 만들면 됩니다.
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
contrastThreshold: 4.5,
},
});
:::warning
contrastThreshold 매개변수는 역효과를 낼 수 있습니다. APCA 색상 대비가 개선되었는지 확인해 주세요 (WCAG 3 는 이 새 알고리즘을 사용할 예정입니다).
:::
Windows 고대비 모드 (Windows High Contrast mode)
Windows 고대비 모드(일명 Forced Colors)에서 컴포넌트 가시성을 개선하려면, v9.1.0부터 enhanceHighContrast 테마 인핸서를 사용하세요. 이 인핸서는 완전히 만들어진 테마를 받아, 영향받는 컴포넌트에 @media (forced-colors: active) 오버라이드를 적용한 향상된 버전을 반환합니다.
import { createTheme, enhanceHighContrast } from '@mui/material/styles';
const theme = enhanceHighContrast(createTheme());
기본적으로 CSS 시스템 색상 키워드(Highlight, HighlightText, ButtonBorder 등)를 사용합니다. 개별 토큰을 브랜드나 디자인 요구에 맞는 다른 system colors로 오버라이드할 수 있어요. 쌍을 이루는 토큰을 오버라이드할 때는 브라우저가 서로 대비를 보장하는 값을 선택하세요—예를 들어 SelectedItem은 항상 SelectedItemText와 쌍을 이루고, Highlight는 HighlightText와 쌍을 이룹니다.
import { createTheme, enhanceHighContrast } from '@mui/material/styles';
const theme = enhanceHighContrast(createTheme(), {
// Use the system's selection colors for active or toggled controls.
activeBackground: 'SelectedItem',
activeText: 'SelectedItemText',
});
다음 토큰을 사용할 수 있으며, 현재 환경에서 브라우저가 각 시스템 색상 키워드를 어떻게 해석하는지 보여줍니다.
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { styled } from '@mui/material/styles';
// TODO: Ideally this should be generated from the HighContrastTokens TS interface
// rather than maintained manually, but auto-extraction would lose descriptions and
// default values. A solution that exports token metadata from the interface is needed.
const tokens = [
{
token: 'disabled',
color: 'GrayText',
description: 'Color for disabled elements',
},
{ token: 'error', color: 'ActiveText', description: 'Color for error states' },
{
token: 'selectedBackground',
color: 'SelectedItem',
description: 'Background color for selected items',
},
{
token: 'selectedText',
color: 'SelectedItemText',
description: 'Text color on selected items',
},
{
token: 'activeBackground',
color: 'Highlight',
description: 'Background color for active or toggled controls',
},
{
token: 'activeText',
color: 'HighlightText',
description: 'Text color on active or toggled controls',
},
{
token: 'buttonBorder',
color: 'ButtonBorder',
description: 'Border color for interactive controls',
},
{
token: 'buttonText',
color: 'ButtonText',
description: 'Text and icon color on buttons',
},
{
token: 'canvas',
color: 'Canvas',
description: 'Background color for the page or canvas',
},
];
const Table = styled('table')(({ theme }) => ({
width: '100%',
borderCollapse: 'collapse',
...theme.typography.body2,
}));
const Th = styled('th')(({ theme }) => ({
textAlign: 'left',
padding: theme.spacing(1, 2),
borderBottom: `2px solid ${theme.palette.divider}`,
whiteSpace: 'nowrap',
}));
const Td = styled('td')(({ theme }) => ({
padding: theme.spacing(1, 2),
borderBottom: `1px solid ${theme.palette.divider}`,
verticalAlign: 'middle',
}));
const ColorSwatch = styled('div')(({ theme }) => ({
display: 'inline-block',
width: theme.spacing(3),
height: theme.spacing(3),
marginRight: theme.spacing(1),
borderRadius: theme.shape.borderRadius,
outline: '1px solid ButtonBorder',
verticalAlign: 'middle',
}));
export default function HighContrastTokens() {
return (
<Box sx={{ width: '100%', overflowX: 'auto' }}>
<Table>
<thead>
<tr>
<Th>Token</Th>
<Th>Default</Th>
<Th>Description</Th>
</tr>
</thead>
<tbody>
{tokens.map(({ token, color, description }) => (
<tr key={token}>
<Td>
<code>{token}</code>
</Td>
<Td>
<ColorSwatch style={{ backgroundColor: color }} />
<Typography component="code" variant="body2">
{color}
</Typography>
</Td>
<Td sx={{ color: 'text.secondary' }}>{description}</Td>
</tr>
))}
</tbody>
</Table>
</Box>
);
}
색상 고르기 (Picking colors)
영감이 필요하신가요? Material Design 팀이 팔레트 구성 도구를 만들어 줬습니다.
컬러 스킴 (Color schemes)
별도의 테마를 만들지 않고 내장 라이트/다크 컬러 스킴을 모두 추가하려면 colorSchemes: { light: true, dark: true }를 사용하세요. 이렇게 하면 두 컬러 스킴 모두에 기본 토큰을 생성합니다.
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
colorSchemes: {
light: true,
dark: true,
},
});
각 컬러 스킴의 기본 토큰을 오버라이드하려면 아래와 같이 palette 객체를 사용하면 됩니다.
const theme = createTheme({
colorSchemes: {
light: {
palette: {
primary: {
main: '#FF5733',
},
// ...other tokens
},
},
dark: {
palette: {
primary: {
main: '#E0C2FF',
},
// ...other tokens
},
},
},
});
:::warning
colorSchemes API는 palette API의 향상된 버전이며, Material UI v6부터 이 목적에 선호되는 API입니다. colorSchemes와 palette를 모두 제공하면, 후자가 전자에 정의된 모든 스타일을 오버라이드합니다.
const theme = createTheme({
palette: {
primary: {
main: '...',
},
},
colorSchemes: {
light: {
// This will be replaced by the palette defined above
palette: {
primary: {
main: '...',
},
},
},
dark: { ... },
},
});
:::
다크 모드 (Dark mode)
테마에 다크 모드를 설정하는 방법에 대한 자세한 내용은 dark mode guide로 이동하세요.