diff --git a/public/css/resume.css b/public/css/resume.css index c7aee86..7132ee1 100644 --- a/public/css/resume.css +++ b/public/css/resume.css @@ -14,13 +14,22 @@ } @media screen { - body { + body.resume-body { background: #e0e0e0; display: flex; justify-content: center; padding: 20px; } - .resume-paper { + :root[data-view-type="web"] .resume-paper { + background: transparent; + width: 100%; + max-width: 100%; + margin: 0; + padding: 0; + box-shadow: none; + min-height: 0; + } + :root[data-view-type="paper"] .resume-paper { background: white; width: 8.5in; min-height: 11in; @@ -32,10 +41,6 @@ } @media print { - @page { - size: letter; - margin: 0; /* This removes the default browser margins where text is injected */ - } .resume-pdf-layout { background: white !important; margin: 0.5in; /* Re-establish a visual margin for the actual content */ @@ -48,51 +53,75 @@ margin: 0; padding: 0.5in; /* Re-establish margins inside the paper for the printer */ box-shadow: none; - } - .no-print { - display: none; + + .resume-paper { + width: 8.5in !important; + height: 11in !important; + padding: 0.5in !important; + margin: 0 !important; + box-shadow: none !important; + background: white !important; + color: black !important; + overflow: hidden; /* Ensure single-page fit */ } } -body { +:root[data-view-type="web"] body.resume-body { + font-family: inherit; + color: var(--text-main); + line-height: 1.6; +} +:root[data-view-type="paper"] body.resume-body { font-family: "Helvetica", "Arial", sans-serif; color: #333; line-height: 1.2; } -h1 { +:root[data-view-type="web"] h1.resume-h1 { + font-size: 2rem; + border-bottom: 2px solid var(--accent-primary); + text-align: left; + margin-bottom: 1rem; +} +:root[data-view-type="paper"] h1.resume-h1 { text-align: center; text-transform: uppercase; margin: 0; font-size: 24pt; } -.contact-bar { +.resume-contact-bar { text-align: center; font-size: 10pt; border-bottom: 2px solid #444; padding-bottom: 5px; margin-bottom: 15px; } -h2 { +:root[data-view-type="web"] h2.resume-h2 { + font-size: 1.4rem; + color: var(--accent-primary); + border-bottom: 1px solid var(--border-medium); + margin-top: 1.5rem; +} +:root[data-view-type="paper"] h2.resume-h2 { font-size: 14pt; border-bottom: 1px solid #888; text-transform: uppercase; margin: 15px 0 5px 0; } -.entry { +.resume-entry { margin-bottom: 10px; } -.entry-header { +.resume-entry-header { display: flex; justify-content: space-between; font-weight: bold; } -.sub-header { +.resume-sub-header { display: flex; justify-content: space-between; font-style: italic; font-size: 11pt; } -ul { +ul.resume { margin: 5px 0; padding-left: 20px; } diff --git a/public/css/styles.css b/public/css/styles.css index 5da230e..95ca2f0 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -935,3 +935,55 @@ left: 4px; transform: rotate(-45deg); } +@media print { + @page { + size: letter; + margin: 0; + } + + .no-print { + display: none; + } + + /* 1. Strip UI Elements */ + header#site-header, + footer, + aside.sidebar, + .no-print { + display: none !important; + } + /* 2. Reset Layout Constraints */ + .layout { + display: block !important; + margin: 0 !important; + padding: 0 !important; + max-width: none !important; + } + /* 3. Transform Main Content into "Paper" */ + main.container { + width: 8.5in !important; + height: 11in !important; + padding: 0.5in !important; + margin: 0 !important; + box-shadow: none !important; + background: white !important; + color: black !important; + overflow: hidden; /* Ensure single-page fit */ + } + /* 4. Kill global dot patterns and 100vh */ + body.pattern-dots, + html { + background: white !important; + min-height: 0 !important; + height: auto !important; + } + + .pattern-dots::after { + display: none !important; + } +} +:root[data-view-type="paper"] #site-header, +:root[data-view-type="paper"] footer, +:root[data-view-type="paper"] aside.sidebar { + display: none !important; +} diff --git a/public/js/printButton.js b/public/js/printButton.js new file mode 100644 index 0000000..07c1954 --- /dev/null +++ b/public/js/printButton.js @@ -0,0 +1,32 @@ +document.addEventListener("DOMContentLoaded", () => { + const htmlEl = document.documentElement; + + const originalView = htmlEl.getAttribute("data-view-type"); + const currentView = () => htmlEl.getAttribute("data-view-type"); + + const printPreviewButton = document.getElementById("print-preview-button"); + const printButton = document.getElementById("print-button"); + + if (printPreviewButton) { + printPreviewButton.addEventListener("click", () => { + const view = currentView() == "paper" ? originalView : "paper"; + console.log("Original view: ", originalView); + console.log("Setting view to ", view); + + // 1. Switch to paper view instantly without a reload + htmlEl.setAttribute("data-view-type", view); + }); + } + if (printButton) { + printButton.addEventListener("click", () => { + // 1. Switch to paper view instantly without a reload + htmlEl.setAttribute("data-view-type", "paper"); + + // 2. Trigger the system print dialog + window.print(); + + // 3. Revert to original view once the dialog closes + htmlEl.setAttribute("data-view-type", originalView); + }); + } +}); diff --git a/src/css/print.css b/src/css/print.css new file mode 100644 index 0000000..f2e5a39 --- /dev/null +++ b/src/css/print.css @@ -0,0 +1,53 @@ +@media print { + @page { + size: letter; + margin: 0; + } + + .no-print { + display: none; + } + + /* 1. Strip UI Elements */ + header#site-header, + footer, + aside.sidebar, + .no-print { + display: none !important; + } + /* 2. Reset Layout Constraints */ + .layout { + display: block !important; + margin: 0 !important; + padding: 0 !important; + max-width: none !important; + } + /* 3. Transform Main Content into "Paper" */ + main.container { + width: 8.5in !important; + height: 11in !important; + padding: 0.5in !important; + margin: 0 !important; + box-shadow: none !important; + background: white !important; + color: black !important; + overflow: hidden; /* Ensure single-page fit */ + } + /* 4. Kill global dot patterns and 100vh */ + body.pattern-dots, + html { + background: white !important; + min-height: 0 !important; + height: auto !important; + } + + .pattern-dots::after { + display: none !important; + } +} + +:root[data-view-type="paper"] #site-header, +:root[data-view-type="paper"] footer, +:root[data-view-type="paper"] aside.sidebar { + display: none !important; +} diff --git a/src/css/styles.css b/src/css/styles.css index 899c828..c8ceb09 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -9,3 +9,4 @@ @import url("toc.css"); @import url("responsive.css"); @import url("favicon.css"); +@import url("print.css"); diff --git a/src/middleware/baseContext.js b/src/middleware/baseContext.js index 904e625..f06ae7d 100644 --- a/src/middleware/baseContext.js +++ b/src/middleware/baseContext.js @@ -38,11 +38,15 @@ }; } -const DEFAULT_CONTEXT = { - showSidebar: true, - showFooter: true, - showHeader: true, - css: cssOverride(), +const getDefaultContext = (view = "web") => { + const isPaper = view == "paper"; + return { + showSidebar: !isPaper, + showFooter: !isPaper, + showHeader: !isPaper, + viewType: view, + css: cssOverride(), + }; }; module.exports.attachBaseContextGetter = async (req, res, next) => { @@ -66,7 +70,7 @@ formatMonth, baseUrl, isAuthenticated, - ...DEFAULT_CONTEXT, + ...getDefaultContext(req.query.view ?? "web"), ...overrides, }; diff --git a/src/routes/resume.js b/src/routes/resume.js index aaa45b0..45a335a 100644 --- a/src/routes/resume.js +++ b/src/routes/resume.js @@ -16,14 +16,27 @@ const fileContent = await fs.readFile(dataPath, "utf8"); const resumeData = JSON.parse(fileContent); + const isPaper = req.query.view === "paper"; + + const cssOverrrides = { + css: res.cssOverride({ + classes: { + body: "resume-pdf-layout", + layout: "resume-container", + container: "resume-paper", + }, + }), + }; + // Render using your existing Handlebars engine logic // This follows the pattern in src/routes/post.js [cite: 25] res.renderWithBaseContext("pages/resume", { ...resumeData, title: `Resume - ${resumeData.name}`, - showSidebar: false, - showFooter: false, - showHeader: false, + showSidebar: !isPaper, + showFooter: !isPaper, + showHeader: !isPaper, + ...((isPaper && cssOverrrides) || {}), // css: res.cssOverride({ // classes: { // body: "resume-pdf-layout", diff --git a/src/views/layouts/main.handlebars b/src/views/layouts/main.handlebars index 28d2446..fe2ac2a 100644 --- a/src/views/layouts/main.handlebars +++ b/src/views/layouts/main.handlebars @@ -1,5 +1,5 @@ - +
{{> site_headers }} diff --git a/src/views/pages/resume.handlebars b/src/views/pages/resume.handlebars index bc05b28..e9ffe43 100644 --- a/src/views/pages/resume.handlebars +++ b/src/views/pages/resume.handlebars @@ -1,25 +1,35 @@ -{{overrideCSS target="classes" prop="body" value="resume-pdf-layout"}} -{{overrideCSS target="classes" prop="layout" value="resume-container"}} -{{overrideCSS target="classes" prop="container" value="resume-paper"}} +{{!-- overrideCSS target="classes" prop="body" value="resume-pdf-layout" --}} +{{!-- overrideCSS target="classes" prop="layout" value="resume-container" --}} +{{!-- overrideCSS target="classes" prop="container" value="resume-paper" --}} {{#section "styles"}} {{/section}} -