CV page improvements #22
@@ -0,0 +1,81 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
type IconName =
|
||||||
|
| 'phone'
|
||||||
|
| 'email'
|
||||||
|
| 'location'
|
||||||
|
| 'linkedin'
|
||||||
|
| 'github'
|
||||||
|
| 'code'
|
||||||
|
| 'verified'
|
||||||
|
| 'summary'
|
||||||
|
| 'experience'
|
||||||
|
| 'education'
|
||||||
|
| 'achievements'
|
||||||
|
| 'languages'
|
||||||
|
| 'skills'
|
||||||
|
| 'additional'
|
||||||
|
| 'interests'
|
||||||
|
|
||||||
|
const props = defineProps<{ name: IconName }>()
|
||||||
|
|
||||||
|
// Sourced from lucide-static (ISC license): phone, mail, map-pin, linkedin,
|
||||||
|
// github, code, badge-check, file-text, briefcase, graduation-cap, award,
|
||||||
|
// languages, chart-column, layers, heart.
|
||||||
|
const MARKUP: Record<IconName, string> = {
|
||||||
|
phone:
|
||||||
|
'<path d="M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384" />',
|
||||||
|
email:
|
||||||
|
'<path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" /><rect x="2" y="4" width="20" height="16" rx="2" />',
|
||||||
|
location:
|
||||||
|
'<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /><circle cx="12" cy="10" r="3" />',
|
||||||
|
linkedin:
|
||||||
|
'<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z" /><rect width="4" height="12" x="2" y="9" /><circle cx="4" cy="4" r="2" />',
|
||||||
|
github:
|
||||||
|
'<path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4" /><path d="M9 18c-4.51 2-5-2-7-2" />',
|
||||||
|
code: '<path d="m16 18 6-6-6-6" /><path d="m8 6-6 6 6 6" />',
|
||||||
|
verified:
|
||||||
|
'<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m16 9-5.5 5.5L8 12" />',
|
||||||
|
summary:
|
||||||
|
'<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /><path d="M10 9H8" /><path d="M16 13H8" /><path d="M16 17H8" />',
|
||||||
|
experience:
|
||||||
|
'<path d="M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" /><rect width="20" height="14" x="2" y="6" rx="2" />',
|
||||||
|
education:
|
||||||
|
'<path d="M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z" /><path d="M22 10v6" /><path d="M6 12.5V16a6 3 0 0 0 12 0v-3.5" />',
|
||||||
|
achievements:
|
||||||
|
'<path d="m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526" /><circle cx="12" cy="8" r="6" />',
|
||||||
|
languages:
|
||||||
|
'<path d="m5 8 6 6" /><path d="m4 14 6-6 2-3" /><path d="M2 5h12" /><path d="M7 2h1" /><path d="m22 22-5-10-5 10" /><path d="M14 18h6" />',
|
||||||
|
skills:
|
||||||
|
'<path d="M3 3v16a2 2 0 0 0 2 2h16" /><path d="M18 17V9" /><path d="M13 17V5" /><path d="M8 17v-3" />',
|
||||||
|
additional:
|
||||||
|
'<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z" /><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12" /><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17" />',
|
||||||
|
interests:
|
||||||
|
'<path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5" />',
|
||||||
|
}
|
||||||
|
|
||||||
|
const markup = computed(() => MARKUP[props.name])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
class="cv-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
v-html="markup"
|
||||||
|
></svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.cv-icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+290
-47
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted } from 'vue'
|
import { onBeforeUnmount, onMounted } from 'vue'
|
||||||
import cvPhoto from '@/assets/cv-photo.jpg'
|
import cvPhoto from '@/assets/cv-photo.jpg'
|
||||||
|
import CvIcon from '@/components/CvIcon.vue'
|
||||||
|
|
||||||
// This is a public sub-page with no link from the rest of the app, and it
|
// This is a public sub-page with no link from the rest of the app, and it
|
||||||
// shouldn't show up in search results. index.html is shared across every
|
// shouldn't show up in search results. index.html is shared across every
|
||||||
@@ -19,6 +20,13 @@ onBeforeUnmount(() => {
|
|||||||
robotsMeta?.remove()
|
robotsMeta?.remove()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
interface Project {
|
||||||
|
title: string
|
||||||
|
repo: string
|
||||||
|
desc: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
interface ExperienceEntry {
|
interface ExperienceEntry {
|
||||||
company: string
|
company: string
|
||||||
location: string
|
location: string
|
||||||
@@ -58,6 +66,21 @@ interface AdditionalExperience {
|
|||||||
const SKILL_MAX = 8
|
const SKILL_MAX = 8
|
||||||
const LANGUAGE_MAX = 5
|
const LANGUAGE_MAX = 5
|
||||||
|
|
||||||
|
const projects: Project[] = [
|
||||||
|
{
|
||||||
|
title: 'Frontend',
|
||||||
|
repo: 'dttmr-fe',
|
||||||
|
desc: 'Offline-first PWA for shared, syncable to-do lists and tracking workouts. Vue 3 + TypeScript.',
|
||||||
|
url: 'https://git.dittmar.dev/robin/dttmr-fe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'API',
|
||||||
|
repo: 'dttmr-api',
|
||||||
|
desc: 'Go backend powering auth and sync for the to-do list/workout tracking app.',
|
||||||
|
url: 'https://git.dittmar.dev/robin/dttmr-api',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const experience: ExperienceEntry[] = [
|
const experience: ExperienceEntry[] = [
|
||||||
{
|
{
|
||||||
company: 'Greenbone AG',
|
company: 'Greenbone AG',
|
||||||
@@ -229,37 +252,54 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
<img class="avatar" :src="cvPhoto" alt="Portrait of Robin Dittmar" />
|
<img class="avatar" :src="cvPhoto" alt="Portrait of Robin Dittmar" />
|
||||||
<div>
|
<div>
|
||||||
<h1>Robin Dittmar</h1>
|
<h1>Robin Dittmar</h1>
|
||||||
<span class="tag tag-accent">Software Developer</span>
|
<span class="role-badge">Software Developer</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ledger-rule" aria-hidden="true"></div>
|
||||||
|
|
||||||
<section class="card info-card">
|
<section class="card info-card contact-card">
|
||||||
<h4>Contacts</h4>
|
<h4>Contacts</h4>
|
||||||
<p class="row">
|
<p class="row">
|
||||||
<span>Phone</span>
|
<span class="row-label"><CvIcon name="phone" />Phone</span>
|
||||||
<strong class="mono-num">+49159/01668799</strong>
|
<a class="mono-num" href="tel:+4915901668799">+49 1590 1668799</a>
|
||||||
</p>
|
</p>
|
||||||
<p class="row">
|
<p class="row">
|
||||||
<span>Email</span>
|
<span class="row-label"><CvIcon name="email" />Email</span>
|
||||||
<strong>robindittmar@gmail.com</strong>
|
<a href="mailto:robindittmar@gmail.com">robindittmar@gmail.com</a>
|
||||||
</p>
|
</p>
|
||||||
<p class="row">
|
<p class="row">
|
||||||
<span>Location</span>
|
<span class="row-label"><CvIcon name="location" />Location</span>
|
||||||
<strong>Düsseldorf</strong>
|
<strong>Düsseldorf</strong>
|
||||||
</p>
|
</p>
|
||||||
<p class="row">
|
<p class="row">
|
||||||
<span>LinkedIn</span>
|
<span class="row-label"><CvIcon name="linkedin" />LinkedIn</span>
|
||||||
<a href="https://www.linkedin.com/in/robindittmar-948424310/" target="_blank" rel="noopener"
|
<a href="https://www.linkedin.com/in/robin-dittmar-948424310/" target="_blank" rel="noopener"
|
||||||
>robindittmar</a
|
>robindittmar</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<p class="row">
|
<p class="row">
|
||||||
<span>GitHub</span>
|
<span class="row-label"><CvIcon name="github" />GitHub</span>
|
||||||
<a href="https://github.com/rdttmr" target="_blank" rel="noopener">rdttmr</a>
|
<a href="https://github.com/rdttmr" target="_blank" rel="noopener">rdttmr</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h1 class="section-heading">Summary</h1>
|
<h1 class="section-heading"><CvIcon name="code" />Check Out My Work</h1>
|
||||||
|
<ul class="cards">
|
||||||
|
<li v-for="project in projects" :key="project.repo">
|
||||||
|
<a class="card project-card" :href="project.url" target="_blank" rel="noopener">
|
||||||
|
<span class="project-icon"><CvIcon name="code" /></span>
|
||||||
|
<span class="project-body">
|
||||||
|
<span class="project-title">
|
||||||
|
{{ project.title }}
|
||||||
|
<span class="mono-num project-repo">{{ project.repo }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="project-desc">{{ project.desc }}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1 class="section-heading"><CvIcon name="summary" />Summary</h1>
|
||||||
<section class="card info-card">
|
<section class="card info-card">
|
||||||
<p class="summary-text">
|
<p class="summary-text">
|
||||||
Passionate Software Developer professional with over 8 years of experience in backend
|
Passionate Software Developer professional with over 8 years of experience in backend
|
||||||
@@ -270,8 +310,8 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h1 class="section-heading">Experience</h1>
|
<h1 class="section-heading"><CvIcon name="experience" />Experience</h1>
|
||||||
<ul class="cards">
|
<ul class="cards timeline timeline-current">
|
||||||
<li v-for="job in experience" :key="job.company + job.dates">
|
<li v-for="job in experience" :key="job.company + job.dates">
|
||||||
<article class="card job-card">
|
<article class="card job-card">
|
||||||
<div class="job-card-head">
|
<div class="job-card-head">
|
||||||
@@ -287,8 +327,8 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1 class="section-heading">Education</h1>
|
<h1 class="section-heading"><CvIcon name="education" />Education</h1>
|
||||||
<ul class="cards">
|
<ul class="cards timeline">
|
||||||
<li v-for="edu in education" :key="edu.school">
|
<li v-for="edu in education" :key="edu.school">
|
||||||
<article class="card job-card">
|
<article class="card job-card">
|
||||||
<div class="job-card-head">
|
<div class="job-card-head">
|
||||||
@@ -300,40 +340,53 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1 class="section-heading">Key Achievements</h1>
|
<h1 class="section-heading"><CvIcon name="achievements" />Key Achievements</h1>
|
||||||
<section class="card info-card">
|
<section class="card info-card">
|
||||||
<div v-for="item in achievements" :key="item.title" class="achievement-row">
|
<div v-for="item in achievements" :key="item.title" class="achievement-row">
|
||||||
<h4>{{ item.title }}</h4>
|
<span class="achv-icon"><CvIcon name="verified" /></span>
|
||||||
<p>{{ item.desc }}</p>
|
<div>
|
||||||
|
<h4>{{ item.title }}</h4>
|
||||||
|
<p>{{ item.desc }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h1 class="section-heading">Languages</h1>
|
<h1 class="section-heading"><CvIcon name="languages" />Languages</h1>
|
||||||
<section class="card info-card">
|
<section class="card info-card">
|
||||||
<div v-for="lang in languages" :key="lang.name" class="rating-row">
|
<div v-for="lang in languages" :key="lang.name" class="rating-row">
|
||||||
<p class="row">
|
<p class="row">
|
||||||
<span>{{ lang.name }}</span>
|
<span>{{ lang.name }}</span>
|
||||||
<span class="tag">{{ lang.level }}</span>
|
<span class="tag">{{ lang.level }}</span>
|
||||||
</p>
|
</p>
|
||||||
<div class="meter meter-lg">
|
<div class="tally tally-lg">
|
||||||
<div class="meter-fill" :style="{ width: (lang.rating / LANGUAGE_MAX) * 100 + '%' }"></div>
|
<span
|
||||||
|
v-for="n in LANGUAGE_MAX"
|
||||||
|
:key="n"
|
||||||
|
class="tally-tick"
|
||||||
|
:class="{ filled: n <= lang.rating }"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h1 class="section-heading">Industry Experience</h1>
|
<h1 class="section-heading"><CvIcon name="skills" />Industry Experience</h1>
|
||||||
<section class="card info-card">
|
<section class="card info-card">
|
||||||
<div class="skill-grid">
|
<div class="skill-grid">
|
||||||
<div v-for="skill in skills" :key="skill.name" class="skill-row">
|
<div v-for="skill in skills" :key="skill.name" class="skill-row">
|
||||||
<span class="skill-name">{{ skill.name }}</span>
|
<span class="skill-name">{{ skill.name }}</span>
|
||||||
<div class="meter">
|
<div class="tally">
|
||||||
<div class="meter-fill" :style="{ width: (skill.rating / SKILL_MAX) * 100 + '%' }"></div>
|
<span
|
||||||
|
v-for="n in SKILL_MAX"
|
||||||
|
:key="n"
|
||||||
|
class="tally-tick"
|
||||||
|
:class="{ filled: n <= skill.rating }"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h1 class="section-heading">Additional Experience</h1>
|
<h1 class="section-heading"><CvIcon name="additional" />Additional Experience</h1>
|
||||||
<ul class="cards">
|
<ul class="cards">
|
||||||
<li v-for="item in additionalExperiences" :key="item.title">
|
<li v-for="item in additionalExperiences" :key="item.title">
|
||||||
<article class="card job-card">
|
<article class="card job-card">
|
||||||
@@ -343,7 +396,7 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1 class="section-heading">Interests</h1>
|
<h1 class="section-heading"><CvIcon name="interests" />Interests</h1>
|
||||||
<section class="card info-card">
|
<section class="card info-card">
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span v-for="interest in interests" :key="interest" class="tag">{{ interest }}</span>
|
<span v-for="interest in interests" :key="interest" class="tag">{{ interest }}</span>
|
||||||
@@ -370,16 +423,18 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
.profile-top {
|
.profile-top {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1.1rem;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-top h1 {
|
.profile-top h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.45rem;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
display: block;
|
||||||
width: 76px;
|
width: 76px;
|
||||||
height: 76px;
|
height: 76px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -389,9 +444,48 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.role-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0.22rem 0.65rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: var(--c-accent-bg);
|
||||||
|
color: var(--c-accent-strong);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ledger-rule {
|
||||||
|
height: 4px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-top: 1.5px solid var(--c-border-hover);
|
||||||
|
border-bottom: 1px dashed var(--c-border);
|
||||||
|
}
|
||||||
|
|
||||||
.section-heading {
|
.section-heading {
|
||||||
font-size: 1.1rem;
|
display: flex;
|
||||||
margin: 1.75rem 0 0.75rem;
|
align-items: center;
|
||||||
|
gap: 0.55rem;
|
||||||
|
font-size: 1.08rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 2rem 0 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--c-border);
|
||||||
|
margin-left: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading .cv-icon {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--c-accent-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card {
|
.info-card {
|
||||||
@@ -403,6 +497,11 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact-card {
|
||||||
|
border-color: var(--c-accent-soft);
|
||||||
|
box-shadow: 0 10px 28px rgba(61, 114, 180, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -417,10 +516,32 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-card > .row {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px dashed var(--c-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card > .row:last-child {
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.row strong {
|
.row strong {
|
||||||
color: var(--c-heading);
|
color: var(--c-heading);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row-label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-label .cv-icon {
|
||||||
|
color: var(--c-text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
.row a {
|
.row a {
|
||||||
color: var(--c-accent-strong);
|
color: var(--c-accent-strong);
|
||||||
}
|
}
|
||||||
@@ -430,8 +551,30 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
color: var(--c-text);
|
color: var(--c-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.achievement-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.7rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.achievement-row + .achievement-row {
|
.achievement-row + .achievement-row {
|
||||||
margin-top: 0.85rem;
|
margin-top: 0.85rem;
|
||||||
|
padding-top: 0.85rem;
|
||||||
|
border-top: 1px dashed var(--c-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.achv-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.6rem;
|
||||||
|
height: 1.6rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 0.05rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--c-accent-bg);
|
||||||
|
color: var(--c-accent-strong);
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.achievement-row h4 {
|
.achievement-row h4 {
|
||||||
@@ -449,22 +592,28 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
margin-top: 0.85rem;
|
margin-top: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rating meters */
|
/* rating tallies */
|
||||||
.meter {
|
.tally {
|
||||||
width: 100%;
|
display: flex;
|
||||||
height: 6px;
|
gap: 3px;
|
||||||
border-radius: 999px;
|
|
||||||
background: var(--c-bg-mute);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.meter-fill {
|
.tally-tick {
|
||||||
height: 100%;
|
flex: 1;
|
||||||
border-radius: 999px;
|
height: 6px;
|
||||||
|
border-radius: 1px;
|
||||||
|
background: var(--c-bg-mute);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tally-tick.filled {
|
||||||
background: var(--c-accent);
|
background: var(--c-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.meter-lg {
|
.tally-lg {
|
||||||
|
max-width: 82%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tally-lg .tally-tick {
|
||||||
height: 7px;
|
height: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,6 +627,46 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* timeline (experience / education): a real chronological record, so the
|
||||||
|
rail + nodes carry meaning rather than decorate */
|
||||||
|
.timeline {
|
||||||
|
position: relative;
|
||||||
|
gap: 0.9rem;
|
||||||
|
padding-left: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 3px;
|
||||||
|
top: 0.6rem;
|
||||||
|
bottom: 0.6rem;
|
||||||
|
width: 1px;
|
||||||
|
background: var(--c-border-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline > li {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline > li::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -1.1rem;
|
||||||
|
top: 1.1rem;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--c-bg);
|
||||||
|
border: 1.5px solid var(--c-text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-current > li:first-child::before {
|
||||||
|
border-color: var(--c-accent-strong);
|
||||||
|
background: var(--c-accent-strong);
|
||||||
|
box-shadow: 0 0 0 3px var(--c-accent-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.job-card {
|
.job-card {
|
||||||
padding: 0.9rem 1.1rem;
|
padding: 0.9rem 1.1rem;
|
||||||
}
|
}
|
||||||
@@ -539,6 +728,65 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
background: var(--c-accent);
|
background: var(--c-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* project links */
|
||||||
|
.project-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.9rem;
|
||||||
|
padding: 0.9rem 1.1rem;
|
||||||
|
border: 1px solid var(--c-accent-soft);
|
||||||
|
transition:
|
||||||
|
transform 0.15s ease-in-out,
|
||||||
|
border-color 0.15s ease-in-out,
|
||||||
|
box-shadow 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card:hover,
|
||||||
|
.project-card:focus-visible {
|
||||||
|
border-color: var(--c-accent-strong);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2.2rem;
|
||||||
|
height: 2.2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--c-accent-bg);
|
||||||
|
color: var(--c-accent-strong);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--c-heading);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-repo {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--c-accent-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-desc {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--c-text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
/* skills */
|
/* skills */
|
||||||
.skill-grid {
|
.skill-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -580,9 +828,4 @@ const additionalExperiences: AdditionalExperience[] = [
|
|||||||
background-color: var(--c-bg-mute);
|
background-color: var(--c-bg-mute);
|
||||||
color: var(--c-text-soft);
|
color: var(--c-text-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-accent {
|
|
||||||
background-color: var(--c-accent-bg);
|
|
||||||
color: var(--c-accent-strong);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user