commit 64f618ff748c68713b5a9f75e3d2beac725ee594
parent c43fb140f19986879a7ceff383e8d35ea556921c
Author: Hunter
Date: Mon, 27 Jul 2026 23:26:26 -0400
(attempt to) render onscreen elements at print size
Diffstat:
| M | script.js | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | style.css | | | 23 | ++++++++++------------- |
2 files changed, 65 insertions(+), 13 deletions(-)
diff --git a/script.js b/script.js
@@ -50,6 +50,8 @@ function isSafari() {
const isUsingSafari = isSafari();
+const isUsingWindows = navigator.userAgent.includes('Windows');
+
// Parse URL parameters for custom cell size
function parseCellSizeFromURL() {
const urlParams = new URLSearchParams(window.location.search);
@@ -82,6 +84,58 @@ function parseCellSizeFromURL() {
// Initialize cell size from URL
CELL_SIZE_MM = parseCellSizeFromURL();
+const PANEL_PPI = {
+ 1920: 141, // 15.6" 1080p laptop
+ 2560: 109, // 27" 1440p
+ 2736: 267, // 12.3" 3:2 tablet
+ 2880: 267, // 13" 3:2 tablet
+ 3840: 163 // 27" 4K
+};
+
+function estimateWindowsPPI() {
+ const pixelRatio = window.devicePixelRatio;
+ if (pixelRatio === 1) return null;
+
+ const nativeWidth = screen.width * pixelRatio;
+ if (nativeWidth >= 3800 && pixelRatio >= 2) return null;
+
+ const panel = Object.keys(PANEL_PPI).find(width => Math.abs(width - nativeWidth) <= width * 0.02);
+
+ return panel ? PANEL_PPI[panel] / pixelRatio : null;
+}
+
+function estimateScreenPPI() {
+ const logicalWidth = screen.width;
+ const pixelRatio = window.devicePixelRatio;
+
+ if (isUsingWindows) {
+ const windowsPPI = estimateWindowsPPI();
+ if (windowsPPI) return windowsPPI;
+ }
+
+ if (logicalWidth >= 3840) return 163; // 27" 4K, OS scaling off
+ if (logicalWidth >= 2048) return 109; // 27" 5K retina or 1440p
+ if (logicalWidth >= 1800) return 92.6; // 23.8" 1080p and friends
+
+ return pixelRatio > 1 ? 127.7 : 92.6; // retina laptop (2560px / 227ppi), else standard density
+}
+
+// Life-size page width; CSS clamps this to the viewport so it never overflows horizontally
+function applyPageWidth() {
+ const pageWidth = (LETTER_WIDTH_MM / 25.4) * estimateScreenPPI();
+ document.documentElement.style.setProperty('--page-width', `${pageWidth}px`);
+ updateGridLineWidth();
+}
+
+const baselinePixelRatio = window.devicePixelRatio;
+
+function updateGridLineWidth() {
+ const zoom = window.devicePixelRatio / baselinePixelRatio;
+ document.documentElement.style.setProperty('--line-width', `${Math.min(1, 1 / zoom)}px`);
+}
+
+applyPageWidth();
+
// Calculate grid dimensions based on cell size
const gridDimensions = calculateGridDimensions(CELL_SIZE_MM);
GRID_COLS = gridDimensions.cols;
@@ -1109,6 +1163,7 @@ document.addEventListener('wheel', (e) => {
// Update all image positions when window resizes (for responsive scaling)
window.addEventListener('resize', () => {
+ applyPageWidth(); // display or zoom may have changed
images.forEach(img => {
updateImagePosition(img);
});
diff --git a/style.css b/style.css
@@ -123,15 +123,13 @@ body {
display: flex;
justify-content: center;
align-items: flex-start;
+ align-items: safe center;
min-height: 100vh;
padding: 20px;
}
.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) */
+ width: min(100%, var(--page-width, 8.5in));
aspect-ratio: 8.5 / 11;
background: var(--page);
position: relative;
@@ -159,8 +157,8 @@ body {
width: 100%;
height: 100%;
box-sizing: border-box;
- border-top: 1px dashed var(--grid-line);
- border-left: 1px dashed var(--grid-line);
+ border-top: var(--line-width, 1px) dashed var(--grid-line);
+ border-left: var(--line-width, 1px) dashed var(--grid-line);
margin: 0;
padding: 0;
display: block;
@@ -168,14 +166,14 @@ body {
/* Add right border to rightmost column */
.grid-cell.right-edge {
- border-right: 1px dashed var(--grid-line);
- width: calc(100% + 1px);
+ border-right: var(--line-width, 1px) dashed var(--grid-line);
+ width: calc(100% + var(--line-width, 1px));
}
/* Add bottom border to bottom row */
.grid-cell.bottom-edge {
- border-bottom: 1px dashed var(--grid-line);
- height: calc(100% + 1px);
+ border-bottom: var(--line-width, 1px) dashed var(--grid-line);
+ height: calc(100% + var(--line-width, 1px));
}
/* Solid grid lines for cell sizes <= 3.56mm (screen preview only) */
@@ -202,7 +200,6 @@ body {
}
.page {
- max-width: 100vw;
box-shadow: none;
}
@@ -216,13 +213,13 @@ body {
.grid-cell.right-edge {
border-right-style: solid;
border-right-color: var(--grid-line-light);
- width: calc(100% + 1px);
+ width: calc(100% + var(--line-width, 1px));
}
.grid-cell.bottom-edge {
border-bottom-style: solid;
border-bottom-color: var(--grid-line-light);
- height: calc(100% + 1px);
+ height: calc(100% + var(--line-width, 1px));
}
}