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
- 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"). - Filterable skills render as clickable badge elements (with pointer cursor and keyboard support). Non-filterable skills remain informational only.
- Multiple skills can be active at the same time. An entry is shown if it matches any of the active filter tags (OR logic).
- 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:
- Collect all skill names from the
skillsconfiguration. - Collect all tag values from
experience[].tagsandprojects[].tags. - 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.
Print options
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.