improve pdf layout and add profile pics

This commit is contained in:
Joakim Repomaa
2026-02-19 18:21:11 +02:00
parent 60567717eb
commit 1744466b2b
10 changed files with 87 additions and 55 deletions

21
package-lock.json generated
View File

@@ -27,6 +27,7 @@
"puppeteer": "^24.37.3",
"svelte": "^5.51.0",
"svelte-check": "^4.1.0",
"svelte-feather-icons": "^4.2.0",
"tailwindcss": "^4.1.18",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
@@ -3964,6 +3965,26 @@
"typescript": ">=5.0.0"
}
},
"node_modules/svelte-feather-icons": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/svelte-feather-icons/-/svelte-feather-icons-4.2.0.tgz",
"integrity": "sha512-KuMTDrL6sA8aCxBv3RMgmmnnyIaAXaYcmWkmNa2r2Qj70vi+An2T6ZBAdiZr6wjx+a3eZJy+FRseeRkzQFGHPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"svelte": "^3.38.2"
}
},
"node_modules/svelte-feather-icons/node_modules/svelte": {
"version": "3.59.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
"integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/tailwindcss": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",

View File

@@ -38,6 +38,7 @@
"puppeteer": "^24.37.3",
"svelte": "^5.51.0",
"svelte-check": "^4.1.0",
"svelte-feather-icons": "^4.2.0",
"tailwindcss": "^4.1.18",
"tslib": "^2.8.1",
"typescript": "^5.9.3",

View File

@@ -8,12 +8,19 @@
<header class="border-b-2 border-fg/10 pb-8 mb-8">
<div class="flex flex-col md:flex-row md:items-start md:justify-between gap-6">
<div class="flex-1">
<h1 class="text-4xl font-bold mb-2 tracking-tight text-fg">
{profile.name}
</h1>
<p class="text-xl text-muted mb-4 font-medium">
{profile.title}
</p>
<div class="flex items-start gap-6 mb-4">
{#if profile.avatar}
<img src={profile.avatar} alt={profile.name} class="w-20 h-20 object-cover" />
{/if}
<div>
<h1 class="text-4xl font-bold mb-2 tracking-tight text-fg">
{profile.name}
</h1>
<p class="text-xl text-muted font-medium">
{profile.title}
</p>
</div>
</div>
<p class="text-muted leading-relaxed max-w-2xl">
{profile.summary}
</p>

View File

@@ -4,6 +4,8 @@
import PDFSection from './PDFSection.svelte';
import PDFTags from './PDFTags.svelte';
import PDFTimelineItem from './PDFTimelineItem.svelte';
import profilePicture from '$lib/media/profile-picture.jpg';
import { MailIcon, MapPinIcon, GithubIcon, GlobeIcon } from 'svelte-feather-icons';
let {
profile,
@@ -26,54 +28,44 @@
class="font-sans max-w-[210mm] mx-auto px-[20mm] py-[18mm] bg-pdf-bg text-pdf-fg leading-relaxed text-sm"
>
<!-- Header -->
<header class="text-center mb-6 pb-4 border-b-2 border-pdf-fg">
<div>
<h1 class="text-3xl font-extrabold tracking-tight text-pdf-fg leading-tight m-0">
{profile.name}
</h1>
<p class="text-base font-medium text-pdf-muted mt-1.5 mb-3">{profile.title}</p>
</div>
<div class="flex justify-center flex-wrap gap-x-6 gap-y-3 text-xs mb-2">
{#if profile.email}
<a class="inline-flex items-center gap-1.5 text-pdf-accent" href="mailto:{profile.email}">
<svg
class="w-3.5 h-3.5 text-pdf-accent"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
<polyline points="22,6 12,13 2,6" />
</svg>
{profile.email}
</a>
{/if}
<span class="inline-flex items-center gap-1.5 text-pdf-muted">
<svg
class="w-3.5 h-3.5 text-pdf-accent"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
<circle cx="12" cy="10" r="3" />
</svg>
{profile.location}
</span>
</div>
<div class="flex justify-center flex-wrap gap-x-5 gap-y-2 text-xs text-pdf-accent">
{#if profile.github}
<a href="https://github.com/{profile.github}" class="no-underline"
>github.com/{profile.github}</a
>
{/if}
{#if profile.website}
<a href={profile.website} class="no-underline"
>{profile.website.replace(/^https?:\/\//, '')}</a
>
{/if}
<header class="mb-6 pb-4 border-b-2 border-pdf-fg">
<div class="flex items-start justify-between gap-4">
<div>
<h1 class="text-3xl font-extrabold tracking-tight text-pdf-fg leading-tight m-0">
{profile.name}
</h1>
<p class="text-base font-medium text-pdf-muted mt-1.5 mb-3">{profile.title}</p>
<div class="grid grid-cols-2 gap-x-6 gap-y-2 text-xs text-pdf-accent">
{#if profile.email}
<a class="inline-flex items-center gap-1.5" href="mailto:{profile.email}">
<MailIcon class="w-3.5 h-3.5" />
{profile.email}
</a>
{/if}
<span class="inline-flex items-center gap-1.5 text-pdf-muted">
<MapPinIcon class="w-3.5 h-3.5" />
{profile.location}
</span>
{#if profile.github}
<a
href="https://github.com/{profile.github}"
class="no-underline inline-flex items-center gap-1.5"
>
<GithubIcon class="w-3.5 h-3.5" />
github.com/{profile.github}
</a>
{/if}
{#if profile.website}
<a href={profile.website} class="no-underline inline-flex items-center gap-1.5">
<GlobeIcon class="w-3.5 h-3.5" />
{profile.website.replace(/^https?:\/\//, '')}
</a>
{/if}
</div>
</div>
<div class="rounded-full p-2 inset-shadow-sm">
<img src={profilePicture} alt={profile.name} class="w-26 h-26 rounded-full object-cover" />
</div>
</div>
</header>

View File

@@ -9,7 +9,7 @@
let { title, children }: Props = $props();
</script>
<section class="mb-6 pb-3 border-b border-pdf-muted/30 last:border-b-0">
<section class="mb-6 pb-3 border-b border-pdf-muted/30 last:border-b-0 break-inside-avoid">
<h2 class="text-sm font-bold text-pdf-fg flex items-center uppercase tracking-wide">
{title}
</h2>

View File

@@ -5,5 +5,7 @@
"location": "Espoo - Finnland",
"website": "https://joakim.repomaa.com",
"github": "repomaa",
"avatar": "src/lib/media/avatar.jpg",
"profilePicture": "src/lib/media/profile-picture.jpg",
"summary": "Senior Full-Stack Engineer with deep expertise in Ruby on Rails backends, React/TypeScript frontends, and GraphQL/REST API design. Experienced in AI integration, third-party API integrations, and analyzing large-scale data systems. Fluent in German, Finnish, and English. Passionate about system architecture, performance optimization, clean code, and pragmatic technical decision-making in fast-paced product environments."
}

View File

@@ -9,6 +9,8 @@ export interface Profile {
website?: string;
github: string;
summary: string;
avatar?: string;
profilePicture?: string;
}
export interface Experience {

BIN
src/lib/media/avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -31,6 +31,13 @@
{ name: 'location', label: 'Location', widget: 'string' },
{ name: 'website', label: 'Website', widget: 'string', required: false },
{ name: 'github', label: 'GitHub', widget: 'string' },
{ name: 'avatar', label: 'Avatar', widget: 'image', required: false },
{
name: 'profilePicture',
label: 'Profile Picture (for PDF)',
widget: 'image',
required: false,
},
{ name: 'summary', label: 'Summary', widget: 'text' },
],
},