페르소나는 문서 속 인물 소개로 끝나면 아무 힘이 없습니다. 멀미에 약한 사람, 밤에 화면을 보는 사람, 손가락으로만 누르는 사람, 글자를 키워 쓰는 사람을 실제로 배려하려면 그 특성을 CSS 조건으로 옮겨야 합니다. 각 페르소나를 하나씩 미디어쿼리로 번역해 보겠습니다.
18.1 움직임에 예민한 사용자
어떤 사람은 화면이 크게 미끄러지거나 튀면 어지러움을 느낍니다. 브라우저 설정으로 이를 알려 온 사용자에게는 애니메이션을 걷어 줍니다.
.card {
transition: transform .3s ease;
}
.card:hover {
transform: translateY(-6px);
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
.card:hover {
transform: none;
}
}
여기서 prefers-reduced-motion: reduce 는 운영체제에서 동작 줄이기를 켠 사람에게만 반응합니다. 그런 사용자에게는 카드가 떠오르지 않고 색이나 그림자 같은 조용한 변화만 남깁니다.
18.2 밤에 화면을 보는 사용자
어두운 방에서 흰 화면은 눈을 찌릅니다. 시스템을 다크 모드로 둔 사람에게는 배경과 글자색을 통째로 뒤집어 줍니다.
:root {
--bg: #ffffff;
--text: #111827;
--card: #f3f4f6;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text: #e5e7eb;
--card: #1e293b;
}
}
body {
background: var(--bg);
color: var(--text);
}
색을 변수로 묶어 두면 밝고 어두운 두 벌을 각각 관리하지 않아도 됩니다. prefers-color-scheme 가 값만 바꿔 끼우고, 그 변수를 쓰는 모든 요소가 알아서 따라옵니다.
18.3 손가락으로 누르는 사용자
마우스 커서는 1px도 정확히 짚지만 손가락은 그렇지 못합니다. 입력 수단이 거칠다는 신호가 오면 누르는 판을 넉넉히 키웁니다.
.menu-item {
padding: 6px 10px;
}
@media (pointer: coarse) {
.menu-item {
padding: 12px 16px;
min-height: 44px;
}
}
여기서 pointer: coarse 는 터치스크린처럼 정밀하지 못한 입력 장치를 뜻합니다. 같은 메뉴라도 손가락 사용자에게는 항목 사이가 벌어져 오누름이 줄어듭니다.
18.4 글자를 키워 쓰는 사용자
눈이 편치 않은 사람은 브라우저 기본 글자 크기를 키워 둡니다. 이 배려를 존중하려면 글자 단위를 px가 아니라 rem으로 잡아야 합니다.
/* 사용자 설정을 무시하는 방식 */
.body-text {
font-size: 16px;
}
/* 사용자 설정을 존중하는 방식 */
.body-text {
font-size: 1rem;
line-height: 1.6;
}
.title {
font-size: 1.5rem;
}
rem 은 사용자가 정한 기본 글자 크기를 기준으로 커지고 작아집니다. px로 못 박으면 아무리 설정을 키워도 글자가 꿈쩍 않지만, rem은 그 사람의 선택을 그대로 반영합니다.
18.5 여백도 함께 늘어나게
글자만 rem이고 여백은 px면, 글자를 키웠을 때 답답하게 서로 붙습니다. 간격도 rem으로 두면 글자와 함께 숨 쉴 공간이 늘어납니다.
.card {
padding: 1.25rem;
border-radius: .5rem;
}
.card + .card {
margin-top: 1rem;
}
글자를 키우면 카드 안쪽 여백과 카드 사이 간격이 같은 비율로 벌어집니다. 확대해도 레이아웃이 무너지지 않고 비례를 유지합니다.
18.6 대비를 더 원하는 사용자
연한 회색 글씨는 멀쩡한 눈에는 세련돼 보여도 누군가에게는 잘 안 읽힙니다. 높은 대비를 요청한 사람에게는 색을 더 또렷하게 바꿔 줍니다.
.desc {
color: #9ca3af;
}
.btn {
border: 1px solid transparent;
}
@media (prefers-contrast: more) {
.desc {
color: #374151;
}
.btn {
border-color: currentColor;
}
}
prefers-contrast: more 를 켠 사용자에게는 흐린 설명 글씨가 진해지고, 배경으로만 구분하던 버튼에 또렷한 테두리가 생깁니다. 색만으로 알던 경계를 선으로 한 번 더 그어 주는 셈입니다.
18.7 페르소나를 토큰으로 묶기
여러 페르소나의 요구가 겹치는 값은 변수 한 곳에 모아 두면 관리가 쉽습니다. 조건이 바뀔 때 토큰만 갈아 끼우면 됩니다.
:root {
--gap: 12px;
--radius: 8px;
--motion: .25s;
}
@media (pointer: coarse) {
:root { --gap: 16px; }
}
@media (prefers-reduced-motion: reduce) {
:root { --motion: 0s; }
}
.list {
gap: var(--gap);
}
.fade {
transition: opacity var(--motion) ease;
}
간격과 모션 시간을 토큰으로 뽑아 두니, 터치 사용자에게는 간격이 넓어지고 멀미에 약한 사용자에게는 전환 시간이 0이 됩니다. 컴포넌트 코드는 그대로 두고 조건별로 값만 흘려보내는 구조입니다.
18.8 마우스가 없는 기기
hover로만 메뉴를 펼치게 만들면 터치 기기에서는 그 메뉴를 영영 못 엽니다. hover가 실제로 가능한 환경에서만 hover 동작을 걸어야 합니다.
.submenu {
display: none;
}
@media (hover: hover) {
.has-sub:hover .submenu {
display: block;
}
}
@media (hover: none) {
.has-sub .submenu {
display: block;
position: static;
}
}
hover: hover 는 마우스처럼 떠 있는 상태가 가능한 기기, hover: none 은 터치처럼 그렇지 못한 기기입니다. 손가락 사용자에게는 하위 메뉴를 hover 없이 펼쳐 두어 갇히는 상황을 막습니다.
18.9 긴 글을 읽는 사용자
한 줄이 너무 길면 다음 줄 첫 글자를 눈이 놓칩니다. 읽기가 목적인 사람을 위해 본문 폭을 글자 수 기준으로 잡아 줍니다.
.article {
max-width: 65ch;
margin: 0 auto;
line-height: 1.7;
}
여기서 ch 단위는 글자 하나의 폭을 기준으로 삼습니다. 한 줄이 대략 65자를 넘지 않게 묶어 두면 시선이 줄을 갈아탈 때 헤매지 않습니다.
18.10 정리
페르소나는 상상 속 인물이 아니라 브라우저가 실제로 알려 주는 신호입니다. 동작 줄이기, 다크 모드, 거친 포인터, rem 존중, 높은 대비 요청을 각각 미디어쿼리로 받아 두면, 우리가 이름 붙인 사용자들이 코드 안에서 진짜로 대접받습니다. 배려는 문서가 아니라 조건문에 담길 때 비로소 사용자에게 닿습니다.