mirror of
https://github.com/requarks/wiki.git
synced 2026-10-12 22:01:58 -05:00
feat: proper print view styling
This commit is contained in:
11 files changed
+659
-23
No files matched your search
@@ -1597,6 +1597,7 @@
|
||||
"common.actions.optimize": "Optimize",
|
||||
"common.actions.page": "Page",
|
||||
"common.actions.preview": "Preview",
|
||||
"common.actions.print": "Print",
|
||||
"common.actions.proceed": "Proceed",
|
||||
"common.actions.properties": "Properties",
|
||||
"common.actions.refresh": "Refresh",
|
||||
@@ -2282,7 +2283,6 @@
|
||||
"history.downloadFailed": "Failed to download this version.",
|
||||
"history.downloadVersion": "Download Version",
|
||||
"history.emptyPage": "Nothing",
|
||||
"history.exportPdf": "Export to PDF",
|
||||
"history.inline": "Inline",
|
||||
"history.loadFailed": "Failed to load the page history.",
|
||||
"history.none": "No history has been recorded for this page yet.",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
key: puppeteer
|
||||
title: Puppeteer
|
||||
description: >-
|
||||
Headless Chromium browser. Required to export pages as PDF and to render content elements on the
|
||||
server, such as Mermaid or PlantUML diagrams. Installing it downloads a Chromium build of a few
|
||||
hundred megabytes, unless the server already provides one through PUPPETEER_EXECUTABLE_PATH.
|
||||
Headless Chromium browser. Required to re-render a page from its source on the server: the markdown
|
||||
renderer lives in the browser, so the server drives one. Installing it downloads a Chromium build of
|
||||
a few hundred megabytes, unless the server already provides one through PUPPETEER_EXECUTABLE_PATH.
|
||||
website: 'https://pptr.dev'
|
||||
detect:
|
||||
type: module
|
||||
|
||||
@@ -165,6 +165,43 @@ Content of the second tab.
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
ON PAPER
|
||||
--------
|
||||
|
||||
The strip goes. A tabset shows one panel at a time because a screen has one place to put it,
|
||||
and on paper every panel prints, one after another, each under a bar carrying its own label --
|
||||
so a row of buttons naming panels that are all right there is a control with nothing left to
|
||||
control.
|
||||
|
||||
Only this half is here. Showing the panels and drawing those bars belongs to the app's print
|
||||
stylesheet, because the panels are slotted light DOM and which one is showing is an inline
|
||||
display this block writes onto them: nothing in here reaches either. See the block-tab rules in
|
||||
the print section of the app's css/_page-contents.scss.
|
||||
|
||||
The frame stays, and it is the reason for the one thing that does change here: with the strip
|
||||
gone it is all that says these sections were one set, so it keeps its border and its radius and
|
||||
gives up only the shadow, which prints as a grey smudge along two edges and lifts nothing.
|
||||
|
||||
What the panel gives up is its padding, down from 16px/20px to 8px all round. On screen that
|
||||
inset is what holds a panel's content off the frame around it; on paper it is 40px of the
|
||||
measure spent on white space that the label bar and the spine inside it already mark out --
|
||||
and it is spent twice, since each tab pads itself off its own spine as well. Eight is what
|
||||
keeps the content from touching the frame, and no more than that.
|
||||
*/
|
||||
@media print {
|
||||
.strip {
|
||||
display: none;
|
||||
}
|
||||
.tabs,
|
||||
:host([dark]) .tabs {
|
||||
box-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
:host {
|
||||
--tabs-border: #e0e0e0;
|
||||
--tabs-strip-bg: linear-gradient(to bottom, #fdfdfd, #eeeeee);
|
||||
@@ -179,6 +216,19 @@ Content of the second tab.
|
||||
--tabs-active-label: var(--tabs-active-fg);
|
||||
--tabs-panel-bg: #fff;
|
||||
}
|
||||
/*
|
||||
The dark palette, and only where there is a lit screen to read it on.
|
||||
|
||||
The dark attribute comes off the body class, which says nothing about the medium -- so without
|
||||
this a page printed from the dark theme printed a near-black panel, and the app prints the text
|
||||
inside it in black ink (the article's palette is light on paper, by the same reasoning as here:
|
||||
see the note on the dark block in css/_page-contents.scss). Black on #1e232a is a panel of
|
||||
content that cannot be read at all.
|
||||
|
||||
Stated as a media query rather than unpicked token by token in the print block above, so that a
|
||||
token added here later cannot quietly miss it.
|
||||
*/
|
||||
@media not print {
|
||||
:host([dark]) {
|
||||
--tabs-border: rgba(255, 255, 255, 0.15);
|
||||
--tabs-strip-bg: linear-gradient(to bottom, #1b212a, #12161d);
|
||||
@@ -187,6 +237,7 @@ Content of the second tab.
|
||||
--tabs-active-label: var(--color-primary-light);
|
||||
--tabs-panel-bg: #1e232a;
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
@@ -126,9 +126,9 @@
|
||||
dense
|
||||
icon="la:print"
|
||||
color="grey"
|
||||
aria-label="Print"
|
||||
:aria-label="t(`common.actions.print`)"
|
||||
@click="printPage">
|
||||
<w-tooltip>Print</w-tooltip>
|
||||
<w-tooltip>{{ t('common.actions.print') }}</w-tooltip>
|
||||
</w-btn>
|
||||
<!--
|
||||
Only for whoever reviews this page: the server answers `canReview` from the approval rules
|
||||
|
||||
@@ -21,23 +21,25 @@
|
||||
<!-- VERSION ACTIONS -->
|
||||
<!--
|
||||
What this header has in place of Watch / Print / Edit: the three things there are to do with a
|
||||
snapshot. Two are icons -- taking a copy of it, in either of two forms -- and the third is
|
||||
snapshot, and Print, which is the one button from that row meaning the same thing here. Two of
|
||||
the three are icons -- taking a copy of the version, in either of two forms -- and the third is
|
||||
labelled, because it is the one that WRITES, and a button that overwrites the live page should
|
||||
not be a glyph somebody presses to find out what it does. Its ellipsis is doing the same work:
|
||||
restoring asks first.
|
||||
|
||||
Download, Restore and Branch off each do the same as their entry in the history overlay's version
|
||||
menu, through the same code. Export to PDF is the one still disabled, because it is not wired up
|
||||
yet -- disabled rather than inert on purpose: a button that silently does nothing when pressed
|
||||
reads as a bug, where a dimmed one reads as not-yet.
|
||||
menu, through the same code. Print is `PageHeader`'s own button -- the same icon, the same site
|
||||
setting behind it and the same one-line call -- because a snapshot goes on paper exactly as a page
|
||||
does, and `css/_print.scss` keeps this bar on the sheet so the printout says which version it was.
|
||||
|
||||
Restore is the only one that WRITES to the live page, which is why it keeps the orange this app
|
||||
gives an action that changes a page, and why it asks before doing it. Branch off creates a page
|
||||
instead of overwriting one, so it sits with the harmless ones.
|
||||
|
||||
They stay on a phone, where `PageHeader` drops its whole row: that row is icons for things
|
||||
reachable elsewhere -- Print is the browser's own menu -- while these three are the only actions
|
||||
this view offers at all, so hiding them would leave the screen with none.
|
||||
reachable elsewhere -- Print is the browser's own menu -- while these are the only actions this
|
||||
view offers at all, so hiding them would leave the screen with none. Print rides along with them
|
||||
rather than being kept for its own sake; it is one glyph in a row that has to be there anyway.
|
||||
|
||||
What they do instead is take a row of their own, which is what `w-full` at phone widths buys: the
|
||||
bar already wraps, but this block is `flex-none` and about 230px wide, so beside a 32px icon it
|
||||
@@ -65,14 +67,20 @@
|
||||
@click="emit(`download`)">
|
||||
<w-tooltip>{{ t('history.downloadVersion') }}</w-tooltip>
|
||||
</w-btn>
|
||||
<!--
|
||||
On the site's own Print Button setting, as the page header's is: an administrator who has turned
|
||||
it off has said the wiki does not offer one, and a version of a page is not the exception to
|
||||
that.
|
||||
-->
|
||||
<w-btn
|
||||
class="acrylic-btn ml-2"
|
||||
v-if="siteStore.theme.showPrintBtn"
|
||||
flat
|
||||
icon="la:file-pdf"
|
||||
icon="la:print"
|
||||
color="grey"
|
||||
disable
|
||||
:aria-label="t(`history.exportPdf`)">
|
||||
<w-tooltip>{{ t('history.exportPdf') }}</w-tooltip>
|
||||
:aria-label="t(`common.actions.print`)"
|
||||
@click="printPage">
|
||||
<w-tooltip>{{ t('common.actions.print') }}</w-tooltip>
|
||||
</w-btn>
|
||||
<!--
|
||||
Branch off before Restore: it reads as the gentler of the two, and Restore stays next to the
|
||||
@@ -80,7 +88,7 @@
|
||||
|
||||
Indigo, which is the colour this app gives history -- the Schedule tab's calendar, the version
|
||||
timeline's dots, the bar at the top of this very screen. It also tells this button apart from
|
||||
the disabled Export to PDF beside it, which grey did not.
|
||||
the grey pair beside it, which a third grey button did not.
|
||||
|
||||
Two shades, because one will not do: as a LABEL, `indigo` measures 6.3:1 on the light header
|
||||
and 2.5:1 on the dark one, while `indigo-4` is 5.0:1 dark and 3.2:1 light. So each theme takes
|
||||
@@ -152,6 +160,8 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useDark } from '@/composables/dark'
|
||||
import { useMinWidth } from '@/composables/screen'
|
||||
|
||||
import { useSiteStore } from '@/stores/site'
|
||||
|
||||
const emit = defineEmits(['download', 'restore', 'branch'])
|
||||
|
||||
defineProps({
|
||||
@@ -178,6 +188,10 @@ defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
// STORES
|
||||
|
||||
const siteStore = useSiteStore()
|
||||
|
||||
// COMPOSABLES
|
||||
|
||||
const dark = useDark()
|
||||
@@ -195,4 +209,18 @@ const { t } = useI18n()
|
||||
*/
|
||||
const isAtLeastSm = useMinWidth(600)
|
||||
const iconSize = computed(() => (isAtLeastSm.value ? '64px' : '32px'))
|
||||
|
||||
// METHODS
|
||||
|
||||
/**
|
||||
* Hand the snapshot to the browser's print dialog.
|
||||
*
|
||||
* Here rather than emitted to `PageVersion.vue`, unlike every other button in this header: those three
|
||||
* act on the VERSION and go through the API helpers the history overlay uses, so the parent owns them.
|
||||
* This one acts on the window and knows nothing about what is in it -- the same call `PageHeader` makes
|
||||
* for a live page, and what it prints is decided entirely by `css/_print.scss`.
|
||||
*/
|
||||
function printPage() {
|
||||
window.print()
|
||||
}
|
||||
</script>
|
||||
@@ -191,5 +191,47 @@ async function render() {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ON PAPER
|
||||
--------
|
||||
|
||||
The banner survives printing. It is site chrome rather than this page, and the same on every one of
|
||||
them -- but an administrator raises a banner because it has to be seen, and a printout that quietly
|
||||
drops "this documentation is superseded" is worse than one that spends the ink. What goes is the
|
||||
wash, as every other tint does on paper (see `css/_print.scss`); the rule under the notice and its
|
||||
hue are what carry it.
|
||||
|
||||
Here rather than with the rest of the print stylesheet because of the cascade: these styles ship in
|
||||
the page view's lazily-imported chunk, which the browser adds after `main`'s, so a rule over there
|
||||
would tie on specificity and lose on order.
|
||||
|
||||
The margins are the other half. They are measured off the article column's padding -- see the note
|
||||
on `--site-banner-pad` above -- and print zeroes that padding, so left alone they would pull the
|
||||
band a whole rem out past the text on three sides.
|
||||
*/
|
||||
@media print {
|
||||
margin: 0 0 1.5rem;
|
||||
|
||||
.site-banner-alert {
|
||||
background-color: transparent;
|
||||
|
||||
/*
|
||||
The warning triangle is a mask over a background COLOUR, and a print dialog leaves background
|
||||
graphics off by default -- so the one mark that says this notice is a warning rather than an
|
||||
aside was the one part of it that did not print. Same pair as a done task item in
|
||||
`css/_page-contents.scss`, for the same reason.
|
||||
*/
|
||||
&::before {
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
|
||||
/* -> The dark theme's hue is pitched to be seen against a dark page and prints as a pale smear */
|
||||
@at-root .body--dark & .site-banner-alert {
|
||||
--site-banner-hue: #c02636;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -27,9 +27,15 @@
|
||||
:style="{ '--w-drawer-width': `${width}px` }">
|
||||
<teleport to="body">
|
||||
<transition name="w-drawer-scrim">
|
||||
<!--
|
||||
Carries a class of its own, unlike the utility-styled elements around it, because
|
||||
`css/_print.scss` has to reach it: this is teleported out of the drawer it belongs to, so
|
||||
hiding `.w-drawer` for print does not take it along -- and a scrim left on the sheet is a
|
||||
black first page.
|
||||
-->
|
||||
<div
|
||||
v-if="isVisible && isOverlay"
|
||||
class="fixed inset-0 z-30 bg-black/40"
|
||||
class="w-drawer-scrim fixed inset-0 z-30 bg-black/40"
|
||||
@click="$emit('update:modelValue', false)" />
|
||||
</transition>
|
||||
</teleport>
|
||||
|
||||
@@ -219,7 +219,22 @@
|
||||
*/
|
||||
isolation: isolate;
|
||||
|
||||
/*
|
||||
The dark palette, and only where there is a lit screen to read it on.
|
||||
|
||||
`@media not print` rather than a print block that puts each of these back one at a time: this is
|
||||
two dozen tokens and it grows, and the print block below would have to shadow every one of them --
|
||||
at a higher specificity, since `.body--dark .page-contents` outranks this block -- or quietly print
|
||||
whichever it had missed. It missed most of them: a page printed from the dark theme came out with
|
||||
invisible table and code rules (white at 16% opacity, on white paper), the lighter link blue, and
|
||||
the GitHub-dark syntax colours.
|
||||
|
||||
So the condition is stated once, here, where it is also the true one: this palette is for a screen
|
||||
that emits light. Paper does not, and what it falls back to is the light palette declared above,
|
||||
which is the one the print block below was written against.
|
||||
*/
|
||||
@at-root .body--dark & {
|
||||
@media not print {
|
||||
--content-ink: rgba(255, 255, 255, 0.87);
|
||||
--content-ink-muted: rgba(255, 255, 255, 0.62);
|
||||
--content-ink-faint: rgba(255, 255, 255, 0.42);
|
||||
@@ -294,6 +309,7 @@
|
||||
--content-code-deletion: #ffa198;
|
||||
--content-code-deletion-wash: rgba(248, 81, 73, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
/* -> The page's own margins are the container's business; the first and last block give theirs up */
|
||||
> :first-child {
|
||||
@@ -1850,6 +1866,13 @@
|
||||
// PRINT
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Written against the light palette above and reached from either theme, because the dark one is
|
||||
screen-only -- see the note on it.
|
||||
|
||||
The chrome around the article is dealt with in `css/_print.scss`, which is the rest of this story:
|
||||
what is hidden, and what is left of the shell once it stops being a viewport.
|
||||
*/
|
||||
@media print {
|
||||
/* Washes and tints cost ink and print muddy; the structure has to come from the rules instead */
|
||||
--content-ink: #000;
|
||||
@@ -1869,6 +1892,18 @@
|
||||
/* -> And the banding, which on paper is grey ink over every other row for no gain */
|
||||
--content-table-row: transparent;
|
||||
--content-table-row-alt: transparent;
|
||||
/*
|
||||
The article column prints with no padding of its own -- the sheet's own margin is the margin
|
||||
now, see `_print.scss` -- so the rule under the page title has nothing left to reach back
|
||||
through. Left at 1rem it would overhang the text by exactly the padding that is gone.
|
||||
*/
|
||||
/*
|
||||
`0px` and not `0`: the title's rule positions itself with `calc(-1 * var(--content-bleed))`, and
|
||||
a unitless zero makes that a NUMBER rather than a length. The declaration is then invalid, `left`
|
||||
falls back to `auto` against a `right: 0`, and a rule with no width to shrink to disappears --
|
||||
which is exactly how the page title lost the rule under it on paper.
|
||||
*/
|
||||
--content-bleed: 0px;
|
||||
|
||||
font-size: 11pt;
|
||||
line-height: 1.55;
|
||||
@@ -1892,9 +1927,174 @@
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
/* -> On paper there is no "leaves the wiki": every link is equally unfollowable */
|
||||
/*
|
||||
The rule under a title and a section heading is a gradient, which is to say a BACKGROUND -- and a
|
||||
print dialog leaves background graphics off by default, so the one device that makes the sections
|
||||
of a long page findable was the one thing that did not survive being printed. Same pair as a done
|
||||
task item further up, for the same reason.
|
||||
*/
|
||||
h1::after,
|
||||
h2::after {
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
/* -> A single line of a paragraph stranded on either side of a break; three is the usual floor */
|
||||
p,
|
||||
li {
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
|
||||
/*
|
||||
Code wraps.
|
||||
|
||||
On screen a long line scrolls sideways inside its panel and nothing is lost. On paper the panel is
|
||||
as wide as the sheet and everything past that edge is simply gone -- silently, which is the worst
|
||||
way for a code sample to be wrong. So the rule the screen keeps a few hundred lines up ("code does
|
||||
not wrap: a wrapped line is a different line, and indentation stops meaning anything") is the
|
||||
wrong trade here and is inverted: a wrapped line is still readable, an absent one is not.
|
||||
|
||||
`overflow-wrap: break-word` breaks only where a single unbroken run is wider than the panel by
|
||||
itself -- a URL in a comment, a base64 literal, a long path -- so ordinary code still gives way at
|
||||
its spaces and its indentation survives.
|
||||
|
||||
The `width: max-content` the code carries has to go with it: it sizes the block to its longest
|
||||
line, which leaves `pre-wrap` nothing to wrap against.
|
||||
*/
|
||||
pre {
|
||||
overflow-x: visible;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
pre.codeblock > code {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
And the line-number gutter goes with it.
|
||||
|
||||
Neither the digits nor a highlighted line's wash is part of the text: they are an absolutely
|
||||
positioned layer of empty rows, each exactly `1lh` tall, that lands on its line by geometry alone.
|
||||
A wrapped line is taller than one row, so from the first wrap onwards every number sits against
|
||||
the wrong line -- and a code sample whose numbers lie is worse than one with no numbers at all.
|
||||
|
||||
The padding the gutter had reserved goes back to the panel's own, so a numbered block does not
|
||||
print with an empty 3.6rem margin down its left side.
|
||||
*/
|
||||
pre.codeblock .line-numbers-rows {
|
||||
display: none;
|
||||
}
|
||||
pre.codeblock.line-numbers {
|
||||
--code-pad-left: var(--code-pad-x);
|
||||
}
|
||||
|
||||
/*
|
||||
Every panel of a tabset, one after another.
|
||||
|
||||
A tabset shows one panel at a time because a screen has one place to put it. Paper has as many
|
||||
places as it needs, and a panel that does not print is content the reader is never told exists --
|
||||
so they all print, each under a bar carrying its own label. `block-tabs` hides its strip for the
|
||||
same reason, in its own stylesheet, where the strip lives.
|
||||
|
||||
Here rather than in that block because of where the panels live. `block-tab` has no shadow root at
|
||||
all -- its content is page content, styled by this file, which is the whole point of it -- and
|
||||
which panel is showing is an inline `display` its parent writes onto the element. So this is at
|
||||
once the only stylesheet that reaches them and the only weight that beats an inline style.
|
||||
|
||||
`[label]` guards the bar: the label is an attribute, and a tab written without one would otherwise
|
||||
print an empty rule.
|
||||
*/
|
||||
block-tab {
|
||||
display: block !important;
|
||||
}
|
||||
block-tab + block-tab {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
A solid bar and a spine down the side, and they are the same colour on purpose: at the top of a
|
||||
panel the two meet and read as one shape turning the corner, which is what marks out where a
|
||||
panel starts AND how far down the sheet it runs. That second half is the harder one -- a rule
|
||||
under a label says where a panel begins and nothing at all about where it ends, and on paper
|
||||
there is no strip left above to hold the set together.
|
||||
|
||||
This is the one place here that spends solid ink, and it is a deliberate departure from the note
|
||||
a few rules up about a table's head: there, the bold text and the ruled grid already say "head",
|
||||
so the near-black bar was ink buying nothing. Here the bar is the ONLY thing separating one
|
||||
panel's content from the next one's, and drawn as a hairline and small caps it was too quiet to
|
||||
do it.
|
||||
|
||||
Dark grey rather than the near-black the table's head uses on screen: it is enough for white to
|
||||
read on (about 9:1) and costs a third less ink over a bar that repeats once per panel.
|
||||
|
||||
The bar's negative left margin is what takes it back over the padding so that it meets the spine
|
||||
rather than starting inboard of it.
|
||||
|
||||
`print-color-adjust` is what makes any of it appear, for the reason a done task item carries the
|
||||
same pair: a print dialog leaves background graphics OFF by default, so the fill would print as
|
||||
nothing and the white label as white on white -- the panel losing its heading altogether, which
|
||||
is worse than the quiet version this replaces. The property is inherited, so declaring it here
|
||||
covers the bar as well as the spine.
|
||||
*/
|
||||
block-tab[label] {
|
||||
--content-tab-bar: #3a4049;
|
||||
/*
|
||||
Held as a property, and in `rem`, because the bar's negative margin has to cancel this padding
|
||||
EXACTLY. Written as `0.9em` in both places it did not: an `em` here resolves against the
|
||||
panel's font size and the same `em` on the bar against the bar's own smaller one, so the two
|
||||
came out 13.2px and 10.7px and left a 2px seam of white between the spine and the bar.
|
||||
*/
|
||||
--content-tab-pad: 0.75rem;
|
||||
|
||||
border-left: 4px solid var(--content-tab-bar);
|
||||
padding-left: var(--content-tab-pad);
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
block-tab[label]::before {
|
||||
content: attr(label);
|
||||
display: block;
|
||||
margin: 0 0 0.9em calc(-1 * var(--content-tab-pad));
|
||||
padding: 0.35em var(--content-tab-pad);
|
||||
background-color: var(--content-tab-bar);
|
||||
color: #fff;
|
||||
font-size: 0.8125em;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/*
|
||||
The marker becomes the address.
|
||||
|
||||
On screen an external link only has to SAY that it leaves the wiki -- where it goes is in the
|
||||
status bar, one hover away, and a click away after that. On paper there is neither: a link is
|
||||
worth exactly the address printed beside it, so the arrow gives way to the href itself.
|
||||
|
||||
Still the renderer's class rather than a selector on the href, for the reason the screen rule
|
||||
gives: whether an address is off-site is a question about its host, and that is settled at
|
||||
render time. A relative link spelled out in full would be a path nobody can type.
|
||||
|
||||
Everything the screen rule set up for a masked glyph has to be undone, since this is the same
|
||||
pseudo-element carrying text now rather than a shape -- the mask would otherwise cut the
|
||||
address down to a 0.8em square of nothing.
|
||||
|
||||
A link whose text is already its own URL prints it twice. That is the cost of the rule, and it
|
||||
is a good deal cheaper than a printed reference nobody can follow.
|
||||
*/
|
||||
a.is-external-link::after {
|
||||
content: none;
|
||||
content: ' (' attr(href) ')';
|
||||
display: inline;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-left: 0;
|
||||
background-color: transparent;
|
||||
mask-image: none;
|
||||
opacity: 1;
|
||||
font-size: 0.85em;
|
||||
/* -> A URL has no spaces to break at and would otherwise run off the edge of the sheet */
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
THE PRINTED PAGE
|
||||
================
|
||||
|
||||
What the page view and the version view become on paper. This file is the app SHELL's half of it --
|
||||
the chrome a page is drawn in. Two other places hold the rest, each because the rules there have to
|
||||
sit next to the ones they override: `_page-contents.scss` typesets the article, over the content
|
||||
palette declared at the top of that same file, and `SiteBanner.vue` handles the banner, for the
|
||||
cascade reason set out at the foot of this file.
|
||||
|
||||
Two problems, and they are different ones.
|
||||
|
||||
The first is that the shell IS a viewport. `WLayout` is `height: 100vh; overflow: hidden`, and the
|
||||
article scrolls inside `.page-container-scrl` rather than the window -- which is what keeps the
|
||||
header, the sidebar and the action rail still while a reader scrolls. On paper that means exactly one
|
||||
screenful is printed and the rest of the page is clipped away with nothing to say it happened. So
|
||||
every fixed height and every scroll container has to be unwound back into normal flow before
|
||||
anything else here is worth doing.
|
||||
|
||||
The second is that most of what is on screen is not the document. Someone printing a page wants the
|
||||
page: its title, where it sits, when it was last written to, and its contents. The navigation, the
|
||||
contents column, the action rail, the footer and the floating buttons are how they GOT here, not
|
||||
what they are taking away.
|
||||
|
||||
`!important` throughout, deliberately, and it is not laziness: what these rules overrule is inline
|
||||
styles (both views write `height: 100%` on the article column in the markup) and scoped rules from
|
||||
single-file components, which carry a data attribute and so outrank a plain class whatever the order.
|
||||
A print stylesheet that loses those fights prints a blank sheet and gives no clue why.
|
||||
|
||||
Colours are the exception -- there `!important` would be the wrong tool, because the rules being
|
||||
beaten are per-theme ones written as `.body--dark .page-header-title`. Naming both theme classes
|
||||
matches that specificity, and `app.scss` loads this file last so the tie goes to print.
|
||||
*/
|
||||
|
||||
@media print {
|
||||
/* ---------------------------------------------------------------------------
|
||||
THE SHEET
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
The margin the content gets.
|
||||
|
||||
Nothing here draws a running header or a page number: the browser prints its own -- title, URL,
|
||||
date, page count -- outside this margin, and they are the reader's to keep or turn off in the print
|
||||
dialog. CSS could only compete with them, and not well, since no engine this app targets fills a
|
||||
page margin box with the document's own URL.
|
||||
*/
|
||||
@page {
|
||||
margin: 16mm 14mm;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
THE SHELL STOPS BEING A VIEWPORT
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
html,
|
||||
body {
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
/* -> Every layout paints `body` in the dark theme; on paper the sheet is the ground */
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
/*
|
||||
The shell's grid becomes a stack.
|
||||
|
||||
Its three columns exist to seat the drawers beside the page, and both drawers are gone below -- but
|
||||
that is not enough on its own: a grid item is stretched to its track, and the track is sized off a
|
||||
viewport height that means nothing here.
|
||||
*/
|
||||
.w-layout--page,
|
||||
.w-layout--container {
|
||||
display: block !important;
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/*
|
||||
And every box between the shell and the article gives up whatever it was doing to make one screenful
|
||||
fit -- the flex column, the claimed height, the scroll container. What is left is blocks in normal
|
||||
flow, which is the only arrangement that can run over a page break.
|
||||
*/
|
||||
.w-page-container,
|
||||
.w-page,
|
||||
.page-container,
|
||||
.page-container-scrl,
|
||||
.page-container-body,
|
||||
.w-scroll-area {
|
||||
display: block !important;
|
||||
height: auto !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
flex: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
The article column's own padding goes: `@page` above is the margin now, and a second one inside it
|
||||
only narrows the measure. `--content-bleed` is the padding this one is reaching back through (see
|
||||
`_page-contents.scss`), so the print block there zeroes it to match -- left at 1rem the rule under
|
||||
the page title would overhang the text by exactly the padding that is no longer there.
|
||||
*/
|
||||
.page-container-body {
|
||||
/*
|
||||
Sides only, so the article keeps the full measure -- but not the top.
|
||||
|
||||
`.page-contents > :first-child` has its top margin zeroed, since on screen the column's own
|
||||
padding is the space above the article. With that padding gone the first block sat against the
|
||||
header's rule, and a page opening on an h1 -- which is most of them -- put 22pt of display type
|
||||
hard against a line drawn 2px thick. This is the margin that block would otherwise have brought
|
||||
with it.
|
||||
*/
|
||||
padding: 1.25rem 0 0 !important;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
WHAT DOES NOT GO ON PAPER
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
Navigation, the contents column, the action rail, the relation buttons under the article, the
|
||||
footer, and every button that floats over the page -- all of them ways to go somewhere else, which
|
||||
is the one thing a sheet of paper cannot do. The relations are the one that reads as content rather
|
||||
than chrome, and they are not: they are the next page and the previous one, drawn as buttons.
|
||||
|
||||
The last five are the transient overlays, and they are here because printing is not a moment anyone
|
||||
prepares for: a tooltip is on screen the instant the Print button is clicked, and Ctrl+P works just
|
||||
as well with a dialog open. Each would otherwise print over the first page.
|
||||
*/
|
||||
.w-header,
|
||||
.w-drawer,
|
||||
.w-drawer-scrim,
|
||||
.page-sidebar,
|
||||
.page-sidebar-scrim,
|
||||
.page-actions,
|
||||
.page-header-actions,
|
||||
.page-relations,
|
||||
.w-footer,
|
||||
.w-page-scroller,
|
||||
.corner-btn,
|
||||
.w-tooltip,
|
||||
.w-menu,
|
||||
.w-notifications,
|
||||
.w-loading,
|
||||
.w-dialog-backdrop,
|
||||
.w-dialog-viewport {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
WHAT DOES, FLATTENED
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
The title bar keeps the title, the description and the page's icon, and loses the gradient it sits
|
||||
in. Its 95px is a measurement for a bar in a window; here it is a heading, so it takes the height of
|
||||
what is in it and stays with the page it names.
|
||||
*/
|
||||
.page-header {
|
||||
height: auto;
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
/*
|
||||
And the words sit closer to the rule under them.
|
||||
|
||||
The 16px below the title column is pitched for a bar in a window, where the gradient it sits on is
|
||||
what ends the header and the space is how the title is given room inside it. On paper the rule ends
|
||||
the header, so the space under the words is doing nothing but pushing the article down the sheet.
|
||||
Halved rather than dropped: the description still needs to clear the line under it.
|
||||
|
||||
Reached through `:has()` because the column is a stack of layout utilities and the two things in it
|
||||
are the only stable names in the markup -- the same column, class for class, in both headers.
|
||||
*/
|
||||
.page-header > div:has(> .page-header-title) {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* -> Same: the trail says where this page lives, which is no use on the sheet before it */
|
||||
.page-breadcrumbs {
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
/*
|
||||
The home crumb becomes the site's own mark.
|
||||
|
||||
On screen that first crumb is a house because it is a BUTTON -- the way back to the root of the
|
||||
wiki. Nothing on a sheet of paper goes anywhere, so what is left is a house standing at the head of
|
||||
a path for no reason. The favicon in its place answers the question a printout actually raises,
|
||||
which is which wiki this page was printed from, and it does it in the width the house was already
|
||||
taking.
|
||||
|
||||
Matched on `data-icon` rather than on the crumb's position: `WIcon` writes the reference it drew
|
||||
onto every branch it renders, so this says "the home icon" instead of "whatever is first", and it
|
||||
stops matching by itself if that crumb ever stops being a house.
|
||||
|
||||
Drawn as a background on the link rather than swapped in the markup, because the swap is a
|
||||
statement about paper and nothing else -- `WBreadcrumbs` is a shared component and this is not a
|
||||
behaviour worth giving it a prop for. `print-color-adjust` because a background is exactly what a
|
||||
print dialog drops by default, and the whole point here is that the mark appears.
|
||||
|
||||
1.25em is the house's own size: `.w-breadcrumbs__el-icon` sets 125%, so the glyph draws at 1.25em
|
||||
of the bar's text. The crumb carries no label, so it needs no margin either.
|
||||
*/
|
||||
.page-breadcrumbs [data-icon='la:home'] {
|
||||
display: none;
|
||||
}
|
||||
.page-breadcrumbs .w-breadcrumbs__el:has([data-icon='la:home'])::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
background-image: url('/_site/current/favicon');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
/*
|
||||
The indigo frame the version view draws down both sides and along the bottom goes.
|
||||
|
||||
On screen it is a surround sized to the window and drawn once, and it is how that view says a
|
||||
snapshot is being read. On paper there is no window to frame: it becomes a 5px band of colour down
|
||||
the edge of every sheet, saying what the bar at the top of the first one has already said in words
|
||||
-- which is why that bar keeps a heavier rule here than the page view's.
|
||||
*/
|
||||
.page-version {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Ink on paper, whichever theme the screen was in.
|
||||
|
||||
Without this a page printed from the dark theme comes out with a white title on white paper -- the
|
||||
header and the bars paint their own foregrounds per theme, and hiding their backgrounds is what
|
||||
leaves the foreground stranded. Both theme classes are named because that is the specificity those
|
||||
rules were written at.
|
||||
*/
|
||||
.body--light,
|
||||
.body--dark {
|
||||
.page-breadcrumbs {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
background: none;
|
||||
border-bottom: 1px solid #ccc;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/*
|
||||
The version view's bar keeps a heavier rule than the page view's, which is the one piece of
|
||||
emphasis worth the ink here: on screen it is indigo against grey chrome and unmistakable, and
|
||||
printed flat it would say "this is a record of a page and not the page" in the same voice as a
|
||||
breadcrumb trail. A printout that reads as current when it is not is the failure this view has to
|
||||
avoid.
|
||||
*/
|
||||
.page-breadcrumbs--version {
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
|
||||
/*
|
||||
A rule closes the header, in place of the gradient and the hairline it wears on screen.
|
||||
|
||||
Without one the title, the description and the article's first paragraph were three blocks of
|
||||
text with nothing but space between them, and the space alone did not say which of them was the
|
||||
page's name -- the very thing the gradient does on screen.
|
||||
|
||||
A hairline, and the trail's own grey: the same line the trail draws, top and bottom of one
|
||||
masthead. It was 2px, on the reasoning that twice the weight would say which of the two closes
|
||||
the block -- but a rule the full width of the sheet is the widest mark on it, and weight there
|
||||
buys emphasis nobody asked for. The space around it does that work instead.
|
||||
|
||||
1px is also the only width that survives the trip. On paper it is an absolute 1/96in whatever the
|
||||
printer's resolution, but a print PREVIEW is rasterised at the display's scale factor, and on a
|
||||
fractionally-scaled screen 2px lands on 2.5 or 3 device pixels and reads as a bar rather than a
|
||||
rule. A hairline is the one weight both media agree on.
|
||||
*/
|
||||
.page-header {
|
||||
background: none;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.page-header-title {
|
||||
color: #000;
|
||||
}
|
||||
.page-header-subtitle {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The site banner is the third place with print rules of its own, and the reason is the cascade
|
||||
rather than tidiness: `SiteBanner.vue` ships its styles in the page view's own lazily-imported
|
||||
chunk, which the browser adds AFTER this stylesheet. A rule here would tie with it on specificity
|
||||
and lose on order, so the banner's print treatment sits in that component -- and what it does
|
||||
there, in one line, is keep the notice and drop the ink.
|
||||
*/
|
||||
}
|
||||
@@ -2,4 +2,5 @@
|
||||
@use 'animation';
|
||||
@use 'page-chrome';
|
||||
@use 'page-contents';
|
||||
|
||||
/* -> Last, so that where a print rule ties with a screen rule on specificity, print wins */
|
||||
@use 'print';
|
||||
@@ -129,7 +129,15 @@
|
||||
ref="pageContents"
|
||||
v-html="pageStore.render"
|
||||
@click="onContentClick" />
|
||||
<template v-if="pageStore.relations && pageStore.relations.length > 0">
|
||||
<!--
|
||||
A box of its own rather than a bare `<template>`, so that `css/_print.scss` has something
|
||||
to hide: these are links onward -- the next page, the previous one, a related topic --
|
||||
which is the one thing a sheet of paper cannot do anything with. The separator goes with
|
||||
them, since it exists only to divide them from the article above.
|
||||
-->
|
||||
<div
|
||||
class="page-relations"
|
||||
v-if="pageStore.relations && pageStore.relations.length > 0">
|
||||
<w-separator class="my-6" />
|
||||
<div class="flex flex-wrap">
|
||||
<div class="min-w-0 flex-1 text-left" v-if="relationsLeft.length > 0">
|
||||
@@ -185,7 +193,7 @@
|
||||
</w-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
Inside the scrolling column, and last: this is the bottom of the PAGE, so it is reached by
|
||||
|
||||
Reference in new issue
Block a user