| 1 | {{define "layout"}}<!doctype html> |
| 2 | {{/* layout — every page's document: the head, the estilo bar and <main>. |
| 3 | |
| 4 | The bar is estilo's (chrome.Bar, built once per request in |
| 5 | chrome.go), in place of the rastrillo topbar markup this file used to |
| 6 | write out itself: that bar was 45px signed out and 51px signed in, |
| 7 | because the Create and Import buttons were taller than it, so signing |
| 8 | in moved the whole page. estilo's is 52px at every width, signed in |
| 9 | or out, with the account always last in the same corner. |
| 10 | |
| 11 | data-theme is written into the first bytes of the response, from a |
| 12 | cookie read server-side (chrome.Appearance). AGENTS.md: light/dark |
| 13 | must resolve before first paint, because cell fills resolve against |
| 14 | the theme and a late preference repaints every coloured cell. It is |
| 15 | ABSENT for "system", and that is the mechanism, not an omission: |
| 16 | theme.css's whole toggle is :root[data-theme="light"] and |
| 17 | :root[data-theme="dark"], and with no attribute the browser resolves |
| 18 | against the OS. |
| 19 | |
| 20 | The <title> is built here and only here, "{part} · {part} · Sheets", |
| 21 | so no handler can drift from the suite's separator (spec §10). |
| 22 | |
| 23 | <meta charset> comes first, before anything that could be read as |
| 24 | text. The stylesheets are in the suite's fixed order: tokens, theme, |
| 25 | brand, then estilo's base, menu and chrome, then the app's own. */}} |
| 26 | <html lang="en" dir="ltr"{{with .Frame.Theme}} data-theme="{{.}}"{{end}}> |
| 27 | <head> |
| 28 | <meta charset="utf-8"> |
| 29 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 30 | <title>{{range .Frame.TitleParts}}{{.}} · {{end}}{{.Frame.Chrome.App.Name}}</title> |
| 31 | <link rel="icon" type="image/png" sizes="32x32" href="{{asset "static/brand/icon-32.png"}}"> |
| 32 | <link rel="icon" type="image/svg+xml" href="{{asset "static/brand/icon.svg"}}"> |
| 33 | <link rel="apple-touch-icon" sizes="180x180" href="{{asset "static/brand/apple-touch-icon.png"}}"> |
| 34 | <link rel="stylesheet" href="/static/estilo/fonts/v1/fonts.css"> |
| 35 | <link rel="stylesheet" href="{{asset "static/tokens.css"}}"> |
| 36 | <link rel="stylesheet" href="{{asset "static/estilo/busy.css"}}"> |
| 37 | <link rel="stylesheet" href="{{asset "static/theme.css"}}"> |
| 38 | <link rel="stylesheet" href="{{asset "static/brand/brand.css"}}"> |
| 39 | <link rel="stylesheet" href="{{asset "static/estilo/base.css"}}"> |
| 40 | <link rel="stylesheet" href="{{asset "static/estilo/menu.css"}}"> |
| 41 | <link rel="stylesheet" href="{{asset "static/estilo/chrome.css"}}"> |
| 42 | <link rel="stylesheet" href="{{asset "static/estilo/sky.css"}}"> |
| 43 | <link rel="stylesheet" href="{{asset "static/sheets.css"}}"> |
| 44 | {{/* First of every script: while a modal dialog is open, key and |
| 45 | clipboard events stop at the window before any page listener hears |
| 46 | them (static/modal.js). */}} |
| 47 | <script defer src="{{asset "static/modal.js"}}"></script> |
| 48 | <script defer src="{{asset "static/rastrillo.js"}}"></script> |
| 49 | <script defer src="{{asset "static/estilo/busy.js"}}"></script> |
| 50 | <script defer src="{{asset "static/estilo/chrome.js"}}"></script> |
| 51 | {{iconAssets}} |
| 52 | {{block "head" .}}{{end}} |
| 53 | </head> |
| 54 | <body> |
| 55 | {{/* [estilo-shell] wraps every page. On the grid it is |
| 56 | estilo-shell="fill": exactly the viewport, the bar at its 52px and |
| 57 | the grid's <main> taking the rest (estilo chrome.css). That is why |
| 58 | the grid can fill the screen without this app subtracting a bar |
| 59 | height it would have to guess. The "main" block lets the grid page |
| 60 | supply its own <main>, without rst-page's padding and max width. */}} |
| 61 | <div estilo-shell{{with .Frame.Shell}}="{{.}}"{{end}}> |
| 62 | <a rst-skip href="#main">{{.Frame.Chrome.Labels.Skip}}</a> |
| 63 | {{template "estilo-bar" .Frame.Chrome}} |
| 64 | {{block "main" .}}<main rst-page id="main">{{template "content" .}}</main>{{end}} |
| 65 | </div> |
| 66 | {{if .Frame.Shortcuts}}{{template "shortcuts-dialog" .Frame.Keys}}{{end}} |
| 67 | </body> |
| 68 | </html> |
| 69 | {{end}} |
| 70 | |
| 71 | {{/* The keyboard shortcuts, opened from the account menu's Help |
| 72 | ("Keyboard shortcuts…"). It used to be the Help menu on the grid's |
| 73 | menu bar, a definition list inside a dropdown; Help moved into the |
| 74 | account menu with every other app's (spec §8.2), and a list of keys |
| 75 | is not a menu. A native <dialog>, so it sits in the top layer above |
| 76 | the bar (spec §5.3), and it opens with the platform's invoker |
| 77 | command, so no script is needed. The labels are this request's |
| 78 | platform's (menu.Shortcut): ⌘Z on a Mac, Ctrl+Z elsewhere. */}} |
| 79 | {{define "shortcuts-dialog"}}<dialog id="sheets-shortcuts" data-shortcuts aria-labelledby="sheets-shortcuts-title"> |
| 80 | <form method="dialog"> |
| 81 | <h2 id="sheets-shortcuts-title">Keyboard shortcuts</h2> |
| 82 | <dl> |
| 83 | <dt><kbd>{{.undo}}</kbd></dt><dd>Undo your last edit</dd> |
| 84 | <dt><kbd>{{.bold}}</kbd></dt><dd>Bold</dd> |
| 85 | <dt><kbd>{{.italic}}</kbd></dt><dd>Italic</dd> |
| 86 | <dt><kbd>{{.cut}}</kbd></dt><dd>Cut</dd> |
| 87 | <dt><kbd>{{.copy}}</kbd></dt><dd>Copy</dd> |
| 88 | <dt><kbd>{{.paste}}</kbd></dt><dd>Paste</dd> |
| 89 | <dt><kbd>{{.filldown}}</kbd></dt><dd>Fill down</dd> |
| 90 | <dt><kbd>{{.delete}}</kbd></dt><dd>Clear the selected cells</dd> |
| 91 | <dt><kbd>{{.enter}}</kbd></dt><dd>Commit and move down</dd> |
| 92 | <dt><kbd>{{.tab}}</kbd></dt><dd>Commit and move right</dd> |
| 93 | <dt><kbd>{{.esc}}</kbd></dt><dd>Abandon the edit</dd> |
| 94 | </dl> |
| 95 | <div data-shortcuts-foot><button rst-btn="primary" type="submit" autofocus>Done</button></div> |
| 96 | </form> |
| 97 | </dialog>{{end}} |
| 98 | |
| 99 | {{define "contrast-notice"}} |
| 100 | {{if .}} |
| 101 | {{/* Spec §11.1: a retained fill can be unreadable, and that is |
| 102 | correct. We render an imported file as authored — silently |
| 103 | correcting it would lie about the document — so the obligation |
| 104 | that follows is to say so and leave the decision to the person. |
| 105 | This is that, and it is deliberately a notice rather than a |
| 106 | change to any cell. A warning (the shared chrome spec's §4): it |
| 107 | used to say rst-callout="warn", which is no rastrillo tone, so it |
| 108 | rendered as a neutral grey note. */}} |
| 109 | <div rst-callout rst-tone="warning"> |
| 110 | <span rst-callout-ic aria-hidden="true">{{icon "alert-triangle"}}</span> |
| 111 | <div rst-callout-body> |
| 112 | <p><strong>Imported colours below the contrast floor.</strong> |
| 113 | These cells came from a file pairing a fill and a text colour under |
| 114 | WCAG AA's 4.5:1: {{range $i, $ref := .}}{{if $i}}, {{end}}<code>{{$ref}}</code>{{end}}</p> |
| 115 | <p>They are shown exactly as the file wrote them. Changing a colour is |
| 116 | yours to decide, not ours to do quietly.</p> |
| 117 | </div> |
| 118 | </div> |
| 119 | {{end}} |
| 120 | {{end}} |
| 121 | |