diff --git a/B07_DesignDetail/openwebcad/index.html b/B07_DesignDetail/openwebcad/index.html index 365aed00..4eb332cc 100644 --- a/B07_DesignDetail/openwebcad/index.html +++ b/B07_DesignDetail/openwebcad/index.html @@ -7,9 +7,9 @@ Aislo 2D Drawing - -
- + +
+
diff --git a/B07_DesignDetail/openwebcad/src/App.css b/B07_DesignDetail/openwebcad/src/App.css index 8dc87e3f..92bf0607 100644 --- a/B07_DesignDetail/openwebcad/src/App.css +++ b/B07_DesignDetail/openwebcad/src/App.css @@ -1,4 +1,6 @@ @import "tailwindcss"; +/* 색 토큰의 정의처는 앱과 같은 파일 하나 — 여기서 색을 새로 정하지 않는다. */ +@import "../../../ui_template/ui_template_theme.css"; :root { --cad-title-height: 42px; @@ -7,9 +9,21 @@ --cad-command-height: 0px; --cad-status-height: 28px; --cad-panel-width: 248px; - font-family: Inter, Pretendard, "Noto Sans KR", system-ui, sans-serif; - color: #dce6f2; - background: #11161d; + font-family: var(--font-body); + color: var(--color-text-body); + background: var(--color-bg); + + /* CAD 표면 단계 — 앱 토큰을 CAD 골격(툴바·리본·패널)에 이름만 붙여 쓴다. */ + --cad-chrome: var(--color-surface); + --cad-chrome-raised: var(--color-surface-raised); + --cad-chrome-sunken: var(--color-bg); + --cad-line: var(--color-border); + --cad-text: var(--color-text-body); + --cad-text-dim: var(--color-text-secondary); + --cad-text-muted: var(--color-text-muted); + --cad-accent: var(--color-accent); + --cad-hover: var(--color-mist-violet); + --cad-canvas: var(--color-canvas); } * { @@ -56,7 +70,7 @@ body > canvas[data-id="canvas"] { var(--cad-command-height) - var(--cad-status-height) ); - background: #111; + background: var(--cad-canvas); cursor: none; } @@ -68,9 +82,9 @@ body > canvas[data-id="canvas"] { align-items: center; gap: 24px; padding: 0 12px; - background: #19222d; - border-bottom: 1px solid #344252; - box-shadow: 0 1px 4px #0008; + background: var(--cad-chrome); + border-bottom: 1px solid var(--cad-line); + box-shadow: var(--shadow-sm); } .cad-brand { display: flex; @@ -79,12 +93,12 @@ body > canvas[data-id="canvas"] { min-width: 220px; } .cad-brand strong { - color: #f7fbff; + color: var(--color-text); font-size: 14px; } .cad-brand span, .cad-file-state { - color: #91a2b5; + color: var(--cad-text-dim); font-size: 11px; } .cad-file-state { @@ -96,8 +110,8 @@ body > canvas[data-id="canvas"] { width: 7px; height: 7px; border-radius: 50%; - background: #49b675; - box-shadow: 0 0 0 2px #49b67522; + background: var(--color-success); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-success) 25%, transparent); } .cad-title-actions { display: flex; @@ -114,8 +128,8 @@ body > canvas[data-id="canvas"] { font-size: 12px; } .cad-title-actions button:hover { - border-color: #4d6074; - background: #273544; + border-color: var(--cad-line); + background: var(--cad-hover); } .cad-ribbon { @@ -128,15 +142,15 @@ body > canvas[data-id="canvas"] { align-items: stretch; padding: 5px 8px 3px; overflow-x: auto; - background: #202b37; - border-bottom: 1px solid #3c4a59; + background: var(--cad-chrome-raised); + border-bottom: 1px solid var(--cad-line); } .cad-ribbon-group { display: flex; flex-direction: column; min-width: max-content; padding: 0 9px; - border-right: 1px solid #40505f; + border-right: 1px solid var(--cad-line); } .cad-ribbon-tools { display: flex; @@ -145,7 +159,7 @@ body > canvas[data-id="canvas"] { } .cad-ribbon-group__label { margin-top: auto; - color: #8293a5; + color: var(--cad-text-muted); font-size: 10px; text-align: center; } @@ -159,7 +173,7 @@ body > canvas[data-id="canvas"] { gap: 3px; align-items: stretch; font-size: 10px; - color: #8293a5; + color: var(--cad-text-muted); } .cad-prop > span { text-align: center; @@ -169,9 +183,9 @@ body > canvas[data-id="canvas"] { height: 24px; min-width: 64px; padding: 0 4px; - color: #dce6f2; - background: #2a3745; - border: 1px solid #40505f; + color: var(--cad-text); + background: var(--cad-chrome-sunken); + border: 1px solid var(--cad-line); border-radius: 4px; font-size: 11px; } @@ -183,8 +197,8 @@ body > canvas[data-id="canvas"] { height: 24px; width: 40px; padding: 1px; - background: #2a3745; - border: 1px solid #40505f; + background: var(--cad-chrome-sunken); + border: 1px solid var(--cad-line); border-radius: 4px; cursor: pointer; } @@ -203,24 +217,24 @@ body > canvas[data-id="canvas"] { } .cad-tool:hover:not(:disabled), .cad-tool[data-active="true"] { - border-color: #4f87b8; - background: #2b455c; + border-color: var(--cad-accent); + background: var(--cad-hover); } .cad-tool[data-active="true"] { - box-shadow: inset 0 -2px #4ca6e8; + box-shadow: inset 0 -2px var(--cad-accent); } .cad-tool:disabled { - color: #657382; + color: var(--color-smoke); cursor: not-allowed; } .cad-tool__glyph { height: 27px; - color: #a9d7fb; + color: var(--cad-accent); font-size: 22px; line-height: 27px; } .cad-tool:disabled .cad-tool__glyph { - color: #657382; + color: var(--color-smoke); } .cad-inspector { @@ -230,8 +244,8 @@ body > canvas[data-id="canvas"] { bottom: calc(var(--cad-command-height) + var(--cad-status-height)); left: 0; width: var(--cad-panel-width); - background: #1b2530; - border-right: 1px solid #3d4c5c; + background: var(--cad-chrome); + border-right: 1px solid var(--cad-line); transition: width 120ms ease; } .cad-inspector[data-collapsed="true"] { @@ -244,57 +258,57 @@ body > canvas[data-id="canvas"] { left: 100%; width: 20px; height: 38px; - border: 1px solid #465769; + border: 1px solid var(--cad-line); border-left: 0; border-radius: 0 4px 4px 0; - background: #263442; - color: #a9bbcd; + background: var(--cad-chrome-raised); + color: var(--cad-text-dim); } .cad-inspector-tabs { display: grid; grid-template-columns: 1fr 1fr; height: 38px; - border-bottom: 1px solid #394857; + border-bottom: 1px solid var(--cad-line); } .cad-inspector-tabs button { border: 0; border-bottom: 2px solid transparent; - background: #17202a; - color: #94a7ba; + background: var(--cad-chrome-sunken); + color: var(--cad-text-dim); font-size: 12px; } .cad-inspector-tabs button[data-active="true"] { - border-bottom-color: #4da3df; - background: #223141; - color: white; + border-bottom-color: var(--cad-accent); + background: var(--cad-chrome-raised); + color: var(--color-text); } .cad-properties { padding: 14px 12px; } .cad-properties h2 { margin: 0 0 14px; - color: #f3f7fb; + color: var(--color-text); font-size: 13px; font-weight: 600; } .cad-properties dl { margin: 0; - border-top: 1px solid #344352; + border-top: 1px solid var(--cad-line); } .cad-properties dl div { display: grid; grid-template-columns: 92px 1fr; padding: 8px 0; - border-bottom: 1px solid #2d3a47; + border-bottom: 1px solid var(--cad-line); font-size: 11px; } .cad-properties dt { - color: #8092a4; + color: var(--cad-text-dim); } .cad-properties dd { margin: 0; overflow: hidden; - color: #d7e1eb; + color: var(--cad-text); text-overflow: ellipsis; } .cad-layer-manager { @@ -306,7 +320,23 @@ body > canvas[data-id="canvas"] { min-height: 36px; padding-top: 6px; padding-bottom: 6px; - background: #202c38; +} +/* 도면층 버튼 — 색은 토큰으로만, 상태는 Button이 붙이는 data-* 속성으로 가른다. */ +.cad-layer-manager button[data-type="regular"] { + background: var(--cad-chrome-sunken); + color: var(--cad-accent); +} +.cad-layer-manager button[data-type="transparent"] { + background: transparent; + color: var(--cad-accent); +} +.cad-layer-manager button:hover { + background: var(--cad-hover); + color: var(--color-text); +} +.cad-layer-manager button[data-active="true"] { + background: var(--color-primary); + color: var(--color-primary-text); } .cad-view-controls { @@ -317,29 +347,29 @@ body > canvas[data-id="canvas"] { display: flex; align-items: center; overflow: hidden; - border: 1px solid #465565; + border: 1px solid var(--cad-line); border-radius: 4px; - background: #202b37e8; - box-shadow: 0 3px 12px #0008; + background: var(--cad-chrome-raised); + box-shadow: var(--shadow-lg); } .cad-view-controls button { width: 34px; height: 32px; border: 0; - border-right: 1px solid #3d4c5b; + border-right: 1px solid var(--cad-line); background: transparent; font-size: 17px; } .cad-view-controls button:hover { - background: #31516b; + background: var(--cad-hover); } .cad-view-controls__fit { - color: #7fd0ff; + color: var(--cad-accent); font-size: 15px; } .cad-view-controls span { min-width: 48px; - color: #9fb0c1; + color: var(--cad-text-dim); font-size: 10px; text-align: center; } @@ -357,11 +387,11 @@ body > canvas[data-id="canvas"] { white-space: nowrap; } .cad-command-prompt span { - color: #6eaee0; + color: var(--cad-accent); } .cad-command-prompt strong { overflow: hidden; - color: #aab8c6; + color: var(--cad-text-dim); font-weight: 400; text-overflow: ellipsis; } @@ -372,7 +402,7 @@ body > canvas[data-id="canvas"] { height: 28px; } .cad-command-area label { - color: #e6eef6; + color: var(--color-text); font-size: 11px; font-weight: 600; } @@ -380,16 +410,16 @@ body > canvas[data-id="canvas"] { flex: 1; height: 26px; padding: 0 8px; - border: 1px solid #46586a; + border: 1px solid var(--cad-line); border-radius: 2px; outline: none; - background: #0e141a; - color: #eef5fc; + background: var(--cad-chrome-sunken); + color: var(--cad-text); font: 11px Consolas, monospace; } .cad-command-area input:focus { - border-color: #4b9bd3; - box-shadow: 0 0 0 1px #4b9bd344; + border-color: var(--color-focus-ring); + box-shadow: 0 0 0 1px var(--color-focus-ring); } .cad-statusbar { @@ -403,8 +433,8 @@ body > canvas[data-id="canvas"] { align-items: center; gap: 2px; padding: 0 8px; - background: #263544; - border-top: 1px solid #415263; + background: var(--cad-chrome); + border-top: 1px solid var(--cad-line); } .cad-statusbar button { height: 21px; @@ -412,20 +442,20 @@ body > canvas[data-id="canvas"] { border: 1px solid transparent; border-radius: 2px; background: transparent; - color: #9caebf; + color: var(--cad-text-dim); font-size: 10px; } .cad-statusbar button:hover { - background: #33485b; + background: var(--cad-hover); } .cad-statusbar button[data-active="true"] { - border-color: #4f9bd0; - background: #285579; - color: white; + border-color: var(--cad-accent); + background: var(--color-primary); + color: var(--color-primary-text); } .cad-statusbar__hint { margin-left: auto; - color: #7f91a2; + color: var(--cad-text-muted); font-size: 10px; } @@ -441,11 +471,11 @@ body > canvas[data-id="canvas"] { /* 표(가장 넓은 자식) 기준 폭 고정 → 접었다 펴도 폭이 변하지 않는다 */ width: max-content; max-width: min(1040px, calc(100vw - var(--cad-panel-width) - 24px)); - border: 1px solid #465565; + border: 1px solid var(--cad-line); border-radius: 6px; - background: #202b37f2; - box-shadow: 0 4px 16px #0009; - color: #dce6f2; + background: var(--cad-chrome-raised); + box-shadow: var(--shadow-lg); + color: var(--cad-text); } .cad-qtable__header { @@ -463,12 +493,12 @@ body > canvas[data-id="canvas"] { min-width: 0; } .cad-qtable__title strong { - color: #f7fbff; + color: var(--color-text); font-size: 14px; } .cad-qtable__title span { overflow: hidden; - color: #91a2b5; + color: var(--cad-text-dim); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; @@ -480,26 +510,26 @@ body > canvas[data-id="canvas"] { font-style: normal; } .cad-qtable__status[data-confirmed="true"] { - background: #1f4d33; - color: #7fdca4; + background: color-mix(in srgb, var(--color-success) 22%, transparent); + color: var(--color-success); } .cad-qtable__status[data-confirmed="false"] { - background: #4a3a1c; - color: #e6c07a; + background: color-mix(in srgb, var(--color-warning) 22%, transparent); + color: var(--color-warning); } .cad-qtable__nav, .cad-qtable__collapse { width: 26px; height: 24px; - border: 1px solid #40505f; + border: 1px solid var(--cad-line); border-radius: 4px; - background: #2a3745; - color: #cdd8e4; + background: var(--cad-chrome-sunken); + color: var(--cad-text); font-size: 13px; } .cad-qtable__nav:hover:not(:disabled), .cad-qtable__collapse:hover { - background: #31516b; + background: var(--cad-hover); } .cad-qtable__nav:disabled { opacity: 0.35; @@ -525,17 +555,17 @@ body > canvas[data-id="canvas"] { .cad-qtable th, .cad-qtable td { padding: 0; - border: 1px solid #40505f; + border: 1px solid var(--cad-line); font-size: 11px; text-align: center; vertical-align: middle; } .cad-qtable__label { padding: 2px 9px; - color: #a9bccd; + color: var(--cad-text-dim); font-weight: 600; white-space: nowrap; - background: #26333f; + background: var(--cad-chrome); } .cad-qtable__label--vertical { width: 18px; @@ -548,18 +578,18 @@ body > canvas[data-id="canvas"] { padding: 3px 4px; border: 0; background: transparent; - color: #eaf1f8; + color: var(--cad-text); font: inherit; text-align: center; } .cad-qtable__value input:focus { - outline: 2px solid #4ca6e8; + outline: 2px solid var(--color-focus-ring); outline-offset: -2px; - background: #12324a; + background: var(--cad-hover); } .cad-qtable__value--derived input { - color: #8fa3b5; - background: #1a242e; + color: var(--cad-text-muted); + background: var(--cad-chrome-sunken); cursor: not-allowed; } diff --git a/B07_DesignDetail/openwebcad/src/components/Button.tsx b/B07_DesignDetail/openwebcad/src/components/Button.tsx index 0b139b7f..b1f3f087 100644 --- a/B07_DesignDetail/openwebcad/src/components/Button.tsx +++ b/B07_DesignDetail/openwebcad/src/components/Button.tsx @@ -37,11 +37,9 @@ export const Button: FC = ({ left = null, right = null, }) => { + // 색은 App.css의 테마 토큰이 data-type/data-active로 준다 — 여기서는 배치만. const classParts = [ - 'font-semibold py-4 h-10 flex flex-row justify-start w-full items-center hover:bg-blue-500 hover:text-white hover:border-transparent', - type === 'regular' ? 'bg-gray-950 text-blue-500' : '', - type === 'transparent' ? 'bg-transparent text-blue-500' : '', - active ? 'bg-blue-500 text-white border-transparent hover:bg-blue-400' : '', + 'font-semibold py-4 h-10 flex flex-row justify-start w-full items-center', size === 'regular' ? 'pl-2 pr-2 gap-2' : '', size === 'small' ? 'pl-2 pr-2 gap-0' : '', className || '', @@ -68,7 +66,7 @@ export const Button: FC = ({ (iconName && ( ))}
{left}
diff --git a/B07_DesignDetail/openwebcad/src/components/LayerManager.tsx b/B07_DesignDetail/openwebcad/src/components/LayerManager.tsx index 2891d5f6..c0962b7b 100644 --- a/B07_DesignDetail/openwebcad/src/components/LayerManager.tsx +++ b/B07_DesignDetail/openwebcad/src/components/LayerManager.tsx @@ -93,7 +93,7 @@ export const LayerManager: FC = ({ title="Set this layer as active" active={activeLayerId === layer.id} onClick={(evt) => handleLayerClick(evt, layer.id)} - className="data-[active=true]:text-white flex-grow" + className="flex-grow" left={ <>