Commit fc374bc57b
Verified · cmc
internal/httpd/page.go +6 −27
| @@ -13,27 +13,14 @@ type railRepo struct { | ||
| 13 | 13 | Name string |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | // railItem is one merge request in the rail's review queue. | |
| 17 | type railItem struct { | |
| 18 | RepoPath string | |
| 19 | Number int64 | |
| 20 | Title string | |
| 21 | } | |
| 22 | ||
| 23 | // rail is the cross-repo state in the left rail: the repositories and the | |
| 24 | // review work a viewer moves between. A repo's own navigation lives in its | |
| 25 | // header tabs, not here — the rail holds only what changes as you switch | |
| 26 | // context, which is what earns it the width. | |
| 16 | // rail is the viewer's cross-repo state the layout needs. The rail itself | |
| 17 | // is icons only, so what is left is the unread count on its bell; the | |
| 18 | // pinned repositories it used to list are rendered by the dashboard. | |
| 27 | 19 | type rail struct { |
| 28 | Pinned []railRepo | |
| 29 | Reviews []railItem | |
| 30 | Unread int | |
| 20 | Pinned []railRepo | |
| 21 | Unread int | |
| 31 | 22 | } |
| 32 | 23 | |
| 33 | // Empty reports whether the rail has nothing to show beyond the global | |
| 34 | // links, so the template can skip its group headings. | |
| 35 | func (r rail) Empty() bool { return len(r.Pinned) == 0 && len(r.Reviews) == 0 } | |
| 36 | ||
| 37 | 24 | // basePage is what the layout needs on every page, repo or not. Page |
| 38 | 25 | // structs embed it so the rail and the site name are always in scope. |
| 39 | 26 | type basePage struct { |
| @@ -68,7 +55,7 @@ func (s *Server) baseFor(viewer store.User) basePage { | ||
| 68 | 55 | return b |
| 69 | 56 | } |
| 70 | 57 | |
| 71 | // railFor collects the viewer's pinned repositories and review queue, | |
| 58 | // railFor collects the viewer's pinned repositories and unread count, | |
| 72 | 59 | // dropping anything they may no longer read. |
| 73 | 60 | func (s *Server) railFor(viewer store.User) rail { |
| 74 | 61 | var rl rail |
| @@ -80,13 +67,5 @@ func (s *Server) railFor(viewer store.User) rail { | ||
| 80 | 67 | } |
| 81 | 68 | } |
| 82 | 69 | rl.Unread = s.st.UnreadNotices(viewer.ID) |
| 83 | queue, _ := s.st.ReviewQueue(viewer.ID) | |
| 84 | for _, q := range queue { | |
| 85 | rl.Reviews = append(rl.Reviews, railItem{ | |
| 86 | RepoPath: q.RepoPath, | |
| 87 | Number: q.Number, | |
| 88 | Title: q.Title, | |
| 89 | }) | |
| 90 | } | |
| 91 | 70 | return rl |
| 92 | 71 | } |
internal/httpd/searchweb.go +2 −1
| @@ -53,11 +53,12 @@ func (s *Server) globalSearch(w http.ResponseWriter, r *http.Request) { | ||
| 53 | 53 | } |
| 54 | 54 | s.render(w, "globalsearch.html", struct { |
| 55 | 55 | basePage |
| 56 | Tab string | |
| 56 | 57 | Query string |
| 57 | 58 | Kind string |
| 58 | 59 | QueryErr string |
| 59 | 60 | Results []searchResult |
| 60 | }{s.baseFor(viewer), q, kind, queryErr, results}) | |
| 61 | }{s.baseFor(viewer), "sitesearch", q, kind, queryErr, results}) | |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | 64 | func searchHref(h control.SearchResult) string { |
internal/httpd/web.go +4 −2
| @@ -190,12 +190,13 @@ func (s *Server) dashboard(w http.ResponseWriter, r *http.Request, viewer store. | ||
| 190 | 190 | events, _ := s.st.RecentEvents(viewer.ID, 20, 0) |
| 191 | 191 | s.render(w, "dashboard.html", struct { |
| 192 | 192 | basePage |
| 193 | Tab string | |
| 193 | 194 | Reviews []store.DashboardItem |
| 194 | 195 | Assigned []store.DashboardItem |
| 195 | 196 | MRs []store.DashboardItem |
| 196 | 197 | Issues []store.DashboardItem |
| 197 | 198 | Feed []feedLine |
| 198 | }{s.baseFor(viewer), reviews, assigned, mrs, issues, feedLines(events)}) | |
| 199 | }{s.baseFor(viewer), "dashboard", reviews, assigned, mrs, issues, feedLines(events)}) | |
| 199 | 200 | } |
| 200 | 201 | |
| 201 | 202 | func (s *Server) explore(w http.ResponseWriter, r *http.Request) { |
| @@ -211,9 +212,10 @@ func (s *Server) explore(w http.ResponseWriter, r *http.Request) { | ||
| 211 | 212 | q := strings.TrimSpace(r.URL.Query().Get("q")) |
| 212 | 213 | s.render(w, "explore.html", struct { |
| 213 | 214 | basePage |
| 215 | Tab string | |
| 214 | 216 | Query string |
| 215 | 217 | Repos []describedRepo |
| 216 | }{s.baseFor(viewer), q, s.filterRepos(q, s.describeAll(repos))}) | |
| 218 | }{s.baseFor(viewer), "explore", q, s.filterRepos(q, s.describeAll(repos))}) | |
| 217 | 219 | } |
| 218 | 220 | |
| 219 | 221 | // privacy renders the privacy page: what the gitbay software does with |
internal/web/static/style.css +66 −80
| @@ -119,6 +119,13 @@ | ||
| 119 | 119 | --sp-6: 32px; |
| 120 | 120 | --sp-7: 48px; |
| 121 | 121 | |
| 122 | /* the rail: a column the width of one hit target, a row one tall */ | |
| 123 | --rail-w: 3.5rem; | |
| 124 | --rail-hit: 40px; | |
| 125 | --rail-strip-h: 48px; | |
| 126 | --rail-mark: 24px; | |
| 127 | --rail-mark-box: 32px; | |
| 128 | ||
| 122 | 129 | --r-ctl: 4px; /* controls and chips */ |
| 123 | 130 | --r-card: 6px; /* cards, lists, code blocks */ |
| 124 | 131 | --shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| @@ -244,10 +251,14 @@ a.skip { | ||
| 244 | 251 | } |
| 245 | 252 | a.skip:focus { top: var(--sp-3); text-decoration: none; } |
| 246 | 253 | |
| 247 | /* ---- rail ---- */ | |
| 254 | /* ---- rail: one glyph per destination, no labels ---- | |
| 255 | The column is the width of a hit target and nothing else. Every item is | |
| 256 | a square link carrying an inline svg, its name in an aria-label and in a | |
| 257 | visually hidden span, so the nav reads the same to a screen reader as | |
| 258 | the old text list did. */ | |
| 248 | 259 | .rail { |
| 249 | 260 | flex: none; |
| 250 | width: 14rem; | |
| 261 | width: var(--rail-w); | |
| 251 | 262 | background: var(--shell-bg); |
| 252 | 263 | color: var(--shell-fg); |
| 253 | 264 | border-right: 1px solid var(--shell-line); |
| @@ -256,83 +267,55 @@ a.skip:focus { top: var(--sp-3); text-decoration: none; } | ||
| 256 | 267 | height: 100vh; |
| 257 | 268 | display: flex; |
| 258 | 269 | flex-direction: column; |
| 270 | align-items: center; | |
| 271 | gap: var(--sp-1); | |
| 272 | padding: var(--sp-3) 0; | |
| 259 | 273 | } |
| 260 | 274 | a.brand { |
| 261 | 275 | display: flex; |
| 262 | 276 | align-items: center; |
| 263 | gap: var(--sp-2); | |
| 264 | padding: var(--sp-4) var(--sp-4) var(--sp-3); | |
| 277 | justify-content: center; | |
| 278 | flex: none; | |
| 279 | width: var(--rail-hit); | |
| 280 | height: var(--rail-hit); | |
| 265 | 281 | color: var(--shell-fg); |
| 266 | font-weight: 600; | |
| 267 | font-size: var(--fs-3); | |
| 268 | 282 | } |
| 269 | 283 | a.brand:hover { text-decoration: none; } |
| 270 | a.brand svg.mark { display: block; flex: none; } | |
| 271 | a.brand span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } | |
| 272 | ||
| 273 | .railbody { flex: 1; overflow-y: auto; padding: 0 var(--sp-3); } | |
| 274 | .railtop { display: block; } | |
| 275 | /* search is reachable from every page, so it sits above the rail's links | |
| 276 | rather than on one page of its own */ | |
| 277 | form.railsearch { margin-bottom: var(--sp-3); } | |
| 278 | form.railsearch input[type="search"] { | |
| 279 | width: 100%; | |
| 280 | background: var(--shell-hover); | |
| 281 | border: 1px solid var(--shell-line); | |
| 282 | color: var(--shell-fg); | |
| 283 | border-radius: var(--r-ctl); | |
| 284 | padding: 6px 10px; | |
| 285 | height: 32px; | |
| 286 | font: inherit; | |
| 287 | font-size: var(--fs-2); | |
| 284 | a.brand svg.mark { display: block; flex: none; width: var(--rail-mark); height: var(--rail-mark); } | |
| 285 | ||
| 286 | ul.raillist { | |
| 287 | list-style: none; | |
| 288 | margin: 0; | |
| 289 | padding: 0; | |
| 290 | display: flex; | |
| 291 | flex-direction: column; | |
| 292 | align-items: center; | |
| 293 | gap: var(--sp-1); | |
| 294 | min-width: 0; | |
| 288 | 295 | } |
| 289 | form.railsearch input[type="search"]::placeholder { color: var(--shell-muted); } | |
| 290 | ul.raillist { list-style: none; margin: 0 0 var(--sp-4); padding: 0; } | |
| 291 | ul.raillist a { | |
| 296 | /* the spacer that holds settings, admin and the account cell at the far | |
| 297 | end of the rail in either orientation */ | |
| 298 | .railgap { flex: 1; } | |
| 299 | .railicon { | |
| 300 | position: relative; | |
| 292 | 301 | display: flex; |
| 293 | 302 | align-items: center; |
| 294 | gap: var(--sp-2); | |
| 295 | padding: 5px var(--sp-2); | |
| 303 | justify-content: center; | |
| 304 | flex: none; | |
| 305 | width: var(--rail-hit); | |
| 306 | height: var(--rail-hit); | |
| 307 | padding: 0; | |
| 308 | border: 0; | |
| 309 | background: none; | |
| 296 | 310 | color: var(--shell-fg); |
| 297 | font-size: var(--fs-2); | |
| 298 | 311 | border-radius: var(--r-ctl); |
| 299 | overflow: hidden; text-overflow: ellipsis; white-space: nowrap; | |
| 312 | cursor: pointer; | |
| 300 | 313 | } |
| 301 | ul.raillist a:hover { background: var(--shell-hover); text-decoration: none; } | |
| 314 | .railicon:hover { background: var(--shell-hover); text-decoration: none; } | |
| 302 | 315 | /* orange marks position — the rail's only accent */ |
| 303 | ul.raillist a[aria-current] { | |
| 316 | .railicon[aria-current] { | |
| 304 | 317 | background: var(--shell-hover); |
| 305 | 318 | box-shadow: inset 3px 0 0 var(--shell-mark); |
| 306 | font-weight: 600; | |
| 307 | } | |
| 308 | ul.raillist .owner { color: var(--shell-muted); } | |
| 309 | ul.raillist a.newrepo { | |
| 310 | display: inline-flex; | |
| 311 | width: 28px; | |
| 312 | height: 28px; | |
| 313 | align-items: center; | |
| 314 | justify-content: center; | |
| 315 | padding: 0; | |
| 316 | border: 1px solid var(--shell-line); | |
| 317 | border-radius: var(--r-ctl); | |
| 318 | font-size: var(--fs-3); | |
| 319 | font-weight: 600; | |
| 320 | } | |
| 321 | ul.raillist.wide a { white-space: normal; display: block; } | |
| 322 | ul.raillist.wide b { display: block; font-weight: 500; font-size: var(--fs-1); } | |
| 323 | ul.raillist.wide .owner { display: block; font-size: var(--fs-0); } | |
| 324 | ||
| 325 | .railgroup { margin-bottom: var(--sp-4); } | |
| 326 | .raillabel { | |
| 327 | display: flex; | |
| 328 | align-items: center; | |
| 329 | gap: var(--sp-2); | |
| 330 | margin: 0 var(--sp-2) var(--sp-1); | |
| 331 | font-size: var(--fs-0); | |
| 332 | font-weight: 500; | |
| 333 | letter-spacing: 0.08em; | |
| 334 | text-transform: uppercase; | |
| 335 | color: var(--shell-muted); | |
| 336 | 319 | } |
| 337 | 320 | .count { |
| 338 | 321 | font-size: var(--fs-0); |
| @@ -344,32 +327,35 @@ ul.raillist.wide .owner { display: block; font-size: var(--fs-0); } | ||
| 344 | 327 | border-radius: var(--r-ctl); |
| 345 | 328 | margin-left: auto; |
| 346 | 329 | } |
| 347 | /* pinned above the account cell: always reachable, never scrolled away */ | |
| 348 | .railpinned { | |
| 349 | flex: none; | |
| 350 | padding: var(--sp-2) var(--sp-3); | |
| 330 | /* the unread count is a badge on the bell's corner, not a trailing number */ | |
| 331 | .railicon .count { | |
| 332 | position: absolute; | |
| 333 | top: 2px; | |
| 334 | right: 2px; | |
| 351 | 335 | margin: 0; |
| 352 | border-top: 1px solid var(--shell-line); | |
| 336 | padding: 0 3px; | |
| 337 | line-height: 1.35; | |
| 353 | 338 | } |
| 354 | 339 | .railfoot { |
| 355 | 340 | flex: none; |
| 356 | border-top: 1px solid var(--shell-line); | |
| 357 | padding: var(--sp-3) var(--sp-4); | |
| 358 | 341 | display: flex; |
| 342 | flex-direction: column; | |
| 359 | 343 | align-items: center; |
| 360 | justify-content: space-between; | |
| 361 | gap: var(--sp-3); | |
| 362 | font-size: var(--fs-2); | |
| 344 | gap: var(--sp-1); | |
| 345 | min-width: 0; | |
| 363 | 346 | } |
| 364 | 347 | .railfoot a { color: var(--shell-fg); } |
| 365 | a.railuser { display: inline-flex; align-items: center; gap: var(--sp-2); } | |
| 366 | /* a descendant cannot cancel an ancestor's underline, so the link carries | |
| 367 | none and the name draws its own */ | |
| 348 | .railfoot form { margin: 0; } | |
| 349 | a.railuser { | |
| 350 | display: flex; | |
| 351 | align-items: center; | |
| 352 | justify-content: center; | |
| 353 | flex: none; | |
| 354 | width: var(--rail-hit); | |
| 355 | height: var(--rail-hit); | |
| 356 | } | |
| 368 | 357 | a.railuser:hover { text-decoration: none; } |
| 369 | a.railuser:hover .uname { text-decoration: underline; } | |
| 370 | .railfoot form { margin-left: auto; } | |
| 371 | .railfoot button.linklike { color: var(--shell-muted); text-decoration: underline; } | |
| 372 | .railfoot button.linklike:hover { color: var(--shell-fg); } | |
| 358 | a.railuser .avatar { width: var(--rail-mark); height: var(--rail-mark); } | |
| 373 | 359 | .avatar { |
| 374 | 360 | display: inline-grid; |
| 375 | 361 | place-items: center; |
internal/web/templates/layout.html +34 −41
| @@ -12,49 +12,25 @@ | ||
| 12 | 12 | <a class="skip" href="#content">Skip to content</a> |
| 13 | 13 | <div class="shell"> |
| 14 | 14 | |
| 15 | {{$here := ""}}{{with field . "Repo"}}{{$here = printf "%s/%s" .OwnerName .Name}}{{end}} | |
| 16 | 15 | <nav class="rail" aria-label="Site"> |
| 17 | <a class="brand" href="/">{{template "mark"}}<span>{{.Site}}</span></a> | |
| 18 | <div class="railbody"> | |
| 19 | <div class="railtop"> | |
| 20 | <form method="get" action="/search" class="railsearch" role="search"> | |
| 21 | <input type="search" name="q" aria-label="Search {{.Site}}" placeholder="Search"> | |
| 22 | </form> | |
| 23 | <ul class="raillist"> | |
| 24 | {{if .Viewer}}<li><a href="/">Dashboard</a></li>{{end}} | |
| 25 | <li><a {{if eq (str . "Tab") "explore"}}aria-current="page" {{end}}href="/explore">Explore</a></li> | |
| 26 | {{if .Viewer}}<li><a {{if eq (str . "Tab") "notifications"}}aria-current="page" {{end}}href="/notifications">Notifications{{with .Rail.Unread}} <b class="count">{{.}}</b>{{end}}</a></li> | |
| 27 | <li><a {{if eq (str . "Tab") "bookmarks"}}aria-current="page" {{end}}href="/bookmarks">Bookmarks</a></li> | |
| 28 | <li><a class="newrepo" href="/new" aria-label="New repository" title="New repository">+</a></li>{{end}} | |
| 29 | </ul> | |
| 30 | </div> | |
| 31 | {{with .Rail.Pinned}} | |
| 32 | <div class="railgroup pinned"> | |
| 33 | <p class="raillabel" id="rail-pinned">Pinned</p> | |
| 34 | <ul class="raillist" aria-labelledby="rail-pinned"> | |
| 35 | {{range .}}<li><a {{if eq $here (printf "%s/%s" .Owner .Name)}}aria-current="page" {{end}}href="/{{.Owner}}/{{.Name}}"><span class="owner">{{.Owner}}/</span>{{.Name}}</a></li> | |
| 36 | {{end}} | |
| 37 | </ul> | |
| 38 | </div> | |
| 39 | {{end}} | |
| 40 | {{with .Rail.Reviews}} | |
| 41 | <div class="railgroup"> | |
| 42 | <p class="raillabel" id="rail-reviews">Waiting on you <b class="count">{{len .}}</b></p> | |
| 43 | <ul class="raillist wide" aria-labelledby="rail-reviews"> | |
| 44 | {{range .}}<li><a href="/{{.RepoPath}}/mrs/{{.Number}}"><b>!{{.Number}} {{.Title}}</b><span class="owner">{{.RepoPath}}</span></a></li> | |
| 45 | {{end}} | |
| 46 | </ul> | |
| 47 | </div> | |
| 48 | {{end}} | |
| 49 | </div> | |
| 50 | <ul class="raillist railpinned"> | |
| 51 | {{if .Viewer}}<li><a {{if eq (str . "Tab") "account"}}aria-current="page" {{end}}href="/settings">Settings</a></li>{{end}} | |
| 52 | {{if .Admin}}<li><a {{if eq (str . "Tab") "admin"}}aria-current="page" {{end}}href="/admin">Admin</a></li>{{end}} | |
| 16 | <a class="brand" href="/" aria-label="{{.Site}} home">{{template "mark"}}<span class="vh">{{.Site}}</span></a> | |
| 17 | <ul class="raillist"> | |
| 18 | {{if .Viewer}}<li><a class="railicon" {{if eq (str . "Tab") "dashboard"}}aria-current="page" {{end}}href="/" aria-label="Dashboard" title="Dashboard">{{template "icon" "home"}}<span class="vh">Dashboard</span></a></li>{{end}} | |
| 19 | <li><a class="railicon" {{if eq (str . "Tab") "explore"}}aria-current="page" {{end}}href="/explore" aria-label="Explore" title="Explore">{{template "icon" "compass"}}<span class="vh">Explore</span></a></li> | |
| 20 | <li><a class="railicon" {{if eq (str . "Tab") "sitesearch"}}aria-current="page" {{end}}href="/search" aria-label="Search" title="Search">{{template "icon" "search"}}<span class="vh">Search</span></a></li> | |
| 21 | {{if .Viewer}}<li><a class="railicon" {{if eq (str . "Tab") "notifications"}}aria-current="page" {{end}}href="/notifications" aria-label="Notifications" title="Notifications">{{template "icon" "bell"}}<span class="vh">Notifications</span>{{with .Rail.Unread}}<b class="count">{{.}}</b>{{end}}</a></li> | |
| 22 | <li><a class="railicon" {{if eq (str . "Tab") "bookmarks"}}aria-current="page" {{end}}href="/bookmarks" aria-label="Bookmarks" title="Bookmarks">{{template "icon" "bookmark"}}<span class="vh">Bookmarks</span></a></li> | |
| 23 | <li><a class="railicon" href="/new" aria-label="New repository" title="New repository">{{template "icon" "plus"}}<span class="vh">New repository</span></a></li>{{end}} | |
| 24 | </ul> | |
| 25 | <span class="railgap"></span> | |
| 26 | <ul class="raillist"> | |
| 27 | {{if .Viewer}}<li><a class="railicon" {{if eq (str . "Tab") "account"}}aria-current="page" {{end}}href="/settings" aria-label="Settings" title="Settings">{{template "icon" "gear"}}<span class="vh">Settings</span></a></li>{{end}} | |
| 28 | {{if .Admin}}<li><a class="railicon" {{if eq (str . "Tab") "admin"}}aria-current="page" {{end}}href="/admin" aria-label="Admin" title="Admin">{{template "icon" "shield"}}<span class="vh">Admin</span></a></li>{{end}} | |
| 53 | 29 | </ul> |
| 54 | 30 | <div class="railfoot"> |
| 55 | {{if .Viewer}}<a class="railuser" href="/{{.Viewer}}"><span class="avatar">{{initial .Viewer}}</span><span class="uname">{{.Viewer}}</span></a> | |
| 56 | <form method="post" action="/logout"><button type="submit" class="linklike">Log out</button></form> | |
| 57 | {{else}}<a href="/login">Sign in</a>{{end}} | |
| 31 | {{if .Viewer}}<a class="railuser" href="/{{.Viewer}}" aria-label="Your profile" title="{{.Viewer}}"><span class="avatar">{{initial .Viewer}}</span></a> | |
| 32 | <form method="post" action="/logout"><button type="submit" class="railicon" aria-label="Log out" title="Log out">{{template "icon" "signout"}}<span class="vh">Log out</span></button></form> | |
| 33 | {{else}}<a class="railicon" href="/login" aria-label="Sign in" title="Sign in">{{template "icon" "person"}}<span class="vh">Sign in</span></a>{{end}} | |
| 58 | 34 | </div> |
| 59 | 35 | </nav> |
| 60 | 36 | |
| @@ -79,7 +55,7 @@ | ||
| 79 | 55 | {{if eq $top "code"}} |
| 80 | 56 | {{if or (field $ "Desc") (field $ "Topics")}}<p class="repodesc">{{with field $ "Desc"}}{{.}}{{end}} {{with field $ "Topics"}}{{range .}}<a class="chip topic" href="/explore?q={{.}}">{{.}}</a> {{end}}{{end}}</p>{{end}} |
| 81 | 57 | {{if or $.Repo.Settings.Website (field $ "Mirrors")}}<p class="repometa">{{with $.Repo.Settings.Website}}<a href="{{.}}" rel="nofollow">{{.}}</a>{{end}}{{range $i, $m := field $ "Mirrors"}}{{if or $i $.Repo.Settings.Website}} · {{end}}{{if eq $m.Direction "push"}}mirrors to{{else}}mirrors from{{end}} <a href="{{$m.URL}}" rel="nofollow">{{$m.Target}}</a>{{if $m.Error}}, <span class="bad">sync error: {{$m.Error}}</span>{{else if $m.Synced}}, synced {{$m.Synced}}{{end}}{{end}}</p>{{end}} |
| 82 | {{if $.Viewer}}<p class="toggles">Pinned shows in your rail. Watching sends every issue, request and build to your inbox. Bookmarked lists it under Bookmarks.</p>{{end}} | |
| 58 | {{if $.Viewer}}<p class="toggles">Pinned shows on your dashboard. Watching sends every issue, request and build to your inbox. Bookmarked lists it under Bookmarks.</p>{{end}} | |
| 83 | 59 | {{end}} |
| 84 | 60 | <nav class="tabs" aria-label="Repository"> |
| 85 | 61 | <a {{if eq $top "code"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}">Code</a> |
| @@ -105,6 +81,23 @@ | ||
| 105 | 81 | </body> |
| 106 | 82 | </html>{{end}} |
| 107 | 83 | |
| 84 | {{/* icon draws one 16px glyph for the rail. The argument names it. The | |
| 85 | paths are stroked with currentColor, so a link's own colour and the | |
| 86 | current marker carry through, and every one is hidden from the | |
| 87 | accessibility tree — the link around it carries the name. */}} | |
| 88 | {{define "icon"}}<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | |
| 89 | {{- if eq . "home"}}<path d="M2.25 7 8 2.5 13.75 7v6.25a.5.5 0 0 1-.5.5H2.75a.5.5 0 0 1-.5-.5Z"/><path d="M6.25 13.75V9.25h3.5v4.5"/> | |
| 90 | {{- else if eq . "search"}}<circle cx="6.75" cy="6.75" r="4.25"/><path d="m9.9 9.9 3.6 3.6"/> | |
| 91 | {{- else if eq . "bell"}}<path d="M4.25 7a3.75 3.75 0 0 1 7.5 0v2.25l1.25 2.25H3l1.25-2.25Z"/><path d="M6.5 11.5a1.5 1.5 0 0 0 3 0"/> | |
| 92 | {{- else if eq . "bookmark"}}<path d="M4 2.75h8v11L8 10.5l-4 3.25Z"/> | |
| 93 | {{- else if eq . "compass"}}<circle cx="8" cy="8" r="6"/><path d="m10.5 5.5-1.25 3.75-3.75 1.25 1.25-3.75Z"/> | |
| 94 | {{- else if eq . "plus"}}<path d="M8 3.25v9.5M3.25 8h9.5"/> | |
| 95 | {{- else if eq . "gear"}}<circle cx="8" cy="8" r="3"/><path d="M8 1.5v1.75M8 12.75v1.75M1.5 8h1.75M12.75 8h1.75M3.4 3.4l1.25 1.25M10.35 10.35l1.25 1.25M12.6 3.4l-1.25 1.25M4.65 10.35 3.4 11.6"/> | |
| 96 | {{- else if eq . "shield"}}<path d="M8 1.75 13 3.5v4.25c0 3.1-2 5.4-5 6.5-3-1.1-5-3.4-5-6.5V3.5Z"/><path d="m5.9 7.75 1.6 1.6 2.85-3.1"/> | |
| 97 | {{- else if eq . "signout"}}<path d="M6.5 2.25H3.25a1 1 0 0 0-1 1v9.5a1 1 0 0 0 1 1H6.5"/><path d="M7.75 8h6M11.25 5.5 13.75 8l-2.5 2.5"/> | |
| 98 | {{- else if eq . "person"}}<circle cx="8" cy="5.5" r="2.75"/><path d="M2.75 14a5.25 5.25 0 0 1 10.5 0"/> | |
| 99 | {{- end}}</svg>{{end}} | |
| 100 | ||
| 108 | 101 | {{define "mark"}}<svg class="mark" width="19" height="19" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.25 18.75 12H5.25z" fill="#ff6b3d"/><rect x="3" y="13.5" width="18" height="3" fill="currentColor"/><rect x="7.5" y="18" width="9" height="3" fill="currentColor"/></svg>{{end}} |
| 109 | 102 | {{define "width"}}reading{{end}} |
| 110 | 103 | |