From 2d46bf19697339105b82619eeb764c66c5f88ef8 Mon Sep 17 00:00:00 2001
From: "Florine W. Dekker"
Date: Wed, 23 Nov 2022 00:58:13 +0100
Subject: [PATCH] Rewrite nav bar
Fixes #31. Also resolves some small issues with incorrect margins and such.
---
package.json | 2 +-
src/main/css/snippets/colors.css | 1 +
src/main/css/snippets/nav.css | 190 +++++++++++++++----------------
src/main/js/Template.ts | 38 +++----
src/test/index.html | 12 +-
5 files changed, 107 insertions(+), 136 deletions(-)
diff --git a/package.json b/package.json
index a7d4f29..3cb8576 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/main/css/snippets/colors.css b/src/main/css/snippets/colors.css
index d4dfca9..853b925 100644
--- a/src/main/css/snippets/colors.css
+++ b/src/main/css/snippets/colors.css
@@ -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;
diff --git a/src/main/css/snippets/nav.css b/src/main/css/snippets/nav.css
index 012eed7..a68402a 100644
--- a/src/main/css/snippets/nav.css
+++ b/src/main/css/snippets/nav.css
@@ -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;
}
}
diff --git a/src/main/js/Template.ts b/src/main/js/Template.ts
index 3beb98e..c468f3d 100644
--- a/src/main/js/Template.ts
+++ b/src/main/js/Template.ts
@@ -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"
- )!;
+ const base = stringToHtml(``);
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"
- )!;
+ const nav = stringToHtml(``);
+ nav.appendChild(stringToHtml(``));
nav.appendChild(base);
+ nav.appendChild(stringToHtml(``));
return nav;
}
@@ -128,7 +117,7 @@ function unpackEntry(entry: any, path: string = "/", highlightPath?: string): st
if (entry.entries.length === 0)
return "" +
- `` +
+ `` +
`${entry.name}` +
``;
@@ -136,7 +125,7 @@ function unpackEntry(entry: any, path: string = "/", highlightPath?: string): st
const arrow = depth === 0 ? "▾" : "▸";
return "" +
- `` +
+ `` +
`${entry.name} ${arrow}` +
`${entry.entries.map((it: any) => unpackEntry(it, `${path + entry.name}/`, highlightPath)).join("")}
` +
``;
@@ -192,9 +181,8 @@ function footer(
footerLink("Licensed ", license, licenseURL, ". ") +
footerLink("Source and support on ", vcs, vcsURL, ". ") +
footerLink("Read the ", privacyPolicyURL && "privacy policy", privacyPolicyURL, ". ") +
- ``,
- "footer"
- )!;
+ ``
+ );
}
/**
diff --git a/src/test/index.html b/src/test/index.html
index 3479c8d..cdcfb9d 100644
--- a/src/test/index.html
+++ b/src/test/index.html
@@ -29,7 +29,7 @@
-
+
-