Rewrite nav bar

Fixes #31. Also resolves some small issues with incorrect margins and such.
This commit is contained in:
Florine W. Dekker 2022-11-23 00:58:13 +01:00
parent 537d8db0e2
commit 2d46bf1969
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
5 changed files with 107 additions and 136 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@fwdekker/template",
"version": "3.3.0",
"version": "3.3.1",
"description": "The base template for pages on fwdekker.com.",
"author": "Florine W. Dekker",
"license": "MIT",

View File

@ -4,6 +4,7 @@
--primary-hover: rgb(0, 61, 245) !important;
--primary-focus: rgba(0, 41, 163, 0.125) !important;
--primary-focus-opaque: rgb(0, 41, 163) !important;
--primary-focus-dark: rgb(0, 29, 114) !important;
--primary-inverse: white !important;
--form-element-active-border-color: var(--primary) !important;

View File

@ -1,107 +1,47 @@
/* Base elements */
nav.fwd-nav {
display: block;
z-index: 10;
margin: 0;
width: 100%;
background-color: var(--primary);
border-bottom: 1px solid var(--nav-border-color);
--padding: calc(2em / 3);
border-bottom: 1px solid #ccc;
}
nav.fwd-nav * {
vertical-align: middle;
nav.fwd-nav > ul {
display: flex;
align-items: start;
flex-wrap: wrap;
}
nav.fwd-nav a,
nav.fwd-nav a:link,
nav.fwd-nav a:visited,
nav.fwd-nav a:hover,
nav.fwd-nav a:active {
/* Ensures whole li is clickable */
width: 100%;
nav.fwd-nav > ul > li {
flex-grow: 0;
flex-basis: 0;
}
nav.fwd-nav a,
nav.fwd-nav a:link,
nav.fwd-nav a:visited,
nav.fwd-nav a:hover,
nav.fwd-nav a:active,
nav.fwd-nav #nav-hamburger-label {
display: inline-block;
margin: 0;
padding: calc(var(--padding)) calc(var(--padding));
height: 100%;
color: var(--primary-inverse);
}
nav.fwd-nav #nav-hamburger-label {
float: right;
}
nav.fwd-nav a[target="_blank"]::after {
margin-bottom: 0.2rem;
background-color: var(--primary-inverse);
}
/* Logo */
nav.fwd-nav .logo {
width: calc(1em + var(--padding));
height: calc(1em + var(--padding));
vertical-align: middle;
filter: brightness(0) invert(1);
}
nav.fwd-nav div.logo {
display: inline-block;
margin-right: calc(1em / 3);
}
/* First level nesting */
nav.fwd-nav ul {
display: block;
margin: 0;
padding: 0;
list-style: none;
}
nav.fwd-nav ul li {
display: inline-block;
margin: 0;
padding: 0;
nav.fwd-nav li {
position: relative;
background-color: var(--primary);
width: 100%;
padding: var(--nav-element-spacing-horizontal);
white-space: nowrap;
}
nav.fwd-nav ul li:hover,
nav.fwd-nav ul li:focus-within,
nav.fwd-nav #nav-hamburger-label:hover,
nav.fwd-nav #nav-hamburger-label:focus-within {
cursor: pointer;
background-color: var(--primary-hover);
nav.fwd-nav li > :first-child {
display: inline-block;
width: 100%;
margin: 0;
}
nav.fwd-nav li.currentPage {
background-color: var(--primary-focus-opaque);
}
/* Second level nesting */
nav.fwd-nav ul li ul {
z-index: 11;
display: none;
position: absolute;
top: 100%;
left: 0;
margin: 0;
}
nav.fwd-nav ul li:where(:active, :focus-within, :hover) > ul {
display: flex;
flex-direction: column;
align-items: start;
}
nav.fwd-nav ul li ul li ul {
@ -109,22 +49,74 @@ nav.fwd-nav ul li ul li ul {
top: 0;
}
nav.fwd-nav ul li:hover > ul,
nav.fwd-nav ul li:focus-within > ul,
nav.fwd-nav ul li ul:hover {
display: block;
/* z-index */
nav.fwd-nav a {
position: relative;
}
nav.fwd-nav ul li ul li {
min-width: 7em;
width: 100%;
white-space: nowrap;
nav.fwd-nav > ul > li > ul {
z-index: 10;
}
/* Colors */
nav.fwd-nav,
nav.fwd-nav ul {
background-color: var(--primary);
}
/* Hide hamburger-related elements */
nav.fwd-nav input[type="checkbox"] {
display: none;
nav.fwd-nav ul li:where(:active, :focus-within, :hover) {
background-color: var(--primary-focus-dark);
}
nav.fwd-nav ul li.current-page:not(:where(:active, :focus-within, :hover)) {
background-color: var(--primary-focus-opaque);
}
nav.fwd-nav a {
color: var(--primary-inverse);
}
nav.fwd-nav a::after {
background-color: var(--primary-inverse);
}
/* Logo */
nav.fwd-nav #logo {
font-weight: bold;
}
nav.fwd-nav #logo::before {
display: inline-block;
width: calc(1em * var(--line-height));
height: calc(1em * var(--line-height));
margin-right: 0.25rem;
vertical-align: top;
--mask-image: url("https://fwdekker.com/favicon.png");
-webkit-mask-image: var(--mask-image);
-webkit-mask-size: cover;
mask-image: var(--mask-image);
mask-size: cover;
background-repeat: no-repeat no-repeat;
background-position: center center;
background-size: cover;
background-color: var(--primary-inverse);
content: "";
}
/* Hamburger */
nav.fwd-nav #nav-hamburger-label {
height: fit-content;
margin: 0;
padding: calc(var(--nav-element-spacing-horizontal) + var(--nav-link-spacing-vertical));
color: var(--primary-inverse);
}
nav.fwd-nav #nav-hamburger-label:where(:active, :focus-within, :hover) {
background-color: var(--primary-focus-dark);
}
@media (min-width: 576px) {
@ -134,7 +126,7 @@ nav.fwd-nav input[type="checkbox"] {
}
@media (max-width: 576px) {
nav.fwd-nav input[type="checkbox"]:not(:checked) ~ ul li:not(:first-child) {
nav.fwd-nav #nav-hamburger-checkbox:not(:checked) ~ ul li:not(:first-child) {
display: none;
}
}

View File

@ -2,11 +2,11 @@
* Converts the given string to an HTML element.
*
* @param string the string to convert to an HTML element
* @param query the type of element to return
* @returns the HTML element described by the given string, or `null` if `query` did not match any element
* @param query the type of element to return, or `undefined` to return the first element
* @returns the HTML element described by the given string
*/
export function stringToHtml(string: string, query: string): HTMLElement | null {
return (new DOMParser()).parseFromString(string, "text/html").body.querySelector(query);
export function stringToHtml(string: string, query: string = "*"): HTMLElement {
return (new DOMParser()).parseFromString(string, "text/html").body.querySelector(query)!;
}
/**
@ -81,13 +81,7 @@ export function getMetaProperty(name: string): string | null | undefined {
* @returns a base navigation element that will eventually be filled with contents
*/
function nav(highlightPath?: string, cb?: (json: any) => void): HTMLElement {
const base = stringToHtml(
`<ul><li><a href="https://fwdekker.com/">` +
`<div class="logo"><img class="logo" src="https://fwdekker.com/favicon.png" alt="FWDekker" /></div>` +
`<b>FWDekker</b>` +
`</a></li></ul>`,
"ul"
)!;
const base = stringToHtml(`<ul><li><a id="logo" href="https://fwdekker.com/">FWDekker</a></span></li></ul>`);
fetch("https://fwdekker.com/api/nav/")
.then(it => it.json())
@ -96,20 +90,15 @@ function nav(highlightPath?: string, cb?: (json: any) => void): HTMLElement {
cb(json);
json.entries.forEach(
(entry: any) =>
base.appendChild(stringToHtml(unpackEntry(entry, "/", highlightPath), "li")!)
(entry: any) => base.appendChild(stringToHtml(unpackEntry(entry, "/", highlightPath)))
);
})
.catch(error => console.error("Failed to fetch navigation elements", error));
const nav = stringToHtml(
`<nav class="fwd-nav">` +
`<input id="nav-hamburger-checkbox" type="checkbox" hidden />` +
`<label id="nav-hamburger-label" for="nav-hamburger-checkbox">&#9776;</label>` +
`</nav>`,
"nav"
)!;
const nav = stringToHtml(`<nav class="fwd-nav"></nav>`);
nav.appendChild(stringToHtml(`<input id="nav-hamburger-checkbox" type="checkbox" hidden />`));
nav.appendChild(base);
nav.appendChild(stringToHtml(`<label id="nav-hamburger-label" for="nav-hamburger-checkbox">&#9776;</label>`));
return nav;
}
@ -128,7 +117,7 @@ function unpackEntry(entry: any, path: string = "/", highlightPath?: string): st
if (entry.entries.length === 0)
return "" +
`<li ${shouldHighlight ? "class=\"currentPage\"" : ""}>` +
`<li ${shouldHighlight ? "class=\"current-page\"" : ""}>` +
`<a href="${entry.link}" ${isExternalLink ? "target=\"_blank\"" : ""}>${entry.name}</a>` +
`</li>`;
@ -136,7 +125,7 @@ function unpackEntry(entry: any, path: string = "/", highlightPath?: string): st
const arrow = depth === 0 ? "&#9662;" : "&#9656;";
return "" +
`<li class="${shouldHighlight ? "currentPage" : ""}">` +
`<li class="${shouldHighlight ? "current-page" : ""}">` +
`<a href="${entry.link}">${entry.name} ${arrow}</a>` +
`<ul>${entry.entries.map((it: any) => unpackEntry(it, `${path + entry.name}/`, highlightPath)).join("")}</ul>` +
`</li>`;
@ -192,9 +181,8 @@ function footer(
footerLink("Licensed ", license, licenseURL, ". ") +
footerLink("Source and support on ", vcs, vcsURL, ". ") +
footerLink("Read the ", privacyPolicyURL && "privacy policy", privacyPolicyURL, ". ") +
`</small><div id="fwd-footer-version"><small>${version || ""}</small></div></footer>`,
"footer"
)!;
`</small><div id="fwd-footer-version"><small>${version || ""}</small></div></footer>`
);
}
/**

View File

@ -29,7 +29,7 @@
</p>
</noscript>
<nav id="nav"></nav>
<main class="container container-with-aside-right">
<main class="container">
<div role="document">
<section>
<header class="fwd-header">
@ -81,16 +81,6 @@
</section>
<footer id="footer"></footer>
</div>
<aside>
<nav>
<b class="hidden-no-mobile">Table of contents</b>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<hr class="hidden-no-mobile" />
</nav>
</aside>
</main>
<!--suppress HtmlUnknownTarget -->