- 발행일
TV에 연결하니 테두리가 안 보였다 — 열흘 전 "일부러 안 고침"을 94개 파일에서 뒤집은 기록
TV에 연결하니 테두리가 안 보였다
열흘 전에 팔레트에 없는 색은 조용히 안 칠해진다를 쓰면서 대비 감사를 했습니다. 그때 border-gray-300 260곳은 일부러 안 고쳤어요. 근거도 문서에 적어 뒀습니다.
- border-gray-300 260곳 — 테이블 격자 56 · 카드 컨테이너 43 · 구분선 15 로, 폼 입력은 3곳뿐이다.
WCAG 1.4.11 의 3:1 은 상태를 전달하는 UI 경계에만 적용된다. 진하게 하면 화면만 딱딱해진다.
표의 격자선이나 카드의 외곽선은 정보가 아니라 장식이고, 기준은 정보를 전달하는 경계에만 적용되니 손대지 말자는 판단이었습니다. 논리로는 맞았어요.
그리고 제보가 왔습니다. 고해상도 TV에 HDMI로 화면을 확장해서 띄웠더니 표 격자가 안 보인다는 거였어요. 격자만이 아니라 카드 테두리, 구분선, 다이얼로그 외곽까지 전부. 흰 판 위에 글자만 떠 있는 화면이었습니다.
기준이 아니라 환경이 틀렸다
내 모니터에서는 보였습니다. 지금도 보여요. gray-300이 흰 배경 대비 1.24:1인데, 사무실 모니터는 그 차이를 그려 줍니다. TV는 안 그려 줍니다. 패널이 다르면 그 차이를 안 그립니다. 왜 그 환경에서만 사라지는지는 재현하지 못했고, 제보를 그대로 믿고 수치로 골랐습니다.
열흘 전의 판단은 "이 경계가 정보인가"만 물었습니다. "이 경계가 보이는 환경이 어디까지인가"는 안 물었어요. WCAG가 장식 경계를 면제하는 건 그게 안 보여도 내용을 잃지 않아서인데, 표 격자가 사라진 표는 행이 어디서 끊기는지 알 수 없는 표입니다. 장식이라 부르던 것이 실제로는 읽기를 돕고 있었던 거예요.
| 선택지 | 판단 |
|---|---|
| ① 제보한 환경만 예외 — 그 TV에서만 안 보이는 것이니 그대로 둠 | 기각. 어느 화면에서 열릴지 고를 수 없다 |
② gray-300의 정의값 자체를 진하게 | 기각. 배경으로 쓰는 bg-gray-300까지 같이 진해진다. 팔레트는 시험 시스템과 맞춘 값이라 못 건드린다 |
③ 테두리에 쓰인 gray-200/300/400을 전수로 한 단계 이상 올림 | 채택. 이름은 그대로, 쓰는 자리만 바꾼다 |
②가 제일 적은 diff인데, 이 팔레트는 다른 시스템과 정합을 맞추느라 표준 Tailwind보다 한 칸 밝게 정의돼 있고, 그 값을 바꾸면 배경·글자까지 전부 따라갑니다. 열흘 전 글에서 "회색 스케일이 한 칸 밝은 것 자체는 안 고쳤다"고 적은 그 이유가 그대로예요. 그래서 이번에도 정의는 두고 사용처를 바꿨습니다.
장식과 연결선은 다른 단계로 올렸다
전부 같은 값으로 올리지는 않았습니다. 두 종류로 갈랐어요.
/* input.css */
> td {
- @apply h-[2.5rem] border-y border-gray-400;
+ @apply h-[2.5rem] border-y border-gray-500;
}
표 격자·카드·구분선·다이얼로그·토글 노브 같은 장식은 gray-500(2.60:1)까지. 보이기만 하면 되는 선이라 그 이상 진하게 하면 열흘 전에 걱정한 "화면만 딱딱해진다"가 실제로 됩니다.
// oidTreeChart.js
-const LINK_STROKE = "#CACDD2";
+// 부모-자식 연결선은 장식이 아니라 그 자체가 정보(관계)라 gray-600 까지 진하게 둔다
+const LINK_STROKE = "#73787E"; // gray-600
OID 조직도의 부모-자식 선과 관계 그래프의 엣지는 gray-600(4.71:1)까지 올렸습니다. 이 선은 없으면 그림이 아니라 점 무더기예요. 선 자체가 정보라서 텍스트 기준에 가깝게 뒀습니다.
관계 그래프의 엣지에는 덤이 하나 있었습니다.
- .attr("stroke", "#999")
+ .attr("stroke", "#73787E") // gray-600. 팔레트 밖 #999(2.85:1)는 opacity 0.8 이 겹쳐 실제로 더 흐렸다
.attr("stroke-width", 2)
.attr("stroke-opacity", 0.8);
#999는 팔레트에 없는 색인 데다 stroke-opacity: 0.8이 겹쳐 있어서 계산한 대비보다 실제로는 더 흐렸습니다. 열흘 전 감사가 클래스 이름만 훑고 D3가 직접 찍는 hex는 못 본 자리예요. 이번에 #EAEBED(gray-300의 정의값)를 박아 둔 곳도 같이 #9FA4A8로 바꿨습니다. 범례 상자, 데이터 맵 패널 테두리, 조직도의 유령 노드 점선이 그것입니다.
전부 합쳐 94개 파일, 삭제 512줄에 삽입 517줄이었습니다. 템플릿에서 border-gray-200/300/400이 지워진 줄이 398, JS에서 94였어요.
일부러 남긴 것과 못 고친 것
requestFormSteps.js의 잠긴 단계 탭은 안 올렸습니다.
locked: "border-gray-400 bg-gray-200 text-gray-500 cursor-not-allowed",
이건 흐려야 하는 자리예요. 아직 못 가는 단계라는 뜻을 흐림으로 전달하고 있어서, 진하게 하면 갈 수 있는 단계처럼 읽힙니다. 열흘 전 판단에서 "비활성 요소는 면제"라고 적은 부분은 이번에도 유지됐습니다.
문서에는 열흘 전 판단 바로 아래에 뒤집힌 사실을 붙였습니다.
🔄 2026-09-03 뒤집힘 — 고해상도 TV·HDMI 확장 환경에서 이 대비(1.24:1)로는 테두리 자체가
안 보인다는 제보로 판단 기준이 바뀌었다. border-gray-200/300/400 전체를 장식(테이블 격자·
카드·구분선)은 gray-500(2.60:1), 관계를 나타내는 연결선은 선 자체가 정보라 gray-600(4.71:1)까지 올렸다.
옛 판단을 지우지 않고 그 밑에 붙인 이유는, 다음에 누가 "격자를 왜 이렇게 진하게 했나"를 물었을 때 두 판단이 같이 보여야 해서입니다. 한쪽만 남으면 다시 뒤집힙니다.
검증
- 고친 뒤
templates·static에서border-gray-[234]00을 다시 셌습니다. 507곳에서 7곳으로. 남은 7곳은 아래 한계에 적은 것들입니다. - 지운 줄과 넣은 줄을 대조했습니다. 삭제 512줄, 삽입 517줄. 늘어난 5줄은 주석과 문서의 뒤집힘 기록이라 색 치환 자체는 1:1이었습니다.
- OID 조직도와 관계 그래프에서 선택 강조색
#006FF1은 그대로 두고 기본 엣지만#73787E로 바꿨습니다. 파랑과 회색이라 강조가 묻히지 않습니다. - 잠긴 단계 탭(
requestFormSteps.js)이 치환에서 빠진 것을 고친 뒤 파일에서 다시 확인했습니다. 의도한 예외가 실수로 같이 올라가지 않았습니다.
남은 것 · 한계
- 남은 7곳 중 3곳은
chat.js의 마크다운 렌더 문자열 안입니다. 클래스가 JS 문자열 안에 조립돼 있어 전수 치환에서 빠졌어요. 채팅 화면의 인용 블록과 구분선이 아직 옛 색입니다. stdDataFilter.js3곳은classList.add("border-gray-300")처럼 런타임에 붙이는 클래스입니다. 열흘 전tocSidebar.js에서 겪은 것과 같은 모양이에요. 템플릿을 고쳐도 JS가 다시 옛 클래스를 붙이는 자리인데 이번에도 놓쳤습니다.- CSS 변수로 쓴 자리는 grep에 안 잡혔습니다.
d3-graph-common.css의 줌 버튼이var(--color-gray-400)로 테두리를 그리는데, 클래스명 기준 치환이라 지나쳤어요. 관계도 화면의 확대·축소 버튼이 TV에서는 여전히 테두리 없이 뜰 겁니다. - 제보한 환경에서 다시 봤다는 기록이 없습니다. 커밋과 문서에는 제보를 반영했다는 것까지만 있어요.
gray-500(2.60:1)이 그 TV에서 보인다는 건 수치로 고른 것이지 그 자리에서 확인한 것이 아니고, 더 나쁜 패널에서는 이것도 모자랄 수 있습니다. - 회색 스케일이 한 칸 밝은 근본은 이번에도 안 건드렸습니다. 열흘 전 글의 한계가 그대로 이번 글의 한계예요. 사용처를 올리는 방식은 다음 사람이
border-gray-300을 쓰는 순간 같은 문제를 다시 만듭니다. 색 클래스 가드(eslint.sh)는 팔레트 밖 이름만 잡지, 팔레트 안의 옅은 단계를 테두리에 쓰는 것은 못 막습니다.
관련 글: 팔레트에 없는 색은 조용히 안 칠해진다 · Tailwind v3에서 v4로 170개 파일을 옮겼다 · 버튼 기본값이 w-full이었다