---
title: 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`.

```ts
{
  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.

```ts
{
  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](#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.

```ts
{
  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](#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.

```ts
// 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:

```ts
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:

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

The widget renders the value matching the active locale.
