From 7e07da4b3195b68e5d879e1a3c296d8888cf70c4 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 26 Oct 2026 20:59:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(=ED=85=8C=EB=A7=88):=20=EA=B7=B8=EB=A6=BC?= =?UTF-8?q?=20=EC=A0=84=EC=9A=A9=20=EA=B0=92=20--draw-*=20=E2=80=94=20?= =?UTF-8?q?=EC=A2=85=EB=8B=A8=20=C2=B7=20=ED=9A=A1=EB=8B=A8=20=C2=B7=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=95=ED=91=9C=20SVG=20=C2=B7=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20=EA=B3=84=ED=9A=8D=EC=84=A0=20=C2=B7=20CAD=20=EB=B0=94?= =?UTF-8?q?=ED=83=95=20=C2=B7=203D=20=EB=B0=94=ED=83=95=20=EC=83=89?= =?UTF-8?q?=EC=9D=84=20=ED=99=94=EB=A9=B4=20=ED=85=8C=EB=A7=88=EC=99=80=20?= =?UTF-8?q?=EB=96=BC=EC=96=B4=20=EC=A7=80=EA=B8=88=20=EC=83=89=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=20(PLAN=20551-2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ui_template_theme.css 끝 블록 = --draw-* 17 개 · 밝음 / 어두움 / 시스템 어두움 글자 hex - B05 · B06 · B08 그림 CSS stroke · fill 125 줄 · --b06-tag = --draw-* - 캔버스 · 3D 읽기 = B05 노선 편집 지도 · 노선 추천 지도 · 3D 바탕 · B04 3D 바탕 · 지도 MC 글 · D03 고른 측점 · CAD 바탕 · 격자 - 지킴 시험 test_551_2_draw_tokens(화면 변수를 모두 바꿔도 그림 풀린 색 같음) - 기존 시험 7 개 = 그림 줄이 옛 --color-* 이름을 잡던 단정을 --draw-* 로 고침(값 같음) Co-Authored-By: Claude Opus 5.5 --- B04_PreProcess/B04_PreProcess_UI_Camera.ts | 2 +- .../B04_PreProcess_UI_MapOverlays.ts | 4 +- .../B05_Profile_UI_RouteEdit_Recommend_Map.ts | 4 +- .../B05_Profile_UI_RouteEdit_Render.ts | 8 +- B05_Profile/B05_Profile_UI_Style.css | 2 +- .../B05_Profile_UI_Style_RouteEdit.css | 4 +- .../B05_Profile_UI_Style_RouteRecommend.css | 4 +- B05_Profile/B05_Profile_UI_Style_Table.css | 26 +-- B05_Profile/B05_Profile_UI_Viewer.ts | 2 +- B06_Section/B06_Section_UI_Style.css | 12 +- .../B06_Section_UI_Style_Cross_Areas.css | 98 +++++------ .../B06_Section_UI_Style_Cross_Chart.css | 38 ++--- .../B06_Section_UI_Style_Cross_Detail.css | 18 +- B07_DesignDetail/openwebcad/src/App.css | 2 +- .../screenCanvas.drawController.ts | 2 +- B08_Quantity/B08_Quantity_UI_Hydro.css | 8 +- B08_Quantity/B08_Quantity_UI_Milestone.css | 40 ++--- D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts | 2 +- resources/tester/test_164_detail_zoom.py | 6 +- resources/tester/test_215_1_free_profile.py | 14 +- .../test_250_1_b08_milestone_roads_width.py | 6 +- resources/tester/test_271_straighten_pick.py | 2 +- resources/tester/test_318_cross_card.py | 6 +- resources/tester/test_507_3_mc_label.py | 14 +- resources/tester/test_551_2_draw_tokens.py | 155 ++++++++++++++++++ resources/tester/test_b06_wall_drain_pipe.py | 2 +- ui_template/ui_template_theme.css | 58 +++++++ 27 files changed, 379 insertions(+), 160 deletions(-) create mode 100644 resources/tester/test_551_2_draw_tokens.py diff --git a/B04_PreProcess/B04_PreProcess_UI_Camera.ts b/B04_PreProcess/B04_PreProcess_UI_Camera.ts index 8bf42cc44..232f05569 100644 --- a/B04_PreProcess/B04_PreProcess_UI_Camera.ts +++ b/B04_PreProcess/B04_PreProcess_UI_Camera.ts @@ -468,7 +468,7 @@ export function bindSurfaceViewerTheme( return; } const surfaceRaised = getComputedStyle(document.documentElement) - .getPropertyValue("--color-surface-raised") + .getPropertyValue("--draw-surface-raised") .trim(); applyBackground( surfaceRaised && CSS.supports("color", surfaceRaised) diff --git a/B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts b/B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts index c0b468e71..59585d521 100644 --- a/B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts +++ b/B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts @@ -330,9 +330,9 @@ export function drawStationTicks( context.translate(-lx, -ly); } // 배경을 깔아 등고선 위에서도 읽히게 한다. - context.fillStyle = mc ? themeColor("--color-canvas", "#ffffff") : "rgba(255, 255, 255, 0.78)"; + context.fillStyle = mc ? themeColor("--draw-canvas", "#ffffff") : "rgba(255, 255, 255, 0.78)"; context.fillRect(lx - half, ly - 8, width, 16); - context.fillStyle = mc ? themeColor("--color-warning", "#b8860b") : "#222222"; + context.fillStyle = mc ? themeColor("--draw-warning", "#b8860b") : "#222222"; context.fillText(label, lx, ly); context.restore(); } diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts index 9408a1c1e..b215188ce 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts @@ -118,7 +118,7 @@ export function createRecommendMap(box: HTMLElement): RecommendMap { context.lineJoin = "round"; const styles: Record void> = { expected: () => { - context.strokeStyle = themeColor("--color-text-secondary", "#9ca3af"); + context.strokeStyle = themeColor("--draw-text-secondary", "#9ca3af"); context.lineWidth = 1.5; context.setLineDash([5, 4]); }, @@ -129,7 +129,7 @@ export function createRecommendMap(box: HTMLElement): RecommendMap { context.setLineDash([]); }, preview: () => { - context.strokeStyle = themeColor("--color-primary", "#6d28d9"); + context.strokeStyle = themeColor("--draw-primary", "#6d28d9"); context.lineWidth = 3; context.setLineDash([]); }, diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts index 2f1658c37..eb38a92cf 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts @@ -169,7 +169,7 @@ export function drawRouteEditScene(context: CanvasRenderingContext2D, scene: Rou const style = getComputedStyle(document.documentElement); const line = scene.plannedLine.length ? scene.plannedLine : scene.planned; context.clearRect(0, 0, view.width, view.height); - context.fillStyle = style.getPropertyValue("--color-surface") || "#111"; + context.fillStyle = style.getPropertyValue("--draw-surface") || "#111"; context.fillRect(0, 0, view.width, view.height); // 여기서부터 **그림 전체**가 돈다 — 글자도 함께 돈다(CAD 도면과 같은 방식, 사용자 지시 ⑯). @@ -246,7 +246,7 @@ export function drawRouteEditScene(context: CanvasRenderingContext2D, scene: Rou toScreen, scene.expected, [6, 5], - style.getPropertyValue("--color-text-secondary") || "#9ca3af", + style.getPropertyValue("--draw-text-secondary") || "#9ca3af", 1.6, ); // 곡선 시작·끝점 — 잡아서 직선 각도와 R 을 함께 바꾸는 손잡이(2026-09-07 사용자 지시). @@ -307,7 +307,7 @@ export function drawRouteEditScene(context: CanvasRenderingContext2D, scene: Rou const bad = (scene.nodeInfo[index]?.violations?.length ?? 0) > 0; // 고정한 점은 고정 색 — 기준 미달(붉음)이 먼저다. context.fillStyle = bad - ? style.getPropertyValue("--color-danger") || "#dc2626" + ? style.getPropertyValue("--draw-danger") || "#dc2626" : scene.locked[index] ? lockedColor : style.getPropertyValue("--map-route-node") || "#15803d"; @@ -367,7 +367,7 @@ function drawContourLabels( style.getPropertyValue("--map-sheet-contour") || "#a5b4fc", scene.contourStepM, scene.uprightRad, - style.getPropertyValue("--color-surface") || "#111", // 바탕칠과 같은 색 — 어두움에서 흰 상자 안 생김 + style.getPropertyValue("--draw-surface") || "#111", // 바탕칠과 같은 색 — 어두움에서 흰 상자 안 생김 ); if (scene.pickedContour >= 0) drawPickedContourLabel(context, scene, scene.view); context.restore(); diff --git a/B05_Profile/B05_Profile_UI_Style.css b/B05_Profile/B05_Profile_UI_Style.css index a7cd14835..565299592 100644 --- a/B05_Profile/B05_Profile_UI_Style.css +++ b/B05_Profile/B05_Profile_UI_Style.css @@ -153,7 +153,7 @@ /* 비정규 측점(구조물)은 그래프에서도 파선 세로선으로 구분해 규칙 측점과 헷갈리지 않게 한다. */ .b05-route-profile .b06-chart__station-line--irregular { - stroke: var(--color-royal-amethyst, rgb(139 92 246)); + stroke: var(--draw-accent); stroke-width: 1.4; stroke-dasharray: 5 3; } diff --git a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css index 0c674c595..986c779ab 100644 --- a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css +++ b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css @@ -344,7 +344,7 @@ .b05-routeedit__cross-ground { fill: none; - stroke: var(--color-text-secondary, #94a3b8); + stroke: var(--draw-text-secondary); stroke-width: 1.6; vector-effect: non-scaling-stroke; } @@ -357,7 +357,7 @@ } .b05-routeedit__cross-key { - fill: var(--color-text-secondary, #94a3b8); + fill: var(--draw-text-secondary); font-size: 11px; } diff --git a/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css b/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css index b0b71043e..55ccb906e 100644 --- a/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css +++ b/B05_Profile/B05_Profile_UI_Style_RouteRecommend.css @@ -503,13 +503,13 @@ .b05-routerec__profile .is-ground { fill: none; - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1.5; } .b05-routerec__profile .is-plan { fill: none; - stroke: var(--color-primary); + stroke: var(--draw-primary); stroke-width: 2; } diff --git a/B05_Profile/B05_Profile_UI_Style_Table.css b/B05_Profile/B05_Profile_UI_Style_Table.css index 51be9fe46..48c37ede7 100644 --- a/B05_Profile/B05_Profile_UI_Style_Table.css +++ b/B05_Profile/B05_Profile_UI_Style_Table.css @@ -331,7 +331,7 @@ .b05-profile-runmark__line { fill: none; - stroke: var(--color-danger, #d64545); + stroke: var(--draw-danger); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; @@ -340,7 +340,7 @@ /* 구간 경계(라운드 시·종점) 세로 표식 — 어디까지 함께 움직이는지 알린다. */ .b05-profile-runmark__edge { - stroke: var(--color-danger, #d64545); + stroke: var(--draw-danger); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.5; @@ -356,26 +356,26 @@ } .b05-profile-freemark__band { - fill: color-mix(in srgb, var(--color-primary) 10%, transparent); + fill: color-mix(in srgb, var(--draw-primary) 10%, transparent); } .b05-profile-freemark__edge { - stroke: var(--color-primary); + stroke: var(--draw-primary); stroke-width: 1.5; } /* 범위 「시작–끝 · 길이」 글 — [직선화] · [자유지정] 실시간 띠 위(271-2 · 3). */ .b05-profile-freemark__span { - fill: var(--color-primary); + fill: var(--draw-primary); font-size: var(--text-caption); paint-order: stroke; - stroke: var(--color-surface); + stroke: var(--draw-surface); stroke-width: 3px; } /* 지나온 선 · 미리 보기 · 점 = 녹색(367-4) — 범위 띠 · 경계는 보라 그대로. */ .b05-profile-freemark__line { - stroke: var(--color-success); + stroke: var(--draw-success); stroke-width: 2; stroke-dasharray: 6 4; } @@ -385,25 +385,25 @@ } .b05-profile-freemark__line.is-over { - stroke: var(--color-danger, #d64545); + stroke: var(--draw-danger); } .b05-profile-freemark__dot { - fill: var(--color-surface); - stroke: var(--color-success); + fill: var(--draw-surface); + stroke: var(--draw-success); stroke-width: 1.5; } .b05-profile-freemark__grade { - fill: var(--color-text); + fill: var(--draw-text); font-size: var(--text-caption); paint-order: stroke; - stroke: var(--color-surface); + stroke: var(--draw-surface); stroke-width: 3px; } .b05-profile-freemark__grade.is-over { - fill: var(--color-danger, #d64545); + fill: var(--draw-danger); } .b05-profile-edit { diff --git a/B05_Profile/B05_Profile_UI_Viewer.ts b/B05_Profile/B05_Profile_UI_Viewer.ts index 8c148ee27..168f35d13 100644 --- a/B05_Profile/B05_Profile_UI_Viewer.ts +++ b/B05_Profile/B05_Profile_UI_Viewer.ts @@ -121,7 +121,7 @@ export function createRouteViewer(): RouteViewer { return; } const surfaceRaised = getComputedStyle(document.documentElement) - .getPropertyValue("--color-surface-raised") + .getPropertyValue("--draw-surface-raised") .trim(); scene.background = new THREE.Color( surfaceRaised && CSS.supports("color", surfaceRaised) diff --git a/B06_Section/B06_Section_UI_Style.css b/B06_Section/B06_Section_UI_Style.css index 497f66f12..2d8c2b3d7 100644 --- a/B06_Section/B06_Section_UI_Style.css +++ b/B06_Section/B06_Section_UI_Style.css @@ -205,25 +205,25 @@ .b06-diag__ground { fill: none; - stroke: var(--color-text-muted); + stroke: var(--draw-text-muted); stroke-width: 1; stroke-dasharray: 3 2; } .b06-diag__road { fill: none; - stroke: var(--color-text-body); + stroke: var(--draw-text-body); stroke-width: 2; } .b06-diag__center { - stroke: var(--color-royal-amethyst, rgb(109 40 217)); + stroke: var(--draw-accent); stroke-width: 1; stroke-dasharray: 4 3; } .b06-diag__tick { - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1; } @@ -244,12 +244,12 @@ } .b06-diag__label { - fill: var(--color-text-body); + fill: var(--draw-text-body); font-size: 14px; } .b06-diag__label-sm { - fill: var(--color-text-secondary); + fill: var(--draw-text-secondary); font-size: 12px; } diff --git a/B06_Section/B06_Section_UI_Style_Cross_Areas.css b/B06_Section/B06_Section_UI_Style_Cross_Areas.css index f57d24e85..50de72507 100644 --- a/B06_Section/B06_Section_UI_Style_Cross_Areas.css +++ b/B06_Section/B06_Section_UI_Style_Cross_Areas.css @@ -14,11 +14,11 @@ /* Y축 중심고 상자 색(379-3) — 원지반 = 원지반선 · 계획 = 설계선. */ .b06-chart__center-tag--ground { - --b06-tag: var(--color-chart-1); + --b06-tag: var(--draw-chart-1); } .b06-chart__center-tag--design { - --b06-tag: var(--color-royal-amethyst); + --b06-tag: var(--draw-accent); } /* 면적 강조 밴드 — 평소엔 클릭 대상으로만 남고, 켜질 때만 채워진다. */ @@ -30,34 +30,34 @@ /* 밴드 색은 위 면적표 글자색과 짝을 맞춘다 — 어느 숫자를 켰는지 색으로 바로 읽히게. */ .b06-chart__area--cut_soil.is-active > polygon { - fill: color-mix(in srgb, var(--color-chart-2) 28%, transparent); - stroke: var(--color-chart-2); + fill: color-mix(in srgb, var(--draw-chart-2) 28%, transparent); + stroke: var(--draw-chart-2); stroke-width: 1.2; } /* 암반 밴드는 하나지만 색은 암종을 따라간다 — 표의 RR/BR 글자색과 같아야 짝이 읽힌다. */ .b06-chart__area--cut_rr.is-active > polygon { - fill: color-mix(in srgb, var(--color-success) 28%, transparent); - stroke: var(--color-success); + fill: color-mix(in srgb, var(--draw-success) 28%, transparent); + stroke: var(--draw-success); stroke-width: 1.2; } .b06-chart__area--cut_br.is-active > polygon { - fill: color-mix(in srgb, var(--color-chart-3) 28%, transparent); - stroke: var(--color-chart-3); + fill: color-mix(in srgb, var(--draw-chart-3) 28%, transparent); + stroke: var(--draw-chart-3); stroke-width: 1.2; } .b06-chart__area--fill.is-active > polygon { - fill: color-mix(in srgb, var(--color-chart-0) 28%, transparent); - stroke: var(--color-chart-0); + fill: color-mix(in srgb, var(--draw-chart-0) 28%, transparent); + stroke: var(--draw-chart-0); stroke-width: 1.2; } /* 횡단 표준단면 설계선 오버레이. 기본은 실선. */ .b06-chart__design-cross { fill: none; - stroke: var(--color-royal-amethyst); + stroke: var(--draw-accent); stroke-width: 1.8; stroke-linejoin: round; } @@ -80,7 +80,7 @@ /* 마우스 올림 = 고른 모양(379-1) · 고름은 누를 때만 */ .b06-chart__design-hit:hover, .b06-chart__design-hit.is-active { - stroke: color-mix(in srgb, var(--color-royal-amethyst) 45%, transparent); + stroke: color-mix(in srgb, var(--draw-accent) 45%, transparent); stroke-width: 7; } @@ -92,7 +92,7 @@ /* 원지반 십자선(413) = 원지반선 색 · 계획 십자선 아래(먼저 그림) */ .b06-chart__ground-center-marker { - stroke: var(--color-chart-1); + stroke: var(--draw-chart-1); stroke-width: 2.5; } @@ -104,8 +104,8 @@ } .b06-chart__center-hit.is-active { - fill: color-mix(in srgb, var(--color-royal-amethyst) 18%, transparent); - stroke: var(--color-royal-amethyst); + fill: color-mix(in srgb, var(--draw-accent) 18%, transparent); + stroke: var(--draw-accent); cursor: ew-resize; } @@ -117,7 +117,7 @@ .b06-chart__toe-hit:hover, .b06-chart__toe-hit.is-active { - fill: var(--color-royal-amethyst); + fill: var(--draw-accent); stroke: transparent; cursor: pointer; } @@ -125,7 +125,7 @@ /* 암 경계선 고름(370-3) — 점선 위 투명 판정 선 · 고르면 경고색 띠 · 세로 끌기. */ .b06-chart__design-hit.b06-chart__rock-hit:hover, .b06-chart__design-hit.b06-chart__rock-hit.is-active { - stroke: color-mix(in srgb, var(--color-warning) 40%, transparent); + stroke: color-mix(in srgb, var(--draw-warning) 40%, transparent); } .b06-chart__design-hit.b06-chart__rock-hit.is-active { @@ -134,21 +134,21 @@ /* 차도·노견 경계 짧은 수직 틱(N-4-2) — 설계선과 같은 계열, 얇게. */ .b06-chart__carriageway-tick { - stroke: var(--color-royal-amethyst); + stroke: var(--draw-accent); stroke-width: 1.2; } /* 노견 바깥 끝 틱(2026-09-12) — 차도 틱보다 짧고 옅다. 차도 틱과 이 틱 사이가 노견이라, 노면이 넓어졌을 때 차도가 늘었는지 노견이 늘었는지 눈으로 바로 갈린다. */ .b06-chart__shoulder-tick { - stroke: var(--color-royal-amethyst); + stroke: var(--draw-accent); stroke-width: 1; opacity: 0.55; } /* 노폭 라벨(2026-09-06) — 차도 위 가운데. 확폭이 걸린 측점을 눈으로 가려내는 표기다. */ .b06-chart__carriageway-label { - fill: var(--color-royal-amethyst); + fill: var(--draw-accent); font-size: 11px; text-anchor: middle; pointer-events: none; @@ -157,7 +157,7 @@ /* 암 경계선(설계선 복사 + 오프셋): 리핑암·발파암 구간 점선 */ .b06-chart__rock-boundary { fill: none; - stroke: var(--color-warning); + stroke: var(--draw-warning); stroke-width: 1.6; stroke-dasharray: 6 4; stroke-linejoin: round; @@ -166,31 +166,31 @@ /* 포장층 박스: 노면 양 끝점 기준 두께만큼 하향 채움 */ .b06-chart__pavement { - fill: color-mix(in srgb, var(--color-text-secondary) 30%, transparent); - stroke: var(--color-text-secondary); + fill: color-mix(in srgb, var(--draw-text-secondary) 30%, transparent); + stroke: var(--draw-text-secondary); stroke-width: 1; } /* 고른 노면공의 포장층(152-5) — 범위 카드마다 · 무늬 채움은 inline 이라 무늬 없는 층만 채움이 바뀌고 테두리로 강조 */ .b06-chart__pavement.is-active { - fill: color-mix(in srgb, var(--color-danger) 30%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 30%, transparent); + stroke: var(--draw-danger); stroke-width: 2.2; } /* 노면공 포장사양 무늬(88): 포장 박스 채움 = 바탕 + 무늬 — 콘크리트 빗금 · 아스콘 점 · 쇄석 돌 */ .b06-chart__pave-bg { - fill: color-mix(in srgb, var(--color-text-secondary) 30%, transparent); + fill: color-mix(in srgb, var(--draw-text-secondary) 30%, transparent); } .b06-chart__pave-mark { fill: none; - stroke: color-mix(in srgb, var(--color-text-body) 70%, transparent); + stroke: color-mix(in srgb, var(--draw-text-body) 70%, transparent); stroke-width: 1; } .b06-chart__pave-mark--dot { - fill: color-mix(in srgb, var(--color-text-body) 70%, transparent); + fill: color-mix(in srgb, var(--draw-text-body) 70%, transparent); stroke: none; } @@ -214,13 +214,13 @@ /* 물넘이포장(287-2 · 287-4): 노면공 포장층(노견까지) + 월류 높이 점선 */ .b06-chart__ford-deck-plan { fill: none; - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1; stroke-dasharray: 5 4; } .b06-chart__ford-pavement { - stroke: var(--color-text-body); + stroke: var(--draw-text-body); stroke-width: 1; } @@ -303,14 +303,14 @@ } .b06-chart__culvert-revet.is-active { - fill: color-mix(in srgb, var(--color-danger) 45%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 45%, transparent); + stroke: var(--draw-danger); stroke-width: 2.2; } /* 흙막이 · 옹벽 덮개(66-5) — 벽 안으로 지나가는 다른 선을 바탕색으로 가림. */ .b06-chart__wall-cover { - fill: var(--color-surface-raised); + fill: var(--draw-surface-raised); stroke: none; pointer-events: none; } @@ -333,15 +333,15 @@ (2026-08-30 사용자: 선택해도 하이라이트가 없었다). 색은 기슭막이·집수정 강조와 같은 계열이되, 면이 넓어 채도를 낮춘다. */ .b06-chart__box-hit.is-active { - fill: color-mix(in srgb, var(--color-danger) 16%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 16%, transparent); + stroke: var(--draw-danger); stroke-width: 1.6; stroke-dasharray: 4 3; } .b06-chart__culvert-apron { - fill: color-mix(in srgb, var(--color-text-secondary) 22%, transparent); - stroke: var(--color-text-secondary); + fill: color-mix(in srgb, var(--draw-text-secondary) 22%, transparent); + stroke: var(--draw-text-secondary); stroke-width: 1; stroke-dasharray: 3 2; /* 지반 아래 매설부 — 점선으로 구분 */ } @@ -349,7 +349,7 @@ .b06-chart__culvert-label { font-size: 9px; font-weight: var(--font-weight-medium); - fill: var(--color-text-secondary); + fill: var(--draw-text-secondary); } /* 관 정보 글(관매설 · 세월교 · 267-1) — 노폭 라벨(`.b06-chart__carriageway-label`)과 같은 글 크기 */ @@ -359,7 +359,7 @@ /* 유입 보호공 공간 확보 절토선 — 말단에서 표준단면 절토경사로 지반까지 */ .b06-chart__culvert-cut { - stroke: var(--color-warning); + stroke: var(--draw-warning); stroke-width: 1.4; stroke-dasharray: 5 3; } @@ -374,7 +374,7 @@ /* 옹벽 물빼기 파이프 단면(73-2) — 두 줄 평행선 · 밝음 · 어두움 토큰을 따름. */ .b06-chart__wall-drain line { - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1.2; } @@ -388,8 +388,8 @@ /* 집수정 선택 강조(2026-08-22 사용자) — 기슭막이와 같은 규칙. */ .b06-chart__culvert-basin.is-active { - fill: color-mix(in srgb, var(--color-danger) 45%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 45%, transparent); + stroke: var(--draw-danger); stroke-width: 2.2; } @@ -404,8 +404,8 @@ /* 도수로 · 돌붙임 띠 고름(152-4) — 관 고름과 같은 강조 · 판정면은 굵은 투명 테두리로 여유 */ .b06-chart__culvert-pitching.is-active { - fill: color-mix(in srgb, var(--color-danger) 30%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 30%, transparent); + stroke: var(--draw-danger); stroke-width: 2.2; stroke-dasharray: none; } @@ -452,8 +452,8 @@ /* 세월교 구체(2026-08-25) — 바닥판은 콘크리트. 측벽은 ㄴ형 집수정 형상을 그대로 쓰므로 `.b06-chart__culvert-basin` 색을 공유한다. */ .b06-chart__ford-slab { - fill: color-mix(in srgb, var(--color-text-secondary) 30%, transparent); - stroke: var(--color-text-secondary); + fill: color-mix(in srgb, var(--draw-text-secondary) 30%, transparent); + stroke: var(--draw-text-secondary); stroke-width: 1.2; stroke-linejoin: round; } @@ -461,15 +461,15 @@ /* 세월교 본체(바닥판 · 관) 선택 강조(66-1) — 측벽 강조와 같은 색. */ .b06-chart__ford-slab.is-active, .b06-chart__culvert-pipe.is-active { - fill: color-mix(in srgb, var(--color-danger) 30%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 30%, transparent); + stroke: var(--draw-danger); stroke-width: 2.2; } /* BOX암거 내공(유로) — 면은 비우고 윤곽만. 물이 지나는 빈 공간이라 채우지 않는다. */ .b06-chart__box-barrel { fill: none; - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1; stroke-dasharray: 4 3; stroke-linejoin: round; diff --git a/B06_Section/B06_Section_UI_Style_Cross_Chart.css b/B06_Section/B06_Section_UI_Style_Cross_Chart.css index 7f447bcbd..82c054f5f 100644 --- a/B06_Section/B06_Section_UI_Style_Cross_Chart.css +++ b/B06_Section/B06_Section_UI_Style_Cross_Chart.css @@ -1,6 +1,6 @@ /* SVG 차트 색상은 테마 토큰만 사용한다. */ .b06-chart__bg { - fill: var(--color-surface-raised); + fill: var(--draw-surface-raised); } /* 세로 창을 변환으로 옮기는 겹(2026-09-04) — 세로로 눌리거나 늘어나도 선 굵기·점선 간격은 @@ -10,19 +10,19 @@ } .b06-chart__grid { - stroke: var(--color-border); + stroke: var(--draw-grid); stroke-width: 1; } .b06-chart__axis { - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1; } .b06-chart__tick, .b06-chart__station-label, .b06-chart__axis-label { - fill: var(--color-text-secondary); + fill: var(--draw-text-secondary); font-family: var(--font-body); } @@ -35,7 +35,7 @@ .b06-chart__station-label--mc { font-size: 12px; font-weight: var(--font-weight-bold); - fill: var(--color-warning); + fill: var(--draw-warning); } .b06-station--mc { @@ -47,7 +47,7 @@ /* 중심고 Y축 라벨(318-2) — 눈금과 같은 10px · 둥근 상자 · 축선 앞에서 멈춤 · 바탕 = 불투명 섞음(겹친 눈금 글을 위에서 가림 · 319-1) · 색 = 원지반선 · 설계선(379-3 · `--b06-tag` = _Cross_Areas.css) */ .b06-chart__center-box { - fill: color-mix(in srgb, var(--b06-tag) 14%, var(--color-surface-raised)); + fill: color-mix(in srgb, var(--b06-tag) 14%, var(--draw-surface-raised)); stroke: var(--b06-tag); stroke-width: 1; } @@ -60,7 +60,7 @@ /* 겹쳐 위 단으로 올린 측점 이름(407-4) — 선 · 지반선 위라 바탕색 테두리로 읽히게. */ .b06-chart__station-label--lifted { paint-order: stroke; - stroke: var(--color-surface-raised); + stroke: var(--draw-surface-raised); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; @@ -74,14 +74,14 @@ } .b06-chart__axis-label { - fill: var(--color-text-body); + fill: var(--draw-text-body); font-size: var(--text-caption); font-weight: var(--font-weight-medium); } .b06-chart__profile { fill: none; - stroke: var(--color-chart-0); + stroke: var(--draw-chart-0); stroke-width: 2.5; } @@ -93,14 +93,14 @@ .b06-chart__cross-profile { fill: none; - stroke: var(--color-chart-1); + stroke: var(--draw-chart-1); stroke-width: 2.4; } /* 종단 계획선(시공계획고): 지반선과 구분되도록 파선 + 강조색 */ .b06-chart__design-profile { fill: none; - stroke: var(--color-royal-amethyst); + stroke: var(--draw-accent); stroke-width: 2.2; stroke-dasharray: 8 4; } @@ -119,7 +119,7 @@ } .b06-chart__balance-boundary { - stroke: var(--color-royal-amethyst); + stroke: var(--draw-accent); stroke-width: 1.2; stroke-dasharray: 3 3; opacity: 0.55; @@ -148,42 +148,42 @@ .b06-chart__station-line--bp, .b06-chart__station-line--regular, .b06-chart__station-line--curve { - stroke: var(--color-warning); + stroke: var(--draw-warning); } .b06-chart__station-line--ep { - stroke: var(--color-accent); + stroke: var(--draw-accent); stroke-dasharray: 4 3; } /* 비정규 측점(구조물)은 종단면도에서도 자수정색 파선으로 구분한다(B05 종단 규칙과 정합). 미선택 시 stroke 미정의로 세로선이 안 그려지던 문제 해소. */ .b06-chart__station-line--irregular { - stroke: var(--color-royal-amethyst, rgb(139 92 246)); + stroke: var(--draw-accent); stroke-width: 1.4; stroke-dasharray: 5 3; } /* 추가 측점(368-3) = 일반측점 색 점선 */ .b06-chart__station-line--plus { - stroke: var(--color-warning); + stroke: var(--draw-warning); stroke-dasharray: 5 3; } .b06-chart__station-line--selected, .b06-chart__center-marker { - stroke: var(--color-danger); + stroke: var(--draw-danger); stroke-width: 2.5; } /* 십자선이 계획 노선(계획고) 위치를 가리킬 때: 종단 계획선과 동일 색으로 대응시킨다 */ .b06-chart__center-marker--design { - stroke: var(--color-royal-amethyst); + stroke: var(--draw-accent); } .b06-chart__station--selected .b06-chart__station-label, .b06-chart__station-label--selected { - fill: var(--color-danger); + fill: var(--draw-danger); font-weight: var(--font-weight-bold); } diff --git a/B06_Section/B06_Section_UI_Style_Cross_Detail.css b/B06_Section/B06_Section_UI_Style_Cross_Detail.css index 561f61880..2466d491e 100644 --- a/B06_Section/B06_Section_UI_Style_Cross_Detail.css +++ b/B06_Section/B06_Section_UI_Style_Cross_Detail.css @@ -122,7 +122,7 @@ .b06-detail__pick-outline path { fill: none; - stroke: var(--color-danger); + stroke: var(--draw-danger); stroke-width: 2; stroke-linecap: square; } @@ -144,19 +144,19 @@ /* 그림 강조 한 벌 = 붉은 계통(164-2 · 카드 색은 그대로) — 띠 · 판정 선 · 중심 십자 · 사토장 */ .b06-detail__cad .b06-chart__area.is-active > polygon { - fill: color-mix(in srgb, var(--color-danger) 35%, transparent); - stroke: var(--color-danger); + fill: color-mix(in srgb, var(--draw-danger) 35%, transparent); + stroke: var(--draw-danger); stroke-width: 2; } .b06-detail__cad .b06-chart__design-hit.is-active { - stroke: color-mix(in srgb, var(--color-danger) 70%, transparent); + stroke: color-mix(in srgb, var(--draw-danger) 70%, transparent); stroke-width: 9; } .b06-detail__cad .b06-chart__ground-center.is-active line, .b06-detail__cad .b06-chart__center.is-active line { - stroke: var(--color-danger); + stroke: var(--draw-danger); stroke-width: 3; } @@ -206,11 +206,11 @@ /* 구조물 선 묶음(295-3) — 나뉜 조각 위 한 줄 · 걸치면 선 하나로 비침 */ .b06-detail__piece:hover, .b06-detail__piece-group:hover { - stroke: color-mix(in srgb, var(--color-accent) 25%, transparent); + stroke: color-mix(in srgb, var(--draw-accent) 25%, transparent); } .b06-detail__piece.is-selected { - stroke: color-mix(in srgb, var(--color-danger) 55%, transparent); + stroke: color-mix(in srgb, var(--draw-danger) 55%, transparent); stroke-width: 6; } @@ -223,11 +223,11 @@ } .b06-detail__face:hover { - fill: color-mix(in srgb, var(--color-accent) 8%, transparent); + fill: color-mix(in srgb, var(--draw-accent) 8%, transparent); } .b06-detail__face.is-selected { - fill: color-mix(in srgb, var(--color-danger) 22%, transparent); + fill: color-mix(in srgb, var(--draw-danger) 22%, transparent); } /* 고른 것 값(카드 179-1 꼴) · [칸에 넣기] — [+ − ⤢] 왼쪽 */ diff --git a/B07_DesignDetail/openwebcad/src/App.css b/B07_DesignDetail/openwebcad/src/App.css index 7239e8173..9c4c7197a 100644 --- a/B07_DesignDetail/openwebcad/src/App.css +++ b/B07_DesignDetail/openwebcad/src/App.css @@ -28,7 +28,7 @@ --cad-text-muted: var(--color-text-muted); --cad-accent: var(--color-accent); --cad-hover: var(--color-mist-violet); - --cad-canvas: var(--color-canvas); + --cad-canvas: var(--draw-canvas); } * { diff --git a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts index d2053fbc1..496e0ab02 100644 --- a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts +++ b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts @@ -339,7 +339,7 @@ export class ScreenCanvasDrawController implements DrawController { this.context.fillStyle = themeColor('--cad-canvas', CANVAS_BACKGROUND_FALLBACK); this.context.fillRect(0, 0, this.canvasSize?.x, this.canvasSize?.y); if (getGridEnabled()) { - this.context.strokeStyle = themeColor('--color-mist', '#242b35'); + this.context.strokeStyle = themeColor('--draw-grid', '#242b35'); this.context.lineWidth = 1; this.context.setLineDash([]); this.context.beginPath(); diff --git a/B08_Quantity/B08_Quantity_UI_Hydro.css b/B08_Quantity/B08_Quantity_UI_Hydro.css index c079a276a..1454806e7 100644 --- a/B08_Quantity/B08_Quantity_UI_Hydro.css +++ b/B08_Quantity/B08_Quantity_UI_Hydro.css @@ -179,22 +179,22 @@ .b08-hydro__pipe text { font-size: var(--text-caption); - fill: var(--color-text-secondary); + fill: var(--draw-text-secondary); } .b08-hydro__water { - fill: color-mix(in srgb, var(--color-chart-0) 30%, transparent); + fill: color-mix(in srgb, var(--draw-chart-0) 30%, transparent); } .b08-hydro__wall { fill: none; - stroke: var(--color-text-body); + stroke: var(--draw-text-body); stroke-width: 2; } .b08-hydro__line { fill: none; - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1; } diff --git a/B08_Quantity/B08_Quantity_UI_Milestone.css b/B08_Quantity/B08_Quantity_UI_Milestone.css index 747844737..bb1727f37 100644 --- a/B08_Quantity/B08_Quantity_UI_Milestone.css +++ b/B08_Quantity/B08_Quantity_UI_Milestone.css @@ -81,41 +81,41 @@ } .b08-milestone__line { - stroke: var(--color-text-secondary); + stroke: var(--draw-text-secondary); stroke-width: 1.5; } .b08-milestone__box rect { - fill: var(--color-surface); - stroke: var(--color-text-secondary); + fill: var(--draw-surface); + stroke: var(--draw-text-secondary); stroke-width: 1.2; } .b08-milestone__box text { - fill: var(--color-text); + fill: var(--draw-text); } /* 153 상자 색 — 공사지(시점 · 중점) = 강조색 · 기준점 = 경고색 · 거래처 = 성공색 · 옅은 채움 + 테두리(테마 변수 · 밝음 · 어두움 같이) */ .b08-milestone__box.is-site rect { - fill: color-mix(in srgb, var(--color-accent) 7%, var(--color-surface)); - stroke: var(--color-accent); + fill: color-mix(in srgb, var(--draw-accent) 7%, var(--draw-surface)); + stroke: var(--draw-accent); stroke-dasharray: 4 3; } .b08-milestone__box.is-start rect, .b08-milestone__box.is-mid rect { - fill: color-mix(in srgb, var(--color-accent) 16%, var(--color-surface)); - stroke: var(--color-accent); + fill: color-mix(in srgb, var(--draw-accent) 16%, var(--draw-surface)); + stroke: var(--draw-accent); } .b08-milestone__box.is-base rect { - fill: color-mix(in srgb, var(--color-warning) 18%, var(--color-surface)); - stroke: var(--color-warning); + fill: color-mix(in srgb, var(--draw-warning) 18%, var(--draw-surface)); + stroke: var(--draw-warning); } .b08-milestone__box.is-route rect { - fill: color-mix(in srgb, var(--color-success) 14%, var(--color-surface)); - stroke: var(--color-success); + fill: color-mix(in srgb, var(--draw-success) 14%, var(--draw-surface)); + stroke: var(--draw-success); } .b08-milestone__box.is-route, @@ -125,15 +125,15 @@ .b08-milestone__box.is-route:hover rect, .b08-milestone__box.is-route:focus-visible rect { - stroke: var(--color-accent); + stroke: var(--draw-accent); stroke-width: 2; } .b08-milestone__km { - fill: var(--color-text); + fill: var(--draw-text); font-size: 12px; paint-order: stroke; - stroke: var(--color-surface); + stroke: var(--draw-surface); stroke-width: 4px; stroke-linejoin: round; } @@ -173,13 +173,13 @@ /* 거래처 상자 윗줄 = 항목(244-6) · 글 크기 = 거래처 이름(254) */ .b08-milestone__items { - fill: var(--color-text-secondary); + fill: var(--draw-text-secondary); font-size: 13px; } /* 상자 작은 글씨 = 주소 · 좌표(244-12) */ .b08-milestone__note { - fill: var(--color-text-secondary); + fill: var(--draw-text-secondary); font-size: 10px; } @@ -228,8 +228,8 @@ /* 154 연결 점 ● — 거래처 · 기준점 상자 왼쪽 위 안쪽(254) · 끌어 기준점 · 공사지에 놓으면 연결 */ .b08-milestone__connect { - fill: var(--color-success); - stroke: var(--color-surface); + fill: var(--draw-success); + stroke: var(--draw-surface); stroke-width: 2; cursor: crosshair; opacity: 0.85; @@ -240,7 +240,7 @@ } .b08-milestone__wire { - stroke: var(--color-success); + stroke: var(--draw-success); stroke-width: 2; stroke-dasharray: 6 4; pointer-events: none; diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts index 29975cf1b..e3fe536e3 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts @@ -248,7 +248,7 @@ export function createPlanMap(events: PlanMapEvents): PlanMap { context.beginPath(); context.arc(at[0], at[1], 7, 0, Math.PI * 2); context.lineWidth = 2.5; - context.strokeStyle = themeColor("--color-primary", "#6d28d9"); + context.strokeStyle = themeColor("--draw-primary", "#6d28d9"); context.stroke(); } } diff --git a/resources/tester/test_164_detail_zoom.py b/resources/tester/test_164_detail_zoom.py index 0785d055c..330a6f33f 100644 --- a/resources/tester/test_164_detail_zoom.py +++ b/resources/tester/test_164_detail_zoom.py @@ -105,9 +105,9 @@ def test_모달_배선_단추_칸_비율(): def test_강조_붉은_한_벌_고름_hover_다름_빈칸(): css = _read("B06_Section_UI_Style_Cross_Detail.css") for selector in ( - ".b06-detail__cad .b06-chart__area.is-active > polygon {\n fill: color-mix(in srgb, var(--color-danger)", - ".b06-detail__cad .b06-chart__design-hit.is-active {\n stroke: color-mix(in srgb, var(--color-danger)", - ".b06-detail__cad .b06-chart__ground-center.is-active line,\n.b06-detail__cad .b06-chart__center.is-active line {\n stroke: var(--color-danger);", + ".b06-detail__cad .b06-chart__area.is-active > polygon {\n fill: color-mix(in srgb, var(--draw-danger)", + ".b06-detail__cad .b06-chart__design-hit.is-active {\n stroke: color-mix(in srgb, var(--draw-danger)", + ".b06-detail__cad .b06-chart__ground-center.is-active line,\n.b06-detail__cad .b06-chart__center.is-active line {\n stroke: var(--draw-danger);", ".b06-detail__qtable .is-picked {\n background: color-mix(in srgb, var(--color-danger) 18%", ".b06-detail__qtable [data-key][data-empty] {\n cursor: default;", ): diff --git a/resources/tester/test_215_1_free_profile.py b/resources/tester/test_215_1_free_profile.py index 3d253318e..947c9cbdf 100644 --- a/resources/tester/test_215_1_free_profile.py +++ b/resources/tester/test_215_1_free_profile.py @@ -180,14 +180,14 @@ def _css_rule(css: str, selector: str) -> str: def test_자유지정_녹색_넘음_빨강_띠_보라(): - # 367-4 선 · 미리 보기 · 점 = --color-success · 넘음 빨강 · 범위 띠 · 경계 보라 그대로 + # 367-4 선 · 미리 보기 · 점 = --draw-success · 넘음 빨강 · 범위 띠 · 경계 보라 그대로 css = _read("B05_Profile/B05_Profile_UI_Style_Table.css") - assert "stroke: var(--color-success);" in _css_rule(css, ".b05-profile-freemark__line") - assert "stroke: var(--color-success);" in _css_rule(css, ".b05-profile-freemark__dot") - assert "var(--color-danger" in _css_rule(css, ".b05-profile-freemark__line.is-over") - assert "var(--color-danger" in _css_rule(css, ".b05-profile-freemark__grade.is-over") - assert "--color-primary" in _css_rule(css, ".b05-profile-freemark__band") - assert "--color-primary" in _css_rule(css, ".b05-profile-freemark__edge") + assert "stroke: var(--draw-success);" in _css_rule(css, ".b05-profile-freemark__line") + assert "stroke: var(--draw-success);" in _css_rule(css, ".b05-profile-freemark__dot") + assert "var(--draw-danger" in _css_rule(css, ".b05-profile-freemark__line.is-over") + assert "var(--draw-danger" in _css_rule(css, ".b05-profile-freemark__grade.is-over") + assert "--draw-primary" in _css_rule(css, ".b05-profile-freemark__band") + assert "--draw-primary" in _css_rule(css, ".b05-profile-freemark__edge") # 밝음 · 어두움(명시 · OS 자동) 모두 success · danger 토큰 있음 theme = _read("ui_template/ui_template_theme.css") auto = theme[theme.index("@media (prefers-color-scheme: dark)") :] diff --git a/resources/tester/test_250_1_b08_milestone_roads_width.py b/resources/tester/test_250_1_b08_milestone_roads_width.py index 6eb744146..f0ea6d058 100644 --- a/resources/tester/test_250_1_b08_milestone_roads_width.py +++ b/resources/tester/test_250_1_b08_milestone_roads_width.py @@ -84,9 +84,9 @@ def test_153_지도_보기_유지_상자_색_끌어_옮김(): ) # 처음 한 번만 맞춤 · 그 뒤 줌 그대로 css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css") for kind, var in ( - ("is-site", "--color-accent"), - ("is-base", "--color-warning"), - ("is-route", "--color-success"), + ("is-site", "--draw-accent"), + ("is-base", "--draw-warning"), + ("is-route", "--draw-success"), ): block = css[css.index(f".b08-milestone__box.{kind} rect {{") :] assert var in block[: block.index("}")], kind diff --git a/resources/tester/test_271_straighten_pick.py b/resources/tester/test_271_straighten_pick.py index e5fbab5fd..999b7e269 100644 --- a/resources/tester/test_271_straighten_pick.py +++ b/resources/tester/test_271_straighten_pick.py @@ -186,7 +186,7 @@ def test_배선(): css = _read("B05_Profile/B05_Profile_UI_Style_Table.css") rule = css[css.index(".b05-profile-freemark__span {") :] rule = rule[: rule.index("}")] - assert "--color-primary" in rule and "--color-surface" in rule # 밝음 · 어두움 토큰 + assert "--draw-primary" in rule and "--draw-surface" in rule # 밝음 · 어두움 토큰 def test_줄_수(): diff --git a/resources/tester/test_318_cross_card.py b/resources/tester/test_318_cross_card.py index 65300ab7a..e67a0b6d0 100644 --- a/resources/tester/test_318_cross_card.py +++ b/resources/tester/test_318_cross_card.py @@ -159,7 +159,7 @@ def test_two_crosshairs(result: dict) -> None: assert cross["design_only_ground"] is None # 설계 없음 = 십자 하나(지반 폴백) assert cross["scaled"] == cross["want_scaled"] # 둘 다 자기 중심에서 배율 되돌림 areas = _src("B06_Section", "B06_Section_UI_Style_Cross_Areas.css") - assert "var(--color-chart-1)" in _rule(areas, ".b06-chart__ground-center-marker") + assert "var(--draw-chart-1)" in _rule(areas, ".b06-chart__ground-center-marker") def test_draft_detail_profile_is_draft(result: dict) -> None: @@ -192,8 +192,8 @@ def test_css_layout() -> None: box = _rule(css, ".b06-chart__center-box") assert "var(--b06-tag)" in box # 색 = 갈래 변수(379-3) areas = _src("B06_Section", "B06_Section_UI_Style_Cross_Areas.css") - assert "var(--color-chart-1)" in _rule(areas, ".b06-chart__center-tag--ground") # 원지반선 - assert "var(--color-royal-amethyst)" in _rule(areas, ".b06-chart__center-tag--design") # 설계선 + assert "var(--draw-chart-1)" in _rule(areas, ".b06-chart__center-tag--ground") # 원지반선 + assert "var(--draw-accent)" in _rule(areas, ".b06-chart__center-tag--design") # 설계선 assert "font-size" not in _rule(css, ".b06-chart__center-text") # 글자 크기 = 눈금 그대로 diff --git a/resources/tester/test_507_3_mc_label.py b/resources/tester/test_507_3_mc_label.py index 6d73c4e6f..015f8e52d 100644 --- a/resources/tester/test_507_3_mc_label.py +++ b/resources/tester/test_507_3_mc_label.py @@ -65,11 +65,17 @@ def test_b08_b09_earthwork_station_mc() -> None: def test_b05_b06_screen_mc_emphasis() -> None: - css = _src("B06_Section/B06_Section_UI_Style_Cross.css") + _src("B06_Section/B06_Section_UI_Style_Cross_Chart.css") + css = _src("B06_Section/B06_Section_UI_Style_Cross.css") + _src( + "B06_Section/B06_Section_UI_Style_Cross_Chart.css" + ) assert "header strong.b06-station--mc {" in css # 카드 머리 strong 규칙을 이김 - for rule in ("\n.b06-chart__station-label--mc {", "\n.b06-station--mc {"): + # 그림 글 = 그림 전용 값(551-2) · 카드 머리 글 = 화면 경고색 + for rule, warn in ( + ("\n.b06-chart__station-label--mc {", "var(--draw-warning)"), + ("\n.b06-station--mc {", "var(--color-warning)"), + ): body = css[css.index(rule) : css.index("}", css.index(rule))] - assert "var(--color-warning)" in body + assert warn in body assert "var(--font-weight-bold)" in body # 고름 빨강이 MC 색을 이김 — 뒤에 섬 assert css.index(".b06-chart__station-label--mc {") < css.index( @@ -84,7 +90,7 @@ def test_b05_b06_screen_mc_emphasis() -> None: assert '${mc ? " b06-station--mc" : ""}' in cross ticks = _src("B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts") assert 'mc ? "bold 13px' in ticks - assert 'themeColor("--color-warning"' in ticks + assert 'themeColor("--draw-warning"' in ticks swap = _src("B06_Section/logic/B06_Section_Station_Swap.ts") assert 'formatStation(station.chainageM) + (kind === "curve" ? " (MC)" : "")' in swap diff --git a/resources/tester/test_551_2_draw_tokens.py b/resources/tester/test_551_2_draw_tokens.py new file mode 100644 index 000000000..ead51bea7 --- /dev/null +++ b/resources/tester/test_551_2_draw_tokens.py @@ -0,0 +1,155 @@ +"""551-2 그림 전용 값(--draw-*) 지킴 — 화면 테마 변수(--color-*)를 바꿔도 그림 색은 그대로. + +- `ui_template_theme.css` 끝 블록의 --draw-* 는 밝음 · 어두움 · 시스템 어두움 모두 글자 hex. +- 그림 CSS(B05 · B06 · B08 SVG)의 stroke · fill 은 --color-* 를 쓰지 않음. +- 지도 · 3D · CAD 캔버스가 읽는 바탕 · 선 색은 --draw-*. +- 옛 변수 값을 일부러 바꿔도 그림 선언의 풀린 색이 같음(밝음 · 어두움 · 시스템 어두움). +""" + +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +THEME = ROOT / "ui_template" / "ui_template_theme.css" + +DRAW_CSS = [ + "B05_Profile/B05_Profile_UI_Style.css", + "B05_Profile/B05_Profile_UI_Style_RouteEdit.css", + "B05_Profile/B05_Profile_UI_Style_RouteRecommend.css", + "B05_Profile/B05_Profile_UI_Style_Table.css", + "B06_Section/B06_Section_UI_Style.css", + "B06_Section/B06_Section_UI_Style_Cross_Areas.css", + "B06_Section/B06_Section_UI_Style_Cross_Chart.css", + "B06_Section/B06_Section_UI_Style_Cross_Detail.css", + "B08_Quantity/B08_Quantity_UI_Hydro.css", + "B08_Quantity/B08_Quantity_UI_Milestone.css", +] + +# 캔버스 · 3D 가 읽는 그림 색 — (파일, 꼭 있어야 할 글) +DRAW_READS = [ + ("B05_Profile/B05_Profile_UI_RouteEdit_Render.ts", '"--draw-surface"'), + ("B05_Profile/B05_Profile_UI_RouteEdit_Render.ts", '"--draw-text-secondary"'), + ("B05_Profile/B05_Profile_UI_RouteEdit_Render.ts", '"--draw-danger"'), + ("B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts", '"--draw-primary"'), + ("B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts", '"--draw-text-secondary"'), + ("B05_Profile/B05_Profile_UI_Viewer.ts", '"--draw-surface-raised"'), + ("B04_PreProcess/B04_PreProcess_UI_Camera.ts", '"--draw-surface-raised"'), + ("D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts", '"--draw-primary"'), + ("B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts", '"--draw-warning"'), + ("B04_PreProcess/B04_PreProcess_UI_MapOverlays.ts", '"--draw-canvas"'), + ("B07_DesignDetail/openwebcad/src/App.css", "--cad-canvas: var(--draw-canvas);"), + ( + "B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts", + "themeColor('--draw-grid'", + ), +] +NO_COLOR_READS = [ + "B05_Profile/B05_Profile_UI_RouteEdit_Render.ts", + "B05_Profile/B05_Profile_UI_RouteEdit_Recommend_Map.ts", + "B05_Profile/B05_Profile_UI_Viewer.ts", + "B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts", +] + +DECL = re.compile(r"^\s*(stroke|fill|stop-color|--b06-tag)\s*:(.*);", re.M) +VAR = re.compile(r"var\((--[a-z0-9-]+)(?:\s*,\s*([^()]*(?:\([^()]*\))?[^()]*))?\)") +HEX = re.compile(r"#[0-9a-f]{6}") + + +def _blocks(css: str) -> dict[str, dict[str, str]]: + """밝음 · 어두움 · 시스템 어두움 블록의 --변수 정의(뒤 블록이 앞을 덮음).""" + css = re.sub(r"/\*.*?\*/", "", css, flags=re.S) + out: dict[str, dict[str, str]] = {"light": {}, "dark": {}, "auto": {}} + + def walk(text: str, media: bool) -> None: + i = 0 + while True: + open_at = text.find("{", i) + if open_at < 0: + return + head = text[i:open_at].strip().split("}")[-1].strip() + depth, j = 1, open_at + 1 + while depth: + depth += {"{": 1, "}": -1}.get(text[j], 0) + j += 1 + body = text[open_at + 1 : j - 1] + if head.startswith("@media (prefers-color-scheme: dark)"): + walk(body, True) + else: + mode = None + if head == ":root": + mode = "light" + elif head == ':root[data-theme="dark"]': + mode = "dark" + elif media and head == ':root:not([data-theme="light"])': + mode = "auto" + if mode: + for name, value in re.findall(r"(--[a-z0-9-]+)\s*:([^;]*);", body): + out[mode][name] = " ".join(value.split()) + i = j + + walk(css, False) + return out + + +def _modes(css: str) -> dict[str, dict[str, str]]: + b = _blocks(css) + return { + "light": b["light"], + "dark": {**b["light"], **b["dark"]}, + "auto": {**b["light"], **b["auto"]}, + } + + +def resolve(value: str, env: dict[str, str]) -> str: + for _ in range(10): + new = VAR.sub(lambda m: env.get(m.group(1), (m.group(2) or "").strip()), value) + if new == value: + break + value = new + return " ".join(value.split()).lower() + + +def draw_decls() -> list[tuple[str, str]]: + out = [] + for rel in DRAW_CSS: + text = re.sub(r"/\*.*?\*/", "", (ROOT / rel).read_text(encoding="utf-8"), flags=re.S) + out += [(rel, m.group(2).strip()) for m in DECL.finditer(text)] + return out + + +def test_draw_values_are_literal_hex(): + blocks = _blocks(THEME.read_text(encoding="utf-8")) + for mode, env in blocks.items(): + draws = {k: v for k, v in env.items() if k.startswith("--draw-")} + assert draws, mode + for name, value in draws.items(): + assert HEX.fullmatch(value), (mode, name, value) + assert len([k for k in blocks["light"] if k.startswith("--draw-")]) == 17 + + +def test_drawing_css_uses_no_screen_tokens(): + decls = draw_decls() + assert len(decls) > 100 + bad = [(rel, v) for rel, v in decls if "var(--color-" in v] + assert not bad, bad + + +def test_canvas_and_3d_read_draw_tokens(): + for rel, needle in DRAW_READS: + assert needle in (ROOT / rel).read_text(encoding="utf-8"), (rel, needle) + for rel in NO_COLOR_READS: + text = (ROOT / rel).read_text(encoding="utf-8") + assert not re.search(r"""(getPropertyValue|themeColor)\(\s*["']--color-""", text), rel + + +def test_changing_screen_tokens_keeps_drawing_colors(): + css = THEME.read_text(encoding="utf-8") + # 옛 화면 변수를 모두 초록으로 바꾼 판 — sub1 이 테마를 갈아도 그림이 안 따라가는지 + swapped = re.sub(r"(--color-[a-z0-9-]+\s*:)[^;]*;", r"\1 #00ff00;", css) + before, after = _modes(css), _modes(swapped) + decls = draw_decls() + for mode in ("light", "dark", "auto"): + for rel, value in decls: + a = resolve(value, before[mode]) + assert "var(" not in a, (mode, rel, value) + assert a == resolve(value, after[mode]), (mode, rel, value) diff --git a/resources/tester/test_b06_wall_drain_pipe.py b/resources/tester/test_b06_wall_drain_pipe.py index c26459f0f..2d434cf4a 100644 --- a/resources/tester/test_b06_wall_drain_pipe.py +++ b/resources/tester/test_b06_wall_drain_pipe.py @@ -132,4 +132,4 @@ def test_옹벽_표식은_구조물_종류로() -> None: "retaining_wall" ] css = (B06 / "B06_Section_UI_Style_Cross_Areas.css").read_text(encoding="utf-8") - assert re.search(r"\.b06-chart__wall-drain line \{[^}]*var\(--color-", css), "색 토큰 없음" + assert re.search(r"\.b06-chart__wall-drain line \{[^}]*var\(--draw-", css), "색 토큰 없음" diff --git a/ui_template/ui_template_theme.css b/ui_template/ui_template_theme.css index e77b4ed91..be9b5becf 100644 --- a/ui_template/ui_template_theme.css +++ b/ui_template/ui_template_theme.css @@ -562,3 +562,61 @@ html.is-bm-route .m01lab-src__body * { user-select: text; -webkit-user-select: text; } + +/* ============================================================================= + * 551-2 그림 전용 값 — 화면 테마와 따로 · 지금 색 고정 + * 종단 · 횡단 · 이정표 SVG 선 · 지도 계획선 · CAD 바탕 · 3D 바탕은 --draw-* 만 씀. + * 값은 글자 hex 로 박음(--color-* 를 가리키지 않음) — 화면 테마가 바뀌어도 그림 색은 그대로. + * ========================================================================== */ +:root { + --draw-accent: #3e0079; /* 설계선 · EP 선 · 이정표 강조 */ + --draw-primary: #26114a; /* 자유 표시 · 노선 추천 계획선 */ + --draw-chart-0: #5b8def; /* 원지반선 · 성토 */ + --draw-chart-1: #34c38f; /* 횡단 원지반선 */ + --draw-chart-2: #f1963b; /* 토사 깎기 */ + --draw-chart-3: #9b6dde; /* 발파암 깎기 */ + --draw-success: #1f8a4c; + --draw-warning: #b8860b; + --draw-danger: #c0392b; + --draw-text: #26114a; + --draw-text-body: #333333; + --draw-text-secondary: #615e6e; + --draw-text-muted: #9491a1; + --draw-grid: #e6e2e3; /* 눈금 · CAD 격자 */ + --draw-surface: #f6f7fa; + --draw-surface-raised: #ffffff; /* 그림 바탕 · 3D 바탕 */ + --draw-canvas: #ffffff; /* CAD 바탕 */ +} + +:root[data-theme="dark"] { + --draw-accent: #b99aff; + --draw-primary: #b99aff; + --draw-success: #3fbb6f; + --draw-warning: #e0b64a; + --draw-danger: #e8695a; + --draw-text: #d8ccff; + --draw-text-body: #e4e0f0; + --draw-text-secondary: #b3aec6; + --draw-text-muted: #8b86a0; + --draw-grid: #322a45; + --draw-surface: #1e1930; + --draw-surface-raised: #251f38; + --draw-canvas: #16121f; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --draw-accent: #b99aff; + --draw-primary: #b99aff; + --draw-success: #3fbb6f; + --draw-danger: #e8695a; + --draw-text: #d8ccff; + --draw-text-body: #e4e0f0; + --draw-text-secondary: #b3aec6; + --draw-text-muted: #8b86a0; + --draw-grid: #322a45; + --draw-surface: #1e1930; + --draw-surface-raised: #251f38; + --draw-canvas: #16121f; + } +}