Add selected work section

This commit is contained in:
2026-06-22 00:28:43 -05:00
parent 7ef3843174
commit 6bc49d258a
18 changed files with 273 additions and 122 deletions
+7
View File
@@ -6,16 +6,21 @@
/* --font: 'Plantin MT Pro', serif; */
/* --font: 'Young Serif', serif; */
/* --font: "SohneBuch", sans-serif; */
--font-plain: 'SohneBuch', sans-serif;
--font: 'Redaction', serif;
--font-hover: 'Redaction 70', serif;
/* --font-hover: var(--font); */
--text: #333;
--muted: #666;
--bg: #fbfbf7;
--bg-secondary: #efefe8;
--dot-color: #666;
--color-white-500: #fbfbf7;
--gap: 1rem;
--radius: 4px;
}
::selection {
@@ -26,6 +31,8 @@
.dark-mode {
--text: #fbfbf7;
--bg: #0c0c0c;
--bg-secondary: #1c1c1c;
--dot-color: #666;
--muted: #a3a3a3;
--color-white-500: #fbfbf7;
+1 -1
View File
@@ -120,7 +120,7 @@ onMounted(() => {
<slot name="header-lower">
<div class="mask">
<h1 class="name">
{{ title || "Hello, I'm Jaron" }}
{{ title || "Hey, I'm Jaron" }}
</h1>
</div>
<div class="mask">
+265 -121
View File
@@ -6,14 +6,142 @@ let sectionsRevealed = false;
<script setup lang="ts">
import { ref } from 'vue';
useSeoMeta({
title: "Hey, I'm Jaron.",
ogTitle: "Hey, I'm Jaron.",
description:
'Software Engineer. I build things to solve problems from the ground up.',
ogDescription:
'Software Engineer. I build things to solve problems from the ground up.',
ogUrl: useRequestURL().href,
});
const titles = [
"Hello, I'm Jaron.",
"Hey, I'm Jaron.",
'Nice to meet you.',
'Welcome.',
'Willkommen.',
'Bienvenue.',
];
const selectedWorks = [
{
title: 'Edlink',
description:
"I built many things I'm proud of at Edlink. Notably, I designed and shipped Edlink’s internal billing platform for ~150 clients, a flat file ingestion engine that enabled arbitrary data mapping, client-embedded UI widgets, user onboarding flows, and a secure sandbox for running untrusted code with a live data-preview service.",
link: 'https://ed.link',
images: [
{
src: '/work/edlink/embedded-onboarding.gif',
alt: 'Embedded Onboarding',
},
{
src: '/work/edlink/authentication-widget.gif',
alt: 'Auth Widget',
},
{
src: '/work/edlink/integration-sharing-rules-widget.png',
alt: 'Data Sharing Editor Widget',
},
{
src: '/work/edlink/billing-overview.png',
alt: 'Billing Overview',
},
{
src: '/work/edlink/invoice-sheet.png',
alt: 'Invoice View',
},
{
src: '/work/edlink/add-payment-method.png',
alt: 'Add Payment Method',
},
{
src: '/work/edlink/onboarding-select-provider.png',
alt: 'Provider Selection',
},
{
src: '/work/edlink/onboarding-edit-source.png',
alt: 'Source Creation',
},
{
src: '/work/edlink/onboarding-start.png',
alt: 'Onboarding Start',
},
{
src: '/work/edlink/custom-code-editor.png',
alt: 'Custom Code Editor',
},
{
src: '/work/edlink/edit-licensing-rules.png',
alt: 'Licensing Editor',
},
{
src: '/work/edlink/materialization-preview.png',
alt: 'Pending Data Change Preview',
},
],
},
];
const projects = [
{
title: 'Unicord',
href: 'https://unicord.hat.fish/',
description: 'Discord bot SDK inspired by webserver architecture.',
},
{
title: 'Mystwright',
href: 'https://mystwright.com/',
description:
'An AI-driven mystery detective game. Generate, investigate, solve.',
},
{
title: 'Rune Buddy',
href: 'https://github.com/jaronpate/rift-buddy',
description:
'League of Legends runepage manager. Save & organise your pages for free.',
},
];
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 = () => {
@@ -24,22 +152,6 @@ const rotateTitle = () => {
}, 2000);
};
rotateTitle();
useHead({
title: pageTitle,
});
useSeoMeta({
title: "Hey, I'm Jaron",
ogTitle: "Hey, I'm Jaron",
description:
'Software Engineer. I build things to solve problems from the ground up.',
ogDescription:
'Software Engineer. I build things to solve problems from the ground up.',
ogUrl: useRequestURL().href,
});
definePageMeta({
layout: false,
});
@@ -48,6 +160,12 @@ const { $gsap } = useNuxtApp();
const pageRoot = ref<HTMLElement | null>(null);
onMounted(() => {
rotateTitle();
useHead({
title: pageTitle,
});
if (!$gsap || !pageRoot.value || sectionsRevealed) return;
sectionsRevealed = true;
@@ -84,43 +202,45 @@ onMounted(() => {
<template>
<div ref="pageRoot">
<NuxtLayout name="main">
<!-- <section class="work">
<section class="selected-work">
<h5>Selected Work</h5>
<div class="section__content"></div>
</section> -->
<div class="section__content">
<a
v-for="work in selectedWorks"
:key="work.title"
class="work"
:href="work.link"
target="_blank"
>
<h3 class="work__title">{{ work.title }}</h3>
<p class="work__description">
{{ 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>
</section>
<section class="projects">
<h5>Projects</h5>
<div class="section__content">
<a
href="https://unicord.hat.fish/"
v-for="project in projects"
:key="project.href"
:href="project.href"
target="_blank"
class="project"
>
<h3 class="project__title">Unicord</h3>
<h3 class="project__title">{{ project.title }}</h3>
<p class="project__description">
Discord bot SDK inspired by webserver architecture.
</p>
</a>
<a
href="https://mystwright.com/"
target="_blank"
class="project"
>
<h3 class="project__title">Mystwright</h3>
<p class="project__description">
An AI-driven mystery detective game. Generate,
investigate, solve.
</p>
</a>
<a
href="https://github.com/jaronpate/rift-buddy"
target="_blank"
class="project"
>
<h3 class="project__title">Rune Buddy</h3>
<p class="project__description">
League of Legends runepage manager. Save & organise
your pages for free.
{{ project.description }}
</p>
</a>
</div>
@@ -147,76 +267,17 @@ onMounted(() => {
<h5>Photos</h5>
<div class="section__content">
<div class="photo-grid">
<div class="photo-grid__item">
<div
v-for="photo in photos"
:key="photo.src"
class="photo-grid__item"
>
<div class="photo__image">
<img
src="/photography/athens_red_scooter.jpg"
alt="Red scooter in Athens"
/>
<img :src="photo.src" :alt="photo.alt" />
</div>
<div class="photo__overlay">
<p>Athens</p>
<p>2026</p>
</div>
</div>
<div class="photo-grid__item">
<div class="photo__image">
<img
src="/photography/athens_subway.jpg"
alt="Athens metro station"
/>
</div>
<div class="photo__overlay">
<p>Athens</p>
<p>2026</p>
</div>
</div>
<div class="photo-grid__item">
<div class="photo__image">
<img
src="/photography/athens_sunset.jpg"
alt="Athens at sunset"
/>
</div>
<div class="photo__overlay">
<p>Athens</p>
<p>2026</p>
</div>
</div>
<div class="photo-grid__item">
<div class="photo__image">
<img
src="/photography/london_porsche.jpg"
alt="Porsche shop in London"
/>
</div>
<div class="photo__overlay">
<p>London</p>
<p>2025</p>
</div>
</div>
<div class="photo-grid__item">
<div class="photo__image">
<img
src="/photography/underground.jpg"
alt="Entrace to the London underground"
/>
</div>
<div class="photo__overlay">
<p>London</p>
<p>2026</p>
</div>
</div>
<div class="photo-grid__item">
<div class="photo__image">
<img
src="/photography/austin_parking.jpg"
alt="Parking garage in Austin"
/>
</div>
<div class="photo__overlay">
<p>Austin</p>
<p>2024</p>
<p>{{ photo.location }}</p>
<p>{{ photo.year }}</p>
</div>
</div>
</div>
@@ -268,19 +329,97 @@ section:last-of-type {
margin-bottom: 2rem;
}
.project {
border-bottom: 1px solid var(--muted);
padding: 1rem 0;
.work {
}
.work__images {
/*display: flex;
flex-wrap: no-wrap;
align-items: center;
justify-content: center;*/
display: grid;
grid-auto-flow: column;
grid-auto-columns: max-content;
gap: var(--gap);
overflow-x: auto;
overflow-y: hidden;
margin-top: var(--gap);
}
.work__image {
--dot-size: 6%;
margin: 0;
/*padding: calc(var(--gap) * 2) var(--gap) var(--gap);*/
padding: var(--gap);
flex-shrink: 0;
border-radius: var(--radius);
background-image: radial-gradient(
circle at 4px 4px,
var(--dot-color) var(--dot-size),
transparent calc(var(--dot-size) + 1%)
);
background-position: 4px 4px;
background-size: 12px 12px;
background-repeat: repeat;
background-color: var(--bg-secondary);
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: var(--gap);
}
.work__image img {
border-radius: var(--radius);
object-fit: cover;
max-height: 350px;
}
@media screen and (max-width: 750px) {
.work__image img {
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;
}
.project,
.work {
/*border-bottom: 1px solid var(--muted);*/
padding: var(--gap) 0;
display: block;
text-decoration: none;
color: inherit;
}
.project:first-of-type {
.work {
border: 0;
}
.project:first-of-type,
.work:first-of-type {
padding-top: 0;
}
.project:last-of-type {
.project:last-of-type,
.work:last-of-type {
padding-bottom: 0;
}
@@ -295,12 +434,15 @@ section:last-of-type {
font-family: var(--font-hover);
}
.project__title {
.project__title,
.work__title {
margin: 0;
font-weight: 400;
margin-bottom: calc(var(--gap) * 0.5);
}
.project__title::after {
.project__title::after,
.work__title::after {
content: '↗';
position: relative;
display: inline-block;
@@ -314,12 +456,14 @@ section:last-of-type {
transform 0.25s ease;
}
.project:hover .project__title::after {
.project:hover .project__title::after,
.work:hover .work__title::after {
opacity: 1;
transform: translate(0, 0);
}
.project__description {
.project__description,
.work__description {
margin: 0;
font-size: small;
font-weight: 400;
Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB