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 }