feat: add hub

This commit is contained in:
2026-10-03 23:57:41 +02:00
parent 5452f72087
commit fb7b560696
52 changed files with 290 additions and 162 deletions
+46 -36
View File
@@ -1,7 +1,4 @@
<script setup lang="ts">
// Always light: white background, also when the device is in dark mode
definePageMeta({ colorMode: 'light' })
const { cv: profile } = useAppConfig()
const { lang, t } = useCvLang()
@@ -33,10 +30,11 @@ const experience = computed(() => entries.value.filter(entry => !EDUCATION.inclu
</script>
<template>
<UMain :style="{ '--ui-primary': profile.accent }">
<UMain class="cv" :style="{ '--cv-accent': profile.accent }">
<UContainer class="max-w-3xl py-8 print:max-w-none print:px-[12mm] print:pt-[15mm] print:pb-0">
<div class="mb-6 flex justify-end print:hidden">
<UFieldGroup size="xs">
<div class="mb-6 flex justify-end gap-2 print:hidden">
<AppHeaderActions />
<UFieldGroup>
<UButton
v-for="option in (['de', 'en'] as const)"
:key="option"
@@ -49,36 +47,43 @@ const experience = computed(() => entries.value.filter(entry => !EDUCATION.inclu
</UFieldGroup>
</div>
<header class="mb-10 flex items-center gap-5">
<UAvatar v-if="profile.photo" :src="profile.photo" :alt="profile.name || t.photo" class="size-20 shrink-0" />
<div class="min-w-0">
<h1 class="text-2xl font-semibold text-highlighted">
{{ profile.name || 'CV' }}
</h1>
<p v-if="profile.description[lang]" class="mt-1 text-sm text-muted">
{{ profile.description[lang] }}
</p>
<div class="mt-1 flex flex-wrap gap-x-4">
<UButton
v-if="profile.email"
:to="`mailto:${profile.email}`"
:label="profile.email"
color="primary"
variant="link"
class="p-0"
/>
<UButton
v-if="profile.linkedin"
:to="profile.linkedin"
target="_blank"
label="LinkedIn"
color="primary"
variant="link"
class="p-0 print:hidden"
/>
<!-- In the PDF links are written out, since the label alone can't be followed on paper -->
<a v-if="profile.linkedin" :href="profile.linkedin" class="hidden text-sm font-medium text-primary print:inline">{{ cvLinkText(profile.linkedin) }}</a>
</div>
<header class="mb-10">
<UAvatar v-if="profile.photo" :src="profile.photo" :alt="profile.name || t.photo" class="mb-4 size-20" />
<!-- Under the photo: name, description, links -->
<h1 class="text-2xl font-semibold text-highlighted">
{{ profile.name || 'CV' }}
</h1>
<p v-if="profile.description[lang]" class="mt-1 text-sm text-muted">
{{ profile.description[lang] }}
</p>
<div class="mt-3 flex flex-wrap gap-x-4 gap-y-1">
<UButton
v-if="profile.email"
:to="`mailto:${profile.email}`"
icon="i-lucide-mail"
:label="profile.email"
:title="profile.email"
:aria-label="profile.email"
color="primary"
variant="link"
class="p-0"
:ui="CV_LINK_UI"
/>
<UButton
v-if="profile.linkedin"
:to="profile.linkedin"
target="_blank"
icon="i-lucide-linkedin"
label="LinkedIn"
title="LinkedIn"
aria-label="LinkedIn"
color="primary"
variant="link"
class="p-0 print:hidden"
:ui="CV_LINK_UI"
/>
<!-- In the PDF links are written out, since the label alone can't be followed on paper -->
<a v-if="profile.linkedin" :href="profile.linkedin" class="hidden text-sm font-medium text-primary print:inline">{{ cvLinkText(profile.linkedin) }}</a>
</div>
</header>
@@ -109,6 +114,11 @@ const experience = computed(() => entries.value.filter(entry => !EDUCATION.inclu
</template>
<style>
/* The CV's accent instead of the site's primary color in light mode (and so in the PDF); dark mode keeps the site's */
html:not(.dark) .cv {
--ui-primary: var(--cv-accent);
}
/* Top and bottom of every printed page. The sides and the extra space above the first page come from the container's
print padding, which also works in browsers that ignore @page margins */
@page {