(attempt to) render onscreen elements at print size

Hunter
Monday, July 27th, 2026 at 11:26 PM

script.js+55 
style.css+10-13

script.js
  50  
  51  const isUsingSafari = isSafari();
  52  
  53 +const isUsingWindows = navigator.userAgent.includes('Windows');
  54 +
  55  // Parse URL parameters for custom cell size
  56  function parseCellSizeFromURL() {
  57  	const urlParams = new URLSearchParams(window.location.search);

  84  // Initialize cell size from URL
  85  CELL_SIZE_MM = parseCellSizeFromURL();
  86  
  87 +const PANEL_PPI = {
  88 +	1920: 141,   // 15.6" 1080p laptop
  89 +	2560: 109,   // 27" 1440p
  90 +	2736: 267,   // 12.3" 3:2 tablet
  91 +	2880: 267,   // 13" 3:2 tablet
  92 +	3840: 163    // 27" 4K
  93 +};
  94 +
  95 +function estimateWindowsPPI() {
  96 +	const pixelRatio = window.devicePixelRatio;
  97 +	if (pixelRatio === 1) return null;
  98 +
  99 +	const nativeWidth = screen.width * pixelRatio;
 100 +	if (nativeWidth >= 3800 && pixelRatio >= 2) return null;
 101 +
 102 +	const panel = Object.keys(PANEL_PPI).find(width => Math.abs(width - nativeWidth) <= width * 0.02);
 103 +
 104 +	return panel ? PANEL_PPI[panel] / pixelRatio : null;
 105 +}
 106 +
 107 +function estimateScreenPPI() {
 108 +	const logicalWidth = screen.width;
 109 +	const pixelRatio = window.devicePixelRatio;
 110 +
 111 +	if (isUsingWindows) {
 112 +		const windowsPPI = estimateWindowsPPI();
 113 +		if (windowsPPI) return windowsPPI;
 114 +	}
 115 +
 116 +	if (logicalWidth >= 3840) return 163;    // 27" 4K, OS scaling off
 117 +	if (logicalWidth >= 2048) return 109;    // 27" 5K retina or 1440p
 118 +	if (logicalWidth >= 1800) return 92.6;   // 23.8" 1080p and friends
 119 +
 120 +	return pixelRatio > 1 ? 127.7 : 92.6;    // retina laptop (2560px / 227ppi), else standard density
 121 +}
 122 +
 123 +// Life-size page width; CSS clamps this to the viewport so it never overflows horizontally
 124 +function applyPageWidth() {
 125 +	const pageWidth = (LETTER_WIDTH_MM / 25.4) * estimateScreenPPI();
 126 +	document.documentElement.style.setProperty('--page-width', `${pageWidth}px`);
 127 +	updateGridLineWidth();
 128 +}
 129 +
 130 +const baselinePixelRatio = window.devicePixelRatio;
 131 +
 132 +function updateGridLineWidth() {
 133 +	const zoom = window.devicePixelRatio / baselinePixelRatio;
 134 +	document.documentElement.style.setProperty('--line-width', `${Math.min(1, 1 / zoom)}px`);
 135 +}
 136 +
 137 +applyPageWidth();
 138 +
 139  // Calculate grid dimensions based on cell size
 140  const gridDimensions = calculateGridDimensions(CELL_SIZE_MM);
 141  GRID_COLS = gridDimensions.cols;

1163  
1164  // Update all image positions when window resizes (for responsive scaling)
1165  window.addEventListener('resize', () => {
1166 +	applyPageWidth(); // display or zoom may have changed
1167  	images.forEach(img => {
1168  		updateImagePosition(img);
1169  	});
style.css
123  	display: flex;
124  	justify-content: center;
125  	align-items: flex-start;
126 +	align-items: safe center;
127  	min-height: 100vh;
128  	padding: 20px;
129  }
130  
131  .page {
    -	/* Scale down on narrow screens, but never larger than 8.5in */
    -	max-width: min(8.5in, calc(100vw - 40px));
    -	width: 100%;
    -	/* Maintain 8.5:11 aspect ratio (11/8.5 = 1.294117647) */
132 +	width: min(100%, var(--page-width, 8.5in));
133  	aspect-ratio: 8.5 / 11;
134  	background: var(--page);
135  	position: relative;

157  	width: 100%;
158  	height: 100%;
159  	box-sizing: border-box;
    -	border-top: 1px dashed var(--grid-line);
    -	border-left: 1px dashed var(--grid-line);
160 +	border-top: var(--line-width, 1px) dashed var(--grid-line);
161 +	border-left: var(--line-width, 1px) dashed var(--grid-line);
162  	margin: 0;
163  	padding: 0;
164  	display: block;

166  
167  /* Add right border to rightmost column */
168  .grid-cell.right-edge {
    -	border-right: 1px dashed var(--grid-line);
    -	width: calc(100% + 1px);
169 +	border-right: var(--line-width, 1px) dashed var(--grid-line);
170 +	width: calc(100% + var(--line-width, 1px));
171  }
172  
173  /* Add bottom border to bottom row */
174  .grid-cell.bottom-edge {
    -	border-bottom: 1px dashed var(--grid-line);
    -	height: calc(100% + 1px);
175 +	border-bottom: var(--line-width, 1px) dashed var(--grid-line);
176 +	height: calc(100% + var(--line-width, 1px));
177  }
178  
179  /* Solid grid lines for cell sizes <= 3.56mm (screen preview only) */

200  	}
201  
202  	.page {
    -		max-width: 100vw;
203  		box-shadow: none;
204  	}
205  

213  	.grid-cell.right-edge {
214  		border-right-style: solid;
215  		border-right-color: var(--grid-line-light);
    -		width: calc(100% + 1px);
216 +		width: calc(100% + var(--line-width, 1px));
217  	}
218  
219  	.grid-cell.bottom-edge {
220  		border-bottom-style: solid;
221  		border-bottom-color: var(--grid-line-light);
    -		height: calc(100% + 1px);
222 +		height: calc(100% + var(--line-width, 1px));
223  	}
224  }
225