| 1 | {{define "layout"}}<!doctype html> |
| 2 | <html lang="{{block "lang" .}}en{{end}}" dir="{{block "dir" .}}ltr{{end}}"> |
| 3 | <head> |
| 4 | <meta charset="utf-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | <title>{{block "title" .}}Hello{{end}}</title> |
| 7 | <link rel="stylesheet" href="{{asset "static/tokens.css"}}"> |
| 8 | <link rel="stylesheet" href="{{asset "static/theme.css"}}"> |
| 9 | {{/* CONTRA's own grammar, last of the three so it wins: theme.css is |
| 10 | the palette, this is the ticket the palette is printed on. */}} |
| 11 | <link rel="stylesheet" href="{{asset "static/contra.css"}}"> |
| 12 | <script defer src="{{asset "static/rastrillo.js"}}"></script> |
| 13 | {{iconAssets}} |
| 14 | <script defer src="{{asset "static/select.js"}}"></script> |
| 15 | <script defer src="{{asset "static/datetime.js"}}"></script> |
| 16 | {{/* head is the app's own slot in <head>: a favicon, a meta tag, one |
| 17 | more stylesheet. Empty by default, last in the head so an app's |
| 18 | own CSS wins over the framework's, and the only block in the |
| 19 | four shells that is not chrome. */}} |
| 20 | {{block "head" .}}{{end}} |
| 21 | </head> |
| 22 | <body> |
| 23 | <!-- DIRECTION — CONTRA |
| 24 | THESIS: A cloakroom holds your bag, never learns what is in it, and |
| 25 | will not release it without the stub. Refuses this category's |
| 26 | navy-shield-and-rounded-cards arrangement. |
| 27 | OWN-WORLD: Eau-de-nil ticket stock, black ink, one vermilion spent |
| 28 | only on the numbering and on what cannot be undone; manila codes a |
| 29 | shared vault. Square-cut, no cards, condensed gothic caps, ruled |
| 30 | fields instead of boxes. |
| 31 | STORY: The visitor learns the server cannot help them, believes |
| 32 | that is the protection rather than a gap, and prints the kit. |
| 33 | FIRST VIEWPORT: Ticket stock; a condensed-gothic title in sentence |
| 34 | case over a 3px ink rule (capitals are for labels, not sentences); |
| 35 | ruled fields; and on the two screens with a real client/server |
| 36 | boundary — sign-in and the vault — one perforation with the sealed |
| 37 | half below it. The numbering lives where things are counted, which |
| 38 | is the vault and the device ladder; the public pages count nothing |
| 39 | and do not pretend to. |
| 40 | FORM: Left Luggage, 7th of 7 on the ordered list; seed bfd735a7. |
| 41 | FINISH: unreviewed and undocumented is unfinished; this build ends |
| 42 | with the finish review, the verdict, and DESIGN.md --> |
| 43 | {{/* The console shell — a brand-and-account bar across the top AND a |
| 44 | navigation rail down the side, which is the shape most admin |
| 45 | consoles are and the one the other three shells could not make. |
| 46 | topbar is this without the rail; sidebar is this without the bar. |
| 47 | |
| 48 | THE TWO CHROMES, AND WHY THERE IS STILL ONE CONTROL. |
| 49 | |
| 50 | Every other shell has one thing to fold below 800px. This one has |
| 51 | two — a bar tail and a rail — and the wrong answer is two |
| 52 | disclosures a reader has to learn, one for "the account" and one |
| 53 | for "the navigation", stacked on top of each other on a phone. |
| 54 | |
| 55 | So: ONE <details>, two reveals. The disclosure below gates its own |
| 56 | next sibling (the bar's tail) with the adjacent-sibling combinator |
| 57 | the topbar already uses, and gates the rail from the shell root |
| 58 | with :has(). One state, one control, one summary drawn at 390px. |
| 59 | |
| 60 | The reveals are written as HIDE-WHEN-CLOSED rather than |
| 61 | show-when-open, and that is deliberate: :has() is the newer |
| 62 | selector of the two, and in an engine that does not support it the |
| 63 | rule that hides the rail simply never matches, so the rail renders |
| 64 | as a plain block of links under the bar. The page gets longer; the |
| 65 | navigation stays reachable. The show-when-open spelling fails the |
| 66 | other way, and the other way is a phone with no way to navigate. |
| 67 | |
| 68 | NOTHING MOVES. The DOM order here — bar, then rail, then main — is |
| 69 | the order at 320px and the order at 1280px. Grid places the bar |
| 70 | across the top and the rail beside the page at width, and the |
| 71 | collapsed panel discloses the bar's tail above the rail's nav |
| 72 | because that is where they already are. So focus order matches |
| 73 | visual order at every width and in both directions of the |
| 74 | language, which is the thing an `order` or a `grid-row` shuffle |
| 75 | would have quietly cost. */}} |
| 76 | <div rst-shell-console> |
| 77 | <a rst-skip href="#main">{{T "rastrillo.ui.shell_skip"}}</a> |
| 78 | <header rst-shell-bar> |
| 79 | {{block "brand" .}}<a rst-shell-brand href="/">{{T "rastrillo.ui.shell_home"}}</a>{{end}} |
| 80 | {{/* name="rst-shell-menu", and NOT rst-menus — the same group the |
| 81 | topbar's disclosure is in, for the same reason and with one more |
| 82 | element depending on it here. <details name> exclusivity is |
| 83 | DOCUMENT-WIDE rather than sibling-scoped: a disclosure in the |
| 84 | menus' group would be closed by the account menu it just revealed, |
| 85 | and in this shell that closes the rail too, because the rail is |
| 86 | gated on the very same [open]. One wrong attribute takes both |
| 87 | chromes away at once. Read this as the reason for the gates rather |
| 88 | than as advice to change the line below. */}} |
| 89 | <details rst-shell-menu name="rst-shell-menu"><summary>{{icon "menu"}}{{T "rastrillo.ui.shell_menu"}}</summary></details> |
| 90 | {{/* The tail is a SIBLING of the disclosure, not its content: a closed |
| 91 | <details> hides its own content and no CSS reliably un-hides it, |
| 92 | so the wide layout could not be recovered from inside one. Above |
| 93 | 800px tokens.css gives it display: contents and the account and |
| 94 | locale menus become flex items of the bar itself. |
| 95 | |
| 96 | account here is the DROPDOWN'S MENU BODY, exactly as in topbar: |
| 97 | the layout owns the details/summary and an override supplies only |
| 98 | the links inside [rst-dropdown-menu]. sidebar's account block is |
| 99 | the other shape — a bare slot in the rail — so a screen moving |
| 100 | between console and sidebar edits that one block, while console |
| 101 | and topbar are interchangeable. */}} |
| 102 | <div rst-shell-tail> |
| 103 | <details rst-dropdown rst-shell-account name="rst-menus"> |
| 104 | <summary>{{T "rastrillo.ui.shell_account"}}<span rst-caret aria-hidden="true">{{icon "chevron-down"}}</span></summary> |
| 105 | <div rst-dropdown-menu>{{block "account" .}}{{end}}</div> |
| 106 | </details> |
| 107 | {{block "locale" .}}{{end}} |
| 108 | </div> |
| 109 | </header> |
| 110 | {{/* The rail carries navigation and nothing else — the language |
| 111 | switcher and the person are up on the bar, which is what makes |
| 112 | this shell a console rather than a sidebar with a strip above it. |
| 113 | So it has no rail-foot, and the bug that cost this project a |
| 114 | release — a person pinned to the bottom of a rail that hung 32px |
| 115 | under the window — has nothing here to pin. What it has instead is |
| 116 | a nav that sticks: see the max-block-size in tokens.css, which is |
| 117 | measured against the viewport and gated against the viewport. */}} |
| 118 | <aside rst-shell-rail> |
| 119 | {{/* The default navigation, defined once here rather than per page: a |
| 120 | page that forgets to define it would render an empty rail, which is |
| 121 | what the canary shipped with until somebody looked at it. Any page |
| 122 | can still override the block. */}} |
| 123 | <nav rst-shell-nav>{{block "nav" .}}<a href="/">Home</a> |
| 124 | {{if .SignedIn}}<a href="/vault">Your vault</a> |
| 125 | <a href="/signup">Add another account</a>{{else}}<a href="/signup">Create an account</a> |
| 126 | <a href="/signin">Sign in</a>{{end}}{{end}}</nav> |
| 127 | </aside> |
| 128 | <main rst-shell-main id="main"> |
| 129 | <div rst-page> |
| 130 | {{template "content" .}} |
| 131 | </div> |
| 132 | </main> |
| 133 | {{/* A sibling of <main>, not a child of it: a <footer> whose nearest |
| 134 | sectioning ancestor is <body> is the contentinfo landmark, and one |
| 135 | inside <main> is a plain generic footer. Grid puts it under the |
| 136 | page column rather than under the rail. */}} |
| 137 | {{/* The overprint that ends the ticket. Every printed ticket says which |
| 138 | office issued it and carries a serial; this one carries the build, |
| 139 | which is the same fact GET /api/version reports to the platform's |
| 140 | deploy check. It moved here from index.html so it appears on every |
| 141 | sheet rather than only the front page — the same words, one home. */}} |
| 142 | <footer rst-shell-foot>{{block "foot" .}}<p data-overprint>Build <code>{{.Version}}</code>.</p>{{end}}</footer> |
| 143 | </div> |
| 144 | </body> |
| 145 | </html> |
| 146 | {{end}} |
| 147 | |