dvFallbackCss top-level constant
The fallback's own stylesheet, and the rules that print it.
The crawler-visible block is real semantic HTML -- headings, links, code blocks -- and it shipped with none. Viewed with scripting off, or by anything that does not run the app, every line ran the full width of the window in the browser's default serif.
Every rule is scoped to .dv-fallback, so none of it reaches the running
application: a max-width on body would break every Dartvel app's own
layout, and nothing here sets one.
On the screen the block is a clipped box one pixel square: laid out, so
the browser's find searches it, and painting nothing, so the application
keeps the screen. It used to be display:none, which is the one kind of
hidden find skips, and is why Ctrl+F on a Dartvel page found nothing.
Each paragraph inside is hidden="until-found" besides (see
dvFindableHtml), which is what makes the browser say which one it
matched.
It is shown again in two places. A reader with scripting off gets it from
the <noscript> override, which is the one thing noscript is still needed
for. A printer gets it from the @media print rules, which also take away
what Flutter paints into: a canvas prints as one bitmap the width of the
window -- clipped, at screen resolution, with no text to select and no
page break anywhere sensible. Both turn the until-found paragraphs on as
well, and by [data-dv-anchor] rather than by the attribute's value: a
browser that does not know until-found reads it as plain hidden, and a
page that opted out of find keeps its paragraphs plainly hidden.
A reading column, a system font, and the reader's colour scheme. Nothing decorative: this is the page someone sees when the app cannot run, and it should look like a document rather than like a broken site.
Implementation
const String dvFallbackCss =
'.dv-fallback{max-width:44rem;margin:0 auto;padding:2rem 1.25rem;'
'font:16px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;'
'color:#0b1020;background:#fff}'
'.dv-fallback h1{font-size:1.9rem;line-height:1.2;margin:0 0 1rem}'
'.dv-fallback h2{font-size:1.35rem;line-height:1.25;margin:2rem 0 .75rem}'
'.dv-fallback h3{font-size:1.1rem;margin:1.5rem 0 .5rem}'
'.dv-fallback p{margin:0 0 1rem}'
'.dv-fallback a{color:#2f6bff}'
'.dv-fallback pre{overflow-x:auto;padding:1rem;border-radius:8px;'
'background:#0b1020;color:#c0caf5}'
'.dv-fallback code{font:13.5px/1.6 ui-monospace,Menlo,Consolas,monospace}'
'@media (prefers-color-scheme:dark){'
'.dv-fallback{color:#f2f5fa;background:#0a0d13}'
'.dv-fallback a{color:#7ba2ff}}'
// In the document, off the screen. The application is what the reader
// came for; this is what the crawler, the printer, the browser's find
// and a browser with no scripting get instead. Fixed, so a find that
// scrolls to it moves nothing the reader can see.
'@media screen{.dv-fallback{position:fixed;top:0;left:0;width:1px;'
'height:1px;max-width:none;margin:0;padding:0;border:0;overflow:hidden;'
'clip-path:inset(50%);white-space:normal;pointer-events:none}}'
// What the printer is given. Flutter's own host elements go first: they
// are what the page looks like and not what it says.
'@media print{'
'flutter-view,flt-glass-pane,flt-scene-host,flt-semantics-host,canvas'
'{display:none!important}'
'.dv-fallback{display:block!important;position:static!important;'
'width:auto!important;height:auto!important;overflow:visible!important;'
'clip-path:none!important;max-width:none;margin:0;padding:0;'
'color:#000;background:#fff;font-size:11pt}'
'.dv-fallback [data-dv-anchor]{display:block!important;'
'content-visibility:visible!important}'
'.dv-fallback a{color:#000;text-decoration:underline}'
// Nobody clicks a printed link, so it has to say where it went.
'.dv-fallback a[href]::after{content:" (" attr(href) ")";font-size:.85em}'
'.dv-fallback pre{background:#fff;color:#000;border:1px solid #999;'
'white-space:pre-wrap}'
'.dv-fallback h1,.dv-fallback h2,.dv-fallback h3{break-after:avoid}'
'@page{margin:18mm}'
'}';