diff options
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | css/banner.scss | 91 | ||||
-rw-r--r-- | css/footer.scss | 33 | ||||
-rw-r--r-- | css/glyph.scss | 20 | ||||
-rw-r--r-- | css/header.scss | 89 | ||||
-rw-r--r-- | css/main.scss | 5 | ||||
-rw-r--r-- | css/navBar.scss | 43 | ||||
-rw-r--r-- | css/page.scss | 2 | ||||
-rw-r--r-- | css/sideMenu.scss | 102 | ||||
-rw-r--r-- | include/footer.shtml | 17 | ||||
-rw-r--r-- | include/header.shtml | 35 | ||||
-rw-r--r-- | js/init.ts | 1 | ||||
-rw-r--r-- | js/navigation.ts | 133 | ||||
-rw-r--r-- | js/page.ts | 116 | ||||
-rw-r--r-- | svg/hamburger.svg | 6 |
15 files changed, 328 insertions, 372 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e2b7c3..3083ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 8.0 + +* Rework side menu and headers +* Improve reduced motion support +* Rework footer +* Improve mobile styling + ## 7.3 * Update vertical styling diff --git a/css/banner.scss b/css/banner.scss deleted file mode 100644 index 04c0956..0000000 --- a/css/banner.scss +++ /dev/null @@ -1,91 +0,0 @@ -#banner { - align-items: center; - background-size: cover; - display: flex; - height: 100%; - image-rendering: pixelated; - justify-content: center; - padding-bottom: 1rem; - position: absolute; - top: 0; - width: 100%; - - body[data-page = "achernar"] & { - --textColour: #FFFFFF; - - background-color: #007B34; - - @media not (prefers-reduced-motion) { - background-image: url("/image/vertex.webp"); - } - } - - body[data-page = "agbsum"] & { - --textColour: #FFFFFF; - - background-color: #422984; - } - - body[data-page = "ax"] & { - --textColour: #FFFFFF; - - background-color: #422984; - } - - body[data-page = "backspace"] & { - --textColour: #000000; - - background-color: #FFFFFF; - } - - body[data-page = "benoit"] & { - --textColour: #FFFFFF; - - background-image: url("/svg/benoitBackground.svg"); - } - - body[data-page = "bowshock"] & { - --textColour: #FFEEE0; - - background-color: #B61833; - } - - body[data-page = "bzipper"] & { - --textColour: #B4202D; - - background-color: #FFFFFF; - } - - body[data-page = "deltaWorld"] & { - --textColour: #FFFFFF; - - background-color: #000000; - } - - body[data-page = "eas"] & { - --textColour: #00291B; - - background-color: #01CD93; - } - - body[data-page = "luma"] & { - --textColour: #FFFFFF; - - background-color: #6051B2; - } - - body[data-page = "pollex"] & { - --textColour: #FFFFFF; - - background-color: #4D4084; - } - - body[data-page = "u8c"] & { - --textColour: #A9E13D; - - background-color: #444747; - } - - @import "navBar"; - @import "glyph"; -} diff --git a/css/footer.scss b/css/footer.scss index 06243b0..0a1867f 100644 --- a/css/footer.scss +++ b/css/footer.scss @@ -4,6 +4,7 @@ font-family: $monospaceFont; margin-top: 1rem; padding: 1rem; + text-align: center; h1 { font-size: 2em; @@ -21,38 +22,22 @@ } } - table { - border-collapse: collapse; - - tr { - th, td { - line-height: 2em; - } - - th { - text-align: left; - - &::before { - content: "\00B7\0020"; - } - } - - td { - padding-left: 2rem; - } - } + #cvrNumber { + font-weight: bold; } #emailAddress { + aspect-ratio: 183/8; background-color: var(--textColour); - display: inline-block; - height: 1em; + display: block; image-rendering: pixelated; + margin: auto; mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAAICAYAAAC78m4/AAAA/UlEQVRYw+1XQQ7EIAiExv9/2T01IQQJDNZ0rdyEFmEcBJmIqPfe6ciRzYRvYjMzS4Mk/Mim9RXbbY/YkGLM5BDdI4PZKAe5zuJi+fDWmTPK5F3hS2RPNPdLK/RHnk1ulLFJnbWWOsuGEhuNJetT27I5eP95WFskqJ5fJM4oX1Cso3FqXUNvrZHTWSNO5Zb+i5bpkGhmh/rMCGJwsEWJ6oEmq4mZefRftkpXEmq1T3Q0QPGMFNNul8kVBRYdD9CW94TMGHFW+ETx9OLw4nzTGVVw0cXa5INlxq2APEisNow8cN8EtMYzE2clP+9humIUqHKpwjOd+9Zz7ZFvyw87v+eHE6E5DAAAAABJRU5ErkJggg"); mask-position: center; mask-repeat: no-repeat; mask-size: cover; - vertical-align: middle; - width: calc(183em / 8); + max-height: 1em; + max-width: calc(183em / 8); + width: 100%; } } diff --git a/css/glyph.scss b/css/glyph.scss index 9a91b28..04d6568 100644 --- a/css/glyph.scss +++ b/css/glyph.scss @@ -9,20 +9,14 @@ body[data-page = "achernar"] & { $pixelWIdth: calc(100vmax / 256); - @media not (prefers-reduced-motion) { - background-image: url("/image/achernarPixelated.webp"); - height: floor(calc($pixelWIdth * 42)); - width: floor(calc($pixelWIdth * 154)); + background-image: url("/image/achernarPixelated.webp"); + height: floor(calc($pixelWIdth * 42)); + width: floor(calc($pixelWIdth * 154)); - @media (orientation: portrait) { - background-image: url("/image/achernarVerticalPixelated.webp"); - height: floor(calc($pixelWIdth * 90)); - width: floor(calc($pixelWIdth * 74)); - } - } - - @media (prefers-reduced-motion) { - background-image: url("/svg/glyph/achernar.svg"); + @media (orientation: portrait) { + background-image: url("/image/achernarVerticalPixelated.webp"); + height: floor(calc($pixelWIdth * 90)); + width: floor(calc($pixelWIdth * 74)); } } diff --git a/css/header.scss b/css/header.scss index c6c0213..423d0f5 100644 --- a/css/header.scss +++ b/css/header.scss @@ -1,17 +1,94 @@ #header { $slideDuration: 0.5s; - --hamburgerWidth: 2rem; + --hamburgerHeight: 2rem; + align-items: center; + background-color: var(--backgroundColour); + background-size: cover; border-bottom-left-radius: 1rem; border-bottom-right-radius: 1rem; + display: flex; height: calc(100vh + 1rem); + image-rendering: pixelated; + justify-content: center; overflow: hidden; + padding-bottom: 1rem; position: relative; width: 100%; + body[data-page = "achernar"] & { + --backgroundColour: #007B34; + --textColour: #FFFFFF; + + @media not (prefers-reduced-motion) { + background-image: url("/image/vertex.webp"); + } + } + + body[data-page = "agbsum"] & { + --backgroundColour: #422984; + --textColour: #FFFFFF; + } + + body[data-page = "ax"] & { + --backgroundColour: #422984; + --textColour: #FFFFFF; + } + + body[data-page = "backspace"] & { + --backgroundColour: #FFFFFF; + --textColour: #000000; + } + + body[data-page = "benoit"] & { + --backgroundColour: #BA0035; + --textColour: #FFFFFF; + + background-image: url("/svg/benoitBackground.svg"); + } + + body[data-page = "bowshock"] & { + --backgroundColour: #B61833; + --textColour: #FFEEE0; + } + + body[data-page = "bzipper"] & { + --backgroundColour: #FFFFFF; + --textColour: #B4202D; + } + + body[data-page = "deltaWorld"] & { + --backgroundColour: #000000; + --textColour: #FFFFFF; + } + + body[data-page = "eas"] & { + --backgroundColour: #01CD93; + --textColour: #00291B; + } + + body[data-page = "luma"] & { + --backgroundColour: #6051B2; + --textColour: #FFFFFF; + } + + body[data-page = "pollex"] & { + --backgroundColour: #4D4084; + --textColour: #FFFFFF; + } + + body[data-page = "u8c"] & { + --backgroundColour: #444747; + --textColour: #A9E13D; + } + @media (pointer: coarse) { - --hamburgerWidth: 4rem; + --hamburgerHeight: 4rem; + } + + p, a, a:hover { + color: var(--textColour) } a { @@ -19,14 +96,18 @@ font-weight: bold; text-decoration: underline; text-decoration-color: #00000000; - transition: text-decoration-color 0.125s; user-select: none; + @media not (prefers-reduced-motion) { + transition: text-decoration-color 0.125s; + } + &:hover { text-decoration-color: var(--textColour); } } - @import "banner"; + @import "navBar"; @import "sideMenu"; + @import "glyph"; } diff --git a/css/main.scss b/css/main.scss index 6f4025d..8e5960f 100644 --- a/css/main.scss +++ b/css/main.scss @@ -2,6 +2,7 @@ $monospaceFont: "Fira Mono", "monospace"; +$contentWidth: 72rem; $separatorWidth: 0.25rem; :root { @@ -16,8 +17,8 @@ $separatorWidth: 0.25rem; } body { - --backgroundColour: oklch( 20% 0.014800 253.71); - --foregroundColour: oklch( 25% 0.014800 253.71); + --backgroundColour: oklch( 20% 0.029600 253.71); + --foregroundColour: oklch( 25% 0.029600 253.71); --textColour: oklch(100% 0 0); background-color: var(--backgroundColour); diff --git a/css/navBar.scss b/css/navBar.scss index 9eba669..16c003e 100644 --- a/css/navBar.scss +++ b/css/navBar.scss @@ -1,19 +1,25 @@ #navBar { - $gap: 1rem; - align-items: center; display: flex; - height: calc($gap * 2 + var(--hamburgerWidth)); + height: calc(2rem + var(--hamburgerHeight)); justify-content: space-between; - padding: $gap; + padding: 1rem; position: absolute; top: 0; + max-width: $contentWidth; width: 100%; + body[data-page = "achernar"] & { + a, #home, #hamburger { + mix-blend-mode: difference; + } + } + section { - display: flex; - flex: 1; - gap: $gap; + display: flex; + flex: 1; + gap: 1rem; + justify-content: center; &:first-of-type { justify-content: start; @@ -25,7 +31,10 @@ a { text-decoration-color: #00000000; - transition: opacity $slideDuration, text-decoration-color 0.125s; + + @media not (prefers-reduced-motion) { + transition: opacity $slideDuration, text-decoration-color 0.125s; + } @media (orientation: portrait) or (pointer: coarse) { display: none; @@ -34,28 +43,20 @@ &:hover { text-decoration-color: var(--textColour); } + } - &.hidden:not(#home) { - opacity: 0; - pointer-events: none; - } + #home { + z-index: 255; } #hamburger { aspect-ratio: 1; background-color: var(--textColour); cursor: pointer; + height: var(--hamburgerHeight); mask-image: url("/svg/hamburger.svg"); mask-size: cover; - width: var(--hamburgerWidth); - } - - body[data-page = "achernar"] & { - a, div { - @media not (prefers-reduced-motion) { - mix-blend-mode: difference; - } - } + z-index: 255; } } } diff --git a/css/page.scss b/css/page.scss index 13c0aa2..de9edd7 100644 --- a/css/page.scss +++ b/css/page.scss @@ -2,7 +2,7 @@ $padding: 1rem; margin: auto; - max-width: 72rem; + max-width: $contentWidth; padding: $padding; a, a:visited { diff --git a/css/sideMenu.scss b/css/sideMenu.scss index b9c0614..480d35c 100644 --- a/css/sideMenu.scss +++ b/css/sideMenu.scss @@ -1,86 +1,66 @@ #sideMenu { - backdrop-filter: blur(1rem); - -webkit-backdrop-filter: blur(1rem); - background-color: color-mix(in srgb, var(--backgroundColour), transparent 50%); - box-shadow: 0 0 1rem color-mix(in srgb, black, transparent 50%);; - display: flex; - flex-direction: column; - gap: 1rem; - height: calc(100vh - var(--hamburgerWidth) - 3rem); - justify-content: space-between; - left: 100%; - overflow: scroll; - padding: 1rem; - position: absolute; - top: calc(var(--hamburgerWidth) + 2rem); - transition: left $slideDuration; - width: 100vw; - z-index: 255; + align-items: center; + background-color: var(--backgroundColour); + display: flex; + flex-direction: column; + gap: 1rem; + height: calc(100% + 1rem); + justify-content: space-between; + left: 100%; + overflow: hidden; + padding: 1rem; + padding-bottom: 2rem; + padding-top: calc(var(--hamburgerHeight) + 2rem); + position: absolute; + width: 100%; + z-index: 127; - &.visible { - left: 0; + body[data-page = "achernar"] & { + background-color: var(--backgroundColour); } - @media (orientation: landscape) and (pointer: fine) { - border-bottom-left-radius: 1rem; - border-top-left-radius: 1rem; - width: 50vw; - - &.visible { - left: 50%; - } + @media (pointer: fine) and (not (prefers-reduced-motion)) { + transition: left $slideDuration; + transition-timing-function: ease-in-out; } @media (pointer: coarse) { - border-style: solid; - border-width: $separatorWidth; - font-size: 2em; + font-size: 2em; + overflow: scroll; + } + + &.visible { + left: 0; } div.links { - display: flex; - gap: 1rem; + display: flex; + gap: 1rem; + max-width: $contentWidth; + padding: 1rem; + width: 100%; @media (pointer: coarse) { flex-direction: column; } section { - $gap: 0.25rem; - display: flex; flex-direction: column; - gap: $gap; + gap: 0.25rem; p { - text-align: center; - - &::after { - background-color: var(--textColour); - border-radius: calc($separatorWidth / 2); - content: ""; - display: block; - height: $separatorWidth; - margin-top: $gap; - width: 100%; - } + background-color: var(--textColour); + border-radius: 0.25rem; + color: var(--backgroundColour); + font-weight: bold; + padding: 0.25rem 1rem; + text-align: center; + } - @media (pointer: coarse) { - &::before { - background-color: var(--textColour); - border-radius: calc($separatorWidth / 2); - content: ""; - display: block; - height: $separatorWidth; - margin-top: $gap; - width: 100%; - } - } + a { + padding: 0.25rem; } } } - - #themeToggler { - align-self: center; - } } diff --git a/include/footer.shtml b/include/footer.shtml index 878f228..37e3d19 100644 --- a/include/footer.shtml +++ b/include/footer.shtml @@ -1,20 +1,11 @@ <footer id="footer"> <h1>achernar</h1> - <table> - <tbody> - <tr> - <th>cvr</th> - <td>44936429</td> - </tr> - <tr> - <th>e-mail</th> - <td><div id="emailAddress"></div></td> - </tr> - </tbody> - </table> + <p id="cvrNumber">cvr: 44936429</p> <br> <p>Communications can be done in English and Danish.</p> <br> - <p>This webservice can be cloned from <a href="https://mandelbrot.dk/achernar/" rel="noopener noreferrer" target="_blank"><code>mandelbrot.dk</code></a>.</p> + <p>This webservice may be cloned from <a href="https://mandelbrot.dk/achernar/" rel="noopener noreferrer" target="_blank"><code>mandelbrot.dk</code></a>.</p> + <br> + <div class="center" id="emailAddress"></div> </footer> diff --git a/include/header.shtml b/include/header.shtml index 77168d4..63cb519 100644 --- a/include/header.shtml +++ b/include/header.shtml @@ -1,23 +1,18 @@ <header id="header"> - <div id="banner"> - <div id="navBar"> - <section> - <a id="home" onclick="loadPage('achernar');">ACHERNAR</a> - </section> - - <section> - <a onclick="loadPage('benoit');">BENOIT</a> - <a onclick="loadPage('bowshock');">BOWSHOCK</a> - <a onclick="loadPage('deltaWorld');">DELTA·WORLD</a> - <a onclick="loadPage('eas');">eAS</a> - </section> - - <section> - <div id="hamburger" onclick="toggleSideMenu();"></div> - </section> - </div> - - <div id="glyph"></div> + <div id="navBar"> + <section> + <a id="home" onclick="loadPage('achernar');">ACHERNAR</a> + </section> + + <section> + <a onclick="loadPage('benoit');">BENOIT</a> + <a onclick="loadPage('bowshock');">BOWSHOCK</a> + <a onclick="loadPage('deltaWorld');">DELTA·WORLD</a> + </section> + + <section> + <div id="hamburger" onclick="toggleSideMenu();"></div> + </section> </div> <div id="sideMenu"> @@ -55,4 +50,6 @@ <a id="themeToggler" onclick="toggleTheme();">TOGGLE THEME</a> </div> + + <div id="glyph"></div> </header> @@ -1,5 +1,4 @@ /// <reference path="navigation.ts" /> -/// <reference path="page.ts" /> /// <reference path="theme.ts" /> function init() { diff --git a/js/navigation.ts b/js/navigation.ts index 1e9ec81..4a83be6 100644 --- a/js/navigation.ts +++ b/js/navigation.ts @@ -1,18 +1,145 @@ +async function loadPage(page_name: string, anchor?: string) { + console.log(`loading page \`${page_name}\``); + + let url = `/html/${page_name}.html`; + console.log(`note: page is at "${url}"`); + + window.history.pushState(page_name, "", url); + + let response = await fetch(url); + + if (!response.ok) { + throw new Error(`unable to load page: \"${response.status}\"`); + } + + let markup = await response.text(); + + let parser = new DOMParser(); + let dom = parser.parseFromString(markup, "text/html"); + + let titles = document.getElementsByTagName("title"); + let bodies = document.getElementsByTagName("body"); + let page = document.getElementById("page")!; + + if (titles.length < 0x1) { + throw new Error("unable to find title"); + } + + if (bodies.length < 0x1) { + throw new Error("unable to find body"); + } + + if (!page) { + throw new Error("unable to find page element"); + } + + let newTitles = dom.getElementsByTagName("title"); + let newPage = dom.getElementById("page"); + + if (newTitles.length < 0x1) { + throw new Error("unable to find new title"); + } + + if (!newPage) { + throw new Error("unable to find new page element"); + } + + titles[0x0].replaceWith(newTitles[0x0]); + bodies[0x0].setAttribute("data-page", page_name); + page.replaceWith(newPage); + + initImages(); + + if (anchor) { + console.log(`going to anchor \`${anchor}\``); + + anchor = `anchor.${anchor}`; + + console.log(`note: anchor has id "${anchor}"`); + + let anchor_element = document.getElementById(anchor); + + if (!anchor_element) { + throw new Error(`unable to find anchor "${anchor}"`); + } + + anchor_element.scrollIntoView({ + behavior: "smooth", + }); + } +} + function toggleSideMenu() { - let navBar = document.getElementById("navBar"); + scrollToTop(); + let sideMenu = document.getElementById("sideMenu"); + let navBar = document.getElementById("navBar"); + let glyph = document.getElementById("glyph"); + + if (!sideMenu) { + throw new Error("unable to find sideMenu"); + } if (!navBar) { throw new Error("unable to find navigation bar"); } - if (!sideMenu) { - throw new Error("unable to find side menu"); + if (!glyph) { + throw new Error("unable to find glyph"); } sideMenu.classList.toggle("visible"); + glyph.classList.toggle("hidden"); for (let link of navBar.getElementsByTagName("a")) { link.classList.toggle("hidden"); } } + +async function scrollToTop() { + window.scroll({ + top: 0, + left: 0, + behavior: "smooth", + }); +} + +function initImages() { + let page = document.getElementById("page"); + + if (!page) { + throw new Error("unable to find page"); + } + + let imageList = Array.from(page.getElementsByTagName("x-image")); + + for (let image of imageList) { + let file = image.getAttribute("data-file"); + + if (!file) { + throw new Error("file not set for image element") + } + + console.log("initialising image that links to \"" + file + "\""); + + let sourceUrl = "/image/source/" + file + ".webp"; + let thumbnailUrl = "/image/thumbnail/" + file + ".avif"; + + let blurElement = document.createElement("img"); + blurElement.setAttribute("class", "blur"); + blurElement.setAttribute("src", thumbnailUrl); + + let hyperlinkElement = document.createElement("a"); + hyperlinkElement.setAttribute("href", sourceUrl); + hyperlinkElement.setAttribute("rel", "noopener noreferrer"); + hyperlinkElement.setAttribute("target", "_blank"); + + let image_element = document.createElement("img"); + image_element.setAttribute("src", thumbnailUrl); + + hyperlinkElement.appendChild(image_element); + + image.appendChild(blurElement); + image.appendChild(hyperlinkElement); + } +} diff --git a/js/page.ts b/js/page.ts deleted file mode 100644 index 5a98d13..0000000 --- a/js/page.ts +++ /dev/null @@ -1,116 +0,0 @@ -async function loadPage(page_name: string, anchor?: string) { - window.scroll({ - top: 0, - left: 0, - behavior: "smooth", - }); - - console.log(`loading page \`${page_name}\``); - - let url = `/html/${page_name}.html`; - console.log(`note: page is at "${url}"`); - - window.history.pushState(page_name, "", url); - - let response = await fetch(url); - - if (!response.ok) { - throw new Error(`unable to load page: \"${response.status}\"`); - } - - let markup = await response.text(); - - let parser = new DOMParser(); - let dom = parser.parseFromString(markup, "text/html"); - - let titles = document.getElementsByTagName("title"); - let bodies = document.getElementsByTagName("body"); - let page = document.getElementById("page")!; - - if (titles.length < 0x1) { - throw new Error("unable to find title"); - } - - if (bodies.length < 0x1) { - throw new Error("unable to find body"); - } - - if (!page) { - throw new Error("unable to find page element"); - } - - let newTitles = dom.getElementsByTagName("title"); - let newPage = dom.getElementById("page"); - - if (newTitles.length < 0x1) { - throw new Error("unable to find new title"); - } - - if (!newPage) { - throw new Error("unable to find new page element"); - } - - titles[0x0].replaceWith(newTitles[0x0]); - bodies[0x0].setAttribute("data-page", page_name); - page.replaceWith(newPage); - - initImages(); - - if (anchor) { - console.log(`going to anchor \`${anchor}\``); - - anchor = `anchor.${anchor}`; - - console.log(`note: anchor has id "${anchor}"`); - - let anchor_element = document.getElementById(anchor); - - if (!anchor_element) { - throw new Error(`unable to find anchor "${anchor}"`); - } - - anchor_element.scrollIntoView({ - behavior: "smooth", - }); - } -} - -function initImages() { - let page = document.getElementById("page"); - - if (!page) { - throw new Error("unable to find page"); - } - - let imageList = Array.from(page.getElementsByTagName("x-image")); - - for (let image of imageList) { - let file = image.getAttribute("data-file"); - - if (!file) { - throw new Error("file not set for image element") - } - - console.log("initialising image that links to \"" + file + "\""); - - let sourceUrl = "/image/source/" + file + ".webp"; - let thumbnailUrl = "/image/thumbnail/" + file + ".avif"; - - let blurElement = document.createElement("img"); - blurElement.setAttribute("class", "blur"); - blurElement.setAttribute("src", thumbnailUrl); - - let hyperlinkElement = document.createElement("a"); - hyperlinkElement.setAttribute("href", sourceUrl); - hyperlinkElement.setAttribute("rel", "noopener noreferrer"); - hyperlinkElement.setAttribute("target", "_blank"); - - let image_element = document.createElement("img"); - image_element.setAttribute("src", thumbnailUrl); - - hyperlinkElement.appendChild(image_element); - - image.appendChild(blurElement); - image.appendChild(hyperlinkElement); - } -} diff --git a/svg/hamburger.svg b/svg/hamburger.svg index 41b1c34..6ecae15 100644 --- a/svg/hamburger.svg +++ b/svg/hamburger.svg @@ -1,8 +1,8 @@ <svg height="15" width="15" xmlns="http://www.w3.org/2000/svg"> <mask id="glyph"> - <rect fill="white" height="3" rx="1" width="15" x="0" y="0" /> - <rect fill="white" height="3" rx="1" width="15" x="0" y="6" /> - <rect fill="white" height="3" rx="1" width="15" x="0" y="12" /> + <rect fill="white" height="3" rx="1" width="100%" x="0" y="0" /> + <rect fill="white" height="3" rx="1" width="100%" x="0" y="6" /> + <rect fill="white" height="3" rx="1" width="100%" x="0" y="12" /> </mask> <rect fill="#FFFFFF" mask="url(#glyph)" height="100%" width="100%" x="0" y="0" /> |