diff --git a/public/assets/css/error.css b/public/assets/css/error.css new file mode 100644 index 0000000..a97e374 --- /dev/null +++ b/public/assets/css/error.css @@ -0,0 +1,15 @@ +#card { + position: relative; + 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); +} + +@supports ((not ((backdrop-filter: saturate(1)) and (backdrop-filter: brightness(1)))) and (not ((-webkit-backdrop-filter: saturate(1)) and (-webkit-backdrop-filter: brightness(1))))) { + #card { + background-color: rgba(var(--color-base), .7); + } +} \ No newline at end of file diff --git a/public/assets/css/pages/main.css b/public/assets/css/pages/main.css new file mode 100644 index 0000000..ecb5940 --- /dev/null +++ b/public/assets/css/pages/main.css @@ -0,0 +1,258 @@ +@import url("../style.css"); + +main, +main #content { + display: grid; + grid-template-rows: minmax(0, 1fr) calc(var(--header-height) * 3); +} + +main #content { + grid-template-rows: var(--header-height) calc(var(--header-height) * 2); +} + +/* -- Gaze -- */ + +#gaze { + background: url("../../media/gazer/2x.jpg"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + filter: hue-rotate(0) contrast(1.1); + -webkit-filter: hue-rotate(0) contrast(1.1); + animation: hue-rotate 10s infinite linear; +} + +/* -- Menu -- */ + +nav { + --border-width: 100; + position: relative; + width: 100vw; + padding: 20px; + display: initial; + border: solid 2px transparent; + border-image: repeating-linear-gradient(90deg, black 0px, black 10px, rgba(255,255,255,.3) 10px, rgba(255,255,255,.3) 20px) 2; + border-image-slice: 0 0 0 0; + border-image-repeat: repeat; + box-sizing: border-box; + display: grid; + grid-template-rows: repeat(3, 1fr); + gap: 20px; +} + +nav a { + color: var(--color-accent); + text-align: center; + font-size: clamp(16px, 15vw, 7vh); +} + +#menu { + width: 100%; + background-color: #141414; + display: grid; + grid-template-columns: clamp(1px, 100%, 500px); + grid-template-rows: minmax(0, 1fr); + justify-content: center; + white-space: nowrap; +} + +#menu .inner { + position: relative; + display: grid; + grid-template-columns: repeat(2, 1fr); + transition: var(--transition) transform; + transform: translateX(var(--padding)); +} + +main.menuOpen #menu .inner { + transform: translateX(calc(var(--padding) * -1)); +} + +main.menuOpen #menu .open, +main:not(.menuOpen) #menu .close { + opacity: 0; +} + +#menu .inner :is(.open, .close) { + position: absolute; + padding: var(--padding); + display: flex; + align-items: center; + gap: var(--padding); + transition: var(--transition) opacity; +} + +#menu .inner .close { + right: 0; +} + +#menu .inner :is(.open, .close) svg { + height: 40px; + fill: white; +} + +/* -- Mail -- */ + +#mail { + display: grid; + justify-content: center; + align-content: center; + text-align: right; + line-height: 23px; + gap: var(--padding); +} + +#mail p:last-child { + color: var(--color-accent); +} + +#mail p:last-child::before { + content: "tap "; +} + +#mail p span { + opacity: .3; +} + +@keyframes hue-rotate { + to { + filter: hue-rotate(360deg) contrast(1.1); + -webkit-filter: hue-rotate(360deg) contrast(1.1); + } +} + +/* -- Media Queries -- */ + +@media (pointer: fine) { + #mail p:last-child::before { + content: "click "; + } + + a { + --hover-translate: 20px; + position: relative; + transition: var(--transition) transform, var(--transition) text-shadow; + } + + a:hover { + cursor: pointer; + animation: hue-rotate-hover 2s infinite linear; + text-shadow: var(--hover-translate) var(--hover-translate) #292929; + font-weight: bold; + transform: translate( + calc((var(--hover-translate) / 2) * -1), + calc((var(--hover-translate) / 2) * -1) + ); + } + + a:hover::after { + content: ""; + position: absolute; + left: 0; + bottom: calc((var(--hover-translate) / 2) * -1); + width: calc(100% + calc(var(--hover-translate) / 2)); + height: calc(var(--hover-translate) / 2); + } + + a:hover:active { + text-shadow: calc(var(--hover-translate) / 2) calc(var(--hover-translate) / 2) #292929; + transform: translate( + calc((var(--hover-translate) / 4) * -1), + calc((var(--hover-translate) / 4) * -1) + ); + } + + @keyframes hue-rotate-hover { + to { + filter: hue-rotate(360deg); + -webkit-filter: hue-rotate(360deg); + } + } +} + +@media not all and (min-aspect-ratio: 14/9) and (min-height: 600px) { + #gaze { + transition: var(--transition) transform, var(--transition) opacity; + transition-delay: var(--transition-speed); + transform: translateX(0); + } + + main.menuOpen #gaze { + transition-delay: 1ms; + opacity: 0; + transform: translateX(-40px); + } + + #menuContent { + transition: var(--transition) opacity; + transition-delay: 1ms; + position: absolute; + bottom: calc(var(--header-height) * 3); + width: 100%; + background-color: black; + opacity: 0; + pointer-events: none; + } + + main.menuOpen #menuContent { + transition-delay: var(--transition-speed); + opacity: 1; + pointer-events: all; + } +} + +@media (min-aspect-ratio: 14/9) and (min-height: 600px) { + main { + display: flex; + flex-direction: row-reverse; + } + + main > div { + width: 50%; + } + + main #content { + display: flex; + flex-direction: column; + justify-content: center; + gap: var(--header-height); + } + + main #content > div { + justify-content: right; + } + + #menuContent { + display: flex; + } + + nav { + width: 18vw; + border-image-slice: 1 0 1 0; + } + + nav a { + font-size: 3vw; + text-align: right; + } + + /* -- Menu -- */ + + #menu { + display: none; + } +} + +@media (max-height: 600px) { + main { + grid-template-rows: minmax(0, 1fr) var(--header-height); + } + + #menuContent { + bottom: calc(var(--header-height) * 1); + } + + #mail { + display: none; + } +} \ No newline at end of file diff --git a/public/assets/css/style.css b/public/assets/css/style.css index d67f248..2ad3b2f 100755 --- a/public/assets/css/style.css +++ b/public/assets/css/style.css @@ -1,9 +1,11 @@ :root { - --color-base: 0, 0, 0; - --color-contrast: 256, 256, 256; + --color-accent: yellow; - --padding: clamp(40px, 2vw, 2vw); - --border-size: clamp(4px, .25vw, .25vw); + --header-height: 80px; + --padding: 20px; + + --transition-speed: 300ms; + --transition: cubic-bezier(0,0,0,1) var(--transition-speed); } /* -- Cornerstones -- */ @@ -11,195 +13,86 @@ * { margin: 0; font-family: "Monaco", "Consolas", monospace, sans-serif; - color: rgb(var(--color-contrast)); + color: white; + user-select: none; } -*::selection { - background-color: rgb(var(--color-contrast)); - color: rgb(var(--color-base)); +::selection { + background-color: white; + color: black; } html, body { width: 100%; height: 100%; - overflow-x: hidden; + overflow: hidden; + background-color: black; } -html { - background-color: rgba(var(--color-base), .7); - 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 -- */ - body { - display: flex; - flex-direction: column; - align-items: center; + display: grid; + grid-template-rows: var(--header-height) 1fr; +} + +/* -- Content -- */ + +header { + --border-color: #1e1e1e; + text-align: right; + font-size: 18px; + display: grid; + grid-template-columns: 1fr var(--header-height); + justify-content: right; + border-bottom: solid 1px var(--border-color); +} + +header > div { + display: grid; justify-items: center; - gap: var(--padding, 30px); -} - -body > div { - padding: calc(var(--padding) / 2); -} - -:is(#intro, #card) a { - --padding-vert: clamp(17px, 1.1vw, 1.1vw); - - display: inline-block; - text-decoration: none; - text-align: center; - 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); -} - -/* --- */ - -#intro { - padding: calc(var(--padding) / 2); -} - -#intro a { - padding: var(--padding-vert) 2vw; - border-radius: 100px; - border: solid var(--border-size) rgba(var(--color-contrast), 0); - margin: var(--padding) 0; - width: calc(100% - ((var(--padding) / 2) + var(--border-size))); -} - -#intro p { - margin: 1vh 0; - font-size: clamp(20px, 3vw, 3vh); -} - -/* --- */ - -#card, -#card > div { - display: flex; - flex-direction: column; align-items: center; - gap: calc(var(--padding) / 2); } -#card { - --portrait-size: clamp(128px, 12vw, 12vh); - - 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); +header > div:not(:first-child) { + height: var(--header-height); + border-left: solid 1px var(--border-color); } -#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); +header #logo img { + width: calc(100% - (var(--padding) * 2)); } -#card a { - width: 100%; - padding: var(--padding-vert) 0; - margin-top: calc(var(--padding) / 2); - border-radius: clamp(9px, .5vw, .5vw); +header #crumbs { + justify-self: right; + text-align: right; + padding: 0 20px; +} + +/* -- Page Constraints -- */ + +main { + max-height: calc(100vh - var(--header-height)); } /* -- Media Queries -- */ -@supports ((not ((backdrop-filter: saturate(1)) and (backdrop-filter: brightness(1)))) and (not ((-webkit-backdrop-filter: saturate(1)) and (-webkit-backdrop-filter: brightness(1))))) { - #card { - background-color: rgba(var(--color-base), .7); - } -} - @media (pointer: fine) { - :is(#intro, #card) a { - --transition-speed: 200ms; - transition: - var(--transition-speed) background-color, - var(--transition-speed) box-shadow, - var(--transition-speed) border-color; - } - :is(#intro, #card) a:hover { - background-color: rgba(var(--color-contrast), .2); - 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) { - p, a { - text-align: left; - font-size: 18px; + header { + grid-template-columns: var(--header-height); } - #card { - padding: calc(var(--padding) / 2); + header #crumbs { + display: none; } } @media (min-aspect-ratio: 14/9) and (min-height: 600px) { - body { - display: grid; - 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; + header #crumbs { + justify-self: left; + text-align: right; } } diff --git a/public/assets/js/script.mjs b/public/assets/js/script.mjs index 2592a11..d9f6672 100644 --- a/public/assets/js/script.mjs +++ b/public/assets/js/script.mjs @@ -1,14 +1 @@ -import { default as Glitch } from "./glitch/Glitch.mjs"; - -const logging = "https://victorwesterlund-logging-dnzfgzf6za-lz.a.run.app"; - -// 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); \ No newline at end of file +document.getElementById("menu").addEventListener("click", () => document.getElementsByTagName("main")[0].classList.toggle("menuOpen")); \ No newline at end of file diff --git a/public/assets/media/gazer/1x.jpg b/public/assets/media/gazer/1x.jpg new file mode 100644 index 0000000..e77efce Binary files /dev/null and b/public/assets/media/gazer/1x.jpg differ diff --git a/public/assets/media/gazer/1x.webp b/public/assets/media/gazer/1x.webp new file mode 100644 index 0000000..5ec97e1 Binary files /dev/null and b/public/assets/media/gazer/1x.webp differ diff --git a/public/assets/media/gazer/2x.jpg b/public/assets/media/gazer/2x.jpg new file mode 100644 index 0000000..a703454 Binary files /dev/null and b/public/assets/media/gazer/2x.jpg differ diff --git a/public/assets/media/gazer/2x.webp b/public/assets/media/gazer/2x.webp new file mode 100644 index 0000000..6fa931d Binary files /dev/null and b/public/assets/media/gazer/2x.webp differ diff --git a/public/assets/media/gazer/3x.jpg b/public/assets/media/gazer/3x.jpg new file mode 100644 index 0000000..cee5903 Binary files /dev/null and b/public/assets/media/gazer/3x.jpg differ diff --git a/public/assets/media/gazer/3x.webp b/public/assets/media/gazer/3x.webp new file mode 100644 index 0000000..207c576 Binary files /dev/null and b/public/assets/media/gazer/3x.webp differ diff --git a/public/assets/media/gazer/4x.jpg b/public/assets/media/gazer/4x.jpg new file mode 100644 index 0000000..e1607df Binary files /dev/null and b/public/assets/media/gazer/4x.jpg differ diff --git a/public/assets/media/gazer/4x.webp b/public/assets/media/gazer/4x.webp new file mode 100644 index 0000000..e4fd252 Binary files /dev/null and b/public/assets/media/gazer/4x.webp differ diff --git a/public/assets/media/vw.svg b/public/assets/media/vw.svg new file mode 100644 index 0000000..09d27e8 --- /dev/null +++ b/public/assets/media/vw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html index e67899d..6c96773 100755 --- a/public/index.html +++ b/public/index.html @@ -22,37 +22,45 @@ + -
-
-

hello, my name is

-

victor

-

I'm a

-

full-stack

-

developer

-

from Sweden

- my github -> +
+
+

victor westerlund

-
-
-
- - - - - portrait of victor - -
-

I create things with code. When I'm not creating things with code, I enjoy skiing, watching movies and some occasional gaming

-

And beyond computer science, I'm also an armchair rabbit-holer for engineering, physics and astronomy

-
-
-

...and ☕, full-time

-
- + -
- + +
+
+
+ + +
+

---- BEGIN PRIMARY MAIL BLOCK ----
victor@victorwesterlund.com
---- END PRIMARY MAIL BLOCK ----

+

to copy

+
+
+ +