mirror of
https://codeberg.org/vlw/victorwesterlund.com.git
synced 2025-09-14 11:33:41 +02:00
feat: add core for 'simple' concept
This commit is contained in:
parent
7ce5ceea9e
commit
9f36aee9f0
8 changed files with 320 additions and 190 deletions
|
@ -1,205 +1,209 @@
|
||||||
:root {
|
|
||||||
--color-base: 0, 0, 0;
|
|
||||||
--color-contrast: 256, 256, 256;
|
|
||||||
|
|
||||||
--padding: clamp(40px, 2vw, 2vw);
|
|
||||||
--border-size: clamp(4px, .25vw, .25vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- Cornerstones -- */
|
/* -- Cornerstones -- */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primer-color-base: 255, 255, 255;
|
||||||
|
--primer-color-contrast: 0, 0, 0;
|
||||||
|
|
||||||
|
--color-base: rgb(var(--primer-color-base));
|
||||||
|
--color-contrast: rgb(var(--primer-color-contrast));
|
||||||
|
|
||||||
|
--padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: "Monaco", "Consolas", monospace, sans-serif;
|
box-sizing: border-box;
|
||||||
color: rgb(var(--color-contrast));
|
font-family: 'Courier', sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::selection {
|
::selection {
|
||||||
background-color: rgb(var(--color-contrast));
|
background-color: var(--color-contrast);
|
||||||
color: rgb(var(--color-base));
|
color: var(--color-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
::placeholder {
|
||||||
|
color: rgba(var(--primer-color-contrast), .5);
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
body {
|
||||||
width: 100%;
|
display: flex;
|
||||||
height: 100%;
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
align-items: center;
|
||||||
|
color: var(--color-contrast);
|
||||||
|
background-color: var(--color-base);
|
||||||
|
gap: var(--padding);
|
||||||
|
margin: var(--padding) 0;
|
||||||
|
margin-bottom: 30vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
a:not(p > a) {
|
||||||
background-color: rgba(var(--color-base), .7);
|
text-decoration: none;
|
||||||
background-size: cover;
|
|
||||||
background-blend-mode: overlay;
|
|
||||||
background-position: center;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
picture {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: clamp(45px, 7vw, 6vh);
|
|
||||||
}
|
|
||||||
|
|
||||||
p, a {
|
|
||||||
font-size: clamp(20px, 3vw, 2vh);
|
|
||||||
text-align: justify;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Components -- */
|
/* -- Components -- */
|
||||||
|
|
||||||
body {
|
input {
|
||||||
|
letter-spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
.button {
|
||||||
|
background-color: var(--color-contrast);
|
||||||
|
color: var(--color-base);
|
||||||
|
padding: calc(var(--padding) / 2) calc(var(--padding) * 1.5);
|
||||||
|
text-align: left;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-items: center;
|
justify-content: center;
|
||||||
gap: var(--padding, 30px);
|
gap: var(--padding);
|
||||||
|
height: 3.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > div {
|
input,
|
||||||
padding: calc(var(--padding) / 2);
|
.button.phantom {
|
||||||
|
background-color: transparent;
|
||||||
|
border: solid 3px var(--color-contrast);
|
||||||
|
color: var(--color-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(#intro, #card) a {
|
img,
|
||||||
--padding-vert: clamp(17px, 1.1vw, 1.1vw);
|
.button {
|
||||||
|
|
||||||
display: inline-block;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: rgba(var(--color-contrast), .13);
|
|
||||||
backdrop-filter: blur(2px);
|
|
||||||
-webkit-backdrop-filter: blur(2px);
|
|
||||||
box-shadow:
|
|
||||||
inset 0 .3vh 1.6vh rgba(0, 0, 0, 0),
|
|
||||||
0 .1vh .3vh rgba(0, 0, 0, .12),
|
|
||||||
0 .1vh .2vh rgba(0, 0, 0, .24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- */
|
.button :not(p) {
|
||||||
|
height: 2em;
|
||||||
#intro {
|
|
||||||
padding: calc(var(--padding) / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#intro a {
|
.interact::before {
|
||||||
padding: var(--padding-vert) 2vw;
|
content: "tap";
|
||||||
border-radius: 100px;
|
}
|
||||||
border: solid var(--border-size) rgba(var(--color-contrast), 0);
|
|
||||||
|
/* ---- */
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > .spacer {
|
||||||
margin: var(--padding) 0;
|
margin: var(--padding) 0;
|
||||||
width: calc(100% - ((var(--padding) / 2) + var(--border-size)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#intro p {
|
.spacer div {
|
||||||
margin: 1vh 0;
|
width: 205px;
|
||||||
font-size: clamp(20px, 3vw, 3vh);
|
height: 7px;
|
||||||
|
background-color: var(--color-contrast);
|
||||||
|
transform: rotate(-4deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- */
|
/* -- Content -- */
|
||||||
|
|
||||||
#card,
|
section {
|
||||||
#card > div {
|
max-width: 600px;
|
||||||
|
margin: var(--padding) calc(var(--padding) * 1.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: calc(var(--padding) / 2);
|
text-align: justify;
|
||||||
|
gap: var(--padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
#card {
|
section > *,
|
||||||
--portrait-size: clamp(128px, 12vw, 12vh);
|
section picture > img {
|
||||||
|
|
||||||
position: relative;
|
|
||||||
max-width: 600px;
|
|
||||||
padding: var(--padding);
|
|
||||||
border-radius: clamp(18px, 1vw, 1vw);
|
|
||||||
backdrop-filter: saturate(100) brightness(.4);
|
|
||||||
-webkit-backdrop-filter: saturate(100) brightness(.4);
|
|
||||||
border: solid var(--border-size) rgba(var(--color-contrast), .1);
|
|
||||||
box-shadow: 0 1vh 2vh rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);
|
|
||||||
}
|
|
||||||
|
|
||||||
#card img {
|
|
||||||
width: var(--portrait-size);
|
|
||||||
height: var(--portrait-size);
|
|
||||||
position: absolute;
|
|
||||||
border-radius: 100%;
|
|
||||||
top: calc(((var(--portrait-size) / 2) + var(--border-size)) * -1);
|
|
||||||
background-color: rgb(var(--color-base));
|
|
||||||
box-shadow: 0 1vh 2vh rgba(0, 0, 0 , .19), 0 6px 6px rgba(0, 0, 0 , .23);
|
|
||||||
}
|
|
||||||
|
|
||||||
#card a {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--padding-vert) 0;
|
|
||||||
margin-top: calc(var(--padding) / 2);
|
|
||||||
border-radius: clamp(9px, .5vw, .5vw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Media Queries -- */
|
section#intro {
|
||||||
|
max-width: 250px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
@supports ((not ((backdrop-filter: saturate(1)) and (backdrop-filter: brightness(1)))) and (not ((-webkit-backdrop-filter: saturate(1)) and (-webkit-backdrop-filter: brightness(1))))) {
|
section#contact > div {
|
||||||
#card {
|
display: grid;
|
||||||
background-color: rgba(var(--color-base), .7);
|
grid-template-rows: repeat(2, 1fr);
|
||||||
|
gap: var(--padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
section#sky .button img {
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#coffee > img {
|
||||||
|
width: 100px;
|
||||||
|
margin-bottom: calc(var(--padding) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- */
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
width: 100%;
|
||||||
|
background-color: rgba(var(--primer-color-contrast), .05);
|
||||||
|
text-align: center;
|
||||||
|
padding: calc(var(--padding) / 2) 0;
|
||||||
|
margin: var(--padding) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- Media queries -- */
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.button:hover {
|
||||||
|
background-color: rgba(var(--primer-color-contrast), .75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:active {
|
||||||
|
background-color: rgba(var(--primer-color-contrast), .7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- */
|
||||||
|
|
||||||
|
.button.phantom:hover {
|
||||||
|
background-color: rgba(var(--primer-color-contrast), .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.phantom:active {
|
||||||
|
background-color: rgba(var(--primer-color-contrast), .1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (pointer: fine) {
|
@media (pointer: fine) {
|
||||||
:is(#intro, #card) a {
|
.button {
|
||||||
--transition-speed: 200ms;
|
cursor: pointer;
|
||||||
transition:
|
|
||||||
var(--transition-speed) background-color,
|
|
||||||
var(--transition-speed) box-shadow,
|
|
||||||
var(--transition-speed) border-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(#intro, #card) a:hover {
|
.interact::before {
|
||||||
background-color: rgba(var(--color-contrast), .2);
|
content: "click";
|
||||||
border-color: rgba(var(--color-contrast), .2);
|
|
||||||
box-shadow:
|
|
||||||
inset 0 .3vh 1.6vh rgba(0, 0, 0, .16),
|
|
||||||
0 .3vh .6vh rgba(0, 0, 0, .16),
|
|
||||||
0 .3vh .6vh rgba(0, 0, 0, .23);
|
|
||||||
}
|
|
||||||
|
|
||||||
:is(#intro, #card) a:active {
|
|
||||||
background-color: rgba(var(--color-contrast), .15);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 330px) {
|
@media (prefers-color-scheme: dark) {
|
||||||
p, a {
|
:root {
|
||||||
text-align: left;
|
--primer-color-base: 0, 0, 0;
|
||||||
font-size: 18px;
|
--primer-color-contrast: 255, 255, 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
#card {
|
section#coffee > img {
|
||||||
padding: calc(var(--padding) / 2);
|
filter: invert(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-aspect-ratio: 14/9) and (min-height: 600px) {
|
@media (max-width: 303px) {
|
||||||
body {
|
section#sky img {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
section#code {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 40px 1fr;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#contact > div {
|
||||||
|
grid-template-rows: 1fr;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
body > div {
|
|
||||||
display: grid;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
body > div:last-of-type {
|
|
||||||
padding: calc(var(--padding) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#intro a {
|
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#card {
|
|
||||||
min-width: 300px;
|
|
||||||
max-width: 30vw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
15
public/assets/js/modules/Dialog.mjs
Normal file
15
public/assets/js/modules/Dialog.mjs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export class Dialog {
|
||||||
|
constructor(target) {
|
||||||
|
this.dialog = document.createElement("dialog");
|
||||||
|
|
||||||
|
if (typeof this.dialog.showModal !== "function") {
|
||||||
|
throw new Error("Browser does not support HTMLDialogElement");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(this.dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this.dialog.showModal();
|
||||||
|
}
|
||||||
|
}
|
37
public/assets/js/modules/Player.mjs
Normal file
37
public/assets/js/modules/Player.mjs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Create a new AudioPlayer from template
|
||||||
|
export class AudioPlayer extends Audio {
|
||||||
|
constructor(target) {
|
||||||
|
if (!target instanceof HTMLElement) {
|
||||||
|
throw new Error("Target must be an HTMLElement");
|
||||||
|
}
|
||||||
|
|
||||||
|
const src = "src" in target.dataset ? target.dataset.src : "";
|
||||||
|
super(src);
|
||||||
|
|
||||||
|
// Bind interaction with the player as play/stop
|
||||||
|
target.addEventListener("click", () => this.playState());
|
||||||
|
|
||||||
|
// Start playback when ready
|
||||||
|
this.addEventListener("canplaythrough", () => this.play(), { once: true });
|
||||||
|
// Restart playback if paused (treat as stop)
|
||||||
|
this.addEventListener("pause", () => this.currentTime = 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play or stop media
|
||||||
|
playState() {
|
||||||
|
// Ignore if media is still buffering
|
||||||
|
if (this.readyState !== HTMLMediaElement.HAVE_ENOUGH_DATA) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop media if playing
|
||||||
|
if (this.paused === false && this.ended === false) {
|
||||||
|
this.pause();
|
||||||
|
this.currentTime = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play media
|
||||||
|
return this.play();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
const search = document.getElementById("search").children[0];
|
|
||||||
const results = document.getElementById("results").children[0];
|
|
||||||
|
|
||||||
search.style.setProperty("display","none");
|
|
||||||
results.classList.add("error");
|
|
||||||
results.innerText = "Sorry, your browser isn't supported yet";
|
|
|
@ -1,14 +1,47 @@
|
||||||
import { default as Glitch } from "./glitch/Glitch.mjs";
|
// Bind dialog box interaction
|
||||||
|
[...document.getElementsByClassName("dialog")].forEach(dialog => {
|
||||||
|
dialog.addEventListener("click", () => {
|
||||||
|
import("./modules/Dialog.mjs").then(mod => {
|
||||||
|
// Initialize dialog with interacted element
|
||||||
|
const dialogElement = new mod.Dialog(dialog);
|
||||||
|
|
||||||
const logging = "https://victorwesterlund-logging-dnzfgzf6za-lz.a.run.app";
|
// Will open data-src
|
||||||
|
dialogElement.open();
|
||||||
// Log link clicks
|
});
|
||||||
for(let link of document.getElementsByTagName("a")) {
|
|
||||||
link.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
navigator?.sendBeacon(logging, event);
|
|
||||||
window.location.href = event.target.href;
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
window.glitch = new Glitch(document.body.parentElement);
|
// Create AudioPlayers from template on interaction
|
||||||
|
[...document.getElementsByClassName("player")].forEach(player => {
|
||||||
|
player.addEventListener("click", () => {
|
||||||
|
import("./modules/Player.mjs").then(mod => {
|
||||||
|
// Initialize AudioPlayer from template
|
||||||
|
const audioPlayer = new mod.AudioPlayer(player);
|
||||||
|
let animation;
|
||||||
|
|
||||||
|
// Animate a progress bar as media is playing
|
||||||
|
audioPlayer.addEventListener("playing", () => {
|
||||||
|
const color = "rgba(var(--primer-color-contrast), .1)";
|
||||||
|
|
||||||
|
const keyframes = [
|
||||||
|
{ boxShadow: `inset 0 0 0 0 ${color}` },
|
||||||
|
{ boxShadow: `inset ${player.offsetWidth}px 0 0 0 ${color}` }
|
||||||
|
];
|
||||||
|
|
||||||
|
const timing = {
|
||||||
|
duration: 38000, // Robot36 TX + calibration header
|
||||||
|
iterations: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
animation = player.animate(keyframes, timing);
|
||||||
|
player.querySelector(".playstate").innerText = "stop";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop animation if playback is interrupted
|
||||||
|
audioPlayer.addEventListener("pause", () => {
|
||||||
|
animation.cancel();
|
||||||
|
player.querySelector(".playstate").innerText = "play";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, { once: true });
|
||||||
|
});
|
1
public/assets/media/coffee.svg
Normal file
1
public/assets/media/coffee.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.2 KiB |
1
public/assets/media/wave.svg
Normal file
1
public/assets/media/wave.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.056 22.944"><path d="M3.143 11.479s6.452-20.136 18.27 0 21.315 0 21.315 0" fill="none" stroke="#000" stroke-linecap="round" stroke-width="5"/></svg>
|
After Width: | Height: | Size: 204 B |
|
@ -7,43 +7,88 @@
|
||||||
<meta name="description" content="Full-stack web developer from Stockholm, Sweden.">
|
<meta name="description" content="Full-stack web developer from Stockholm, Sweden.">
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
||||||
<meta property='og:title' content='Victor Westerlund'>
|
<meta property='og:title' content='Victor Westerlund'>
|
||||||
<meta property='og:image' content='//victorwesterlund.com/assets/media/banner.jpg'>
|
<meta property='og:image' content='assets/media/banner.jpg'>
|
||||||
<meta property='og:description' content='Full-stack web developer from Stockholm, Sweden.'>
|
<meta property='og:description' content='Full-stack web developer from Stockholm, Sweden.'>
|
||||||
<meta property='og:url' content='https://victorwesterlund.com'>
|
<meta property='og:url' content='https://victorwesterlund.com'>
|
||||||
<link rel="icon" href="assets/media/favicon-dark.png" media="(prefers-color-scheme:no-preference)">
|
<link rel="icon" href="assets/media/favicon-dark.png" media="(prefers-color-scheme:no-preference)">
|
||||||
<link rel="icon" href="assets/media/favicon-dark.png" media="(prefers-color-scheme:light)">
|
<link rel="icon" href="assets/media/favicon-dark.png" media="(prefers-color-scheme:light)">
|
||||||
<link rel="icon" href="assets/media/favicon-light.png" media="(prefers-color-scheme:dark)">
|
<link rel="icon" href="assets/media/favicon-light.png" media="(prefers-color-scheme:dark)">
|
||||||
<link rel="stylesheet" href="assets/css/style.css">
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<section id="intro">
|
||||||
<div id="intro">
|
<p><strong>victor westerlund</strong></p>
|
||||||
<p>hello, my name is</p>
|
<div class="spacer">
|
||||||
<h1>victor</h1>
|
<div></div>
|
||||||
<p>I'm a</p>
|
|
||||||
<h1>full-stack</h1>
|
|
||||||
<h1>developer</h1>
|
|
||||||
<p>from Sweden</p>
|
|
||||||
<a href="https://github.com/VictorWesterlund">my github -></a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p>I make things with code and coffee</p>
|
||||||
|
</section>
|
||||||
|
<section id="code">
|
||||||
|
<a href="https://github.com/VictorWesterlund" target="_blank" rel="noreferrer noopener">
|
||||||
|
<div class="button">
|
||||||
|
<img src="assets/media/favicon-light.png" alt="Hand-drawn icon of the GitHub mascot"/>
|
||||||
|
<p>on github</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<p>and</p>
|
||||||
|
<a href="#" target="_blank" rel="noreferrer noopener">
|
||||||
|
<div class="button">
|
||||||
|
<p>elsewhere</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
<section id="contact">
|
||||||
|
<p>Wanna chat? The best way to get in touch with me is by sending a mail. The time is currently 00:00 in Sweden, and I will reply as soon as possible.</p>
|
||||||
|
<div>
|
||||||
|
<a href="#">
|
||||||
|
<div class="button">
|
||||||
|
<p>copy email address</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#">
|
||||||
|
<div class="button phantom dialog" data-src="">
|
||||||
|
<p>🔑 show PGP key</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div class="banner">
|
||||||
|
<p><strong>random stuff</strong></p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<section id="sky">
|
||||||
<div id="card">
|
<picture>
|
||||||
<picture>
|
<source srcset="https://friday.victorwesterlund.com/sky/original.webp" type="image/webp">
|
||||||
<source srcset="assets/media/pfp/256.webp" type="image/webp" media="(min-width: 600px)">
|
<img src="https://friday.victorwesterlund.com/sky/original.jpg" style="aspect-ratio: 16 / 9" alt="Webcam stillframe of the sky from my balcony"/>
|
||||||
<source srcset="assets/media/pfp/128.webp" type="image/webp">
|
</picture>
|
||||||
<source srcset="assets/media/pfp/256.jpg" type="image/jpg" media="(min-width: 600px)">
|
<p>Do you have an SSTV receiver? Tune in to a sneaky surprise and this beautiful view of the sky from my balcony as it looked like <strong>4 minutes ago</strong>. Or why not check out this pretty neat time-lapse.</p>
|
||||||
<img src="assets/media/pfp/128.jpg" alt="portrait of victor"/>
|
<a href="#">
|
||||||
</picture>
|
<div class="button">
|
||||||
<div>
|
<p>view timelapse</p>
|
||||||
<p>I create things with code. When I'm not creating things with code, I enjoy skiing, watching movies and some occasional gaming</p>
|
|
||||||
<p>And beyond computer science, I'm also an armchair rabbit-holer for engineering, physics and astronomy</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</a>
|
||||||
<p>...and ☕, full-time</p>
|
<div class="button phantom player" data-src="https://friday.victorwesterlund.com/sky/robot36.wav">
|
||||||
</div>
|
<img src="assets/media/wave.svg"/>
|
||||||
<!--<a href="contact">contact me</a>-->
|
<p><strong>Robot36</strong><br><span class="interact"></span> to <span class="playstate">play</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
<p>⚠️ <i><strong>warning:</strong> loud f*cking noise so adjust your volume accordingly.</i></p>
|
||||||
|
</section>
|
||||||
|
<div class="spacer">
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<section id="coffee">
|
||||||
|
<img src="assets/media/coffee.svg" style="aspect-ratio: 1 / 1" alt="Hand-drawn coffee icon"/>
|
||||||
|
<p>Did I mention that I like coffee? Well in the past 7 days I have had <strong>4 cups of coffee</strong>, which is roughly equivalent to <strong>0.2 cups per day</strong>. Not too impressive given that my weekly average is 2.2 cups per day.</p>
|
||||||
|
</section>
|
||||||
|
<div class="spacer">
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<section id="sneaky">
|
||||||
|
<p>Enter your top sneaky code here. You will know when you've struck the jackpot.</p>
|
||||||
|
<input placeholder="uuddlrlrabs"></input>
|
||||||
|
<p>So you don't have a sneaky code?<br><a href="#">Solve this puzzle</a> and I'll buy you a cup of coffee.</p>
|
||||||
|
</section>
|
||||||
|
<div class="spacer">
|
||||||
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="assets/js/script.mjs"></script>
|
<script type="module" src="assets/js/script.mjs"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Add table
Reference in a new issue