Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
881ac76
List and Table styling changes for print media
webstackdev Mar 29, 2026
e7f8494
Service page fixes for print media
webstackdev Mar 29, 2026
77d04fa
Add social shares to deep dive pages
webstackdev Mar 29, 2026
7d4a6d6
Add copyright notice with QRCode on print
webstackdev Mar 29, 2026
3c6c3fa
Add pagedjs dependency and types
webstackdev Mar 30, 2026
6b90d3b
Hide Footer component on print
webstackdev Mar 30, 2026
237c452
Hide Header component on print
webstackdev Mar 30, 2026
dcd8133
Add Print components - Cover and Header
webstackdev Mar 30, 2026
4d212f9
Rename pdf page to print
webstackdev Mar 30, 2026
9e83d00
Styling fix for user print
webstackdev Mar 30, 2026
35bec1b
PDF generation workflow
webstackdev Mar 31, 2026
d880ea4
Add /print to sitemap exclusion, remove /pdf
webstackdev Mar 31, 2026
ec48e1c
Remove example PDFs
webstackdev Mar 31, 2026
0753c6d
Add color styles for PDF generation
webstackdev Mar 31, 2026
3d0d0e9
Move PDF output directory
webstackdev Mar 31, 2026
ed61da2
Add env var
webstackdev Mar 31, 2026
d52723f
Fix lint errors in print page
webstackdev Mar 31, 2026
42d6427
Use light theme colors in pdf generation
webstackdev Mar 31, 2026
de896ed
Update site map serialize test
webstackdev Mar 31, 2026
3270f31
Remove now-unused pdf.css file from styles
webstackdev Mar 31, 2026
d0a1a1f
Adjust PDF generator script to wait for font loading, remove now-unus…
webstackdev Mar 31, 2026
4ccc45a
Update header and footer styles for pdf generation
webstackdev Mar 31, 2026
3785976
Finish header style for pdf generation
webstackdev Apr 1, 2026
370eaf7
Finish footer style for pdf generation
webstackdev Apr 1, 2026
34138f1
Fix page margins for pdf generation
webstackdev Apr 1, 2026
015e335
Fix ToC margins for pdf generation
webstackdev Apr 1, 2026
ed5ffdb
Update cover generation for PDFs
webstackdev Apr 1, 2026
29c6dce
Fix lint error
webstackdev Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
"outofsync",
"PABC",
"pacticipant",
"PAGEDJS",
"PERC",
"permissioned",
"pgbouncer",
Expand Down Expand Up @@ -396,6 +397,7 @@
"ungap",
"unhold",
"uninstrumented",
"unlayered",
"unsampled",
"unscanned",
"unsexy",
Expand All @@ -421,6 +423,7 @@
"vmax",
"VNIC",
"vtbot",
"WAAPI",
"WHATWG",
"Wolters",
"wordprocessingml",
Expand Down
22 changes: 22 additions & 0 deletions @types/pagedjs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
declare module 'pagedjs' {
export interface PagedJsList {
appendList?: (_rules: unknown) => void
remove?: (_entry: unknown) => void
}

export interface PagedJsHandlerInstance {
getMediaName?(_node: unknown): unknown
onAtMedia?(_node: unknown, _item: unknown, _list: PagedJsList): void
}

export interface PagedJsHandlerConstructor {
new (..._args: never[]): PagedJsHandlerInstance
name: string
}

export class Previewer {
preview(): Promise<unknown>
}

export const registeredHandlers: PagedJsHandlerConstructor[]
}
102 changes: 0 additions & 102 deletions _TODO.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,6 @@
<!-- markdownlint-disable-file -->
# TODO

## Print

1. Add a QR code at the bottom of printed pages so it's easier for someone to navigate to from a printed page. We have a QrCode component.

2. Need to make sure that on print, when we have a tabbed code block with multiple languages, only the first language is printed and the other language tabs are hidden. The styling should be different for print for the code block. Maybe move other language code tabs to an appendix and add a link to them.

[This article](https://excessivelyadequate.com/posts/print.html) shows how to control the following properties in Chrome's Print Properties dialog box from CSS: Layout, Paper size, Margins, Headers and footers, and Background graphics. Headers and footers is the checkbox that by default is enabled and adds information on printed pages. It also shows how to use Chrome from the terminal in headless mode to output a PDF file from an HTML page.

3. For printed pages, your header should shift from a navigation tool to a document identifier. Since users cannot click links or icons on paper, these elements are "cruft" that waste space and ink.

2. Need a layout alternative to Markup that formats for print. It should hide Table Of Contents. Need a fixed header format that adds article title, subtitle, and date.

__Recommended Print Header Format__

A professional print header typically includes only these three elements:

- Brand Identity: A high-contrast version of your logo or the site name in plain text for brand recognition.
- Document Title: The main title of the page (usually the <h1>), ensuring the reader knows exactly what the document is.

__Expand External Links For Print__

We can't (yet) directly interface with a printed page to explore links, so link URLs should be visible on the printed version of the Web page. To keep the page relatively clean, I prefer to expand only outbound links in articles, and suppress internal ones. If you've used relative URLs on your website for local links, you can easily do this through an attribute selector and `:after` pseudo classes, thus preventing internal links and links around images from being printed:

- Break Lists across pages, the separators between columns are broken too
- Break code blocks across pages
- Callouts are breaking across pages, they shouldn't

## PDF File Generation

- Need a workflow to generate PDF files from Markdown for downloads.

- Need a fixed cover page format that adds article title, subtitle, and date.

- Table of Contents (Workarounds)

Because the browser doesn't know which page an element (like an <h1>) will land on until the PDF is fully rendered, you cannot generate a TOC with correct page numbers in a single pass. Common workarounds include:

Paged.js Polyfill: Use the Paged.js library to handle sophisticated print layouts (like TOCs and cross-references) within the browser before Puppeteer "prints" the result.

[Paged.js](https://pagedjs.org/en/documentation/) polyfills `@page` properties, and lays out an HTML document in print format where it can have page numbers generated to update in a table of contents.

This article has different approaches to [print pagination](https://www.customjs.space/blog/html-print-pagination-footer/). One approach overlaps with PagedJS's approach.

- Headers and Footers (Native Support)

Puppeteer can inject dynamic data into your headers and footers using specific CSS classes. To use this, you must set `displayHeaderFooter: true` in the `page.pdf()` options.

Dynamic Classes: Puppeteer automatically replaces these classes with actual values:
`.pageNumber`: Current page number.
`.totalPages`: Total number of pages.
`.title`: The document's `<title>` tag.
`.date`: The date the PDF was generated.

Requirements: You must provide sufficient margins (e.g.,` margin: { top: '50px', bottom: '50px' }`), or the headers/footers will be hidden behind the content.

Styling: You must use inline CSS within your `headerTemplate` or `footerTemplate` strings, as they cannot access your external stylesheet.


Maybe something like this to check if we're on the playwright PDF generation run and set the light theme:

```typescript
window.addEventListener('beforeprint', (event) => {
console.log('Before print dialog opens, run this script.')
// Example: change content or hide elements
document.getElementById('hide-on-print').style.display = 'none'
})

window.addEventListener('afterprint', (event) => {
console.log('After print dialog closes, run this script to revert changes.')
// Example: revert changes
document.getElementById('hide-on-print').style.display = ''
// You can also use this event to send an AJAX request to a server for print tracking.
})
```

Or listen for changes:

```typescript
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener(function(mql) {
if (mql.matches) {
// Equivalent to onbeforeprint
console.log('Entering print mode (before print dialog)');
} else {
// Equivalent to onafterprint
console.log('Exiting print mode (after print dialog)');
}
});
}
```

## ToolTips

Need a tooltip component for consistency.
Expand All @@ -107,8 +15,6 @@ These have tooltips, how are they being generated?
- "Report a Bug" in footer
- RSS feed icon in footer

## Abbreviation Styling

- Improve `<abbr>` styling: https://codepen.io/ire/pen/NoqWpm

## HubSpot Signup Issues / Gated Content (Downloads)
Expand All @@ -117,14 +23,6 @@ These have tooltips, how are they being generated?

- We need a gating system, where the user gets a token to be able to download a PDF and the token is checked before downloading. If a reader has already given their email address - newsletter signup, contact form, download registration, then the download CTA on short form articles should show the PDF download button instead of trying to collect email addresses again.

- We had this copyright statement for articles, it should be in the front matter of PDFs:

```text
Copyright © 2025 Webstack Builders, Inc.
The text, diagrams, and images in this work are licensed under CC BY-NC 4.0
All code samples in this article are licensed under the MIT License. Feel free to use, modify, and distribute them in any project.
```

## Youtube video for Backstage IDP hero on Home page

Discuss agentic AI integrations to add to Backstage
Expand Down
9 changes: 5 additions & 4 deletions astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const viteLogger = createLogger(undefined, { allowClearScreen: false })
const sentryAuthToken = process.env['SENTRY_AUTH_TOKEN']
const shouldEnableSentryIntegration = Boolean(sentryAuthToken)
const sentryReleaseName = getPackageRelease()
const astroEnvConfig = environmentalVariablesConfig as NonNullable<AstroUserConfig['env']>
const astroMarkdownConfig = markdownConfig as unknown as NonNullable<AstroUserConfig['markdown']>

const shouldSuppressViteWarning = (message: string): boolean => {
return (
Expand Down Expand Up @@ -103,7 +105,7 @@ const standardIntegrations = [
})] : []),
sitemap({
serialize: createSerializeFunction({
exclude: ['deep-dive', 'downloads', '/articles/demo', 'testing', 'hero', 'links'],
exclude: ['deep-dive', 'downloads', 'print', '/articles/demo', 'testing', 'hero', 'links'],
}),
}),
/**
Expand All @@ -123,8 +125,8 @@ export default defineConfig({
devToolbar: {
enabled: false,
},
env: environmentalVariablesConfig,
markdown: markdownConfig as AstroUserConfig['markdown'],
env: astroEnvConfig,
markdown: astroMarkdownConfig,
/**
* Astro sets substantial Vite config internally in the framework. When you use Vitest
* in an Astro project, you use Astro's getViteConfig helper to get the resolved internal
Expand Down Expand Up @@ -204,7 +206,6 @@ export default defineConfig({
*/
'process.env.CSS_TRANSFORMER_WASM': 'false',
},
/* @ts-expect-error - tailwindcss plugin type compatibility */
plugins: [
fixContentAssetPropagation(),
tailwindcss(),
Expand Down
5 changes: 4 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import securityPlugin from 'eslint-plugin-security'
import ymlPlugin from 'eslint-plugin-yml'
import tsPlugin from 'typescript-eslint'
import restrictedGlobals from 'confusing-browser-globals'
import globals from 'globals'
import enforceCentralizedEventsRule from './test/eslint/enforce-centralized-events-rule'
import noHtmlElementAssertionsRule from './test/eslint/no-html-element-assertions-rule'
import noQuerySelectorOutsideSelectorsRule from './test/eslint/no-query-selector-outside-selectors-rule'
Expand Down Expand Up @@ -50,7 +51,9 @@ export default [
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
NodeJS: 'readonly',
...globals.node, // Covers 'process', 'Buffer', etc.
NodeJS: 'readonly', // Covers TS namespaces
console: 'readonly', // Specific fix for 'console'
},
},
rules: {
Expand Down
Loading
Loading