new feature: press F2 to cycle interface themes
Hunter
Tuesday, October 21st, 2025 at 5:42 PM
1 <!DOCTYPE html>
-<html lang="en">
2 +<html lang="en" data-theme="sky">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 + <meta name="theme-color" content="#a3d5ff">
7 <title>memori</title>
8 <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>✂️</text></svg>">
9 <link rel="stylesheet" href="style.css">
639 });
640 });
641 });
642 +
643 +// Theme system
644 +let currentThemeIndex = 0;
645 +let isF2Pressed = false;
646 +const themes = ['sea-breeze', 'grape-soda', 'coral', 'guac', 'mojito', 'toast'];
647 +
648 +function setTheme(theme) {
649 + document.documentElement.setAttribute('data-theme', theme);
650 + const backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--desk').trim();
651 + document.querySelector('meta[name="theme-color"]').setAttribute('content', backgroundColor);
652 +}
653 +
654 +function cycleTheme() {
655 + currentThemeIndex = (currentThemeIndex + 1) % themes.length;
656 + const newTheme = themes[currentThemeIndex];
657 + setTheme(newTheme);
658 + saveThemeToLocalStorage(newTheme);
659 +}
660 +
661 +function saveThemeToLocalStorage(theme) {
662 + localStorage.setItem('memori-theme', theme);
663 +}
664 +
665 +function loadThemeFromLocalStorage() {
666 + const savedTheme = localStorage.getItem('memori-theme');
667 + if (savedTheme && themes.includes(savedTheme)) {
668 + currentThemeIndex = themes.indexOf(savedTheme);
669 + setTheme(savedTheme);
670 + } else {
671 + // Use default theme
672 + setTheme(themes[0]);
673 + }
674 +}
675 +
676 +// F2 key handler for theme cycling
677 +document.addEventListener('keydown', (e) => {
678 + if (e.key === 'F2' && !isF2Pressed) {
679 + e.preventDefault();
680 + isF2Pressed = true;
681 + cycleTheme();
682 + }
683 +});
684 +
685 +document.addEventListener('keyup', (e) => {
686 + if (e.key === 'F2') {
687 + isF2Pressed = false;
688 + }
689 +});
690 +
691 +// Load theme on page load
692 +loadThemeFromLocalStorage();
1 +/* Named color palette and default theme */
2 :root {
- --color-bg-body: #f5f5f0;
- --color-bg-page: #fafaf8;
- --color-grid-line: #d0d0d0;
- --color-accent: #79aeea;
- --color-white: white;
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 + /* toast theme colors */
16 + --butter: #ffe598;
17 + --cream: #fffef9;
18 + --crumb: #decca3;
19 + --peanut-butter: #be852a;
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 + /* coral theme colors */
34 + --reef: #ff9b87;
35 + --pearl: #fff8f6;
36 + --sand: #f0bcb3;
37 + --anemone: #ff6b51;
38 +
39 + /* sea-breeze theme colors */
40 + --cerulean: #a3d5ff;
41 + --mist: #f7fcff;
42 + --horizon: #85c0f5;
43 + --lagoon: #2196f3;
44 +
45 /* Grid configuration */
46 --grid-cols: 50;
47 --grid-rows: 66;
52 /* Grid as percentage of page: 200/216 = 92.59%, 264/279.4 = 94.48% */
53 --grid-width-percent: 92.59%;
54 --grid-height-percent: 94.48%;
55 +
56 + /* Default theme (sea-breeze) */
57 + --desk: var(--cerulean);
58 + --page: var(--mist);
59 + --grid-line: var(--horizon);
60 + --accent: var(--lagoon);
61 +
62 + /* Set text color for all themes */
63 + color: var(--white);
64 +}
65 +
66 +/* Theme definitions */
67 +:root[data-theme="guac"] {
68 + --desk: var(--avocado);
69 + --page: var(--garlic);
70 + --grid-line: var(--lime);
71 + --accent: var(--onion);
72 +}
73 +
74 +:root[data-theme="toast"] {
75 + --desk: var(--butter);
76 + --page: var(--cream);
77 + --grid-line: var(--crumb);
78 + --accent: var(--peanut-butter);
79 +}
80 +
81 +:root[data-theme="mojito"] {
82 + --desk: var(--mint);
83 + --page: var(--soda);
84 + --grid-line: var(--zest);
85 + --accent: var(--watermelon);
86 +}
87 +
88 +:root[data-theme="grape-soda"] {
89 + --desk: var(--natural-flavors);
90 + --page: var(--effervescence);
91 + --grid-line: var(--concentrate);
92 + --accent: var(--syrup);
93 +}
94 +
95 +:root[data-theme="coral"] {
96 + --desk: var(--reef);
97 + --page: var(--pearl);
98 + --grid-line: var(--sand);
99 + --accent: var(--anemone);
100 +}
101 +
102 +:root[data-theme="sea-breeze"] {
103 + --desk: var(--cerulean);
104 + --page: var(--mist);
105 + --grid-line: var(--horizon);
106 + --accent: var(--lagoon);
107 }
108
109 * {
113 }
114
115 body {
- background: var(--color-bg-body);
116 + background: var(--desk);
117 display: flex;
118 justify-content: center;
119 align-items: flex-start;
128 width: 100%;
129 /* Maintain 8.5:11 aspect ratio (11/8.5 = 1.294117647) */
130 aspect-ratio: 8.5 / 11;
- background: var(--color-bg-page);
131 + background: var(--page);
132 position: relative;
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
133 + box-shadow: 0 8px 24px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.08);
134 display: flex;
135 align-items: center;
136 justify-content: center;
153 width: 100%;
154 height: 100%;
155 box-sizing: border-box;
- border-right: 1px dashed var(--color-grid-line);
- border-bottom: 1px dashed var(--color-grid-line);
156 + border-right: 1px dashed var(--grid-line);
157 + border-bottom: 1px dashed var(--grid-line);
158 margin: 0;
159 padding: 0;
160 display: block;
161 }
162
163 .grid-cell:nth-child(-n+50) {
- border-top: 1px dashed var(--color-grid-line);
164 + border-top: 1px dashed var(--grid-line);
165 }
166
167 .grid-cell:nth-child(50n+1) {
- border-left: 1px dashed var(--color-grid-line);
168 + border-left: 1px dashed var(--grid-line);
169 }
170
171 .image-container {
174 outline: 2px solid transparent;
175 outline-offset: -2px;
176 transition: outline-color 0.2s;
- background: var(--color-bg-page);
177 + background: var(--page);
178 }
179
180 .image-container::after {
201 }
202
203 .image-container:hover::after {
- border-color: var(--color-accent);
204 + border-color: var(--accent);
205 }
206
207 .image-container.dragging {
209 }
210
211 .image-container.dragging::after {
- border-color: var(--color-accent);
212 + border-color: var(--accent);
213 }
214
215 .image-container.resizing::after {
- border-color: var(--color-accent);
216 + border-color: var(--accent);
217 }
218
219 body.resizing *,
233
234 .resize-handle {
235 position: absolute;
- background: var(--color-accent);
236 + background: var(--accent);
237 opacity: 0;
238 transition: opacity 0.2s;
239 z-index: 2;
245
246 .dimension-label {
247 position: absolute;
- background: var(--color-accent);
- color: var(--color-white);
248 + background: var(--accent);
249 + color: var(--text);
250 padding: 4px 10px;
251 border-radius: 12px;
252 font-size: 16px;
253 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
254 font-weight: 500;
- text-shadow: 1px 1px 1px var(--shadow);
255 line-height: 1;
256 opacity: 0;
257 transition: opacity 0.2s;
289 position: absolute;
290 width: 10px;
291 height: 10px;
- background: var(--color-accent);
292 + background: var(--accent);
293 border-radius: 50%;
294 }
295
321 }
322
323 body {
- background: var(--color-white);
324 + background: var(--white);
325 padding: 0;
326 margin: 0;
327 display: block;
336 box-shadow: none;
337 margin: 0;
338 padding: 0;
- background: var(--color-white);
339 + background: var(--white);
340 display: flex !important;
341 align-items: center !important;
342 justify-content: center !important;
353 }
354
355 .grid-cell {
- border-right: 1px dashed var(--color-grid-line) !important;
- border-bottom: 1px dashed var(--color-grid-line) !important;
356 + border-right: 1px dashed var(--default-grid) !important;
357 + border-bottom: 1px dashed var(--default-grid) !important;
358 box-sizing: border-box !important;
359 width: 100% !important;
360 height: 100% !important;
363 }
364
365 .grid-cell:nth-child(-n+50) {
- border-top: 1px dashed var(--color-grid-line) !important;
366 + border-top: 1px dashed var(--default-grid) !important;
367 }
368
369 .grid-cell:nth-child(50n+1) {
- border-left: 1px dashed var(--color-grid-line) !important;
370 + border-left: 1px dashed var(--default-grid) !important;
371 }
372
373 .image-container {
374 outline: none !important;
375 position: absolute !important;
- background: var(--color-white) !important;
376 + background: var(--white) !important;
377 /* Recalculate positions using cell coordinates and print cell size */
378 left: calc(var(--x-cell) * var(--cell-size-mm)) !important;
379 top: calc(var(--y-cell) * var(--cell-size-mm)) !important;