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 }