mirror of
https://github.com/jaronpate/folio.git
synced 2026-10-19 01:07:39 +00:00
rework to 2000's-ish style site
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import '~/assets/css/main.css';
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_DESCRIPTION,
|
DEFAULT_DESCRIPTION,
|
||||||
DEFAULT_IMAGE,
|
DEFAULT_IMAGE,
|
||||||
@@ -41,8 +40,6 @@ useHead({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
+98
-133
@@ -1,13 +1,22 @@
|
|||||||
/* Sohne Buch */
|
|
||||||
/*@font-face {
|
|
||||||
font-family: 'SohneBuch';
|
|
||||||
src:
|
|
||||||
url('~/assets/fonts/Sohne/Sohne-Buch.woff') format('woff'),
|
|
||||||
url('~/assets/fonts/Sohne/Sohne-Buch.ttf') format('truetype');
|
|
||||||
font-display: swap;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/* Plantin MT Pro */
|
/* Plantin MT Pro */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Plantin MT Pro';
|
||||||
|
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProLight.TTF')
|
||||||
|
format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Plantin MT Pro';
|
||||||
|
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProLightIt.TTF')
|
||||||
|
format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Plantin MT Pro';
|
font-family: 'Plantin MT Pro';
|
||||||
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProRg.TTF')
|
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProRg.TTF')
|
||||||
@@ -46,18 +55,73 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Plantin MT Pro';
|
font-family: 'Plantin MT Pro';
|
||||||
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProLight.TTF')
|
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProSmBd.TTF')
|
||||||
format('truetype');
|
format('truetype');
|
||||||
font-weight: 300;
|
font-weight: 600;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Plantin MT Pro';
|
font-family: 'Plantin MT Pro';
|
||||||
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProLightIt.TTF')
|
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProSemiBdIt.TTF')
|
||||||
format('truetype');
|
format('truetype');
|
||||||
font-weight: 300;
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Plantin MT Pro';
|
||||||
|
src: url('~/assets/fonts/Plantin MT Pro/PlantinMTProCnBd.TTF')
|
||||||
|
format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-stretch: condensed;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sohne Buch */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SohneBuch';
|
||||||
|
src:
|
||||||
|
url('~/assets/fonts/Sohne/Sohne-Buch.woff') format('woff'),
|
||||||
|
url('~/assets/fonts/Sohne/Sohne-Buch.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alegreya */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Alegreya';
|
||||||
|
src: url('~/assets/fonts/Alegreya HT/Alegreya-Regular.woff2') format('woff2');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Alegreya';
|
||||||
|
src: url('~/assets/fonts/Alegreya HT/Alegreya-Italic.woff2') format('woff2');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Alegreya';
|
||||||
|
src: url('~/assets/fonts/Alegreya HT/Alegreya-Bold.woff2') format('woff2');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Alegreya';
|
||||||
|
src: url('~/assets/fonts/Alegreya HT/Alegreya-BoldItalic.woff2')
|
||||||
|
format('woff2');
|
||||||
|
font-weight: 700;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@@ -70,13 +134,7 @@
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction-Bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction';
|
font-family: 'Redaction';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction-Italic.woff2') format('woff2');
|
src: url('~/assets/fonts/Redaction/Redaction-Italic.woff2') format('woff2');
|
||||||
@@ -85,57 +143,14 @@
|
|||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redaction 10 */
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction 10';
|
font-family: 'Redaction';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_10-Regular.woff2')
|
src: url('~/assets/fonts/Redaction/Redaction-Bold.woff2') format('woff2');
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 10';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_10-Bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 10';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_10-Italic.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redaction 20 */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 20';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_20-Regular.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 20';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_20-Bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 20';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_20-Italic.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redaction 35 */
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction 35';
|
font-family: 'Redaction 35';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_35-Regular.woff2')
|
src: url('~/assets/fonts/Redaction/Redaction_35-Regular.woff2')
|
||||||
@@ -144,13 +159,7 @@
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 35';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_35-Bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction 35';
|
font-family: 'Redaction 35';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_35-Italic.woff2')
|
src: url('~/assets/fonts/Redaction/Redaction_35-Italic.woff2')
|
||||||
@@ -160,80 +169,36 @@
|
|||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redaction 50 */
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction 50';
|
font-family: 'Redaction 35';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_50-Regular.woff2')
|
src: url('~/assets/fonts/Redaction/Redaction_35-Bold.woff2') format('woff2');
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 50';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_50-Bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Space Mono */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction 50';
|
font-family: 'Space Mono';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_50-Italic.woff2')
|
src: url('~/assets/fonts/Space Mono/SpaceMono-Regular.ttf')
|
||||||
format('woff2');
|
format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
src: url('~/assets/fonts/Space Mono/SpaceMono-Italic.ttf') format('truetype');
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redaction 70 */
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Redaction 70';
|
font-family: 'Space Mono';
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_70-Regular.woff2')
|
src: url('~/assets/fonts/Space Mono/SpaceMono-Bold.ttf') format('truetype');
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 70';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_70-Bold.woff2') format('woff2');
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 70';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_70-Italic.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redaction 100 */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 100';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_100-Regular.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 100';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_100-Bold.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Redaction 100';
|
|
||||||
src: url('~/assets/fonts/Redaction/Redaction_100-Italic.woff2')
|
|
||||||
format('woff2');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*@import url('https://fonts.googleapis.com/css2?family=Young+Serif&display=swap');*/
|
|
||||||
|
|||||||
+310
-95
@@ -1,163 +1,378 @@
|
|||||||
@import './fonts.css';
|
@import './fonts.css';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/*--font: 'Baskervville', serif;*/
|
--font: 'Plantin MT Pro', Georgia, serif;
|
||||||
/*--font: 'EB Garamond', serif;*/
|
--font-plain: 'SohneBuch', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
--font: 'Plantin MT Pro', serif;
|
--font-mono: 'Space Mono', ui-monospace, 'SF Mono', Menlo, monospace;
|
||||||
/*--font: 'Young Serif', serif;*/
|
|
||||||
/*--font: 'SohneBuch', sans-serif;*/
|
/* paper + ink from the old site; accent/navy sampled from blend.jpeg */
|
||||||
/*--font-plain: 'SohneBuch', sans-serif;*/
|
|
||||||
/*--font: 'Redaction', serif;*/
|
|
||||||
/*--font-hover: 'Redaction 70', serif;*/
|
|
||||||
--font-plain: var(--font);
|
|
||||||
--font-hover: var(--font);
|
|
||||||
--text: #333;
|
|
||||||
--muted: #666;
|
|
||||||
--bg: #f6f7f9;
|
--bg: #f6f7f9;
|
||||||
--bg-secondary: #e9ebef;
|
--bg-secondary: #e9ebef;
|
||||||
--dot-color: #666;
|
--text: #2e3140;
|
||||||
|
--muted: #6a6d7a;
|
||||||
--color-white-500: #f6f7f9;
|
--accent: #c45d52;
|
||||||
|
--accent-soft: #e78c88;
|
||||||
|
--navy: #2a2c61;
|
||||||
|
--hairline: #d8dbe2;
|
||||||
|
--code-bg: #f3e4e2;
|
||||||
|
--img-matte: #e9ebef;
|
||||||
|
|
||||||
|
--body-weight: 300;
|
||||||
|
--measure: 38rem;
|
||||||
|
--note-width: 13rem;
|
||||||
|
--note-gap: 2rem;
|
||||||
|
--page-pad: 1.25rem;
|
||||||
|
--col: min(var(--measure), calc(100vw - var(--page-pad) * 2));
|
||||||
|
--cluster: calc(var(--col) + var(--note-gap) + var(--note-width));
|
||||||
|
--col-left: max(var(--page-pad), calc((100vw - var(--cluster)) / 2));
|
||||||
--gap: 1rem;
|
--gap: 1rem;
|
||||||
|
|
||||||
--radius: 4px;
|
--radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
html.font-plantin {
|
||||||
background: var(--text);
|
--font: 'Plantin MT Pro', Georgia, serif;
|
||||||
color: var(--bg);
|
--body-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark-mode {
|
html.font-alegreya {
|
||||||
|
--font: 'Alegreya', Georgia, serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-redaction {
|
||||||
|
--font: 'Redaction', Georgia, serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-redaction-35 {
|
||||||
|
--font: 'Redaction 35', Georgia, serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-sohne {
|
||||||
|
--font: 'SohneBuch', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-space {
|
||||||
|
--font: 'Space Mono', ui-monospace, Menlo, monospace;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-georgia {
|
||||||
|
--font: Georgia, 'Times New Roman', Times, serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-palatino {
|
||||||
|
--font: Palatino, 'Palatino Linotype', 'Book Antiqua', serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-iowan {
|
||||||
|
--font: 'Iowan Old Style', 'Palatino Linotype', Palatino, serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-system {
|
||||||
|
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.font-courier {
|
||||||
|
--font: 'Courier New', Courier, ui-monospace, monospace;
|
||||||
|
--body-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode,
|
||||||
|
html.theme-navy,
|
||||||
|
html.theme-dusk,
|
||||||
|
html.theme-midnight,
|
||||||
|
html.theme-indigo,
|
||||||
|
html.theme-storm,
|
||||||
|
html.theme-slate,
|
||||||
|
html.theme-ink,
|
||||||
|
html.theme-pine,
|
||||||
|
html.theme-wine,
|
||||||
|
html.theme-cocoa {
|
||||||
--text: #fbfbf7;
|
--text: #fbfbf7;
|
||||||
|
--muted: #9aa4b8;
|
||||||
|
--accent: #e78c88;
|
||||||
|
--accent-soft: #c45d52;
|
||||||
|
--navy: #3c48a8;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-paper {
|
||||||
|
--bg: #f6f7f9;
|
||||||
|
--bg-secondary: #e9ebef;
|
||||||
|
--text: #2e3140;
|
||||||
|
--muted: #6a6d7a;
|
||||||
|
--accent: #c45d52;
|
||||||
|
--accent-soft: #e78c88;
|
||||||
|
--navy: #2a2c61;
|
||||||
|
--hairline: #d8dbe2;
|
||||||
|
--code-bg: #f3e4e2;
|
||||||
|
--img-matte: #e9ebef;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-navy {
|
||||||
|
--bg: #12192c;
|
||||||
|
--bg-secondary: #1a2238;
|
||||||
|
--hairline: #2a334c;
|
||||||
|
--code-bg: #221e34;
|
||||||
|
--img-matte: #1a2238;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-dusk {
|
||||||
|
--bg: #1a2236;
|
||||||
|
--bg-secondary: #252d44;
|
||||||
|
--hairline: #343e58;
|
||||||
|
--code-bg: #2c2840;
|
||||||
|
--img-matte: #252d44;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-midnight {
|
||||||
|
--bg: #0e1424;
|
||||||
|
--bg-secondary: #161d30;
|
||||||
|
--hairline: #242c42;
|
||||||
|
--code-bg: #1c1a2c;
|
||||||
|
--img-matte: #161d30;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-indigo {
|
||||||
|
--bg: #16142a;
|
||||||
|
--bg-secondary: #1f1c38;
|
||||||
|
--hairline: #2e2a4a;
|
||||||
|
--code-bg: #241e38;
|
||||||
|
--img-matte: #1f1c38;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-storm {
|
||||||
|
--bg: #1c2438;
|
||||||
|
--bg-secondary: #283044;
|
||||||
|
--hairline: #3a4458;
|
||||||
|
--code-bg: #2c2840;
|
||||||
|
--img-matte: #283044;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-slate {
|
||||||
|
--bg: #1e212b;
|
||||||
|
--bg-secondary: #2a2d3a;
|
||||||
|
--hairline: #3a3e4c;
|
||||||
|
--code-bg: #322833;
|
||||||
|
--img-matte: #2a2d3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme-ink {
|
||||||
--bg: #0c0c0c;
|
--bg: #0c0c0c;
|
||||||
--bg-secondary: #1c1c1c;
|
--bg-secondary: #1c1c1c;
|
||||||
--dot-color: #666;
|
--hairline: #2a2a2a;
|
||||||
--muted: #a3a3a3;
|
--code-bg: #221c20;
|
||||||
|
--img-matte: #1c1c1c;
|
||||||
--color-white-500: #fbfbf7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex {
|
html.theme-pine {
|
||||||
display: flex;
|
--bg: #121c22;
|
||||||
|
--bg-secondary: #1a262c;
|
||||||
|
--hairline: #2a3840;
|
||||||
|
--code-bg: #1c2428;
|
||||||
|
--img-matte: #1a262c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-center {
|
html.theme-wine {
|
||||||
display: flex;
|
--bg: #1a1520;
|
||||||
justify-content: center;
|
--bg-secondary: #261c28;
|
||||||
align-items: center;
|
--hairline: #3a2c38;
|
||||||
|
--code-bg: #2a1e28;
|
||||||
|
--img-matte: #261c28;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-ff {
|
html.theme-cocoa {
|
||||||
flex: 1;
|
--bg: #1c1814;
|
||||||
}
|
--bg-secondary: #2a241c;
|
||||||
|
--hairline: #3c3428;
|
||||||
.row {
|
--code-bg: #2c241c;
|
||||||
flex-direction: row;
|
--img-matte: #2a241c;
|
||||||
}
|
|
||||||
|
|
||||||
.col {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
overscroll-behavior-y: none;
|
color: var(--text);
|
||||||
overscroll-behavior-x: auto;
|
overflow-x: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
color: var(--text);
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.6;
|
line-height: 1.65;
|
||||||
|
font-weight: var(--body-weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
strong,
|
||||||
|
b {
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* full-page film grain */
|
|
||||||
body::after {
|
body::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.06;
|
opacity: 0.05;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
||||||
mix-blend-mode: multiply;
|
mix-blend-mode: multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html:not(.theme-paper) body::after,
|
||||||
.dark-mode body::after {
|
.dark-mode body::after {
|
||||||
mix-blend-mode: screen;
|
mix-blend-mode: screen;
|
||||||
opacity: 0.045;
|
opacity: 0.04;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* quick page transition */
|
a {
|
||||||
.page-enter-active,
|
color: var(--accent);
|
||||||
.page-leave-active {
|
text-decoration: none;
|
||||||
transition: opacity 0.18s ease;
|
text-underline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-enter-from,
|
a:hover {
|
||||||
.page-leave-to {
|
text-decoration: underline;
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.basic-link {
|
article {
|
||||||
color: var(--text);
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
text-underline-offset: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.basic-link:hover {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.punch-link {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: bottom;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
color: var(--color-white-500);
|
width: var(--col);
|
||||||
text-decoration: none;
|
margin-left: var(--col-left);
|
||||||
transition: all 200ms ease;
|
margin-right: auto;
|
||||||
padding: 0 4px;
|
overflow-wrap: break-word;
|
||||||
margin: 0 -4px;
|
|
||||||
background-image: linear-gradient(
|
|
||||||
var(--color-white-500),
|
|
||||||
var(--color-white-500)
|
|
||||||
);
|
|
||||||
background-size: 100% 1px;
|
|
||||||
background-position: bottom;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
mix-blend-mode: screen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.punch-link:hover {
|
article h2,
|
||||||
background-size: 100% 100%;
|
article h3 {
|
||||||
background-image: linear-gradient(#fff, #fff);
|
font-weight: 400;
|
||||||
color: #000;
|
font-style: italic;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 2.25rem 0 0.75rem;
|
||||||
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-link {
|
article > h3:first-child {
|
||||||
display: flex;
|
margin-top: 0;
|
||||||
align-items: center;
|
}
|
||||||
gap: 0.5rem;
|
|
||||||
font-size: small;
|
article p {
|
||||||
|
margin: 0 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul,
|
||||||
|
article ol {
|
||||||
|
margin: 0 0 1em;
|
||||||
|
padding-left: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
article li {
|
||||||
|
margin: 0 0 0.35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
article li p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
article hr {
|
||||||
|
margin: 2.5rem 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--hairline);
|
||||||
|
}
|
||||||
|
|
||||||
|
article blockquote {
|
||||||
|
--hand-w: 2.35rem;
|
||||||
|
--hand-h: calc(var(--hand-w) * 44 / 100);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
padding: 0.15rem 0 0.15rem 3.15rem;
|
||||||
|
border: 0;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1.08em;
|
||||||
|
line-height: 1.55;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
text-decoration: none;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-link .arrow {
|
article blockquote p:last-child {
|
||||||
transition: transform 0.2s ease;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-link:hover .arrow {
|
article blockquote::before {
|
||||||
transform: translateX(-4px);
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0.35em;
|
||||||
|
left: 0;
|
||||||
|
width: var(--hand-w);
|
||||||
|
height: var(--hand-h);
|
||||||
|
background: url('~/assets/images/L.png') center / contain no-repeat;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:not(.theme-paper) article blockquote::before,
|
||||||
|
.dark-mode article blockquote::before {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
article img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--img-matte);
|
||||||
|
}
|
||||||
|
|
||||||
|
article p:has(> img) {
|
||||||
|
margin: 1.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
article .article-body > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.86em;
|
||||||
|
background: var(--code-bg);
|
||||||
|
padding: 0.12em 0.35em;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0 0 1em;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
overflow-x: auto;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
body {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<span class="margin-note"><slot /></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.margin-note {
|
||||||
|
display: block;
|
||||||
|
margin: 0.4rem 0 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.margin-note {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(100% + 2rem);
|
||||||
|
width: var(--note-width);
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{
|
||||||
|
bleed?: boolean | string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const isBleed = computed(
|
||||||
|
() =>
|
||||||
|
props.bleed === true ||
|
||||||
|
props.bleed === '' ||
|
||||||
|
props.bleed === 'true',
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="notice" :class="{ 'notice--bleed': isBleed }">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.notice {
|
||||||
|
background: var(--navy);
|
||||||
|
color: #f6f7f9;
|
||||||
|
padding: 1rem 1.15rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice :deep(a) {
|
||||||
|
color: var(--accent-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice :deep(a:hover) {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice :deep(p:last-child) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice--bleed {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100vw;
|
||||||
|
max-width: 100vw;
|
||||||
|
margin: 1.5rem 0 1.5rem calc(-1 * var(--col-left));
|
||||||
|
padding: 1.15rem var(--col-left);
|
||||||
|
padding-right: calc(100vw - var(--col-left) - var(--col));
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {
|
||||||
|
BG_THEMES,
|
||||||
|
BG_THEME_STORAGE_KEY,
|
||||||
|
DEFAULT_BG_THEME,
|
||||||
|
isBgThemeId,
|
||||||
|
type BgThemeId,
|
||||||
|
} from '~/utils/themes';
|
||||||
|
|
||||||
|
export function useBgTheme() {
|
||||||
|
const id = useState<BgThemeId>('bg-theme', () => DEFAULT_BG_THEME);
|
||||||
|
const colorMode = useColorMode();
|
||||||
|
|
||||||
|
const current = computed(
|
||||||
|
() => BG_THEMES.find((theme) => theme.id === id.value) ?? BG_THEMES[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
const applyClass = (next: BgThemeId) => {
|
||||||
|
if (!import.meta.client) return;
|
||||||
|
for (const theme of BG_THEMES) {
|
||||||
|
document.documentElement.classList.remove(`theme-${theme.id}`);
|
||||||
|
}
|
||||||
|
document.documentElement.classList.add(`theme-${next}`);
|
||||||
|
colorMode.preference = next === 'paper' ? 'light' : 'dark';
|
||||||
|
};
|
||||||
|
|
||||||
|
const setTheme = (next: BgThemeId) => {
|
||||||
|
id.value = next;
|
||||||
|
applyClass(next);
|
||||||
|
if (import.meta.client) {
|
||||||
|
localStorage.setItem(BG_THEME_STORAGE_KEY, next);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const saved = localStorage.getItem(BG_THEME_STORAGE_KEY);
|
||||||
|
const next = isBgThemeId(saved) ? saved : id.value;
|
||||||
|
setTheme(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { id, current, setTheme, themes: BG_THEMES };
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
BODY_FONTS,
|
||||||
|
BODY_FONT_STORAGE_KEY,
|
||||||
|
DEFAULT_BODY_FONT,
|
||||||
|
isBodyFontId,
|
||||||
|
type BodyFontId,
|
||||||
|
} from '~/utils/fonts';
|
||||||
|
|
||||||
|
export function useBodyFont() {
|
||||||
|
const id = useState<BodyFontId>('body-font', () => DEFAULT_BODY_FONT);
|
||||||
|
|
||||||
|
const applyClass = (next: BodyFontId) => {
|
||||||
|
if (!import.meta.client) return;
|
||||||
|
for (const font of BODY_FONTS) {
|
||||||
|
document.documentElement.classList.remove(`font-${font.id}`);
|
||||||
|
}
|
||||||
|
document.documentElement.classList.add(`font-${next}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFont = (next: BodyFontId) => {
|
||||||
|
id.value = next;
|
||||||
|
applyClass(next);
|
||||||
|
if (import.meta.client) {
|
||||||
|
localStorage.setItem(BODY_FONT_STORAGE_KEY, next);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const saved = localStorage.getItem(BODY_FONT_STORAGE_KEY);
|
||||||
|
const next = isBodyFontId(saved) ? saved : id.value;
|
||||||
|
setFont(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { id, setFont, fonts: BODY_FONTS };
|
||||||
|
}
|
||||||
+11
-64
@@ -9,68 +9,15 @@ useHead({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="error-page">
|
<NuxtLayout
|
||||||
<svg width="0" height="0" aria-hidden="true" style="position: absolute">
|
name="main"
|
||||||
<filter id="grainy" x="0" y="0" width="100%" height="100%">
|
:title="String(props.error.statusCode ?? 500)"
|
||||||
<feTurbulence
|
:description="props.error.message ?? 'Sorry, something went wrong.'"
|
||||||
type="fractalNoise"
|
>
|
||||||
baseFrequency=".85"
|
<article>
|
||||||
numOctaves="3"
|
<p>
|
||||||
></feTurbulence>
|
<NuxtLink to="/">Back to the homepage.</NuxtLink>
|
||||||
<feColorMatrix type="saturate" values="0"></feColorMatrix>
|
</p>
|
||||||
<feBlend mode="multiply" in="SourceGraphic"></feBlend>
|
</article>
|
||||||
</filter>
|
</NuxtLayout>
|
||||||
</svg>
|
|
||||||
<div class="error-card">
|
|
||||||
<div class="error-content">
|
|
||||||
<h1>{{ error.status }}</h1>
|
|
||||||
<p>
|
|
||||||
{{ error.message ?? 'Sorry, something went wrong.' }}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<NuxtLink to="/" class="punch-link"
|
|
||||||
>Back to the homepage.</NuxtLink
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.error-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-card {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 2rem 16rem;
|
|
||||||
color: var(--bg);
|
|
||||||
isolation: isolate;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-card::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: url('~/assets/images/blend.jpeg');
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
filter: url(#grainy);
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-content {
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
+248
-279
@@ -1,332 +1,301 @@
|
|||||||
<script lang="ts">
|
|
||||||
// module-scoped so it survives client-side navigation —
|
|
||||||
// the intro should only ever play once per visit
|
|
||||||
let introPlayed = false;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps<{
|
withDefaults(
|
||||||
title?: string;
|
defineProps<{
|
||||||
description?: string;
|
title?: string;
|
||||||
}>();
|
description?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
title: "Hey, I'm Jaron.",
|
||||||
|
description: 'Software engineer by trade — builder by nature.',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const theme = useColorMode();
|
const { id, themes, setTheme } = useBgTheme();
|
||||||
|
const { id: fontId, fonts, setFont } = useBodyFont();
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const onThemeChange = (event: Event) => {
|
||||||
theme.preference = theme.preference === 'light' ? 'dark' : 'light';
|
const value = (event.target as HTMLSelectElement).value;
|
||||||
|
const next = themes.find((theme) => theme.id === value);
|
||||||
|
if (next) setTheme(next.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { $gsap } = useNuxtApp();
|
const onFontChange = (event: Event) => {
|
||||||
const pageEl = useTemplateRef('pageEl');
|
const value = (event.target as HTMLSelectElement).value;
|
||||||
const initialized = ref(false);
|
const next = fonts.find((font) => font.id === value);
|
||||||
|
if (next) setFont(next.id);
|
||||||
onMounted(() => {
|
};
|
||||||
initialized.value = true;
|
|
||||||
|
|
||||||
if (!$gsap || !pageEl.value || introPlayed) return;
|
|
||||||
introPlayed = true;
|
|
||||||
|
|
||||||
const reduced = window.matchMedia(
|
|
||||||
'(prefers-reduced-motion: reduce)',
|
|
||||||
).matches;
|
|
||||||
|
|
||||||
const ctx = $gsap.context(() => {
|
|
||||||
if (reduced) return;
|
|
||||||
|
|
||||||
const tl = $gsap.timeline({
|
|
||||||
defaults: { ease: 'power3.out' },
|
|
||||||
});
|
|
||||||
|
|
||||||
tl.from('.header-lower', {
|
|
||||||
autoAlpha: 0,
|
|
||||||
y: 12,
|
|
||||||
duration: 0.9,
|
|
||||||
ease: 'power2.out',
|
|
||||||
})
|
|
||||||
.from('.name', { yPercent: 110, duration: 0.9 }, 0.2)
|
|
||||||
.from(
|
|
||||||
'.title',
|
|
||||||
{ yPercent: 120, autoAlpha: 0, duration: 0.8 },
|
|
||||||
0.35,
|
|
||||||
)
|
|
||||||
.from(
|
|
||||||
['.footer-upper', '.footer-lower'],
|
|
||||||
{ autoAlpha: 0, duration: 0.7, ease: 'power2.out' },
|
|
||||||
0.55,
|
|
||||||
)
|
|
||||||
.from('.header-upper', { autoAlpha: 0, duration: 0.7 }, 0.55);
|
|
||||||
|
|
||||||
tl.from(
|
|
||||||
'main',
|
|
||||||
{ autoAlpha: 0, y: 16, duration: 0.8, ease: 'power2.out' },
|
|
||||||
0.4,
|
|
||||||
);
|
|
||||||
}, pageEl.value);
|
|
||||||
|
|
||||||
onBeforeUnmount(() => ctx.revert());
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-show="initialized">
|
<div class="site">
|
||||||
<svg width="0" height="0" aria-hidden="true" style="position: absolute">
|
<header class="hero">
|
||||||
<filter id="grainy" x="0" y="0" width="100%" height="100%">
|
<div class="hero__inner">
|
||||||
<feTurbulence
|
<div class="hero__nav">
|
||||||
type="fractalNoise"
|
<NuxtLink to="/" class="wordmark" aria-label="Home">
|
||||||
baseFrequency=".85"
|
@jaronpate
|
||||||
numOctaves="3"
|
</NuxtLink>
|
||||||
></feTurbulence>
|
<nav class="site-nav">
|
||||||
<feColorMatrix type="saturate" values="0"></feColorMatrix>
|
<NuxtLink to="/writing">Writing</NuxtLink>
|
||||||
<feBlend mode="multiply" in="SourceGraphic"></feBlend>
|
<a href="https://github.com/jaronpate">GitHub</a>
|
||||||
</filter>
|
<label class="theme-picker">
|
||||||
</svg>
|
<span class="visually-hidden">Background</span>
|
||||||
<div class="page-wrapper" ref="pageEl">
|
<select
|
||||||
<header class="header">
|
class="theme-select"
|
||||||
<div class="header-upper">
|
:value="id"
|
||||||
<slot name="header-upper">
|
@change="onThemeChange"
|
||||||
<div class="flex-center header-upper-nav">
|
|
||||||
<button
|
|
||||||
class="theme-toggle"
|
|
||||||
@click="toggleTheme"
|
|
||||||
v-if="!theme.unknown"
|
|
||||||
>
|
>
|
||||||
{{ theme.value === 'dark' ? '🌚' : '🌝' }}
|
<option
|
||||||
</button>
|
v-for="theme in themes"
|
||||||
<div class="flex-ff"></div>
|
:key="theme.id"
|
||||||
<a
|
:value="theme.id"
|
||||||
class="basic-link"
|
>
|
||||||
href="https://github.com/jaronpate"
|
{{ theme.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="theme-picker">
|
||||||
|
<span class="visually-hidden">Font</span>
|
||||||
|
<select
|
||||||
|
class="theme-select"
|
||||||
|
:value="fontId"
|
||||||
|
@change="onFontChange"
|
||||||
>
|
>
|
||||||
GitHub
|
<option
|
||||||
</a>
|
v-for="font in fonts"
|
||||||
<NuxtLink to="/writing" class="basic-link">
|
:key="font.id"
|
||||||
Writing
|
:value="font.id"
|
||||||
</NuxtLink>
|
>
|
||||||
</div>
|
{{ font.label }}
|
||||||
</slot>
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-lower">
|
<h1 class="hero__title">{{ title }}</h1>
|
||||||
<slot name="header-lower">
|
<p v-if="description" class="hero__sub">{{ description }}</p>
|
||||||
<div class="mask">
|
</div>
|
||||||
<h1 class="name">
|
</header>
|
||||||
{{ title || "Hey, I'm Jaron" }}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div class="mask">
|
|
||||||
<p class="title">
|
|
||||||
{{
|
|
||||||
description ||
|
|
||||||
'Software Engineer by trade — builder by nature.'
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
<div class="page-body">
|
||||||
<slot />
|
<slot />
|
||||||
<div style="flex: 1"></div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="footer-upper flex-center">
|
|
||||||
<p>
|
|
||||||
<a class="punch-link" href="mailto:yo@jp.wtf"
|
|
||||||
>Say hi.</a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<div class="flex-ff"></div>
|
|
||||||
<p style="text-align: right">Let's make something cool.</p>
|
|
||||||
</div>
|
|
||||||
<div class="footer-lower flex-center" style="gap: var(--gap)">
|
|
||||||
<p style="text-align: right">Est. 2003</p>
|
|
||||||
<!-- <p style="font-family: monospace">🐟🎩.dev</p> -->
|
|
||||||
<div class="flex-ff"></div>
|
|
||||||
<p style="text-align: right">Austin, TX</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<footer class="site-footer">
|
||||||
|
<a href="mailto:yo@jp.wtf">yo@jp.wtf</a>
|
||||||
|
<div class="flex-ff"></div>
|
||||||
|
<span>Est. 2003</span>
|
||||||
|
<span>Austin, TX</span>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page-wrapper {
|
.site {
|
||||||
--main-width: 50vw;
|
width: 100%;
|
||||||
width: var(--main-width);
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
/*gap: 1.5rem;*/
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
position: relative;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
.page-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
padding-top: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
--hero-text: #fbfbf7;
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
color: var(--hero-text);
|
||||||
|
padding: 1.25rem 0 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
background:
|
||||||
|
linear-gradient(rgba(12, 12, 18, 0.1), rgba(12, 12, 18, 0.18)),
|
||||||
|
url('~/assets/images/blend.jpeg') center / cover no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero__inner {
|
||||||
|
width: var(--col);
|
||||||
|
margin-left: var(--col-left);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero__nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
|
gap: 1.25rem;
|
||||||
|
margin-bottom: 2.75rem;
|
||||||
|
font-family: var(--font-plain);
|
||||||
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg[height='0'] {
|
.wordmark,
|
||||||
position: fixed;
|
.site-nav a,
|
||||||
|
.theme-select {
|
||||||
|
color: var(--hero-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mask {
|
.wordmark {
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
gap: 0.7rem;
|
||||||
.name {
|
text-decoration: none;
|
||||||
font-size: 3rem;
|
font-family: var(--font-mono);
|
||||||
|
font-size: 1.15rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1.2;
|
line-height: 1;
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.wordmark:hover,
|
||||||
font-weight: 300;
|
.wordmark.router-link-active {
|
||||||
margin: 0;
|
color: var(--hero-text);
|
||||||
font-style: italic;
|
text-decoration: none;
|
||||||
color: var(--color-white-500);
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.site-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-upper {
|
|
||||||
height: 8vh;
|
|
||||||
min-height: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-upper-nav {
|
|
||||||
padding: 0 2rem;
|
|
||||||
height: 100%;
|
|
||||||
font-size: small;
|
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-lower {
|
.site-nav a:hover,
|
||||||
display: flex;
|
.site-nav a.router-link-active {
|
||||||
flex-direction: column;
|
color: var(--hero-text);
|
||||||
align-items: flex-start;
|
opacity: 0.7;
|
||||||
justify-content: center;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 3rem 2.5rem;
|
|
||||||
color: var(--color-white-500);
|
|
||||||
border-radius: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
isolation: isolate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-lower::before {
|
.theme-picker {
|
||||||
content: '';
|
display: inline-flex;
|
||||||
position: absolute;
|
align-items: center;
|
||||||
inset: 0;
|
}
|
||||||
background: url('~/assets/images/blend.jpeg');
|
|
||||||
background-size: cover;
|
.theme-select {
|
||||||
background-position: left top;
|
appearance: none;
|
||||||
|
background-color: rgba(255, 255, 255, 0.14);
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath fill='%23fbfbf7' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
filter: url(#grainy);
|
background-position: right 0.55em center;
|
||||||
border-radius: 16px;
|
background-size: 0.5em;
|
||||||
z-index: -1;
|
border: 1px solid rgba(251, 251, 247, 0.35);
|
||||||
}
|
border-radius: 4px;
|
||||||
|
|
||||||
.footer-upper {
|
|
||||||
padding: 3.25rem 2.5rem;
|
|
||||||
color: var(--color-white-500);
|
|
||||||
border-radius: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
isolation: isolate;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-upper::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: url('~/assets/images/blend.jpeg');
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
filter: url(#grainy);
|
|
||||||
border-radius: 16px;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-lower {
|
|
||||||
padding: 0 2rem;
|
|
||||||
height: 8vh;
|
|
||||||
min-height: 3.5rem;
|
|
||||||
font-size: small;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-toggle {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: large;
|
font-family: var(--font-plain);
|
||||||
font-weight: bold;
|
font-size: 0.8rem;
|
||||||
color: var(--text);
|
line-height: 1;
|
||||||
transition: opacity 0.2s ease;
|
padding: 0.35em 1.55em 0.35em 0.65em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-select:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-select option {
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-toggle:hover {
|
.hero__title {
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer p {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
font-size: 2.75rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
.hero__sub {
|
||||||
.page-wrapper {
|
margin: 0.6rem 0 0;
|
||||||
--main-width: 80vw;
|
font-weight: var(--body-weight);
|
||||||
}
|
font-style: italic;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: rgba(251, 251, 247, 0.88);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
.site-footer {
|
||||||
.header-upper-nav {
|
--flare-length: 1.125rem;
|
||||||
padding: 0 1.5rem;
|
--flare-spread: 1.125rem;
|
||||||
|
--flare-overhang: 1.5rem;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 1rem;
|
||||||
|
width: var(--col);
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-left: var(--col-left);
|
||||||
|
margin-right: auto;
|
||||||
|
padding: calc(var(--flare-spread) + 0.45rem) 0 0.85rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: calc(-1 * var(--flare-overhang));
|
||||||
|
right: calc(-1 * var(--flare-overhang));
|
||||||
|
height: var(--flare-spread);
|
||||||
|
background-color: var(--muted);
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-mask:
|
||||||
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M20 8H10M10 8C5 8 2.5 4.5 1 2M10 8C5 8 2.5 11.5 1 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
||||||
|
left center / var(--flare-length) 100% no-repeat,
|
||||||
|
linear-gradient(#000, #000) center /
|
||||||
|
calc(100% - var(--flare-length) * 1.55) 1px no-repeat,
|
||||||
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M0 8H10M10 8C15 8 17.5 4.5 19 2M10 8C15 8 17.5 11.5 19 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
||||||
|
right center / var(--flare-length) 100% no-repeat;
|
||||||
|
mask:
|
||||||
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M20 8H10M10 8C5 8 2.5 4.5 1 2M10 8C5 8 2.5 11.5 1 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
||||||
|
left center / var(--flare-length) 100% no-repeat,
|
||||||
|
linear-gradient(#000, #000) center /
|
||||||
|
calc(100% - var(--flare-length) * 1.55) 1px no-repeat,
|
||||||
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M0 8H10M10 8C15 8 17.5 4.5 19 2M10 8C15 8 17.5 11.5 19 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
||||||
|
right center / var(--flare-length) 100% no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer a {
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer a:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-ff {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.hero {
|
||||||
|
padding-bottom: 2.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.hero__nav {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero__title {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-wrapper {
|
|
||||||
--main-width: 100vw;
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-lower,
|
|
||||||
.footer-upper {
|
|
||||||
padding: 1.5rem 1.5rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-lower {
|
|
||||||
padding: 1.5rem 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-lower::before,
|
|
||||||
.footer-upper::before {
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+66
-437
@@ -1,10 +1,4 @@
|
|||||||
<script lang="ts">
|
|
||||||
// module-scoped so the reveal only plays once per visit
|
|
||||||
let sectionsRevealed = false;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_DESCRIPTION,
|
DEFAULT_DESCRIPTION,
|
||||||
DEFAULT_IMAGE,
|
DEFAULT_IMAGE,
|
||||||
@@ -36,14 +30,6 @@ useHead({
|
|||||||
link: [{ rel: 'canonical', href: SITE_URL }],
|
link: [{ rel: 'canonical', href: SITE_URL }],
|
||||||
});
|
});
|
||||||
|
|
||||||
const titles = [
|
|
||||||
"Hey, I'm Jaron.",
|
|
||||||
'Nice to meet you.',
|
|
||||||
'Welcome.',
|
|
||||||
'Willkommen.',
|
|
||||||
'Bienvenue.',
|
|
||||||
];
|
|
||||||
|
|
||||||
const selectedWorks = [
|
const selectedWorks = [
|
||||||
{
|
{
|
||||||
title: 'Edlink',
|
title: 'Edlink',
|
||||||
@@ -186,312 +172,116 @@ useHead({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const photos = [
|
|
||||||
{
|
|
||||||
src: '/photography/athens_red_scooter.jpg',
|
|
||||||
alt: 'Red scooter in Athens',
|
|
||||||
location: 'Athens',
|
|
||||||
year: '2026',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: '/photography/athens_subway.jpg',
|
|
||||||
alt: 'Athens metro station',
|
|
||||||
location: 'Athens',
|
|
||||||
year: '2026',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: '/photography/athens_sunset.jpg',
|
|
||||||
alt: 'Athens at sunset',
|
|
||||||
location: 'Athens',
|
|
||||||
year: '2026',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: '/photography/london_porsche.jpg',
|
|
||||||
alt: 'Porsche shop in London',
|
|
||||||
location: 'London',
|
|
||||||
year: '2025',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: '/photography/underground.jpg',
|
|
||||||
alt: 'Entrace to the London underground',
|
|
||||||
location: 'London',
|
|
||||||
year: '2026',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: '/photography/austin_parking.jpg',
|
|
||||||
alt: 'Parking garage in Austin',
|
|
||||||
location: 'Austin',
|
|
||||||
year: '2024',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const pageTitle = ref(titles[0]);
|
|
||||||
|
|
||||||
const rotateTitle = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const i = titles.findIndex((t) => t === pageTitle.value);
|
|
||||||
pageTitle.value = titles[i + 1] ?? titles[0] ?? 'Oops!';
|
|
||||||
rotateTitle();
|
|
||||||
}, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: false,
|
layout: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $gsap } = useNuxtApp();
|
|
||||||
const pageRoot = ref<HTMLElement | null>(null);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
rotateTitle();
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: pageTitle,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!$gsap || !pageRoot.value || sectionsRevealed) return;
|
|
||||||
sectionsRevealed = true;
|
|
||||||
|
|
||||||
const reduced = window.matchMedia(
|
|
||||||
'(prefers-reduced-motion: reduce)',
|
|
||||||
).matches;
|
|
||||||
|
|
||||||
const ctx = $gsap.context(() => {
|
|
||||||
if (reduced) return;
|
|
||||||
|
|
||||||
// sections in view on load are revealed by the layout's intro;
|
|
||||||
// below-the-fold sections rise in as they enter the viewport
|
|
||||||
$gsap.utils.toArray<HTMLElement>('main section').forEach((el) => {
|
|
||||||
const inView = el.getBoundingClientRect().top < window.innerHeight;
|
|
||||||
if (inView) return;
|
|
||||||
|
|
||||||
$gsap.from(el, {
|
|
||||||
autoAlpha: 0,
|
|
||||||
y: 24,
|
|
||||||
duration: 0.6,
|
|
||||||
ease: 'power2.out',
|
|
||||||
scrollTrigger: {
|
|
||||||
trigger: el,
|
|
||||||
start: 'top 92%',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, pageRoot.value);
|
|
||||||
|
|
||||||
onBeforeUnmount(() => ctx.revert());
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="pageRoot">
|
<NuxtLayout name="main">
|
||||||
<NuxtLayout name="main">
|
<article>
|
||||||
<section class="rows">
|
<h3>Selected Work</h3>
|
||||||
<h5>Selected Work</h5>
|
<template v-for="work in selectedWorks" :key="work.title">
|
||||||
<div class="section__content">
|
<p>
|
||||||
<a
|
<a :href="work.link" target="_blank" rel="noopener">{{
|
||||||
v-for="work in selectedWorks"
|
work.title
|
||||||
:key="work.title"
|
}}</a>
|
||||||
class="work"
|
— {{ work.description }}
|
||||||
:href="work.link"
|
</p>
|
||||||
target="_blank"
|
<div class="work__images">
|
||||||
|
<figure
|
||||||
|
v-for="image in work.images"
|
||||||
|
:key="image.src"
|
||||||
|
class="work__image"
|
||||||
>
|
>
|
||||||
<h3 class="work__title">{{ work.title }}</h3>
|
<img :src="image.src" :alt="image.alt" />
|
||||||
<p class="work__description">
|
</figure>
|
||||||
{{ work.description }}
|
|
||||||
</p>
|
|
||||||
<div class="work__images">
|
|
||||||
<figure
|
|
||||||
v-for="image in work.images"
|
|
||||||
:key="image.src"
|
|
||||||
class="work__image"
|
|
||||||
>
|
|
||||||
<img :src="image.src" :alt="image.alt" />
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</template>
|
||||||
<section class="rows">
|
|
||||||
<h5>Projects</h5>
|
<h3>Projects</h3>
|
||||||
<div class="section__content">
|
<ul>
|
||||||
<a
|
<li v-for="project in projects" :key="project.href">
|
||||||
v-for="project in projects"
|
|
||||||
:key="project.href"
|
|
||||||
:href="project.href"
|
|
||||||
target="_blank"
|
|
||||||
class="project"
|
|
||||||
>
|
|
||||||
<h3 class="project__title">{{ project.title }}</h3>
|
|
||||||
<p class="project__description">
|
|
||||||
{{ project.description }}
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="about">
|
|
||||||
<h5>About</h5>
|
|
||||||
<div class="section__content">
|
|
||||||
<p>
|
<p>
|
||||||
I left school, my job, and moved to Austin in 2022 to write
|
<a :href="project.href" target="_blank" rel="noopener">{{
|
||||||
code full time. That was definitely one of the best and
|
project.title
|
||||||
most life changing decisions I've ever made. I've learned
|
}}</a>
|
||||||
a lot since then and <em>still</em> have much more to learn.
|
— {{ project.description }}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
</li>
|
||||||
These days I build things because I find joy in making something
|
</ul>
|
||||||
someone else really enjoys using. I get excited about reaching a
|
|
||||||
level of polish that users don't even notice.
|
<h3>About</h3>
|
||||||
</p>
|
<p>
|
||||||
<p>
|
I left school, my job, and moved to Austin in 2022 to write code
|
||||||
In a time where code is cheap I pride myself in taste and execution.
|
full time. That was definitely one of the best and most life
|
||||||
The best sticking point — in my limited opinion so far — is the
|
changing decisions I've ever made. I've learned a lot since then
|
||||||
way using your product <em>feels</em>. So I want everything I make to
|
and <em>still</em> have much more to learn.
|
||||||
simply <em>feel</em> the best to use.
|
</p>
|
||||||
</p>
|
<p>
|
||||||
<p>
|
These days I build things because I find joy in making something
|
||||||
If you want to build something cool or connect
|
someone else really enjoys using. I get excited about reaching a
|
||||||
<a class="basic-link" href="mailto:yo@jp.wtf">hit me up</a>
|
level of polish that users don't even notice.
|
||||||
— we'll grab a coffee or beer if you're in Austin! I
|
</p>
|
||||||
still dont know enough people to form a band yet ;)
|
<p>
|
||||||
</p>
|
In a time where code is cheap I pride myself in taste and
|
||||||
</div>
|
execution. The best sticking point — in my limited opinion so far
|
||||||
</section>
|
— is the way using your product <em>feels</em>. So I want
|
||||||
<!-- <section class="photos">
|
everything I make to simply <em>feel</em> the best to use.
|
||||||
<h5>Photos</h5>
|
</p>
|
||||||
<div class="section__content">
|
<p>
|
||||||
<div class="photo-grid">
|
If you want to build something cool or connect
|
||||||
<div
|
<a href="mailto:yo@jp.wtf">hit me up</a> — we'll grab a coffee or
|
||||||
v-for="photo in photos"
|
beer if you're in Austin! I still dont know enough people to form
|
||||||
:key="photo.src"
|
a band yet ;)
|
||||||
class="photo-grid__item"
|
</p>
|
||||||
>
|
</article>
|
||||||
<div class="photo__image">
|
</NuxtLayout>
|
||||||
<img :src="photo.src" :alt="photo.alt" />
|
|
||||||
</div>
|
|
||||||
<div class="photo__overlay">
|
|
||||||
<p>{{ photo.location }}</p>
|
|
||||||
<p>{{ photo.year }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> -->
|
|
||||||
</NuxtLayout>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
section {
|
|
||||||
margin-top: calc(var(--gap) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
section > .section__content {
|
|
||||||
padding: 0 calc(var(--gap) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* section:not(:has(h5)) > .section__content {
|
|
||||||
padding: 2rem;
|
|
||||||
} */
|
|
||||||
|
|
||||||
section > h5 {
|
|
||||||
--flare-length: 1.125rem; /* how far the flare extends horizontally */
|
|
||||||
--flare-spread: 1.125rem; /* how far the arms open up/down */
|
|
||||||
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
color: var(--muted);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
margin: 0 0 var(--gap) 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
section > h5::before {
|
|
||||||
content: '';
|
|
||||||
width: var(--flare-length);
|
|
||||||
height: var(--flare-spread);
|
|
||||||
flex-shrink: 0;
|
|
||||||
background-color: var(--muted);
|
|
||||||
/* Optically center */
|
|
||||||
transform: translateY(-0.12em);
|
|
||||||
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M20 8H10M10 8C5 8 2.5 4.5 1 2M10 8C5 8 2.5 11.5 1 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
|
||||||
center / 100% 100% no-repeat;
|
|
||||||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M20 8H10M10 8C5 8 2.5 4.5 1 2M10 8C5 8 2.5 11.5 1 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
|
||||||
center / 100% 100% no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
section > h5::after {
|
|
||||||
content: '';
|
|
||||||
height: var(--flare-spread);
|
|
||||||
flex-grow: 1;
|
|
||||||
background-color: var(--muted);
|
|
||||||
/* Optically center */
|
|
||||||
transform: translateY(-0.12em);
|
|
||||||
-webkit-mask:
|
|
||||||
linear-gradient(#000, #000) left center /
|
|
||||||
calc(100% - var(--flare-length) * 0.45) 1px no-repeat,
|
|
||||||
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M0 8H10M10 8C15 8 17.5 4.5 19 2M10 8C15 8 17.5 11.5 19 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
|
||||||
right center / var(--flare-length) 100% no-repeat;
|
|
||||||
mask:
|
|
||||||
linear-gradient(#000, #000) left center /
|
|
||||||
calc(100% - var(--flare-length) * 0.45) 1px no-repeat,
|
|
||||||
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16' preserveAspectRatio='none' fill='none'%3E%3Cpath d='M0 8H10M10 8C15 8 17.5 4.5 19 2M10 8C15 8 17.5 11.5 19 14' stroke='black' stroke-width='1' stroke-linecap='round' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")
|
|
||||||
right center / var(--flare-length) 100% no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:last-of-type {
|
|
||||||
margin-bottom: calc(var(--gap) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.work {
|
|
||||||
}
|
|
||||||
|
|
||||||
.work__images {
|
.work__images {
|
||||||
/*display: flex;
|
--dot-color: #666;
|
||||||
flex-wrap: no-wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;*/
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
grid-auto-columns: max-content;
|
grid-auto-columns: max-content;
|
||||||
gap: var(--gap);
|
gap: var(--gap);
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
margin-top: var(--gap);
|
margin: 1.25rem 0 1.75rem;
|
||||||
|
padding-bottom: 0.35rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.work__image {
|
.work__image {
|
||||||
--dot-size: 6%;
|
--dot-size: 6%;
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
/*padding: calc(var(--gap) * 2) var(--gap) var(--gap);*/
|
|
||||||
padding: var(--gap);
|
padding: var(--gap);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
background-image: radial-gradient(
|
background-image: radial-gradient(
|
||||||
circle at 4px 4px,
|
circle at 4px 4px,
|
||||||
var(--dot-color) var(--dot-size),
|
var(--dot-color) var(--dot-size),
|
||||||
transparent calc(var(--dot-size) + 1%)
|
transparent calc(var(--dot-size) + 1%)
|
||||||
);
|
);
|
||||||
|
|
||||||
background-position: 4px 4px;
|
background-position: 4px 4px;
|
||||||
background-size: 12px 12px;
|
background-size: 12px 12px;
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
background-color: var(--bg-secondary);
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.work__image img {
|
.work__image img {
|
||||||
border-radius: var(--radius);
|
max-width: none;
|
||||||
object-fit: cover;
|
width: auto;
|
||||||
|
height: auto;
|
||||||
max-height: 350px;
|
max-height: 350px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 750px) {
|
@media screen and (max-width: 750px) {
|
||||||
@@ -499,165 +289,4 @@ section:last-of-type {
|
|||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.work__image figcaption {
|
|
||||||
font-size: 0.6rem;
|
|
||||||
line-height: 1;
|
|
||||||
font-family: var(--font-plain);
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 3px 4px;
|
|
||||||
background: var(--dot-color);
|
|
||||||
/*border: 1px solid var(--dot-color);*/
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rows > .section__content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project,
|
|
||||||
.work {
|
|
||||||
/*border-bottom: 1px solid var(--muted);*/
|
|
||||||
/*padding: var(--gap) 0;*/
|
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.work {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project:first-of-type,
|
|
||||||
.work:first-of-type {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project:last-of-type,
|
|
||||||
.work:last-of-type {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project:hover > .project__title {
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 4px;
|
|
||||||
font-style: italic;
|
|
||||||
font-family: var(--font-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project__title,
|
|
||||||
.work__title {
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: calc(var(--gap) * 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project__title::after,
|
|
||||||
.work__title::after {
|
|
||||||
content: '↗';
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 0.4em;
|
|
||||||
font-size: 0.75em;
|
|
||||||
opacity: 0;
|
|
||||||
vertical-align: center;
|
|
||||||
transform: translate(-6px, 6px);
|
|
||||||
transition:
|
|
||||||
opacity 0.25s ease,
|
|
||||||
transform 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project:hover .project__title::after,
|
|
||||||
.work:hover .work__title::after {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translate(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project__description,
|
|
||||||
.work__description {
|
|
||||||
margin: 0;
|
|
||||||
font-size: small;
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-grid__item {
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-grid__item:hover > .photo__overlay {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo__image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo__image img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo__overlay {
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
backdrop-filter: blur(2px);
|
|
||||||
color: var(--color-white-500);
|
|
||||||
z-index: 2;
|
|
||||||
transition: opacity 0.2s ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo__overlay p {
|
|
||||||
font-weight: 500;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo__overlay p:nth-child(2) {
|
|
||||||
font-weight: 300;
|
|
||||||
font-style: italic;
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about p:last-child {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
section > .section__content {
|
|
||||||
padding: 0 var(--gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ const articleUrl = `${SITE_URL}${route.path}`;
|
|||||||
const webpageId = `${articleUrl}#webpage`;
|
const webpageId = `${articleUrl}#webpage`;
|
||||||
const articleId = `${articleUrl}#article`;
|
const articleId = `${articleUrl}#article`;
|
||||||
const blogId = `${SITE_URL}/writing#blog`;
|
const blogId = `${SITE_URL}/writing#blog`;
|
||||||
|
const contentPath =
|
||||||
|
'/' +
|
||||||
|
(Array.isArray(route.params.slug)
|
||||||
|
? route.params.slug.join('/')
|
||||||
|
: String(route.params.slug ?? ''));
|
||||||
|
|
||||||
const { data: page } = await useAsyncData(route.path, () => {
|
const { data: page } = await useAsyncData(route.path, () => {
|
||||||
return queryCollection('writing')
|
return queryCollection('writing').path(contentPath).first();
|
||||||
.path(route.path.replace('/writing/', ''))
|
|
||||||
.first();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!page.value) {
|
if (!page.value) {
|
||||||
@@ -134,52 +138,33 @@ useHead({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const formatDate = (date: Date | string) => {
|
||||||
|
return new Date(date).toLocaleDateString('en-US', {
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
timeZone: 'UTC',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: false,
|
layout: false,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<NuxtLayout
|
||||||
<NuxtLayout
|
name="main"
|
||||||
name="main"
|
:title="page.title"
|
||||||
:title="page?.title"
|
:description="formatDate(page.date)"
|
||||||
:description="page?.description"
|
>
|
||||||
>
|
<article v-if="page">
|
||||||
<template #header-upper>
|
<div class="article-body">
|
||||||
<div class="header-upper-inner">
|
<p v-if="page.description">
|
||||||
<NuxtLink
|
<strong>{{ page.description }}</strong>
|
||||||
to="/writing"
|
</p>
|
||||||
class="back-link"
|
<ContentRenderer :value="page" />
|
||||||
aria-label="Back to writing"
|
</div>
|
||||||
>
|
</article>
|
||||||
<span class="arrow">←</span>
|
</NuxtLayout>
|
||||||
<span>Back</span>
|
|
||||||
</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<ContentRenderer
|
|
||||||
class="writing__content"
|
|
||||||
v-if="page"
|
|
||||||
:value="page"
|
|
||||||
/>
|
|
||||||
</NuxtLayout>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.header-upper-inner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.writing__content {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.writing__content :deep(> :first-child) {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
+61
-100
@@ -7,11 +7,9 @@ import {
|
|||||||
websiteId,
|
websiteId,
|
||||||
} from '~/utils/site';
|
} from '~/utils/site';
|
||||||
|
|
||||||
// const { data: pages } = await useAsyncData('writing-pages', () =>
|
const { data: pages } = await useAsyncData('writing-pages', () =>
|
||||||
// queryCollection('writing').order('date', 'DESC').all(),
|
queryCollection('writing').order('date', 'DESC').all(),
|
||||||
// );
|
);
|
||||||
|
|
||||||
const pages = ref<any[]>([]);
|
|
||||||
|
|
||||||
const writingUrl = `${SITE_URL}/writing`;
|
const writingUrl = `${SITE_URL}/writing`;
|
||||||
const collectionId = `${writingUrl}#collection`;
|
const collectionId = `${writingUrl}#collection`;
|
||||||
@@ -74,7 +72,7 @@ useHead({
|
|||||||
name: SITE_NAME,
|
name: SITE_NAME,
|
||||||
},
|
},
|
||||||
blogPost: (pages.value ?? []).map((page) => ({
|
blogPost: (pages.value ?? []).map((page) => ({
|
||||||
'@id': `${writingUrl}/${page.path}#article`,
|
'@id': `${writingUrl}${page.path}#article`,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -85,7 +83,7 @@ useHead({
|
|||||||
(page, index) => ({
|
(page, index) => ({
|
||||||
'@type': 'ListItem',
|
'@type': 'ListItem',
|
||||||
position: index + 1,
|
position: index + 1,
|
||||||
url: `${writingUrl}/${page.path}`,
|
url: `${writingUrl}${page.path}`,
|
||||||
name: page.title,
|
name: page.title,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -114,10 +112,13 @@ useHead({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string | Date) => {
|
||||||
const date = new Date(dateString);
|
return new Date(dateString).toLocaleDateString('en-US', {
|
||||||
// ex. May 5, 2026
|
month: 'short',
|
||||||
return `${date.toLocaleDateString('en-US', { month: 'short' })} ${date.getDate()}, ${date.getFullYear()}`;
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
timeZone: 'UTC',
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@@ -126,116 +127,76 @@ definePageMeta({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<NuxtLayout
|
||||||
<NuxtLayout
|
name="main"
|
||||||
name="main"
|
title="Writing"
|
||||||
title="Writing"
|
description="Thoughts I've had here and there. Maybe you'll find something helpful or interesting here."
|
||||||
description="Thoughts I've had here and there. Maybe you'll find something helpful or interesting here."
|
>
|
||||||
>
|
<article>
|
||||||
<template #header-upper>
|
<template v-if="pages?.length">
|
||||||
<div class="header-upper-inner">
|
<NuxtLink
|
||||||
<NuxtLink
|
v-for="page in pages"
|
||||||
to="/"
|
:key="page.path"
|
||||||
class="back-link"
|
:to="'/writing' + page.path"
|
||||||
aria-label="Back to Home"
|
class="listing"
|
||||||
>
|
>
|
||||||
<span class="arrow">←</span>
|
<span class="listing__title">{{ page.title }}</span>
|
||||||
<span>Home</span>
|
<time :datetime="new Date(page.date).toISOString()">{{
|
||||||
</NuxtLink>
|
formatDate(page.date)
|
||||||
</div>
|
}}</time>
|
||||||
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
<section>
|
<p v-else class="empty-state">No thoughts...head empty</p>
|
||||||
<div class="section__content">
|
</article>
|
||||||
<template v-if="pages?.length">
|
</NuxtLayout>
|
||||||
<NuxtLink
|
|
||||||
v-for="page in pages"
|
|
||||||
:to="'/writing/' + page.path"
|
|
||||||
:key="page.path"
|
|
||||||
class="article"
|
|
||||||
>
|
|
||||||
<h3 class="article__title">{{ page.title }}</h3>
|
|
||||||
<!-- <p class="article__date">
|
|
||||||
{{ formatDate(page.date) }}
|
|
||||||
</p> -->
|
|
||||||
<p class="article__description">
|
|
||||||
{{ page.description }}
|
|
||||||
</p>
|
|
||||||
</NuxtLink>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="empty-state">
|
|
||||||
<p>No thoughts...head empty</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</NuxtLayout>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.header-upper-inner {
|
.listing {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
height: 100%;
|
gap: 1rem;
|
||||||
padding: 0 2rem;
|
margin: 0 0 0.65rem;
|
||||||
}
|
|
||||||
|
|
||||||
section > .section__content {
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:not(:has(h5)) > .section__content {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article {
|
|
||||||
border-bottom: 1px solid var(--muted);
|
|
||||||
padding: 1rem 0;
|
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article:first-of-type {
|
.listing:hover {
|
||||||
padding-top: 0;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article:last-of-type {
|
.listing:hover .listing__title {
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article:hover > .article__title {
|
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-underline-offset: 4px;
|
text-underline-offset: 3px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-family: var(--font-hover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.article__title {
|
.listing__title {
|
||||||
margin: 0;
|
color: var(--text);
|
||||||
font-weight: 400;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.article__date {
|
.listing time {
|
||||||
margin: 0;
|
margin-left: auto;
|
||||||
font-weight: 400;
|
font-family: var(--font-plain);
|
||||||
font-size: small;
|
font-size: 0.75rem;
|
||||||
|
font-style: italic;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
white-space: nowrap;
|
||||||
|
|
||||||
.article__description {
|
|
||||||
margin: 0;
|
|
||||||
font-size: small;
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.listing {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listing time {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { gsap } from 'gsap';
|
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
|
||||||
gsap.registerPlugin(ScrollTrigger);
|
|
||||||
|
|
||||||
return {
|
|
||||||
provide: {
|
|
||||||
gsap,
|
|
||||||
ScrollTrigger,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export const BODY_FONTS = [
|
||||||
|
{ id: 'plantin', label: 'Plantin' },
|
||||||
|
{ id: 'alegreya', label: 'Alegreya' },
|
||||||
|
{ id: 'redaction', label: 'Redaction' },
|
||||||
|
{ id: 'redaction-35', label: 'Redaction 35' },
|
||||||
|
{ id: 'sohne', label: 'Sohne' },
|
||||||
|
{ id: 'space', label: 'Space Mono' },
|
||||||
|
{ id: 'georgia', label: 'Georgia' },
|
||||||
|
{ id: 'palatino', label: 'Palatino' },
|
||||||
|
{ id: 'iowan', label: 'Iowan' },
|
||||||
|
{ id: 'system', label: 'System Sans' },
|
||||||
|
{ id: 'courier', label: 'Courier' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type BodyFontId = (typeof BODY_FONTS)[number]['id'];
|
||||||
|
|
||||||
|
export const DEFAULT_BODY_FONT: BodyFontId = 'plantin';
|
||||||
|
export const BODY_FONT_STORAGE_KEY = 'body-font';
|
||||||
|
|
||||||
|
export function isBodyFontId(value: string | null): value is BodyFontId {
|
||||||
|
return BODY_FONTS.some((font) => font.id === value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export const BG_THEMES = [
|
||||||
|
{ id: 'dusk', label: 'Dusk' },
|
||||||
|
{ id: 'navy', label: 'Navy' },
|
||||||
|
{ id: 'midnight', label: 'Midnight' },
|
||||||
|
{ id: 'indigo', label: 'Indigo' },
|
||||||
|
{ id: 'storm', label: 'Storm' },
|
||||||
|
{ id: 'slate', label: 'Slate' },
|
||||||
|
{ id: 'ink', label: 'Ink' },
|
||||||
|
{ id: 'pine', label: 'Pine' },
|
||||||
|
{ id: 'wine', label: 'Wine' },
|
||||||
|
{ id: 'cocoa', label: 'Cocoa' },
|
||||||
|
{ id: 'paper', label: 'Paper' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type BgThemeId = (typeof BG_THEMES)[number]['id'];
|
||||||
|
|
||||||
|
export const DEFAULT_BG_THEME: BgThemeId = 'dusk';
|
||||||
|
export const BG_THEME_STORAGE_KEY = 'bg-theme';
|
||||||
|
|
||||||
|
export function isBgThemeId(value: string | null): value is BgThemeId {
|
||||||
|
return BG_THEMES.some((theme) => theme.id === value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: Notes in the margin
|
||||||
|
date: 2026-08-22
|
||||||
|
description: A quick look at the two asides this site knows how to make.
|
||||||
|
---
|
||||||
|
|
||||||
|
Most of what I write should just be the thing itself. Sometimes a thought doesn't belong in the sentence, though, it belongs next to it. :margin-note[(like this — a margin note sits beside the column on a wide screen, and drops under the paragraph on a small one)] That's what margin notes are for. Parentheticals you can actually skip.
|
||||||
|
|
||||||
|
Notices are the opposite. They interrupt on purpose.
|
||||||
|
|
||||||
|
::notice
|
||||||
|
**A note you shouldn't skip.** Use a notice when the aside *is* the point for a minute. It stays in the column, navy, hard to miss.
|
||||||
|
::
|
||||||
|
|
||||||
|
And if it needs to break the column entirely:
|
||||||
|
|
||||||
|
::notice{bleed}
|
||||||
|
**Full-bleed.** Same idea, but it runs edge to edge. Save it for the one time in a piece you actually want to shout.
|
||||||
|
::
|
||||||
|
|
||||||
|
That's the whole vocabulary. Margin for a mutter. Band for a point. Everything else is just the writing.
|
||||||
|
|
||||||
|
> A blockquote gets the little pointing hand in the left margin. Use it when you're quoting someone, or when you want a line to feel pointed at.
|
||||||
+11
-6
@@ -29,8 +29,9 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
css: ['~/assets/css/main.css'],
|
||||||
|
|
||||||
app: {
|
app: {
|
||||||
pageTransition: { name: 'page', mode: 'out-in' },
|
|
||||||
head: {
|
head: {
|
||||||
htmlAttrs: { lang: 'en' },
|
htmlAttrs: { lang: 'en' },
|
||||||
charset: 'utf-8',
|
charset: 'utf-8',
|
||||||
@@ -44,17 +45,21 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
link: [{ rel: 'icon', type: 'image/png', href: '/favicon.png' }],
|
link: [{ rel: 'icon', type: 'image/png', href: '/favicon.png' }],
|
||||||
|
script: [
|
||||||
|
{
|
||||||
|
innerHTML: `(function(){try{var t=localStorage.getItem('bg-theme')||'dusk';var f=localStorage.getItem('body-font')||'plantin';document.documentElement.classList.add('theme-'+t,'font-'+f);}catch(e){document.documentElement.classList.add('theme-dusk','font-plantin');}})();`,
|
||||||
|
tagPosition: 'head',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
modules: ['@nuxt/content', '@nuxtjs/color-mode'],
|
modules: ['@nuxt/content', '@nuxtjs/color-mode'],
|
||||||
colorMode: {
|
colorMode: {
|
||||||
preference: 'system', // default value of $colorMode.preference
|
preference: 'dark',
|
||||||
fallback: 'light', // fallback value if not system preference found
|
fallback: 'dark',
|
||||||
globalName: '__NUXT_COLOR_MODE__',
|
|
||||||
componentName: 'ColorScheme',
|
|
||||||
classPrefix: '',
|
classPrefix: '',
|
||||||
classSuffix: '-mode', // Results in .dark-mode or .light-mode
|
classSuffix: '-mode',
|
||||||
storageKey: 'color-mode',
|
storageKey: 'color-mode',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user