dev21w34a

This commit is contained in:
Victor Westerlund 2021-08-29 16:30:10 +02:00
parent 6645f2b672
commit 49496c9bdc
6 changed files with 191 additions and 344 deletions

View file

@ -1 +0,0 @@
www.victorwesterlund.com

View file

@ -1,17 +0,0 @@
@font-face {
font-family: "Roboto Mono";
font-weight: 400;
src: local("Roboto Mono Regular"),
local("RobotoMono-Regular"),
url("../fonts/RobotoMono-Regular.woff2"),
url("../fonts/RobotoMono-Regular.ttf");
}
@font-face {
font-family: "Roboto Mono";
font-weight: 700;
src: local("Roboto Mono Bold"),
local("RobotoMono-Bold"),
url("../fonts/RobotoMono-Bold.woff2"),
url("../fonts/RobotoMono-Bold.ttf");
}

View file

@ -1,15 +1,43 @@
@import url("fonts.css"); /* Copyright © Victor Westerlund - No libraries! 😲 */
:root { :root {
--comp-background: 255,255,255; --comp-background: 255,255,255;
--comp-highlight: 244,242,255; --comp-contrast: 33,33,33;
--comp-accent: 33,33,33; --comp-accent: 22,183,255;
--color-background: rgb(var(--comp-background)); --color-background: rgb(var(--comp-background));
--color-highlight: rgb(var(--comp-highlight)); --color-contrast: rgb(var(--comp-contrast));
--color-accent: rgb(var(--comp-accent)); --color-accent: rgb(var(--comp-accent));
--page-padding: 5vw; --padding: 20px;
--border-radius: 10px;
}
.dark {
--comp-background: 33,33,33;
--comp-contrast: 255,255,255;
--comp-accent: 255,255,255;
--color-background: rgb(var(--comp-background));
--color-contrast: rgb(var(--comp-contrast));
--color-accent: rgb(var(--comp-accent));
}
@font-face {
font-family: "Roboto Mono";
font-weight: 400;
src: local("Roboto Mono Regular"),
local("RobotoMono-Regular"),
url("../fonts/RobotoMono-Regular.woff2"),
url("../fonts/RobotoMono-Regular.ttf");
}
@font-face {
font-family: "Roboto Mono";
font-weight: 700;
src: local("Roboto Mono Bold"),
local("RobotoMono-Bold"),
url("../fonts/RobotoMono-Bold.woff2"),
url("../fonts/RobotoMono-Bold.ttf");
} }
/* -- Cornerstones -- */ /* -- Cornerstones -- */
@ -17,7 +45,7 @@
* { * {
margin: 0; margin: 0;
font-family: "Roboto Mono","Arial",sans-serif; font-family: "Roboto Mono","Arial",sans-serif;
color: var(--color-accent); color: var(--color-contrast);
} }
*::selection { *::selection {
@ -26,17 +54,51 @@
} }
html, html,
body, body {
main,
main > div {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
}
main {
width: 200vw;
height: 100%;
overflow: hidden;
display: flex;
}
main.active {
transform: translateX(-100vw);
}
/* ---- */
.screen {
width: 100vw;
background-color: var(--color-background);
display: flex;
flex-direction: column;
}
.screen .content {
box-sizing: border-box;
padding: calc(var(--padding) * 1.5);
padding-top: 0;
flex-grow: 1;
}
/* -- Positioning -- */
.center {
display: flex;
justify-content: center;
align-items: center;
} }
/* ---- */ /* ---- */
.logo { .logo {
--size: 5em; --size: 1em;
--skew: calc(var(--size) / 1.7); --skew: calc(var(--size) / 1.7);
width: 0; width: 0;
@ -51,192 +113,100 @@ main > div {
border-top: var(--size) solid rgba(var(--comp-accent),.3); border-top: var(--size) solid rgba(var(--comp-accent),.3);
} }
h1 {
font-size: 2em;
}
h2 {
font-weight: normal;
font-size: 1.42em;
}
h2 span {
color: var(--color-background);
background-color: var(--color-accent);
}
/* -- Layout -- */
main {
--grid-spacing: 3vh;
margin: auto;
max-width: 200vh;
background: radial-gradient(circle, rgba(var(--comp-accent),.2) .1vh, var(--color-background) .1vh),url("../img/pattern.gif");
background-size: var(--grid-spacing) var(--grid-spacing),auto 90%;
background-position: 100% 100%;
background-repeat: repeat,no-repeat;
background-blend-mode: multiply;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
}
main * {
image-rendering: initial;
}
main > div {
position: absolute;
width: 50%;
height: 100%;
}
/* ---- */ /* ---- */
#intro { .button {
left: 0; background-color: var(--color-contrast);
width: 100vh; text-align: center;
padding: 25px;
border-radius: var(--border-radius);
display: flex;
justify-content: center;
align-items: center;
gap: var(--padding);
}
.button svg {
pointer-events: none;
fill: var(--color-contrast);
transform: scale(1.2);
}
.button p {
pointer-events: none;
font-size: clamp(16px,5vw,22px);
color: var(--color-background);
}
.button.phantom {
background-color: rgba(var(--comp-contrast),.1);
}
.button.phantom p {
color: var(--color-contrast);
}
/* -- Screens -- */
header {
--size: 100px;
box-sizing: border-box; box-sizing: border-box;
padding: var(--page-padding); padding: var(--padding);
height: var(--size);
display: flex; display: flex;
align-items: center; align-items: center;
z-index: 1; gap: var(--padding);
}
#intro .inner {
display: flex;
height: 100%;
flex-direction: column;
justify-content: space-between;
}
#intro .logo {
--size: 7vh;
margin-bottom: calc(var(--page-padding) / 2);
}
#intro .block {
width: 100%;
font-size: 3vh;
}
#intro .block:last-child {
display: flex;
align-items: flex-end;
}
#intro .block p {
font-size: .7em;
margin-bottom: 1.5em;
}
#intro .block p span {
margin-right: .9em;
background-color: var(--color-background);
}
/* ---- */
nav a {
text-decoration: none;
margin-right: 2em;
font-size: .9em;
}
nav a::after {
content: "→";
padding-left: .5em;
}
/* ---- */
#myface {
right: 0;
width: 50%;
display: flex;
justify-content: center;
}
#myface picture,
#myface img {
position: absolute;
height: 80%;
bottom: 0;
pointer-events: none;
display: flex;
justify-content: center;
}
#myface img {
height: 100%;
}
/* -- Size Queries -- */
@media (max-width: 1100px) {
:root {
--page-padding: 50px;
}
h1 {
font-size: 7vw;
}
h2 {
font-size: 5vw;
}
/* ---- */
main {
max-width: unset;
background-position: 100% 0;
background-size: var(--grid-spacing) var(--grid-spacing), auto 100vw;
}
/* ---- */
#intro {
width: initial;
}
#intro .logo {
--size: 10vw;
}
#intro p {
text-align: left;
}
#intro .block p:last-of-type {
text-align: initial;
}
#myface {
display: none;
}
/* ---- */
nav a {
text-decoration: none;
margin-right: 5vw;
font-size: 5vw;
}
}
/* -- Accessibility -- */
@media (hover: hover) {
nav a:hover {
background: var(--color-highlight);
font-weight: bold; font-weight: bold;
} }
header .hamburger {
width: calc(var(--size) - (var(--padding) * 2));
height: calc(var(--size) - (var(--padding) * 2));
box-sizing: border-box;
flex-shrink: 0;
padding: 15px;
} }
@media (prefers-color-scheme: dark) { header .hamburger div {
:root { width: 100%;
--comp-background: 0,0,0; height: 2px;
--comp-accent: 255,255,255; background: var(--color-contrast);
box-shadow: 0 -10px 0 0 var(--color-contrast), 0 10px 0 0 var(--color-contrast);
} }
header .hamburger svg {
fill: none;
stroke: var(--color-contrast);
stroke-linecap: round;
stroke-width: 2;
}
header .spacer {
width: 1px;
height: 80%;
background-color: rgba(var(--comp-contrast),.2);
}
.dark header .spacer {
background-color: black;
}
header .logo {
--size: 25px;
margin-top: calc(var(--size) / 2);
margin-right: calc(var(--size) / 2);
}
/* -- Screen > Landingpage -- */
/* -- Screen > Menu -- */
.screen.menu .content {
display: flex;
flex-direction: column;
gap: 20px;
}
.screen.menu .button[data="contact"] {
margin-top: auto;
} }

View file

@ -1,19 +1,12 @@
// Register SW if supported by browser function toggleMenu() {
if(navigator.serviceWorker) { const speed = 200;
navigator.serviceWorker.register("sw.js",{ const menu = document.getElementsByTagName("main")[0];
scope: "/"
}); menu.style.setProperty("transition",`${speed}ms`);
menu.classList.toggle("active");
setTimeout(() => menu.style.removeProperty("transition"),speed + 1);
} }
const theme = window.matchMedia("(prefers-color-scheme: dark)"); for(const element of document.getElementsByClassName("hamburger")) {
element.addEventListener("click",() => toggleMenu());
// Set theme color
function updateTheme() {
// Get theme color from stylesheet
const color = window.getComputedStyle(document.body).getPropertyValue("--color-background");
document.querySelector("meta[name='theme-color']").setAttribute("content",color);
} }
// Set theme color and listen for changes
theme.addEventListener("change",updateTheme);
updateTheme(theme);

View file

@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Copyright © Victor Westerlund - No libraries! 😲 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -11,34 +12,37 @@
</head> </head>
<body> <body>
<main> <main>
<div id="intro"> <div class="screen landingpage">
<div class="inner"> <header>
<div class="block"> <div class="hamburger center">
<div></div>
</div>
<div class="spacer"></div>
<div class="logo"></div> <div class="logo"></div>
<h1><span>victor westerlund</span></h1> <p>victor westerlund</p>
<h2><span>full-stack web developer</span></h2> </header>
</div> <div class="content">
<div class="block">
<p><span>I create things with code. The things I've created for the public reside as open-source repositories on <a href="https://github.com/VictorWesterlund" ping="https://api.victorwesterlund/log/ping/" target="_blank">GitHub</a>, the rest you'll be lucky to hear about some day.</span></p>
<p><span>Other topics (seemingly irrelevant to programming) I find facinating include but is in no way limited to astronomy, psychology, sociology, economics, ...</span></p>
<p><span><strong>hello@victorwesterlund.com</strong></span>&#8203;(<a href="https://storage.googleapis.com/public.victorwesterlund.com/publickey.gpg" ping="https://api.victorwesterlund/log/ping/" target="_blank">PGP&nbsp;Key</a>)</p>
</div>
<div class="block">
<nav>
<!--<a href="#">contact</a>-->
<a href="https://github.com/VictorWesterlund" ping="https://api.victorwesterlund/log/ping/" target="_blank">github</a>
</nav>
</div> </div>
</div> </div>
<div class="screen menu dark">
<header>
<div class="hamburger center">
<svg xmlns="http://www.w3.org/2000/svg" width="28.548" height="22.828"><path d="M2.28 11.414h25.269M1.414 11.414l10-10M1.414 11.414l10 10"/></svg>
</div>
<div class="spacer"></div>
<div class="logo"></div>
<p>victor westerlund</p>
</header>
<div class="content">
<div class="button phantom">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<p>contact me</p>
</div>
<div class="spacer"></div>
<div class="button phantom" data="contact">
<p>contact me</p>
</div>
</div> </div>
<div id="myface">
<picture>
<source srcset="assets/img/myface/highres.avif" type="image/avif" media="(min-width: 1920px)">
<source srcset="assets/img/myface/highres.webp" type="image/webp" media="(min-width: 1920px)">
<source srcset="assets/img/myface/mediumres.avif" type="image/avif">
<source srcset="assets/img/myface/mediumres.webp" type="image/webp">
<img src="assets/img/myface/mediumres.png" type="image/png">
</picture>
</div> </div>
</main> </main>
<script src="assets/js/script.js" type="module"></script> <script src="assets/js/script.js" type="module"></script>

View file

@ -1,102 +0,0 @@
const version = "1628250171";
const root = "/";
let activeCaches = [
`content-${version}`
];
let cacheManifest = [
"index.html",
"assets/css/style.css",
"assets/css/fonts.css",
"assets/img/favicon.png",
"assets/img/pattern.gif",
"assets/fonts/RobotoMono-Bold.woff2",
"assets/fonts/RobotoMono-Regular.woff2",
"assets/js/script.js"
];
// Download assets and install ServiceWorker
self.addEventListener("install", event => {
event.waitUntil(
caches.open(`content-${version}`).then(cache => cache.addAll(cacheManifest.map(asset => {
// Append the root path to all assets
return root + asset;
})))
)
});
// Wipe old assets from Cache Storage
self.addEventListener("activate", event => {
event.waitUntil(
// Delete inactive caches
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if(!activeCaches.includes(cacheName)) {
return caches.delete(cacheName);
}
})
)
})
)
});
// Fetch and cache an asset not defined in the manifest
async function fetchToCache(event) {
const networkFetch = fetch(event.request);
event.waitUntil(
networkFetch.then(response => {
const responseClone = response.clone();
caches.open("bucket").then(cache => cache.put(event.request, responseClone));
})
);
const response = await caches.match(event.request);
return response || networkFetch;
}
// Fetch and follow redirects without caching
async function fetchContent(url,i = 0) {
if(i >= 5) {
throw new Error("ERR_TOO_MANY_REDIRECTS");
}
return await fetch(url).then(response => {
if(response.redirected) {
i++;
return fetchContent(response.url,i);
}
return response;
});
}
self.addEventListener("fetch", event => {
const url = new URL(event.request.url);
const origin = (url.origin == location.origin) ? true : false; // Is same-origin
// Speed up TTFB by serving index file first
if(origin && url.pathname == "/") {
event.respondWith(caches.match(root + "index.html"));
return;
}
// Fetch cross-origin content using the network
if(!origin || (url.pathname.substring(1,7) != "assets")) {
event.respondWith(fetchContent(url.href));
return;
}
// Get pattern.gif from generator. Fallback to cache on failure
if(origin && url.pathname.endsWith("pattern.gif")) {
const pattern = new Request(`${location.origin}${root}assets/img/pattern.php`);
event.respondWith(fetch(pattern).catch(() => caches.match(root + "assets/img/pattern.gif")));
return;
}
// Respond with content from cache or fetch and save
event.respondWith(
caches.match(event.request).then(response => response || fetchToCache(event))
);
});