fix ios toolbar tint not following theme changes; round pantry corners on mobile too

Hunter
Sunday, August 2nd, 2026 at 8:14 PM

resources/style.css+14-18
resources/theme.js+17-21

resources/style.css
    -/* Registered so theme swaps can be transitioned — plain custom properties don't animate.
    -   Everything downstream (borders, color-mix, scrollbars) follows for free. */
    -@property --desk {
    -	syntax: '<color>';
    -	inherits: true;
    -	initial-value: #7a8520;
    -}
    -
  1  @property --page {
  2  	syntax: '<color>';
  3  	inherits: true;

 92  	--marquee-fade: 75ms;
 93  	--page-radius: 0px;
 94  	transition:
    -		--desk var(--theme-transition),
 95  		--page var(--theme-transition),
 96  		--grid-line var(--theme-transition),
 97  		--accent var(--theme-transition);
 98  }
 99  
    -@media (prefers-reduced-motion: reduce) {
    -	:root,
    -	.page.theming {
    -		transition: none;
    -	}
100 +:root.no-theme-transition,
101 +:root.no-theme-transition body {
102 +	transition: none;
103  }
104  
105  /* Theme definitions */

168  }
169  
170  body {
    -	background: var(--desk);
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 {

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);

264  
265  	.page {
266  		box-shadow: none;
    -		border-radius: 0;
267  	}
268  
269  	.grid-cell {

509  		overscroll-behavior: none;
510  	}
511  
    -	/* Only fix position in portrait mode to prevent scroll issues in landscape */
512  	@media (orientation: portrait) {
513  		html, body {
514  			height: 100%;
    -			position: fixed;
515  			width: 100%;
516 +			overflow: hidden;
517  		}
518  	}
519  
resources/theme.js
 5  const DEFAULT_THEME = 'guac';
 6  const DEFAULT_DARK_THEME = 'pantry';
 7  const THEME_TRANSITION_MS = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--theme-transition'));
   -let pageTransitionTimer = null;
 8 +let themeTransitionTimer = null;
 9  
10  function syncThemeColorMeta() {
11  	const backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--desk').trim();

17  	syncThemeColorMeta();
18  }
19  
   -document.documentElement.addEventListener('transitionend', (e) => {
   -	if (e.propertyName === '--desk') {
   -		syncThemeColorMeta();
   -	}
   -});
   -
   -// Only do border-radius transition on page when changing themes
   -function withPageTransition() {
20 +function armPageRadiusTransition() {
21  	const page = document.querySelector('.page');
22  	if (!page) return;
23  
24  	page.classList.add('theming');
   -	clearTimeout(pageTransitionTimer);
   -	pageTransitionTimer = setTimeout(() => page.classList.remove('theming'), THEME_TRANSITION_MS + 50);
25 +	clearTimeout(themeTransitionTimer);
26 +	themeTransitionTimer = setTimeout(() => page.classList.remove('theming'), THEME_TRANSITION_MS + 50);
27  }
28  
29  function cycleTheme(step = 1) {
30  	currentThemeIndex = (currentThemeIndex + step + themes.length) % themes.length;
31  	const newTheme = themes[currentThemeIndex];
   -	withPageTransition();
32 +	armPageRadiusTransition();
33  	setTheme(newTheme);
34  	saveThemeToLocalStorage(newTheme);
35  }

40  
41  function loadThemeFromLocalStorage() {
42  	const savedTheme = localStorage.getItem('memori-theme');
   -	if (savedTheme && themes.includes(savedTheme)) {
   -		currentThemeIndex = themes.indexOf(savedTheme);
   -		setTheme(savedTheme);
   -	} else {
   -		const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
   -		const theme = prefersDark ? DEFAULT_DARK_THEME : DEFAULT_THEME;
   -		currentThemeIndex = themes.indexOf(theme);
   -		setTheme(theme);
   -	}
43 +	const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
44 +	const theme = (savedTheme && themes.includes(savedTheme))
45 +		? savedTheme
46 +		: (prefersDark ? DEFAULT_DARK_THEME : DEFAULT_THEME);
47 +
48 +	currentThemeIndex = themes.indexOf(theme);
49 +
50 +	// the document opens on the default theme, so the first swap needs to land unanimated
51 +	document.documentElement.classList.add('no-theme-transition');
52 +	setTheme(theme);
53 +	document.documentElement.offsetWidth; // flush the change before transitions come back
54 +	document.documentElement.classList.remove('no-theme-transition');
55  }
56  
57  // F2 cycles themes, shift+F2 cycles backwards