반응형 스타일
반응형 스타일 (Responsive styles)
미디어 쿼리
.demo {
background-color: var(--mantine-color-blue-filled);
color: var(--mantine-color-white);
padding: var(--mantine-spacing-md);
text-align: center;
@media (min-width: em(750px)) {
background-color: var(--mantine-color-red-filled);
}
}
출처: 문서
본문
breakpoint 구성
theme.breakpoints는 모든 반응형 Mantine 컴포넌트에서 사용돼요. breakpoint는 em 단위로 설정해야 해요. 이 값들은 MantineProvider로 구성할 수 있어요:
import { createTheme, MantineProvider } from '@mantine/core';
const theme = createTheme({
breakpoints: {
xs: '30em',
sm: '48em',
md: '64em',
lg: '74em',
xl: '90em',
},
});
function Demo() {
return (
<MantineProvider theme={theme}>
{/* Your app here */}
</MantineProvider>
);
}
기본 theme.breakpoints 값:
| Breakpoint | Viewport width | px 값 |
|---|---|---|
| xs | 36em | 576px |
| sm | 48em | 768px |
| md | 62em | 992px |
| lg | 75em | 1200px |
| xl | 88em | 1408px |
CSS modules에서 breakpoint 변수
미디어 쿼리 안에서는 CSS 변수를 사용할 수 없어요 – 이 값들은 MantineProvider가 동적으로 생성할 수 없기 때문이에요. .css 파일에서 Mantine theme breakpoint를 사용하려면 postcss-simple-vars 패키지가 필요해요:
yarn add --dev postcss-simple-vars
PostCSS config의 postcss.config.cjs에 추가해요:
module.exports = {
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': {
variables: {
'mantine-breakpoint-xs': '36em',
'mantine-breakpoint-sm': '48em',
'mantine-breakpoint-md': '62em',
'mantine-breakpoint-lg': '75em',
'mantine-breakpoint-xl': '88em',
},
},
},
};
그러면 .css 파일에서 이 변수들에 접근할 수 있어요:
.demo {
@media (max-width: $mantine-breakpoint-xs) {
background-color: red;
}
}
다음으로 변환돼요:
@media (max-width: 36em) {
.demo {
background-color: red;
}
}
동적 breakpoint는 지원되지 않아요
postcss-simple-vars config에 정의된 값은 정적이며 theme과 연결되지 않아요 – 값이 바뀌면 theme override와 postcss config 양쪽에서 수동으로 업데이트해야 해요.
hiddenFrom, visibleFrom props
루트 요소를 가진 모든 Mantine 컴포넌트는 hiddenFrom과 visibleFrom prop을 지원해요. 이 prop들은 breakpoint(xs, sm, md, lg, xl)를 받아서 뷰포트 너비가 지정된 breakpoint보다 작거나 클 때 컴포넌트를 숨겨요:
import { Button, Group } from '@mantine/core';
function Demo() {
return (
<Group>
<Button hiddenFrom="sm">Hidden from sm</Button>
<Button visibleFrom="sm">Visible from sm</Button>
<Button visibleFrom="md">Visible from md</Button>
</Group>
);
}
클래스로 hidden/visible 사용
커스텀 컴포넌트를 만들면서 hiddenFrom/visibleFrom props와 같은 로직을 사용하고 싶지만 Mantine 컴포넌트를 사용하고 싶지 않다면, mantine-hidden-from-{x}와 mantine-visible-from-{x} 클래스를 사용할 수 있어요.
function CustomComponent() {
return (
<>
<div className="mantine-hidden-from-md">Hidden from md</div>
<div className="mantine-visible-from-xl">Visible from xl</div>
</>
);
}
미디어 쿼리에 기반한 컴포넌트 크기
일부 컴포넌트는 컴포넌트 외관의 여러 측면을 바꾸는 size prop을 지원해요. size prop은 반응형이 아니에요 – 화면 크기마다 다른 컴포넌트 크기를 정의할 수 없어요. 대신 크기가 다른 여러 컴포넌트를 렌더링하고 className 또는 hiddenFrom/visibleFrom props로 미디어 쿼리에 따라 보이거나 숨길 수 있어요:
import { TextInput } from '@mantine/core';
function Demo() {
return (
<>
<TextInput size="md" label="My input" className="mantine-visible-from-sm" />
<TextInput size="lg" label="My input" className="mantine-hidden-from-sm" />
</>
);
}
use-media-query 훅
use-media-query 훅을 사용해 미디어 쿼리에 따라 일부 컴포넌트 props를 바꿀 수 있어요. 이 방식은 애플리케이션에 ssr이 있다면(Next.js, React Router, Gatsby 또는 ssr을 포함하는 다른 프레임워크) 대부분의 경우 권장되지 않는다는 점에 주의하세요 – hydration 불일치가 발생할 수 있기 때문이에요. 애플리케이션에 ssr이 없다면(예: Vite 사용), 이 훅을 안전하게 사용해 훅 반환 값에 따라 컴포넌트 props를 바꾸거나 컴포넌트를 조건부로 렌더링할 수 있어요.
use-media-query 훅은 서버에서 렌더링되지 않는 컴포넌트(모달, 툴팁 등)의 props를 바꾸는 데는 안전하게 사용할 수 있어요. 다음 예시에서는 Tooltip이 서버에서 렌더링되지 않으므로 useMediaQuery 훅으로 Tooltip props를 바꾸는 것이 안전해요:
import { Tooltip, Button, em } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
function Demo() {
const isMobile = useMediaQuery(`(max-width: ${em(750)})`);
return (
<Tooltip label={isMobile ? 'Mobile' : 'Desktop'}>
<Button>Hover me</Button>
</Tooltip>
);
}
use-matches 훅
@mantine/core에서 내보내는 use-matches 훅은 여러 미디어 쿼리와 값을 매칭해야 할 때 use-media-query의 대안이에요. 미디어 쿼리를 키로, 주어진 breakpoint에서의 값을 값으로 가지는 객체를 받아요.
use-matches 훅은 내부적으로 use-media-query와 같은 로직을 사용한다는 점에 주의하세요. 특히 애플리케이션에 ssr이 있다면 반응형 스타일의 주된 수단으로 사용하는 것은 권장되지 않아요.
다음 예시에서:
-
theme.breakpoints.lg부터는 색상이red.9예요 -
theme.breakpoints.sm과theme.breakpoints.lg사이에는 색상이orange.9예요 -
theme.breakpoints.sm아래에서는 색상이blue.9예요
import { Box, useMatches } from '@mantine/core';
function Demo() {
const color = useMatches({
base: 'blue.9',
sm: 'orange.9',
lg: 'red.9',
});
return <Box c={color}>Box with color that changes based on screen size</Box>;
}
Container queries
Container queries를 사용하면 요소 컨테이너의 크기에 기반해 요소에 스타일을 적용할 수 있어요. 예를 들어 컨테이너가 주변 컨텍스트에서 공간이 부족하면 특정 요소를 숨기거나 더 작은 폰트를 사용할 수 있어요. Container queries는 모든 현대 브라우저에서 지원돼요.
Container queries에서 postcss-preset-mantine의 rem과 em 함수를 사용할 수 있어요. CSS 변수는 container queries에서 동작하지 않으므로 rem scaling 기능을 사용할 수 없다는 점에 주의하세요. 이 기능에 의존한다면 breakpoint를 px 단위로 정의하는 것이 좋아요.
.root {
min-width: 200px;
max-width: 100%;
min-height: 120px;
container-type: inline-size;
overflow: auto;
resize: horizontal;
}
.child {
background-color: var(--mantine-color-dimmed);
color: var(--mantine-color-white);
padding: var(--mantine-spacing-md);
@container (max-width: 500px) {
background-color: var(--mantine-color-blue-filled);
}
@container (max-width: 300px) {
background-color: var(--mantine-color-red-filled);
}
}
반응형 style props
style props로 반응형 스타일을 추가하려면 객체 문법을 사용할 수 있어요. 반응형 style props는 일반 style props보다 성능이 떨어지므로 요소가 많은 목록에서는 사용하지 않는 것이 좋아요.
import { Box } from '@mantine/core';
function Demo() {
return (
<Box
w={{ base: 200, sm: 300, lg: 400 }}
>
Box with responsive style props
</Box>
);
}
반응형 값은 다음 방식으로 계산돼요:
-
base값은 breakpoint 값이 적용되지 않을 때 사용돼요 -
xs,sm,md,lg,xl값은 뷰포트 너비가 theme.breakpoints에 지정된 해당 breakpoint 값보다 클 때 사용돼요
import { Box } from '@mantine/core';
function Demo() {
return <Box w={{ base: 200, sm: 300, lg: 400 }} />;
}
이 경우 요소는 다음 스타일을 가지게 돼요:
/* Base styles added to element and then get overwritten with responsive values */
.element {
width: 20rem;
}
/* 48em is theme.breakpoints.sm by default */
@media (min-width: 48em) {
.element {
width: 30rem;
}
}
/* 75em is theme.breakpoints.lg by default */
@media (min-width: 75em) {
.element {
width: 40rem;
}
}