mirror of
https://codeberg.org/vlw/vlw.se.git
synced 2025-09-14 05:13:46 +02:00
Compare commits
3 commits
1de2f897e0
...
96ada4b66c
Author | SHA1 | Date | |
---|---|---|---|
96ada4b66c | |||
4de391fbdb | |||
21fe68754f |
10 changed files with 270 additions and 253 deletions
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
--padding: 20px;
|
--padding: 20px;
|
||||||
--running-size: 80px;
|
--running-size: 80px;
|
||||||
|
--header-search-size: var(--running-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* # Cornerstones */
|
/* # Cornerstones */
|
||||||
|
@ -94,15 +95,23 @@ h3 {
|
||||||
/* ## Buttons */
|
/* ## Buttons */
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
color: inherit;
|
||||||
|
fill: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ### Inline */
|
||||||
|
|
||||||
|
button.inline {
|
||||||
padding: calc(var(--padding) / 2) var(--padding);
|
padding: calc(var(--padding) / 2) var(--padding);
|
||||||
color: white;
|
color: white;
|
||||||
border: solid 2px white;
|
border: solid 2px white;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button.solid {
|
button.inline.solid {
|
||||||
color: black;
|
color: black;
|
||||||
border-color: var(--color-accent);
|
border-color: var(--color-accent);
|
||||||
background-color: var(--color-accent);
|
background-color: var(--color-accent);
|
||||||
|
@ -112,6 +121,8 @@ a > button::after {
|
||||||
content: " ➜";
|
content: " ➜";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ### Text links */
|
||||||
|
|
||||||
a[target="_blank"] > button::after,
|
a[target="_blank"] > button::after,
|
||||||
:is(h1, h2, h3, p, li) > a[target="_blank"]::after {
|
:is(h1, h2, h3, p, li) > a[target="_blank"]::after {
|
||||||
content: " ↑";
|
content: " ↑";
|
||||||
|
@ -135,13 +146,31 @@ header {
|
||||||
border-bottom: var(--border-style);
|
border-bottom: var(--border-style);
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
justify-items: end;
|
grid-template-columns: 1fr var(--header-search-size) var(--running-size);
|
||||||
grid-template-columns: 1fr var(--running-size);
|
|
||||||
grid-template-rows: var(--running-size);
|
grid-template-rows: var(--running-size);
|
||||||
background-color: rgba(0, 0, 0, .8);
|
background-color: rgba(0, 0, 0, .8);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
perspective: 3000px;
|
||||||
-webkit-backdrop-filter: blur(20px);
|
-webkit-backdrop-filter: blur(20px);
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > * {
|
||||||
|
--anim-3d-depth: 5px;
|
||||||
|
--anim-3d-peek: 25deg;
|
||||||
|
|
||||||
|
transition: 300ms background-color;
|
||||||
|
transform: rotateX(0deg);
|
||||||
|
backface-visibility: hidden;
|
||||||
|
box-shadow: 0 var(--anim-3d-depth) 0 0 rgba(255, 255, 255, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable 3d flip animation */
|
||||||
|
@media not (prefers-reduced-motion: reduce) {
|
||||||
|
header > * {
|
||||||
|
transition: 600ms transform, 300ms background-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header nav {
|
header nav {
|
||||||
|
@ -150,27 +179,90 @@ header nav {
|
||||||
padding: var(--padding);
|
padding: var(--padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
header .logo {
|
|
||||||
width: calc(var(--running-size) - 1px);
|
|
||||||
height: calc(var(--running-size) - 1px);
|
|
||||||
display: grid;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
border-left: var(--border-style);
|
|
||||||
}
|
|
||||||
|
|
||||||
header .logo path.stroke {
|
header .logo path.stroke {
|
||||||
fill: var(--color-accent);
|
fill: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
header searchbox {
|
header header .search {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ### Buttons */
|
||||||
|
|
||||||
|
header button {
|
||||||
|
--icon-size: 25px;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
border-left: var(--border-style);
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
padding: var(--padding);
|
||||||
|
gap: var(--padding);
|
||||||
|
fill: var(--color-accent);
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(255, 255, 255, .5);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
header button:not(.logo) svg {
|
||||||
|
width: var(--icon-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
header button.search p {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ### Searchbox */
|
||||||
|
|
||||||
|
header searchbox {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: var(--running-size);
|
||||||
|
background-color: var(--color-accent);
|
||||||
|
display: grid;
|
||||||
|
align-items: stretch;
|
||||||
|
grid-template-columns: 1fr var(--running-size);
|
||||||
|
grid-template-rows: var(--running-size);
|
||||||
|
box-shadow: none;
|
||||||
|
transform: rotateX(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
header searchbox > * {
|
||||||
|
box-shadow: 0 calc(var(--anim-3d-depth) * -1) 0 0 rgba(var(--primer-color-accent), .8);
|
||||||
|
}
|
||||||
|
|
||||||
|
header searchbox button {
|
||||||
|
transition: 300ms background-color, 300ms border-color;
|
||||||
|
border-color: rgba(0, 0, 0, .1);
|
||||||
|
fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
header searchbox input {
|
||||||
|
padding: 0 var(--padding);
|
||||||
|
background-color: transparent;
|
||||||
|
outline: none;
|
||||||
|
color: black;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #### Active */
|
||||||
|
|
||||||
|
header.searchboxActive > * {
|
||||||
|
transform: rotateX(-180deg);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.searchboxActive searchbox {
|
||||||
|
transform: rotateX(0);
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
/* ## Main */
|
/* ## Main */
|
||||||
|
|
||||||
main {
|
main {
|
||||||
transition: 400ms transform;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: calc(var(--padding) * 1.5);
|
padding: calc(var(--padding) * 1.5);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -186,105 +278,49 @@ main.loading > * {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ## Search */
|
/* ## Search results */
|
||||||
|
|
||||||
/* ### Box */
|
search-results {
|
||||||
|
transition: 500ms opacity, 300ms transform;
|
||||||
searchbox {
|
position: fixed;
|
||||||
--icon-size: 25px;
|
top: var(--running-size);
|
||||||
|
right: 0;
|
||||||
display: grid;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-left: var(--border-style);
|
|
||||||
grid-template-columns: var(--icon-size) 1fr;
|
|
||||||
align-items: center;
|
|
||||||
padding: var(--padding);
|
padding: var(--padding);
|
||||||
gap: var(--padding);
|
height: calc(100svh - var(--running-size));
|
||||||
fill: var(--color-accent);
|
background-color: black;
|
||||||
font-size: 13px;
|
pointer-events: none;
|
||||||
color: rgba(255, 255, 255, .5);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
searchbox > svg {
|
|
||||||
width: var(--icon-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ### Dialog */
|
|
||||||
|
|
||||||
body.search-dialog-open main {
|
|
||||||
transform: scale(.94);
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.search {
|
|
||||||
transition: 200ms height cubic-bezier(.41,0,.34,.99);
|
|
||||||
margin: auto;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1000px;
|
|
||||||
height: calc(var(--running-size) + (var(--padding) * 5));
|
|
||||||
max-height: 1000px;
|
|
||||||
border-color: transparent;
|
|
||||||
background-color: transparent;
|
|
||||||
overflow: visible;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.search.active {
|
|
||||||
height: 70vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.search search {
|
|
||||||
transition: 400ms transform, 200ms opacity;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: var(--running-size) 1fr;
|
|
||||||
gap: calc(var(--padding) * 2);
|
|
||||||
transform: scale(1.1);
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: rgba(255, 255, 255, .05);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
backdrop-filter: brightness(.3) blur(20px);
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 10px 30px 10px black;
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
transform: scale(.99);
|
||||||
|
transform-origin: 100% 0;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.search-dialog-open dialog.search search {
|
search-results:not([vv-page]) {
|
||||||
transform: scale(1);
|
display: grid;
|
||||||
padding: calc(var(--padding) * 1.5);
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.searchboxActive ~ search-results {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
search input {
|
/* ### "Start typing" prompt */
|
||||||
transition: 200ms background-color, 200ms box-shadow, 200ms color;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
color: black;
|
|
||||||
font-size: 16px;
|
|
||||||
padding: var(--padding) calc(var(--padding) * 1.5);
|
|
||||||
background-color: rgba(255, 255, 255, .05);
|
|
||||||
box-shadow: 0 5px 70px 10px rgba(0, 0, 0, .3);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
search input:focus {
|
search-results .info {
|
||||||
background-color: rgba(255, 255, 255, .9);
|
display: flex;
|
||||||
box-shadow: 0 10px 30px 10px black;
|
align-items: center;
|
||||||
color: black;
|
flex-direction: column;
|
||||||
}
|
|
||||||
|
|
||||||
/* ### Search results */
|
|
||||||
|
|
||||||
dialog.search search search-results {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.search search search-results > svg {
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 150px;
|
gap: 3svh;
|
||||||
fill: rgba(255, 255, 255, .05);
|
}
|
||||||
|
|
||||||
|
search-results .info :is(svg, img) {
|
||||||
|
width: 128px;
|
||||||
|
fill: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* # Feature queries */
|
/* # Feature queries */
|
||||||
|
@ -298,7 +334,7 @@ dialog.search search search-results > svg {
|
||||||
|
|
||||||
/* # Components */
|
/* # Components */
|
||||||
|
|
||||||
button {
|
button.inline {
|
||||||
transition: 200ms background-color, 200ms border-color, 200ms color;
|
transition: 200ms background-color, 200ms border-color, 200ms color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,18 +356,31 @@ dialog.search search search-results > svg {
|
||||||
fill: var(--color-accent);
|
fill: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchbox {
|
header searchbox button:hover {
|
||||||
transition: 200ms background-color;
|
background-color: rgba(0, 0, 0, .08);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchbox:hover {
|
/* ### Search */
|
||||||
background-color: rgba(255, 255, 255, .07);
|
|
||||||
|
@media not (prefers-reduced-motion: reduce) {
|
||||||
|
header:not(.searchboxActive) button.search:hover,
|
||||||
|
header:not(.searchboxActive) button.search:hover + button.logo {
|
||||||
|
transform: rotateX(calc(var(--anim-3d-peek) * -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
header:not(.searchboxActive) button.search:hover ~ searchbox {
|
||||||
|
transform: rotateX(calc(180deg - var(--anim-3d-peek)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* # Size queries */
|
/* # Size queries */
|
||||||
|
|
||||||
@media (min-width: 700px) {
|
@media (min-width: 700px) {
|
||||||
|
:root {
|
||||||
|
--header-search-size: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
/* # Cornerstones */
|
/* # Cornerstones */
|
||||||
|
|
||||||
body::before {
|
body::before {
|
||||||
|
@ -346,19 +395,49 @@ dialog.search search search-results > svg {
|
||||||
|
|
||||||
/* ## Header */
|
/* ## Header */
|
||||||
|
|
||||||
header {
|
header nav {
|
||||||
grid-template-columns: 1fr 250px var(--running-size);
|
margin: 0 calc(var(--padding) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
header nav {
|
header > button.search {
|
||||||
justify-self: start;
|
grid-template-columns: var(--icon-size) 1fr;
|
||||||
margin: 0 calc(var(--padding) / 2);
|
}
|
||||||
|
|
||||||
|
header > button.search p {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.searchboxActive > nav {
|
||||||
|
transform: rotateX(0deg);
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ### Searchbox */
|
||||||
|
|
||||||
|
header searchbox {
|
||||||
|
width: calc(var(--header-search-size) + var(--running-size));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ### Menu */
|
/* ### Menu */
|
||||||
|
|
||||||
/* Move the search box to the header */
|
/* Move the search box to the header */
|
||||||
header searchbox {
|
header > button.search {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
justify-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-height: 600px) {
|
||||||
|
search-results {
|
||||||
|
top: calc(var(--running-size) + var(--padding));
|
||||||
|
width: 50%;
|
||||||
|
height: calc(100svh - 100px);
|
||||||
|
background-color: rgba(0, 0, 0, .8);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 0 100px 200px rgba(0, 0, 0, 1),
|
||||||
|
0 0 100px 200px rgba(0, 0, 0, 1)
|
||||||
|
;
|
||||||
|
--webkit-backdrop-filter: blur(15px);
|
||||||
|
backdrop-filter: blur(15px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -154,6 +154,10 @@ splash::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
text-shadow: 0 0 10px rgba(var(--primer-color-accent), .4);
|
text-shadow: 0 0 10px rgba(var(--primer-color-accent), .4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.email:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* # Size quries */
|
/* # Size quries */
|
||||||
|
@ -169,8 +173,4 @@ splash::after {
|
||||||
main img {
|
main img {
|
||||||
width: 35vh;
|
width: 35vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
section.search {
|
section.search {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: none;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--padding);
|
gap: var(--padding);
|
||||||
|
@ -21,6 +21,10 @@ section.search {
|
||||||
margin-bottom: calc(var(--padding) * 2);
|
margin-bottom: calc(var(--padding) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main[vv-page="/search"] > section.search {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
section.search form {
|
section.search form {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +35,11 @@ section.search search {
|
||||||
|
|
||||||
section.search input {
|
section.search input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
color: black;
|
||||||
|
outline: none;
|
||||||
|
padding: var(--padding);
|
||||||
|
background-color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
section.search button[type="submit"] {
|
section.search button[type="submit"] {
|
||||||
|
@ -42,10 +51,6 @@ section.search > svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body:not([vv-page="/search"]) section.search {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* # Search results */
|
/* # Search results */
|
||||||
|
|
||||||
section.results .result {
|
section.results .result {
|
||||||
|
@ -54,19 +59,6 @@ section.results .result {
|
||||||
gap: calc(var(--padding) / 2);
|
gap: calc(var(--padding) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- */
|
|
||||||
|
|
||||||
main > svg,
|
|
||||||
dialog.search search search-results > svg {
|
|
||||||
margin: auto;
|
|
||||||
width: 150px;
|
|
||||||
fill: rgba(255, 255, 255, .05);
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.search search search-results .empty {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ## Titles */
|
/* ## Titles */
|
||||||
|
|
||||||
section.title a h2 {
|
section.title a h2 {
|
||||||
|
|
|
@ -1,72 +1,40 @@
|
||||||
new vv.Interactions("document");
|
new vv.Interactions("document", {
|
||||||
|
navigateHome: () => new vv.Navigation("/").navigate(),
|
||||||
const mainElement = document.querySelector(vv._env.MAIN);
|
closeSearchbox: () => document.querySelector("header").classList.remove("searchboxActive"),
|
||||||
|
openSearchbox: () => {
|
||||||
|
document.querySelector("header").classList.add("searchboxActive");
|
||||||
|
// Select searchbox inner input element
|
||||||
|
document.querySelector("searchbox input").focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Crossfade pages on navigation
|
// Crossfade pages on navigation
|
||||||
// Or maybe I shouldn't... hmmm
|
|
||||||
mainElement.addEventListener(vv.Navigation.events.LOADING, () => {
|
|
||||||
mainElement.classList.add("loading");
|
|
||||||
|
|
||||||
// Clean up modified transform-origin if set after search dialog animation
|
|
||||||
mainElement.style.removeProperty("transform-origin");
|
|
||||||
});
|
|
||||||
|
|
||||||
mainElement.addEventListener(vv.Navigation.events.LOADED, () => {
|
|
||||||
[...document.querySelectorAll("dialog")].forEach(element => element.close())
|
|
||||||
|
|
||||||
// Wait 200ms for the page fade-in animation to finish
|
|
||||||
setTimeout(() => mainElement.classList.remove("loading"), 200);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search dialog open/close logic
|
|
||||||
{
|
{
|
||||||
const CLASNAME_DIALOG_OPEN = "search-dialog-open";
|
const mainElement = document.querySelector(vv._env.MAIN);
|
||||||
// Offset in pixels from scroll position when scaling the main element
|
|
||||||
const TRANSFORM_ORIGIN_Y_PADDING = 350;
|
|
||||||
|
|
||||||
const dialog = document.querySelector("dialog.search");
|
mainElement.addEventListener(vv.Navigation.events.LOADING, () => {
|
||||||
|
mainElement.classList.add("loading");
|
||||||
// "Polyfill" for HTMLDialogELement open and close events
|
|
||||||
(new MutationObserver((mutations) => {
|
|
||||||
// There is only one search dialog elemenet
|
|
||||||
const target = mutations[0].target;
|
|
||||||
|
|
||||||
// Set or unset dialog open class on body depending on dialog visibility
|
|
||||||
target.hasAttribute("open")
|
|
||||||
? target.dispatchEvent(new Event("open"))
|
|
||||||
: target.dispatchEvent(new Event("close"));
|
|
||||||
|
|
||||||
}).observe(dialog, { attributes: true }));
|
|
||||||
|
|
||||||
dialog.addEventListener("open", () => {
|
|
||||||
// Scale main element from the current scroll position
|
|
||||||
mainElement.style.setProperty("transform-origin", `50% calc(${window.scrollY}px + ${TRANSFORM_ORIGIN_Y_PADDING}px)`);
|
|
||||||
|
|
||||||
document.body.classList.add(CLASNAME_DIALOG_OPEN);
|
|
||||||
});
|
});
|
||||||
dialog.addEventListener("close", () => document.body.classList.remove(CLASNAME_DIALOG_OPEN));
|
|
||||||
|
|
||||||
// Close search dialog if dialog is clicked outside inner content
|
|
||||||
dialog.addEventListener("click", (event) => event.target === dialog ? dialog.close() : null);
|
|
||||||
|
|
||||||
// Open search dialog when searchbox is clicked
|
mainElement.addEventListener(vv.Navigation.events.LOADED, () => {
|
||||||
document.querySelector("searchbox").addEventListener("click", () => dialog.showModal());
|
// Close searchbox on main page navigation
|
||||||
|
document.querySelector("header").classList.remove("searchboxActive");
|
||||||
|
|
||||||
|
// Wait 200ms for the page fade-in animation to finish
|
||||||
|
setTimeout(() => mainElement.classList.remove("loading"), 200);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search logic
|
// Handle search logic
|
||||||
{
|
{
|
||||||
const searchResultsElement = document.querySelector("search-results");
|
const searchResultsElement = document.querySelector("search-results");
|
||||||
const search = (query) => {
|
|
||||||
new vv.Navigation(`/search?q=${query}`, {
|
|
||||||
carrySearchParams: true
|
|
||||||
}).navigate(searchResultsElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run search on keyup
|
document.querySelector("header input[type='search']").addEventListener("input", (event) => {
|
||||||
document.querySelector("search input").addEventListener("keyup", (event) => search(event.target.value));
|
// Debounce user input
|
||||||
|
clearTimeout(event.target._throttle);
|
||||||
// Trigger expand search box animation
|
event.target._throttle = setTimeout(() => {
|
||||||
document.querySelector("search input").addEventListener("keydown", () => {
|
// Navigate search-results element on user input
|
||||||
searchResultsElement.closest("dialog").classList.add("active");
|
new vv.Navigation(`/search?q=${event.target.value}`).navigate(searchResultsElement);
|
||||||
}, { once: true });
|
}, 100);
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -1,25 +1 @@
|
||||||
// Don't open the search dialog overlay if search page is open stand-alone
|
|
||||||
{
|
|
||||||
const searchBox = document.querySelector("body:not(.search-dialog-open) searchbox");
|
|
||||||
|
|
||||||
// Page is stand-alone
|
|
||||||
if (searchBox) {
|
|
||||||
// Shift focus to the on-page search box instead of opening search dialog on click
|
|
||||||
const shiftSearchboxFocus = () => {
|
|
||||||
// Override normal "open search dialog" behavior
|
|
||||||
document.querySelector("dialog.search").close();
|
|
||||||
|
|
||||||
// Shift focus to the on-page search input instead
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind event listener to searchbox element
|
|
||||||
document.querySelector("body:not(.search-dialog-open) searchbox").addEventListener("click", shiftSearchboxFocus, true);
|
|
||||||
|
|
||||||
// Remove event listener from searchbox element on page navigation
|
|
||||||
mainElement.addEventListener(vv.Navigation.events.LOADING, () => {
|
|
||||||
searchBox.removeEventListener("click", shiftSearchboxFocus);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new vv.Interactions("search");
|
new vv.Interactions("search");
|
BIN
assets/media/travolta.gif
Normal file
BIN
assets/media/travolta.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 238 KiB |
|
@ -49,8 +49,8 @@
|
||||||
<h3>encrypt your message with my OpenPGP key.</h3>
|
<h3>encrypt your message with my OpenPGP key.</h3>
|
||||||
<p>my key is also listed on the <a href="https://keys.openpgp.org/search?q=victor%40vlw.se" target="_blank" rel="noopener noreferer">openPGP key server</a> for victor@vlw.se so your e-mail client can automatically retreive it if supported.</p>
|
<p>my key is also listed on the <a href="https://keys.openpgp.org/search?q=victor%40vlw.se" target="_blank" rel="noopener noreferer">openPGP key server</a> for victor@vlw.se so your e-mail client can automatically retreive it if supported.</p>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<a href="https://keys.openpgp.org/vks/v1/by-fingerprint/DCE987311CB5D2A252F58951D0AD730E1057DFC6"><button class="solid">download ASC</button></a>
|
<a href="https://keys.openpgp.org/vks/v1/by-fingerprint/DCE987311CB5D2A252F58951D0AD730E1057DFC6"><button class="inline solid">download ASC</button></a>
|
||||||
<a href="https://emailselfdefense.fsf.org/en/" target="_blank" rel="noopener noreferer"><button>more info</button></a>
|
<a href="https://emailselfdefense.fsf.org/en/" target="_blank" rel="noopener noreferer"><button class="inline">more info</button></a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<?= VV::media("line.svg") ?>
|
<?= VV::media("line.svg") ?>
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
<label title="this field is required">your message<sup>*</sup></label>
|
<label title="this field is required">your message<sup>*</sup></label>
|
||||||
<textarea name="<?= ContactFieldsEnum::MESSAGE->value ?>" required placeholder="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed molestie dignissim mauris vel dignissim. Sed et aliquet odio, id egestas libero. Vestibulum ut dui a turpis aliquam hendrerit id et dui. Morbi eu tristique quam, sit amet dictum felis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ac nibh a ex accumsan ullamcorper non quis eros. Nam at suscipit lacus. Nullam placerat semper sapien, vitae aliquet nisl elementum a. Duis viverra quam eros, eu vestibulum quam egestas sit amet. Duis lobortis varius malesuada. Mauris in fringilla mi. "></textarea>
|
<textarea name="<?= ContactFieldsEnum::MESSAGE->value ?>" required placeholder="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed molestie dignissim mauris vel dignissim. Sed et aliquet odio, id egestas libero. Vestibulum ut dui a turpis aliquam hendrerit id et dui. Morbi eu tristique quam, sit amet dictum felis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ac nibh a ex accumsan ullamcorper non quis eros. Nam at suscipit lacus. Nullam placerat semper sapien, vitae aliquet nisl elementum a. Duis viverra quam eros, eu vestibulum quam egestas sit amet. Duis lobortis varius malesuada. Mauris in fringilla mi. "></textarea>
|
||||||
</input-group>
|
</input-group>
|
||||||
<button class="solid">send</button>
|
<button class="inline solid">send</button>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<!--//--><![CDATA[//><!--
|
<!--//--><![CDATA[//><!--
|
||||||
|
@ -44,25 +44,25 @@
|
||||||
<nav>
|
<nav>
|
||||||
<p><a href="/" vv="document" vv-call="navigate">victor westerlund</a></p>
|
<p><a href="/" vv="document" vv-call="navigate">victor westerlund</a></p>
|
||||||
</nav>
|
</nav>
|
||||||
<searchbox>
|
<button class="search" vv="document" vv-call="openSearchbox">
|
||||||
<?= VV::media("icons/search.svg") ?>
|
<?= VV::media("icons/search.svg") ?>
|
||||||
<p>search anything...</p>
|
<p>search vlw.se...</p>
|
||||||
|
</button>
|
||||||
|
<button class="logo" vv="document" vv-call="navigateHome"><?= VV::media("vw.svg") ?></button>
|
||||||
|
<searchbox>
|
||||||
|
<input type="search" autocomplete="off" placeholder="search vlw.se...">
|
||||||
|
<button class="close" vv="document" vv-call="closeSearchbox"><?= VV::media("icons/close.svg") ?></button>
|
||||||
</searchbox>
|
</searchbox>
|
||||||
<a href="/" vv="document" vv-call="navigate">
|
|
||||||
<div class="logo">
|
|
||||||
<?= VV::media("vw.svg") ?>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main></main>
|
<main></main>
|
||||||
|
|
||||||
<dialog class="search">
|
<search-results>
|
||||||
<search>
|
<div class="info empty">
|
||||||
<input type="text" placeholder="start typing to search..."></input>
|
<?= VV::media("icons/search.svg") ?>
|
||||||
<search-results></search-results>
|
<p>start typing to search</p>
|
||||||
</search>
|
</div>
|
||||||
</dialog>
|
</search-results>
|
||||||
|
|
||||||
<?php // Bootstrapping ?>
|
<?php // Bootstrapping ?>
|
||||||
<script><?= VV::init() ?></script>
|
<script><?= VV::init() ?></script>
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
<section class="search">
|
<section class="search">
|
||||||
<form method="GET">
|
<form method="GET">
|
||||||
<search>
|
<search>
|
||||||
<input name="q" type="text" placeholder="search anything..." value="<?= $query ?>"></input>
|
<input name="q" type="text" placeholder="search vlw.se..." value="<?= $query ?>"></input>
|
||||||
</search>
|
</search>
|
||||||
<button type="submit" class="solid">Search</button>
|
<button type="submit" class="inline solid">Search</button>
|
||||||
</form>
|
</form>
|
||||||
<?= VV::media("line.svg") ?>
|
<?= VV::media("line.svg") ?>
|
||||||
<button>advanced search options</button>
|
<button class="inline">advanced search options</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if ($response): ?>
|
<?php if ($response): ?>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
<?php // Do things depending on the response code from API ?>
|
<?php // Do things depending on the response code from API ?>
|
||||||
<?php switch ($response->code): default: ?>
|
<?php switch ($response->code): default: ?>
|
||||||
<?php // An unknown error occured ?>
|
<?php // An unknown error occured ?>
|
||||||
<section class="error">
|
<section class="info">
|
||||||
<p>Something went wrong</p>
|
<p>Something went wrong</p>
|
||||||
</section>
|
</section>
|
||||||
<?php break; ?>
|
<?php break; ?>
|
||||||
|
@ -69,9 +69,9 @@
|
||||||
<?php foreach ($result["actions"] as $action): ?>
|
<?php foreach ($result["actions"] as $action): ?>
|
||||||
|
|
||||||
<?php if (!$action["external"]): ?>
|
<?php if (!$action["external"]): ?>
|
||||||
<a href="<?= $action["href"] ?>" vv="search" vv-call="navigate"><button class="<?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
<a href="<?= $action["href"] ?>" vv="search" vv-call="navigate"><button class="inline <?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a href="<?= $action["href"] ?>" target="_blank"><button class="<?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
<a href="<?= $action["href"] ?>" target="_blank"><button class="inline <?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
@ -87,7 +87,8 @@
|
||||||
|
|
||||||
<?php // No search matches were found ?>
|
<?php // No search matches were found ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<section class="empty">
|
<section class="info noresults">
|
||||||
|
<img src="/assets/media/travolta.gif" alt="">
|
||||||
<p>No results for search term "<?= $_GET["q"] ?>"</p>
|
<p>No results for search term "<?= $_GET["q"] ?>"</p>
|
||||||
</section>
|
</section>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
@ -96,7 +97,7 @@
|
||||||
|
|
||||||
<?php // No access to the search endpoint ?>
|
<?php // No access to the search endpoint ?>
|
||||||
<?php case 404: ?>
|
<?php case 404: ?>
|
||||||
<section class="error">
|
<section class="info">
|
||||||
<p>Connection to VLW API was successful but lacking permission to search</p>
|
<p>Connection to VLW API was successful but lacking permission to search</p>
|
||||||
</section>
|
</section>
|
||||||
<?php break; ?>
|
<?php break; ?>
|
||||||
|
@ -109,15 +110,16 @@
|
||||||
|
|
||||||
<?php // Check the error code of the current error ?>
|
<?php // Check the error code of the current error ?>
|
||||||
<?php switch ($error_code): default: ?>
|
<?php switch ($error_code): default: ?>
|
||||||
<section class="error">
|
<section class="info">
|
||||||
<p>Unknown request validation error</p>
|
<p>Unknown request validation error</p>
|
||||||
</section>
|
</section>
|
||||||
<?php break; ?>
|
<?php break; ?>
|
||||||
|
|
||||||
<?php // Search query string is not long enough ?>
|
<?php // Search query string is not long enough ?>
|
||||||
<?php case "VALUE_MIN_ERROR": ?>
|
<?php case "VALUE_MIN_ERROR": ?>
|
||||||
<section class="error">
|
<section class="info">
|
||||||
<p>Type at least <?= $error_msg ?> characters to search!</p>
|
<?= VV::media("icons/search.svg") ?>
|
||||||
|
<p>type at least <?= $error_msg ?> characters to search!</p>
|
||||||
</section>
|
</section>
|
||||||
<?php break; ?>
|
<?php break; ?>
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
<?= VV::media("icons/github.svg") ?>
|
<?= VV::media("icons/github.svg") ?>
|
||||||
<p>Most of my free open-source software is available on GitHub and it's also mirrored on my server</p>
|
<p>Most of my free open-source software is available on GitHub and it's also mirrored on my server</p>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<a href="https://github.com/victorwesterlund"><button class="solid">open GitHub</button></a>
|
<a href="https://github.com/victorwesterlund"><button class="inline solid">open GitHub</button></a>
|
||||||
<a href="https://git.vlw.se"><button>mirror</button></a>
|
<a href="https://git.vlw.se"><button class="inline">mirror</button></a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
$link_href = $action["href"] === null ? "/work/{$item["id"]}" : $action["href"];
|
$link_href = $action["href"] === null ? "/work/{$item["id"]}" : $action["href"];
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<a href="<?= $link_href ?>" <?= $link_attr ?>><button class="<?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
<a href="<?= $link_href ?>" <?= $link_attr ?>><button class="inline <?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
Loading…
Add table
Reference in a new issue