Curriculum Vitae Widget

The CurriculumVitaeWidget displays a structured, interactive résumé including skills, work experience, and projects. It supports pinned highlights, section reordering, and skill-based filtering.


Configuration overview

The widget is configured by passing a single data object that matches the CurriculumVitae type. The top-level fields are:

Field Type Description
name string Full name displayed in the CV header.
title string | InlineTranslation Professional title or tagline.
contact CvContact[] List of contact entries (email, phone, website, etc.).
skills CvSkillCategory[] Grouped skill categories with individual skill items.
experience CvExperience[] Work experience entries.
projects CvProject[] Personal or professional project entries.
sectionOrder CvSectionKey[] Optional override for the display order of sections.

Skills configuration

Skills are grouped into categories. Each category has a label and a list of items.

{
  label: "Languages",
  items: [
    { name: "TypeScript", yearsExp: 5, favored: true },
    { name: "Rust", yearsExp: 2 }
  ]
}

CvSkillItem fields

Field Type Description
name string Skill name. Also used as the filter tag identifier.
yearsExp number Years of experience with this skill.
favored boolean If true, marks the skill as currently favored.

Experience configuration

Each entry in the experience array describes a work position.

{
  company: "Acme Corp",
  role: "Senior Engineer",
  startYear: 2020,
  startMonth: 3,
  endYear: 2023,
  endMonth: 8,
  description: "Led development of the core platform.",
  tags: ["TypeScript", "Rust"],
  pinned: true
}

CvExperience fields

Field Type Description
company string Name of the employer or organisation.
role string | InlineTranslation Job title or role held.
startYear number Year the position started.
startMonth number Month the position started (1–12).
endYear number Year the position ended. Omit for ongoing positions.
endMonth number Month the position ended (1–12). Omit for ongoing positions.
description string | InlineTranslation Free-text description of the role.
tags string[] Skill names that link this experience to the skill filter. See Skill-based filtering.
pinned boolean If true, features this entry in the Pinned Highlights section.

Projects configuration

Each entry in the projects array describes a project.

{
  name: "OpenWidget",
  url: "https://github.com/example/openwidget",
  startYear: 2021,
  endYear: 2022,
  description: "An open-source widget library.",
  tags: ["Svelte", "TypeScript"],
  pinned: false
}

CvProject fields

Field Type Description
name string Project name.
url string Link to the project or repository.
startYear number Year the project started.
startMonth number Month the project started (1–12).
endYear number Year the project ended. Omit for ongoing projects.
endMonth number Month the project ended (1–12). Omit for ongoing projects.
description string | InlineTranslation Free-text description of the project.
tags string[] Skill names that link this project to the skill filter (matched case-insensitively). Also controls which technologies are rendered as chips. See Skill-based filtering.
pinned boolean If true, features this entry in the Pinned Highlights section.

Skill-based filtering

Skills listed in the skills array can act as interactive filters. When a visitor clicks a skill badge, the Experience and Projects sections update to show only entries associated with that skill. Clicking the same skill again deactivates the filter and restores the full list.

How filtering works

  1. A skill becomes filterable when its name matches at least one tag on an experience or project entry. Matching is case-insensitive and uses prefix matching (e.g. skill "Angular" matches tags "Angular", "Angular 16", "Angular 18").
  2. Filterable skills render as clickable badge elements (with pointer cursor and keyboard support). Non-filterable skills remain informational only.
  3. Multiple skills can be active at the same time. An entry is shown if it matches any of the active filter tags (OR logic).
  4. Clicking an active skill badge again deactivates it. Deactivating all tags clears the filter and restores the full list.

Sections and filtering

When a filter is active, the widget automatically adjusts which sections are visible:

  • Experience remains visible if at least one experience entry matches the active filter tags. If no experience entries match, the section is hidden entirely.
  • Projects remains visible if at least one project entry matches the active filter tags. If no project entries match, the section is hidden entirely.
  • All other sections — including About, Open Source, Contributions, Blog & Talks, Certifications, Languages, Testimonials, and any other non-filterable sections — are automatically hidden while a filter is active. They reappear as soon as all filter tags are cleared.

This means that activating a skill filter focuses the CV view exclusively on the filtered experience and project content, reducing noise from sections that cannot be meaningfully narrowed by tag.

Linking entries to skills

To associate an experience or project with a skill, add the skill name (or a prefixed variant) to the entry's tags array.

// In skills
{ name: "Angular", yearsExp: 10 }

// Exact match — tag equals the skill name
{ company: "Acme Corp", tags: ["Angular"] }

// Prefix match — "Angular 16" starts with "Angular", so the skill filter matches
{ name: "MedShop", tags: ["Angular 16", "Typescript"] }

Matching is case-insensitive and prefix-based: a tag of "angular 16" matches a skill named "Angular".

What makes a skill filterable

The widget computes the set of filterable skills at render time using the following logic:

  1. Collect all skill names from the skills configuration.
  2. Collect all tag values from experience[].tags and projects[].tags.
  3. A skill is filterable if at least one collected tag starts with the skill name (case-insensitive prefix match).

Skills that do not match any tag remain displayed as non-interactive badges.


Pinned highlights

Setting pinned: true on an experience or project entry causes it to appear in a dedicated Pinned Highlights section at the top of the CV. This section is only rendered when at least one pinned item exists.


Section ordering

The default section order can be overridden by providing a sectionOrder array containing CvSectionKey values:

sectionOrder: ["skills", "experience", "projects"]

Sections omitted from sectionOrder fall back to the default order.


The widget provides granular control over which sections are included when printing. Each section with content can be toggled independently in the print selection UI.

GitHub contributions print sub-sections

The contributions section is split into four independently toggleable print sub-sections rather than a single all-or-nothing toggle. This allows fine-grained control over how much GitHub data appears in a printed CV.

Sub-section key Controls Shown when
github-languages The language breakdown chart showing total contributions per programming language. GitHub language totals data is present and non-empty.
github-activity The commit activity heatmap visualising contribution frequency over time. GitHub commit heatmap data is present and non-empty.
github-top-repos A curated highlight of the top repositories (requires at least 3 contributions). At least 3 contribution entries exist.
github-all-repos The full list of all contributed repositories. At least 1 contribution entry exists.

Each sub-section only appears as a print option when the corresponding data is available. If no contributions data is present, or the contributions section is hidden via sectionOrder, none of these sub-sections are offered.

This replaces the previous single contributions print toggle, giving visitors the ability to include, for example, the activity heatmap and top repositories while omitting the full repository list from a printed CV.


Internationalisation

Fields typed as InlineTranslation accept an object with en and de keys instead of a plain string:

{
  role: { en: "Software Engineer", de: "Softwareentwickler" }
}

The widget renders the value matching the active locale.