style.css (13.4 KB)
1 /* Named color palette and default theme */ 2 :root { 3 /* Default colors */ 4 --white: white; 5 --black: black; 6 --default-grid: #d0d0d0; 7 --shadow: #00000044; 8 9 /* guac theme colors */ 10 --onion: #a81d8f; 11 --garlic: #fefef4; 12 --lime: #a9cf51; 13 --avocado: #7a8520; 14 15 /* banana theme colors */ 16 --peel: #ffcc12; 17 --flesh: #f8f5e3; 18 --unripe: #a5d269; 19 --bruise: #4d4235; 20 21 /* mojito theme colors */ 22 --mint: #9febaa; 23 --soda: #f8fffe; 24 --zest: #9ee3ad; 25 --watermelon: #f8599b; 26 27 /* grape-soda theme colors */ 28 --natural-flavors: #d4c5f9; 29 --effervescence: #f9f7ff; 30 --concentrate: #b8a9dd; 31 --syrup: #7c5cbe; 32 33 /* grapefruit theme colors */ 34 --rind: #ff9b87; 35 --pith: #fff8f6; 36 --pulp: #f0bcb3; 37 --ruby: #ff6b51; 38 39 /* sea-breeze theme colors */ 40 --cerulean: #a3d5ff; 41 --mist: #f7fcff; 42 --horizon: #85c0f5; 43 --lagoon: #2196f3; 44 45 --grid-cols: 49; 46 --grid-rows: 66; 47 --cell-size-mm: 4mm; 48 49 /* Screen view: Grid as percentage of page (centered on US Letter simulation) */ 50 /* Grid width as percent of Letter: 196/215.9 = 90.78% */ 51 /* Grid height as percent of Letter: 264/279.4 = 94.48% */ 52 --grid-width-percent: 90.78%; 53 --grid-height-percent: 94.48%; 54 55 /* Default theme (sea-breeze) */ 56 --desk: var(--cerulean); 57 --page: var(--mist); 58 --grid-line: var(--horizon); 59 --grid-line-light: color-mix(in srgb, var(--grid-line) 65%, transparent); 60 --accent: var(--lagoon); 61 62 /* Set text color for all themes */ 63 color: var(--white); 64 65 /* Set font */ 66 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; 67 } 68 69 /* Theme definitions */ 70 :root[data-theme="guac"] { 71 --desk: var(--avocado); 72 --page: var(--garlic); 73 --grid-line: var(--lime); 74 --accent: var(--onion); 75 } 76 77 :root[data-theme="banana"] { 78 --desk: var(--peel); 79 --page: var(--flesh); 80 --grid-line: var(--unripe); 81 --accent: var(--bruise); 82 } 83 84 :root[data-theme="mojito"] { 85 --desk: var(--mint); 86 --page: var(--soda); 87 --grid-line: var(--zest); 88 --accent: var(--watermelon); 89 } 90 91 :root[data-theme="grape-soda"] { 92 --desk: var(--natural-flavors); 93 --page: var(--effervescence); 94 --grid-line: var(--concentrate); 95 --accent: var(--syrup); 96 } 97 98 :root[data-theme="grapefruit"] { 99 --desk: var(--rind); 100 --page: var(--pith); 101 --grid-line: var(--pulp); 102 --accent: var(--ruby); 103 } 104 105 :root[data-theme="sea-breeze"] { 106 --desk: var(--cerulean); 107 --page: var(--mist); 108 --grid-line: var(--horizon); 109 --accent: var(--lagoon); 110 } 111 112 * { 113 margin: 0; 114 padding: 0; 115 box-sizing: border-box; 116 -webkit-user-select: none; 117 user-select: none; 118 scrollbar-color: var(--page) var(--desk); 119 } 120 121 body { 122 background: var(--desk); 123 display: flex; 124 justify-content: center; 125 align-items: flex-start; 126 min-height: 100vh; 127 padding: 20px; 128 } 129 130 .page { 131 /* Scale down on narrow screens, but never larger than 8.5in */ 132 max-width: min(8.5in, calc(100vw - 40px)); 133 width: 100%; 134 /* Maintain 8.5:11 aspect ratio (11/8.5 = 1.294117647) */ 135 aspect-ratio: 8.5 / 11; 136 background: var(--page); 137 position: relative; 138 box-shadow: 0 8px 24px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.08); 139 display: flex; 140 align-items: center; 141 justify-content: center; 142 } 143 144 .grid { 145 /* Grid dimensions as percentage of page to scale responsively */ 146 width: var(--grid-width-percent); 147 height: var(--grid-height-percent); 148 display: grid; 149 grid-template-columns: repeat(var(--grid-cols), 1fr); 150 grid-template-rows: repeat(var(--grid-rows), 1fr); 151 gap: 0; 152 line-height: 0; 153 position: relative; /* Image containers are positioned relative to grid */ 154 /* Grid is centered within .page by the parent's flexbox */ 155 box-sizing: border-box; 156 } 157 158 .grid-cell { 159 width: 100%; 160 height: 100%; 161 box-sizing: border-box; 162 border-top: 1px dashed var(--grid-line); 163 border-left: 1px dashed var(--grid-line); 164 margin: 0; 165 padding: 0; 166 display: block; 167 } 168 169 /* Add right border to rightmost column */ 170 .grid-cell.right-edge { 171 border-right: 1px dashed var(--grid-line); 172 width: calc(100% + 1px); 173 } 174 175 /* Add bottom border to bottom row */ 176 .grid-cell.bottom-edge { 177 border-bottom: 1px dashed var(--grid-line); 178 height: calc(100% + 1px); 179 } 180 181 /* Solid grid lines for cell sizes <= 3.56mm (screen preview only) */ 182 .solid-grid .grid-cell { 183 border-top-style: solid; 184 border-left-style: solid; 185 border-top-color: var(--grid-line-light); 186 border-left-color: var(--grid-line-light); 187 } 188 189 .solid-grid .grid-cell.right-edge { 190 border-right-style: solid; 191 border-right-color: var(--grid-line-light); 192 } 193 194 .solid-grid .grid-cell.bottom-edge { 195 border-bottom-style: solid; 196 border-bottom-color: var(--grid-line-light); 197 } 198 199 @media (max-width: 680px) { 200 body { 201 padding: 0; 202 } 203 204 .page { 205 max-width: 100vw; 206 box-shadow: none; 207 } 208 209 .grid-cell { 210 border-top-style: solid; 211 border-left-style: solid; 212 border-top-color: var(--grid-line-light); 213 border-left-color: var(--grid-line-light); 214 } 215 216 .grid-cell.right-edge { 217 border-right-style: solid; 218 border-right-color: var(--grid-line-light); 219 width: calc(100% + 1px); 220 } 221 222 .grid-cell.bottom-edge { 223 border-bottom-style: solid; 224 border-bottom-color: var(--grid-line-light); 225 height: calc(100% + 1px); 226 } 227 } 228 229 .image-container { 230 position: absolute; 231 cursor: grab; 232 outline: 2px solid transparent; 233 outline-offset: -2px; 234 transition: outline-color 0.2s; 235 background: var(--page); 236 touch-action: none; 237 } 238 239 .image-container::after { 240 content: ''; 241 position: absolute; 242 top: 0; 243 left: 0; 244 right: 0; 245 bottom: 0; 246 pointer-events: none; 247 border: 2px solid transparent; 248 transition: border-color 0.2s; 249 z-index: 1; 250 /* overflow: hidden; */ 251 } 252 253 .image-container .image-wrapper { 254 position: absolute; 255 top: 0; 256 left: 0; 257 right: 0; 258 bottom: 0; 259 overflow: hidden; 260 } 261 262 .image-container:hover::after { 263 border-color: var(--accent); 264 } 265 266 .image-container.dragging { 267 opacity: 0.7; 268 } 269 270 .image-container.dragging::after { 271 border-color: var(--accent); 272 } 273 274 /* Visual feedback for pan mode on touch */ 275 .image-container.pan-mode::after { 276 border-color: var(--accent); 277 border-width: 3px; 278 border-style: solid; 279 animation: pulse-border 0.25s ease-out; 280 } 281 282 @keyframes pulse-border { 283 0% { 284 border-width: 2px; 285 opacity: 0.5; 286 } 287 50% { 288 border-width: 5px; 289 opacity: 1; 290 } 291 100% { 292 border-width: 3px; 293 opacity: 1; 294 } 295 } 296 297 /* Hide dimension labels and resize handles in pan mode */ 298 .image-container.pan-mode .dimension-label { 299 opacity: 0 !important; 300 } 301 302 .image-container.pan-mode .resize-handle { 303 opacity: 0 !important; 304 } 305 306 .image-container.resizing::after { 307 border-color: var(--accent); 308 } 309 310 body.resizing *, 311 body.dragging * { 312 cursor: inherit !important; 313 } 314 315 .image-container img { 316 position: absolute; 317 top: 50%; 318 left: 50%; 319 display: block; 320 pointer-events: none; 321 transform-origin: center center; 322 transition: none; 323 } 324 325 .resize-handle { 326 position: absolute; 327 background: var(--accent); 328 opacity: 0; 329 transition: opacity 0.2s; 330 z-index: 2; 331 } 332 333 .image-container:hover .resize-handle, 334 .image-container.resizing .resize-handle { 335 opacity: 1; 336 } 337 338 .dimension-label { 339 position: absolute; 340 background: var(--accent); 341 color: var(--text); 342 padding: 4px 10px; 343 border-radius: 12px; 344 font-size: 16px; 345 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; 346 font-weight: 500; 347 line-height: 1; 348 opacity: 0; 349 transition: opacity 0.2s; 350 pointer-events: none; 351 z-index: 9999; 352 white-space: nowrap; 353 } 354 355 .dimension-label.width { 356 top: 1px; 357 left: 50%; 358 transform: translate(-50%, -50%); 359 } 360 361 .dimension-label.height { 362 right: 1px; 363 top: 50%; 364 transform: translate(50%, -50%) rotate(90deg); 365 } 366 367 .image-container:hover .dimension-label, 368 .image-container.dragging .dimension-label, 369 .image-container.resizing .dimension-label { 370 opacity: 1; 371 } 372 373 .dimension-label[data-hidden="true"] { 374 opacity: 0 !important; 375 } 376 377 .resize-handle.corner { 378 width: 10px; 379 height: 10px; 380 background: transparent; 381 } 382 383 .resize-handle.corner::before { 384 content: ''; 385 position: absolute; 386 width: 10px; 387 height: 10px; 388 background: var(--accent); 389 border-radius: 50%; 390 } 391 392 .resize-handle.edge { 393 background: transparent; 394 } 395 396 /* Desktop: Original 10px hitboxes */ 397 .resize-handle.n { top: -5px; left: 5px; right: 5px; height: 10px; cursor: n-resize; } 398 .resize-handle.s { bottom: -5px; left: 5px; right: 5px; height: 10px; cursor: s-resize; } 399 .resize-handle.e { right: -5px; top: 5px; bottom: 5px; width: 10px; cursor: e-resize; } 400 .resize-handle.w { left: -5px; top: 5px; bottom: 5px; width: 10px; cursor: w-resize; } 401 402 .resize-handle.ne { top: -5px; right: -5px; cursor: ne-resize; } 403 .resize-handle.ne::before { top: 0; right: 0; } 404 405 .resize-handle.nw { top: -5px; left: -5px; cursor: nw-resize; } 406 .resize-handle.nw::before { top: 0; left: 0; } 407 408 .resize-handle.se { bottom: -5px; right: -5px; cursor: se-resize; } 409 .resize-handle.se::before { bottom: 0; right: 0; } 410 411 .resize-handle.sw { bottom: -5px; left: -5px; cursor: sw-resize; } 412 .resize-handle.sw::before { bottom: 0; left: 0; } 413 414 @media (pointer: coarse), (hover: none) { 415 html { 416 overscroll-behavior: none; 417 } 418 419 /* Only fix position in portrait mode to prevent scroll issues in landscape */ 420 @media (orientation: portrait) { 421 html, body { 422 height: 100%; 423 position: fixed; 424 width: 100%; 425 } 426 } 427 428 .resize-handle.corner { 429 width: 12px; 430 height: 12px; 431 } 432 433 .resize-handle.corner::before { 434 /* Center the visual dot within the larger hitbox */ 435 top: 50%; 436 left: 50%; 437 transform: translate(-50%, -50%); 438 } 439 440 /* Edge handles with 12px hitboxes */ 441 .resize-handle.n { top: -6px; height: 12px; } 442 .resize-handle.s { bottom: -6px; height: 12px; } 443 .resize-handle.e { right: -6px; width: 12px; } 444 .resize-handle.w { left: -6px; width: 12px; } 445 446 /* Corner handles with 12px hitboxes, centered on the visual corner */ 447 .resize-handle.ne { top: -6px; right: -6px; } 448 .resize-handle.nw { top: -6px; left: -6px; } 449 .resize-handle.se { bottom: -6px; right: -6px; } 450 .resize-handle.sw { bottom: -6px; left: -6px; } 451 } 452 453 @media print { 454 * { 455 -webkit-print-color-adjust: exact !important; 456 print-color-adjust: exact !important; 457 } 458 459 html, body { 460 margin: 0 !important; 461 padding: 0 !important; 462 width: 100% !important; 463 height: 100vh !important; 464 overflow: visible !important; 465 } 466 467 body { 468 background: var(--white); 469 display: flex !important; 470 align-items: center !important; 471 justify-content: center !important; 472 } 473 474 .page { 475 width: auto !important; 476 height: auto !important; 477 max-width: none !important; 478 box-shadow: none; 479 margin: 0; 480 padding: 0; 481 background: var(--white); 482 display: block !important; 483 } 484 485 .grid { 486 width: calc(var(--grid-cols) * var(--cell-size-mm)) !important; 487 height: calc(var(--grid-rows) * var(--cell-size-mm)) !important; 488 display: grid; 489 grid-template-columns: repeat(var(--grid-cols), 1fr) !important; 490 grid-template-rows: repeat(var(--grid-rows), 1fr) !important; 491 position: relative !important; 492 box-sizing: border-box !important; 493 } 494 495 .grid-cell { 496 border-top: 1px dashed var(--default-grid) !important; 497 border-left: 1px dashed var(--default-grid) !important; 498 box-sizing: border-box !important; 499 width: 100% !important; 500 height: 100% !important; 501 margin: 0 !important; 502 padding: 0 !important; 503 } 504 505 .grid-cell.right-edge { 506 border-right: 1px dashed var(--default-grid) !important; 507 width: calc(100% + 1px) !important; 508 } 509 510 .grid-cell.bottom-edge { 511 border-bottom: 1px dashed var(--default-grid) !important; 512 height: calc(100% + 1px) !important; 513 } 514 515 .image-container { 516 outline: none !important; 517 position: absolute !important; 518 background: var(--white) !important; 519 /* Recalculate positions using cell coordinates and print cell size */ 520 left: calc(var(--x-cell) * var(--cell-size-mm)) !important; 521 top: calc(var(--y-cell) * var(--cell-size-mm)) !important; 522 width: calc(var(--width-cells) * var(--cell-size-mm)) !important; 523 height: calc(var(--height-cells) * var(--cell-size-mm)) !important; 524 } 525 526 .image-container::after { 527 border: none !important; 528 } 529 530 .image-container .image-wrapper { 531 position: absolute !important; 532 top: 0 !important; 533 left: 0 !important; 534 right: 0 !important; 535 bottom: 0 !important; 536 overflow: hidden !important; 537 } 538 539 .image-container img { 540 position: absolute !important; 541 top: 50% !important; 542 left: 50% !important; 543 display: block !important; 544 pointer-events: none !important; 545 /* Preserve the transform from the screen view */ 546 } 547 548 .resize-handle { 549 display: none !important; 550 } 551 552 .dimension-label { 553 display: none !important; 554 } 555 556 .add-images-btn { 557 display: none !important; 558 } 559 560 .popup-notification { 561 display: none !important; 562 } 563 } 564 565 @page { 566 margin: 0; 567 } 568 569 .add-images-btn { 570 display: none; 571 position: fixed; 572 bottom: 24px; 573 left: 50%; 574 transform: translateX(-50%); 575 background: var(--accent); 576 color: var(--page); 577 border: none; 578 border-radius: 100px; 579 padding: 14px 28px; 580 font-size: 16px; 581 font-weight: 600; 582 cursor: pointer; 583 box-shadow: 0 4px 12px rgba(0,0,0,0.2); 584 z-index: 10000; 585 -webkit-tap-highlight-color: transparent; 586 user-select: none; 587 -webkit-user-select: none; 588 transition: transform 0.1s ease, box-shadow 0.1s ease; 589 } 590 591 .add-images-btn:active { 592 transform: translateX(-50%) scale(0.95); 593 box-shadow: 0 2px 8px rgba(0,0,0,0.2); 594 } 595 596 @media (pointer: coarse), (hover: none) { 597 .add-images-btn { 598 display: block; 599 bottom: 0; 600 margin-bottom: 24px; 601 } 602 603 /* On touch devices, don't change opacity when dragging */ 604 .image-container.dragging { 605 opacity: 1; 606 } 607 } 608 609 .popup-notification { 610 position: fixed; 611 bottom: 2rem; 612 left: 50%; 613 transform: translateX(-50%); 614 background-color: var(--accent); 615 color: var(--page); 616 padding: 0.7rem 0.9rem; 617 border-radius: 50rem; 618 font-size: 1.2rem; 619 text-align: center; 620 white-space: nowrap; 621 user-select: none; 622 width: fit-content; 623 height: fit-content; 624 opacity: 0; 625 pointer-events: none; 626 z-index: 10000; 627 } 628 629 .popup-notification.show { 630 opacity: 1; 631 transition: none; 632 } 633 634 .popup-notification.fade-out { 635 opacity: 0; 636 filter: blur(4px); 637 transition: opacity 1.5s ease-out, filter 1.5s ease-out; 638 }