diff --git a/frontend/index.html b/frontend/index.html index 287bd2c..9a89007 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -51,6 +51,18 @@ +
+
+

Legend

+
+
+
Power
+
Ground
+
Clock
+
Signal
+
Analog
+
+
diff --git a/frontend/styles.css b/frontend/styles.css index 404a347..6f5a112 100644 --- a/frontend/styles.css +++ b/frontend/styles.css @@ -1,56 +1,50 @@ :root { - --bg: #eef2f6; - --panel: #ffffff; - --ink: #1d2939; - --ink-soft: #667085; - --line: #d0d5dd; - --accent: #155eef; - --accent-soft: #dbe8ff; - --warn: #b54708; - --error: #b42318; - --ok: #067647; + --bg-0: #f5f7fb; + --bg-1: #ebf1f8; + --bg-2: #dce8f4; + --panel: #fbfdff; + --panel-strong: #ffffff; + --canvas: #f4f8fd; + --ink: #0f1728; + --ink-muted: #4a607c; + --ink-subtle: #7388a3; + --line: #c8d5e4; + --line-strong: #9db1c9; + --accent: #1565d8; + --accent-strong: #0f4dab; + --accent-soft: #e6f0ff; + --ok: #0f7a49; + --warn: #9b6200; + --err: #ac2f24; + --power: #d6691f; + --ground: #60748d; + --clock: #d35c2f; + --signal: #2f72e7; + --analog: #0d978e; + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --shadow-1: 0 1px 2px rgba(16, 24, 40, 0.08); + --shadow-2: 0 10px 24px rgba(16, 24, 40, 0.08); } * { box-sizing: border-box; } +html, body { margin: 0; - font-family: "Manrope", "Segoe UI", sans-serif; + min-height: 100%; +} + +body { + font-family: "IBM Plex Sans", "Manrope", "Segoe UI", sans-serif; color: var(--ink); - background: radial-gradient(circle at 8% 8%, #fef7e6, transparent 30%), - radial-gradient(circle at 88% 12%, #e0f2ff, transparent 30%), var(--bg); - min-height: 100vh; -} - -.topbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 10px 12px; - border-bottom: 1px solid var(--line); - background: #f7fbffde; - backdrop-filter: blur(4px); -} - -.brand h1 { - margin: 0; - font-size: 1.2rem; - letter-spacing: 0.08em; -} - -.brand p { - margin: 2px 0 0; - color: var(--ink-soft); - font-size: 0.8rem; -} - -.actions { - display: flex; - flex-wrap: wrap; - gap: 8px; - align-items: center; + background: + radial-gradient(circle at 0% 0%, #fff4de 0 20%, transparent 30%), + radial-gradient(circle at 96% 5%, #dbf2ff 0 18%, transparent 28%), + linear-gradient(180deg, var(--bg-1), var(--bg-0)); } button, @@ -60,44 +54,101 @@ textarea { font: inherit; } -button { - border: 1px solid var(--line); - border-radius: 8px; - background: #fff; - padding: 6px 10px; - color: var(--ink); - cursor: pointer; -} - -button:disabled { - opacity: 0.5; - cursor: not-allowed; +button, +input, +select, +textarea, +.list li, +details > summary { + transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease, color 120ms ease; } button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, -.list li:focus-visible { - outline: 2px solid #155eef; +.list li:focus-visible, +details > summary:focus-visible { + outline: 2px solid var(--accent); outline-offset: 1px; } +button { + border: 1px solid var(--line); + border-radius: var(--radius-sm); + background: var(--panel-strong); + color: var(--ink); + padding: 6px 11px; + cursor: pointer; + box-shadow: var(--shadow-1); +} + +button:hover { + border-color: var(--line-strong); + background: #f3f8ff; +} + +button:disabled { + opacity: 0.48; + cursor: not-allowed; +} + button.primary { + border-color: var(--accent); background: var(--accent); color: #fff; - border-color: var(--accent); +} + +button.primary:hover { + background: var(--accent-strong); + border-color: var(--accent-strong); } button.chip { - padding: 4px 8px; + padding: 4px 9px; font-size: 0.76rem; } button.activeChip { background: var(--accent-soft); border-color: var(--accent); - color: #0f3ea3; + color: var(--accent-strong); +} + +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + padding: 10px 12px; + border-bottom: 1px solid var(--line); + background: linear-gradient(180deg, #f9fcff, #f2f8ff); + backdrop-filter: blur(2px); + position: sticky; + top: 0; + z-index: 40; +} + +.brand h1 { + margin: 0; + font-size: 1.6rem; + letter-spacing: 0.08em; + line-height: 1; +} + +.brand p { + margin: 2px 0 0; + font-size: 0.84rem; + color: var(--ink-muted); + font-weight: 600; +} + +.actions { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + align-items: center; + gap: 7px; } .inlineSelect, @@ -109,24 +160,22 @@ button.activeChip { } .inlineSelect select { - border: 1px solid var(--line); - border-radius: 8px; - padding: 5px 8px; + min-width: 138px; } .workspace { - height: calc(100vh - 65px); + height: calc(100vh - 77px); display: grid; - grid-template-columns: 270px minmax(480px, 1fr) 380px; gap: 10px; + grid-template-columns: 270px minmax(520px, 1fr) 392px; padding: 10px; } .pane { - background: var(--panel); border: 1px solid var(--line); - border-radius: 12px; - overflow: hidden; + border-radius: var(--radius-md); + background: linear-gradient(180deg, var(--panel), #f6faff); + box-shadow: var(--shadow-1); } .pane.left, @@ -138,16 +187,27 @@ button.activeChip { overflow: auto; } +.pane.center { + overflow: hidden; + background: linear-gradient(180deg, #fcfeff, #f6faff); +} + .sectionHead { display: flex; - justify-content: space-between; align-items: center; + justify-content: space-between; gap: 8px; } .sectionHead h2 { margin: 0; - font-size: 0.9rem; + font-size: 0.92rem; + color: var(--ink); +} + +h2 { + margin: 0; + font-size: 0.94rem; } input, @@ -155,32 +215,44 @@ textarea, select { width: 100%; border: 1px solid var(--line); - border-radius: 8px; + border-radius: var(--radius-sm); padding: 8px; + background: #fff; color: var(--ink); } +input::placeholder, +textarea::placeholder { + color: var(--ink-subtle); +} + textarea { min-height: 250px; - font-family: "JetBrains Mono", monospace; + font-family: "JetBrains Mono", "IBM Plex Mono", monospace; font-size: 12px; - line-height: 1.45; + line-height: 1.42; } .list { margin: 8px 0 0; - padding: 0; list-style: none; - max-height: 230px; + padding: 0; + max-height: 250px; overflow: auto; border: 1px solid var(--line); - border-radius: 8px; + border-radius: var(--radius-sm); + background: rgba(255, 255, 255, 0.72); } .list li { padding: 8px; border-bottom: 1px solid var(--line); cursor: pointer; + color: var(--ink); +} + +.list li:hover { + background: #f2f7ff; } .list li:last-child { @@ -189,6 +261,8 @@ textarea { .list li.active { background: var(--accent-soft); + color: #0b3a84; + font-weight: 600; } .list li.listSpacer { @@ -198,11 +272,60 @@ textarea { background: transparent; } +.legendSection { + margin-top: 2px; +} + +.netLegend { + border: 1px solid var(--line); + border-radius: var(--radius-sm); + background: #fbfdff; + padding: 8px; + display: grid; + gap: 6px; +} + +.legendRow { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 0.8rem; + color: var(--ink-muted); +} + +.legendSwatch { + display: inline-block; + width: 20px; + height: 0; + border-top: 3px solid var(--line-strong); +} + +.legendPower { + border-top-color: var(--power); +} + +.legendGround { + border-top-color: var(--ground); +} + +.legendClock { + border-top-color: var(--clock); +} + +.legendSignal { + border-top-color: var(--signal); +} + +.legendAnalog { + border-top-color: var(--analog); +} + .card { border: 1px solid var(--line); - border-radius: 8px; + border-radius: var(--radius-sm); + background: #fbfdff; padding: 8px; - color: var(--ink-soft); + color: var(--ink-muted); font-size: 0.85rem; white-space: pre-wrap; } @@ -210,9 +333,9 @@ textarea { .editorCard { margin-top: 8px; border: 1px solid var(--line); - border-radius: 8px; - padding: 8px; - background: #fcfcfd; + border-radius: var(--radius-sm); + padding: 9px; + background: #f8fbff; display: flex; flex-direction: column; gap: 8px; @@ -220,9 +343,10 @@ textarea { .editorSection { border: 1px solid var(--line); - border-radius: 8px; + border-radius: var(--radius-sm); margin-top: 8px; - background: #fff; + background: #ffffffcc; + overflow: hidden; } .editorSection > summary { @@ -232,11 +356,15 @@ textarea { font-size: 0.8rem; font-weight: 700; color: var(--ink); - border-bottom: 1px solid transparent; +} + +.editorSection > summary:hover { + background: #f3f8ff; } .editorSection[open] > summary { - border-bottom-color: var(--line); + border-bottom: 1px solid var(--line); + background: #eef4ff; } .editorSection > summary::-webkit-details-marker { @@ -251,27 +379,27 @@ textarea { .editorActions { display: flex; - gap: 8px; flex-wrap: wrap; + gap: 8px; } .hintText { font-size: 0.8rem; - color: var(--ink-soft); + color: var(--ink-muted); } .miniList { border: 1px solid var(--line); - border-radius: 8px; + border-radius: var(--radius-sm); background: #fff; - max-height: 170px; + max-height: 180px; overflow: auto; } .miniRow { display: flex; - justify-content: space-between; align-items: center; + justify-content: space-between; gap: 8px; padding: 6px 8px; border-bottom: 1px solid var(--line); @@ -284,47 +412,48 @@ textarea { .symbolPinRow { display: grid; - grid-template-columns: 1fr 0.9fr 0.9fr 0.8fr 1fr auto auto auto; align-items: center; + grid-template-columns: 1fr 0.9fr 0.9fr 0.8fr 1fr auto auto auto; } .pinCol { min-width: 0; padding: 5px 6px; - font-size: 0.75rem; + font-size: 0.76rem; } .symbolPinRow.invalidRow { - background: #fff3f2; + background: #fff3f1; } .symbolValidationError { - color: var(--error); + color: var(--err); font-size: 0.76rem; } .migrationPreview { border: 1px solid var(--line); - border-radius: 8px; + border-radius: var(--radius-sm); padding: 8px; - background: #f8fafc; - margin-top: 6px; - margin-bottom: 6px; + margin: 6px 0; line-height: 1.35; + background: #f5f9ff; } .canvasTools { display: flex; - gap: 8px; align-items: center; - border-bottom: 1px solid var(--line); + gap: 8px; padding: 8px; + border-bottom: 1px solid var(--line); + background: linear-gradient(180deg, #f8fbff, #f2f7ff); } #compileStatus { margin-left: auto; - color: var(--ink-soft); + color: var(--ink-muted); font-size: 0.84rem; + font-weight: 600; } .status-ok { @@ -333,12 +462,14 @@ textarea { .canvasViewport { height: calc(100% - 52px); - overflow: hidden; position: relative; + overflow: hidden; cursor: grab; - background-image: linear-gradient(0deg, #ebeff3 1px, transparent 1px), - linear-gradient(90deg, #ebeff3 1px, transparent 1px); - background-size: 20px 20px; + background: + linear-gradient(0deg, #d9e4f0 1px, transparent 1px), + linear-gradient(90deg, #d9e4f0 1px, transparent 1px), + linear-gradient(180deg, var(--canvas), #edf4fc); + background-size: 20px 20px, 20px 20px, auto; } .canvasViewport.dragging { @@ -346,10 +477,10 @@ textarea { } .canvasInner { - transform-origin: 0 0; position: absolute; - left: 0; top: 0; + left: 0; + transform-origin: 0 0; } .canvasInner svg { @@ -358,10 +489,23 @@ textarea { .selectionBox { position: absolute; - border: 1px solid #155eef; - background: rgba(21, 94, 239, 0.12); - pointer-events: none; z-index: 15; + pointer-events: none; + border: 1px solid var(--accent); + background: rgba(21, 101, 216, 0.12); +} + +.pinTooltip { + position: absolute; + z-index: 20; + pointer-events: none; + border: 1px solid var(--line); + border-radius: var(--radius-sm); + padding: 6px 8px; + color: var(--ink); + background: rgba(255, 255, 255, 0.95); + box-shadow: var(--shadow-2); + font-size: 0.74rem; } .hidden { @@ -375,20 +519,20 @@ textarea { } .jsonActions button { + font-size: 0.77rem; padding: 4px 8px; - font-size: 0.78rem; } .jsonFeedback { min-height: 18px; - color: var(--ink-soft); - font-size: 0.78rem; margin-bottom: 6px; + color: var(--ink-muted); + font-size: 0.78rem; } .issueRow { border: 1px solid var(--line); - border-radius: 7px; + border-radius: 8px; padding: 7px; margin-bottom: 6px; cursor: pointer; @@ -396,51 +540,39 @@ textarea { } .issueRow:hover { - background: #f8faff; + background: #f4f8ff; } .issueErr { - border-color: #fecdca; - background: #fff6f5; + border-color: #f3bab5; + background: #fff5f4; } .issueWarn { - border-color: #fedf89; - background: #fffcf5; + border-color: #f0d28b; + background: #fffaf0; } .issueTitle { font-size: 0.8rem; font-weight: 700; + color: var(--ink); } .issueMeta { font-size: 0.72rem; - color: var(--ink-soft); -} - -.pinTooltip { - position: absolute; - pointer-events: none; - padding: 6px 8px; - border: 1px solid var(--line); - border-radius: 8px; - background: #ffffffee; - color: var(--ink); - font-size: 0.74rem; - z-index: 20; - box-shadow: 0 6px 20px rgba(16, 24, 40, 0.12); + color: var(--ink-muted); } .modal { position: fixed; inset: 0; - background: rgba(15, 23, 42, 0.45); z-index: 70; display: flex; align-items: center; justify-content: center; padding: 18px; + background: rgba(14, 22, 36, 0.54); } .modal.hidden { @@ -450,9 +582,10 @@ textarea { .modalCard { width: min(1120px, 100%); height: min(88vh, 900px); - background: #fff; border: 1px solid var(--line); - border-radius: 12px; + border-radius: var(--radius-lg); + background: #fff; + box-shadow: 0 24px 60px rgba(16, 24, 40, 0.24); padding: 12px; display: flex; flex-direction: column; @@ -461,19 +594,19 @@ textarea { .modalHead { display: flex; - justify-content: space-between; align-items: center; + justify-content: space-between; gap: 10px; } .modalHead h3 { margin: 0; - font-size: 0.95rem; + font-size: 0.96rem; } .modalHint { margin: 0; - color: var(--ink-soft); + color: var(--ink-muted); font-size: 0.8rem; } @@ -495,14 +628,38 @@ textarea { } } +@media (max-width: 1460px) { + .workspace { + grid-template-columns: 248px minmax(420px, 1fr) 360px; + } +} + @media (max-width: 1300px) { .workspace { + height: auto; grid-template-columns: 1fr; grid-template-rows: auto auto auto; - height: auto; } .pane.center { - min-height: 560px; + min-height: 600px; + } +} + +@media (max-width: 820px) { + .topbar { + position: static; + } + + .brand h1 { + font-size: 1.3rem; + } + + .actions { + justify-content: flex-start; + } + + .editorGrid { + grid-template-columns: 1fr; } } diff --git a/tests/baselines/ui/dense-analog.png b/tests/baselines/ui/dense-analog.png index fba3fa8..bef7b68 100644 Binary files a/tests/baselines/ui/dense-analog.png and b/tests/baselines/ui/dense-analog.png differ diff --git a/tests/baselines/ui/explicit-mode-auto-tidy.png b/tests/baselines/ui/explicit-mode-auto-tidy.png index 35e1d5c..d906f37 100644 Binary files a/tests/baselines/ui/explicit-mode-auto-tidy.png and b/tests/baselines/ui/explicit-mode-auto-tidy.png differ diff --git a/tests/baselines/ui/initial.png b/tests/baselines/ui/initial.png index 028d1ec..bddb8e8 100644 Binary files a/tests/baselines/ui/initial.png and b/tests/baselines/ui/initial.png differ diff --git a/tests/baselines/ui/post-migration-apply.png b/tests/baselines/ui/post-migration-apply.png index 93f1f9a..b1afd6c 100644 Binary files a/tests/baselines/ui/post-migration-apply.png and b/tests/baselines/ui/post-migration-apply.png differ diff --git a/tests/baselines/ui/selected-u2.png b/tests/baselines/ui/selected-u2.png index 15344a0..947ef0b 100644 Binary files a/tests/baselines/ui/selected-u2.png and b/tests/baselines/ui/selected-u2.png differ