“아직도 메모장에서 벗어나지 못했어.” 개업 예산 화면을 두고 들은 말입니다. 캡처를 보니 정말 그랬습니다.
개발노트 · 화면 · CSS
자본금₩20,000,000 보증금 · 주거지역-2,500,000 매장 · 소형 매장-2,000,000 장비 · 균형형-4,000,000 인테리어-1,500,000 초기 재료-1,000,000 개업 후 운영자금₩9,000,000
항목과 금액이 공백 하나 없이 붙어 있습니다. 구분선도 없고, 금액이 오른쪽에 맞춰지지도 않습니다. 「메모장 같다」는 표현이 정확했습니다.
처음에는 띄어쓰기를 빠뜨린 줄 알았습니다. 코드를 보니 라벨과 금액은 별개의 요소로 잘 나뉘어 있었습니다. 문제는 글이 아니라 배치였습니다.
이 게임은 넓은 화면에서 개업 예산을 오른쪽 사이드바 카드로 보여 줍니다. 휴대폰에서는 그럴 자리가 없어서, 아래쪽에 요약 바를 두고 누르면 시트가 올라오게 했습니다.
그 시트를 만드는 코드는 이렇게 생겼습니다.
const body = ledger.querySelector(".card-b");
ov.querySelector(".card-sheet-b").appendChild(body); // 통째로 옮긴다
표 내용을 담은 요소를 통째로 시트 안으로 옮깁니다. DOM을 새로 만들지 않아서 값이 어긋날 일이 없습니다. 여기까지는 좋은 방법입니다.
표 모양은 전부 이렇게 정의돼 있었습니다.
.ledger .row { display:flex; justify-content:space-between;
border-bottom:1px dotted … }
.ledger .row span:last-child { font-family:var(--mono) }
전부 .ledger 안에 있을 때만 적용됩니다. 그런데 시트로 옮기는 순간, 그 요소는 .ledger 바깥으로 나갑니다. 계산된 값을 찍어 봤습니다.
| 속성 | 시트로 옮긴 뒤 | 정상 |
|---|---|---|
display | block | flex |
justify-content | normal | space-between |
| 구분선 | none | dotted |
| 금액 글꼴 | 본문 글꼴 | 고정폭 |
display:flex가 block이 되면 라벨과 금액이 그냥 이어 붙습니다. 띄어쓰기가 빠진 게 아니라, 둘 사이를 벌려 주던 것이 사라진 것입니다.
가장 쉬운 해결은 시트에도 .ledger 클래스를 붙이는 것입니다. 그런데 그 클래스에는 이런 게 딸려 있었습니다.
.ledger { position: sticky; top: 64px }
사이드바 카드가 스크롤을 따라다니게 하는 규칙입니다. 시트 안에서는 필요 없고, 오히려 방해가 됩니다. 한 클래스가 두 가지 일을 하고 있었던 것이 진짜 문제였습니다.
그래서 나눴습니다.
.card.ledger { position: sticky; top: 64px } ← 옆에 붙는 «카드»만
.ledger .row { display:flex; … } ← 표 모양은 클래스만 따라감
이제 시트 본문에 ledger를 얹으면 표 모양만 따라오고, 위치 고정은 따라오지 않습니다.
| 전 | 후 | |
|---|---|---|
| 라벨↔금액 간격 | 0px | 234px (양끝 정렬) |
| 구분선 | 없음 | 점선 |
| 합계 윗선 | 없음 | 2px |
| 금액 글꼴 | 본문 | 고정폭 (자릿수 정렬) |
DOM을 옮겨 담는 방식은 값이 어긋나지 않아서 좋습니다. 대신 스타일이 «어디에 있느냐»에 묶여 있으면 옮기는 순간 벗겨집니다. 이걸 겪고 나서 규칙을 하나 세웠습니다.
요소를 다른 곳으로 옮길 계획이 있다면, 그 요소의 생김새는 부모의 위치가 아니라 자기 클래스에 붙여 둔다.
그리고 클래스 하나가 «어디에 놓일지»와 «어떻게 생겼는지»를 동시에 정하고 있으면, 언젠가 둘 중 하나만 필요한 날이 옵니다. 그때 나누는 것보다 처음부터 나눠 두는 편이 낫습니다.
이 게임에는 휴대폰에서 카드를 시트로 올리는 화면이 몇 군데 더 있습니다. 같은 함정이 남아 있을 가능성이 커서, 하나씩 확인하고 있습니다.