/ concept-collection / numbl-figure-viewer
Sign in
concept-collection / numbl-figure-viewer
numbl-figure-viewer / src / index.css
563 lines · 9.8 KBBlameHistoryRaw
1:root {
2 --bg: #ffffff;
3 --chrome: #f3f3f3;
4 --panel: #f8f8f8;
5 --border: #e2e2e2;
6 --text: #1f1f1f;
7 --muted: #6b6b6b;
8 --accent: #0a66c2;
9 --hover: #ececec;
10 --selected: #d6e8fb;
11 --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
12 font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
13 color: var(--text);
16* {
17 box-sizing: border-box;
20html,
21body,
22#root {
23 height: 100%;
24 margin: 0;
27#root {
28 height: 100vh;
31.app {
32 display: flex;
33 flex-direction: column;
34 height: 100%;
35 overflow: hidden;
36 background: var(--bg);
39/* ── Title bar ─────────────────────────────────────────────────────────── */
40.titlebar {
41 display: flex;
42 align-items: center;
43 gap: 8px;
44 height: 40px;
45 flex-shrink: 0;
46 padding: 0 8px;
47 background: var(--chrome);
48 border-bottom: 1px solid var(--border);
49 font-size: 13px;
52.app-title {
53 font-weight: 600;
54 white-space: nowrap;
55 flex-shrink: 0;
58.file-name {
59 color: var(--muted);
60 font-size: 12px;
61 overflow: hidden;
62 text-overflow: ellipsis;
63 white-space: nowrap;
64 max-width: 40vw;
67.spacer {
68 flex: 1;
71.icon-btn,
72.text-btn {
73 font: inherit;
74 border: 1px solid transparent;
75 background: transparent;
76 color: var(--text);
77 cursor: pointer;
78 border-radius: 5px;
79 padding: 4px 8px;
80 line-height: 1;
83.icon-btn {
84 font-size: 16px;
85 width: 30px;
86 height: 28px;
87 display: inline-flex;
88 align-items: center;
89 justify-content: center;
92.icon-btn:hover,
93.text-btn:hover:not(:disabled) {
94 background: var(--hover);
97.text-btn {
98 border-color: var(--border);
99 font-size: 12px;
100 background: #fff;
103.text-btn:disabled {
104 opacity: 0.5;
105 cursor: default;
108.error-bar {
109 flex-shrink: 0;
110 padding: 8px 12px;
111 background: #fdecea;
112 color: #a4231f;
113 font-size: 12.5px;
114 border-bottom: 1px solid #f3c2bf;
117/* ── Empty state ───────────────────────────────────────────────────────── */
118.empty-state {
119 flex: 1;
120 display: flex;
121 align-items: center;
122 justify-content: center;
123 cursor: pointer;
124 padding: 24px;
127.empty-inner {
128 border: 2px dashed #c4c4c4;
129 border-radius: 12px;
130 padding: 48px 56px;
131 text-align: center;
132 color: var(--muted);
133 max-width: 460px;
136.empty-state.over .empty-inner {
137 border-color: var(--accent);
138 background: #f0f7ff;
141.empty-icon {
142 font-size: 40px;
143 color: #b9b9b9;
146.empty-inner h2 {
147 margin: 12px 0 6px;
148 color: var(--text);
149 font-size: 18px;
152.empty-inner p {
153 margin: 4px 0;
154 font-size: 13px;
157code {
158 font-family: var(--mono);
159 background: #efefef;
160 padding: 1px 5px;
161 border-radius: 4px;
162 font-size: 0.92em;
165.muted {
166 color: var(--muted);
168.small {
169 font-size: 12px;
172/* ── Body / panels ─────────────────────────────────────────────────────── */
173.body {
174 flex: 1;
175 display: flex;
176 min-height: 0;
177 position: relative;
180.panel {
181 display: flex;
182 flex-direction: column;
183 background: var(--panel);
184 min-width: 0;
185 overflow: hidden;
188.panel.right {
189 background: #fbfbfb;
192.panel-head {
193 display: flex;
194 align-items: center;
195 justify-content: space-between;
196 flex-shrink: 0;
197 height: 30px;
198 padding: 0 10px;
199 font-size: 11px;
200 font-weight: 600;
201 letter-spacing: 0.05em;
202 text-transform: uppercase;
203 color: var(--muted);
204 border-bottom: 1px solid var(--border);
207.panel-body {
208 flex: 1;
209 overflow: auto;
212.center {
213 flex: 1;
214 position: relative;
215 min-width: 0;
216 background: #fff;
219.figure-host {
220 position: absolute;
221 inset: 0;
224.drop-hint {
225 position: absolute;
226 inset: 0;
227 display: flex;
228 align-items: center;
229 justify-content: center;
230 background: rgba(10, 102, 194, 0.08);
231 border: 2px dashed var(--accent);
232 color: var(--accent);
233 font-weight: 600;
234 pointer-events: none;
237/* ── Divider ───────────────────────────────────────────────────────────── */
238.divider {
239 width: 6px;
240 flex-shrink: 0;
241 cursor: col-resize;
242 background: transparent;
243 position: relative;
246.divider::after {
247 content: "";
248 position: absolute;
249 inset: 0 2px;
250 background: var(--border);
253.divider:hover::after {
254 background: var(--accent);
257/* ── Tree ──────────────────────────────────────────────────────────────── */
258.tree {
259 padding: 4px 0;
260 font-size: 13px;
261 user-select: none;
264.tree-row {
265 display: flex;
266 align-items: center;
267 height: 24px;
268 padding-right: 8px;
269 cursor: pointer;
270 white-space: nowrap;
273.tree-row:hover {
274 background: var(--hover);
277.tree-row.selected {
278 background: var(--selected);
281.twisty {
282 width: 16px;
283 flex-shrink: 0;
284 text-align: center;
285 color: var(--muted);
286 font-size: 10px;
289.twisty.leaf {
290 visibility: hidden;
293.node-icon {
294 width: 18px;
295 flex-shrink: 0;
296 text-align: center;
297 color: var(--muted);
300.icon-figure {
301 color: #7a5cc4;
303.icon-axes {
304 color: #2f8f5b;
306.icon-trace {
307 color: #c2731f;
309.icon-dataset {
310 color: #8a8a8a;
313.node-label {
314 flex: 1;
315 overflow: hidden;
316 text-overflow: ellipsis;
319.tree-row.dimmed .node-label,
320.tree-row.dimmed .node-icon {
321 opacity: 0.4;
324/* ── Row visibility controls ───────────────────────────────────────────── */
325.row-actions {
326 display: flex;
327 align-items: center;
328 gap: 1px;
329 margin-left: 4px;
330 flex-shrink: 0;
331 visibility: hidden;
334.tree-row:hover .row-actions,
335.tree-row.selected .row-actions,
336.tree-row.el-hidden .row-actions {
337 visibility: visible;
340.act-btn {
341 display: inline-flex;
342 align-items: center;
343 justify-content: center;
344 width: 22px;
345 height: 20px;
346 border: none;
347 background: transparent;
348 color: var(--muted);
349 cursor: pointer;
350 border-radius: 4px;
351 padding: 0;
354.act-btn:hover {
355 background: rgba(0, 0, 0, 0.08);
356 color: var(--text);
359.act-btn.active {
360 color: var(--accent);
363.head-actions {
364 display: flex;
365 align-items: center;
366 gap: 6px;
369.link-btn {
370 font: inherit;
371 font-size: 11px;
372 text-transform: none;
373 letter-spacing: 0;
374 border: none;
375 background: transparent;
376 color: var(--accent);
377 cursor: pointer;
378 padding: 0;
381.link-btn:hover {
382 text-decoration: underline;
385.center-overlay {
386 position: absolute;
387 inset: 0;
388 display: flex;
389 align-items: center;
390 justify-content: center;
391 text-align: center;
392 color: var(--muted);
395.center-overlay button {
396 margin-top: 8px;
399/* ── Info panel ────────────────────────────────────────────────────────── */
400.info {
401 padding: 10px 12px;
402 font-size: 13px;
405.info.empty {
406 color: var(--muted);
409.info-header {
410 display: flex;
411 align-items: center;
412 gap: 6px;
413 margin-bottom: 10px;
416.info-title {
417 font-weight: 600;
418 word-break: break-word;
421.info-kind {
422 margin-left: auto;
423 font-size: 11px;
424 color: var(--muted);
425 background: #ededed;
426 padding: 1px 7px;
427 border-radius: 10px;
430table.props {
431 width: 100%;
432 border-collapse: collapse;
435table.props td {
436 padding: 3px 6px;
437 vertical-align: top;
438 border-bottom: 1px solid #efefef;
441td.pk {
442 color: var(--muted);
443 width: 38%;
444 font-size: 12px;
447td.pv {
448 font-family: var(--mono);
449 font-size: 12px;
450 word-break: break-word;
453.swatch {
454 display: inline-block;
455 width: 11px;
456 height: 11px;
457 border-radius: 2px;
458 border: 1px solid #00000022;
459 margin-right: 6px;
460 vertical-align: middle;
463.preview-label {
464 margin: 12px 0 4px;
465 font-size: 11px;
466 text-transform: uppercase;
467 letter-spacing: 0.05em;
468 color: var(--muted);
471.preview {
472 margin: 0;
473 font-family: var(--mono);
474 font-size: 11.5px;
475 background: #f4f4f4;
476 border: 1px solid var(--border);
477 border-radius: 6px;
478 padding: 8px;
479 white-space: pre-wrap;
480 word-break: break-word;
481 max-height: 48vh;
482 overflow: auto;
485.vfooter {
486 display: flex;
487 align-items: center;
488 gap: 8px;
489 flex-wrap: wrap;
490 margin-top: 6px;
493.text-btn.sm {
494 padding: 2px 8px;
495 font-size: 11px;
498/* ── Mobile drawers ────────────────────────────────────────────────────── */
499.panel.drawer {
500 position: absolute;
501 top: 0;
502 bottom: 0;
503 width: min(82%, 340px);
504 z-index: 20;
505 box-shadow: 0 0 24px rgba(0, 0, 0, 0.22);
508.panel.left.drawer {
509 left: 0;
512.panel.right.drawer {
513 right: 0;
516.backdrop {
517 position: absolute;
518 inset: 0;
519 background: rgba(0, 0, 0, 0.28);
520 z-index: 10;
523/* ── Toast ─────────────────────────────────────────────────────────────── */
524.toast {
525 position: fixed;
526 left: 50%;
527 bottom: 28px;
528 transform: translateX(-50%);
529 z-index: 50;
530 max-width: 90vw;
531 padding: 9px 16px;
532 border-radius: 8px;
533 background: #2b2b2b;
534 color: #fff;
535 font-size: 13px;
536 box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
537 animation: toast-in 0.15s ease-out;
540@keyframes toast-in {
541 from {
542 opacity: 0;
543 transform: translate(-50%, 6px);
544 }
545 to {
546 opacity: 1;
547 transform: translate(-50%, 0);
548 }
551/* Touch / narrow screens: no hover, so always show the row controls. */
552@media (max-width: 820px) {
553 .row-actions {
554 visibility: visible;
555 }
558/* Narrow screens: drop the redundant filename (shown in the tree root). */
559@media (max-width: 560px) {
560 .file-name {
561 display: none;
562 }
moveopenescclose