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