238 lines
6.7 KiB
Typst
238 lines
6.7 KiB
Typst
#set page(
|
||
paper: "a4",
|
||
margin: (left: 0.8cm, right: 0.8cm, top: 0.7cm, bottom: 0.6cm),
|
||
background: place(top + left)[
|
||
#image("assets/blob_background.png", width: 7.8cm)
|
||
],
|
||
)
|
||
|
||
#set text(
|
||
font: ("Liberation Sans", "DejaVu Sans", "Noto Sans"),
|
||
size: 7.5pt,
|
||
fill: rgb("#1e293b"),
|
||
lang: "fr",
|
||
)
|
||
|
||
#set par(justify: false, leading: 0.42em)
|
||
|
||
#let data = yaml("cv_data.yaml")
|
||
|
||
#let primary = rgb("#0284c7") // Bleu moderne (Sky-600)
|
||
#let primary-dark = rgb("#0369a1") // Bleu foncé
|
||
#let text-dark = rgb("#0f172a") // Slate-900
|
||
#let text-muted = rgb("#64748b") // Slate-500
|
||
#let border-color = rgb("#e2e8f0") // Slate-200
|
||
#let badge-bg = rgb("#f1f5f9") // Slate-100
|
||
|
||
// Titre de section stylisé
|
||
#let section-title(title, icon: none) = {
|
||
v(0.4em)
|
||
block(width: 100%)[
|
||
#text(size: 10pt, weight: "bold", fill: text-dark)[#upper(title)]
|
||
#v(-0.35em)
|
||
#line(length: 100%, stroke: 1.5pt + primary)
|
||
]
|
||
v(0.25em)
|
||
}
|
||
|
||
// Badge de compétence
|
||
#let skill-badge(term) = {
|
||
box(
|
||
fill: badge-bg,
|
||
radius: 3pt,
|
||
inset: (x: 4pt, y: 2pt),
|
||
baseline: 0%,
|
||
outset: 0pt,
|
||
)[#text(size: 6.8pt, weight: "medium", fill: rgb("#334155"))[#term]]
|
||
}
|
||
|
||
#let icon-row(icon-path, content) = {
|
||
grid(
|
||
columns: (13pt, 1fr),
|
||
gutter: 5pt,
|
||
align: (center + horizon, left + horizon),
|
||
image(icon-path, width: 10pt),
|
||
content
|
||
)
|
||
}
|
||
|
||
// Layout en 2 colonnes bien proportionnées : Gauche (5.4cm) - Droite (1fr)
|
||
#grid(
|
||
columns: (5.4cm, 1fr),
|
||
column-gutter: 0.7cm,
|
||
|
||
// ==========================================
|
||
// COLONNE DE GAUCHE : Profil, Contact, Compétences
|
||
// ==========================================
|
||
[
|
||
// Photo
|
||
#align(center)[
|
||
#block(width: 3.2cm, height: 4.3cm)[
|
||
#align(center + horizon)[
|
||
#image("assets/profile.png", width: 2.95cm)
|
||
]
|
||
]
|
||
]
|
||
|
||
#section-title("Coordonnées")
|
||
#stack(
|
||
spacing: 0.45em,
|
||
if data.contact.phone != none and data.contact.phone != "" [
|
||
#icon-row("assets/icons/phone.png", text(size: 7.2pt)[#link("tel:" + data.contact.phone.replace(" ", ""))[#data.contact.phone]])
|
||
],
|
||
icon-row("assets/icons/mail.png", text(size: 7.2pt)[#link("mailto:" + data.contact.email)[#data.contact.email]]),
|
||
icon-row("assets/icons/home.png", text(size: 7.2pt)[#data.contact.location]),
|
||
if data.contact.mobility != none and data.contact.mobility != "" [
|
||
#icon-row("assets/icons/pin.png", text(size: 7.2pt)[#data.contact.mobility])
|
||
],
|
||
icon-row("assets/icons/car.png", text(size: 7.2pt)[#data.contact.licenses]),
|
||
icon-row("assets/icons/link.png", text(size: 7.2pt)[#link(data.contact.linkedin_url)[#data.contact.linkedin]]),
|
||
)
|
||
|
||
#section-title("Compétences Clés")
|
||
#for s in data.skills [
|
||
#text(weight: "bold", size: 7.3pt, fill: text-dark)[#s.category] \
|
||
#v(-0.2em)
|
||
#let items = s.items.split(", ")
|
||
#items.map(skill-badge).join(" ")
|
||
#v(0.35em)
|
||
]
|
||
|
||
#section-title("Certifications")
|
||
#for c in data.certifications [
|
||
#let is-rncp = c.contains("RNCP 7")
|
||
#grid(
|
||
columns: (7pt, 1fr),
|
||
gutter: 4pt,
|
||
text(fill: primary)[•],
|
||
text(size: 7pt)[
|
||
#if is-rncp [
|
||
#text(weight: "bold", fill: primary-dark)[#c]
|
||
] else [
|
||
#c
|
||
]
|
||
]
|
||
)
|
||
#v(0.15em)
|
||
]
|
||
|
||
#section-title("Langues & Soft Skills")
|
||
#for lang in data.languages [
|
||
*#lang.name* : #lang.details \
|
||
]
|
||
#v(0.15em)
|
||
#text(size: 7pt, fill: text-muted)[
|
||
*Soft skills :* #data.soft_skills.join(" • ")
|
||
]
|
||
|
||
#section-title("Liens & Références")
|
||
#for l in data.links [
|
||
#grid(
|
||
columns: (7pt, 1fr),
|
||
gutter: 4pt,
|
||
text(fill: primary)[•],
|
||
text(size: 7pt)[#link(l.url)[#l.label.replace("https://", "")]]
|
||
)
|
||
#v(0.1em)
|
||
]
|
||
|
||
#section-title("Centres d'intérêt")
|
||
#text(size: 7pt)[
|
||
#data.interests.join(" • ")
|
||
]
|
||
],
|
||
|
||
// ==========================================
|
||
// COLONNE DE DROITE : En-tête, Résumé, Expériences, Formation
|
||
// ==========================================
|
||
[
|
||
// En-tête avec hiérarchie claire
|
||
#v(0.1cm)
|
||
#text(size: 18pt, weight: "bold", fill: text-dark)[#data.header.name] \
|
||
#v(-0.35em)
|
||
#box[
|
||
#text(size: 11pt, weight: "bold", fill: primary)[#data.header.title]
|
||
#text(size: 9pt, fill: text-muted)[ | #data.header.subtitle]
|
||
]
|
||
|
||
// Résumé
|
||
#section-title("Profil Professionnel")
|
||
#text(size: 7.3pt, fill: rgb("#334155"))[#data.summary]
|
||
|
||
// Expériences professionnelles
|
||
#section-title("Expériences Professionnelles")
|
||
|
||
#for exp in data.experiences [
|
||
#block(width: 100%, breakable: false)[
|
||
// Ligne 1 : Titre + Date alignée à droite
|
||
#grid(
|
||
columns: (1fr, auto),
|
||
align: (left + horizon, right + horizon),
|
||
[
|
||
#text(size: 8.6pt, weight: "bold", fill: text-dark)[#exp.title]
|
||
],
|
||
[
|
||
#box(
|
||
fill: badge-bg,
|
||
radius: 3pt,
|
||
inset: (x: 5pt, y: 1.5pt),
|
||
)[#text(size: 7pt, weight: "bold", fill: primary-dark)[#exp.dates]]
|
||
]
|
||
)
|
||
#v(-0.25em)
|
||
// Ligne 2 : Logo + Entreprise
|
||
#box(height: 12pt)[
|
||
#grid(
|
||
columns: (auto, auto),
|
||
gutter: 5pt,
|
||
align: horizon,
|
||
image(exp.logo, height: 10.5pt),
|
||
text(size: 8pt, weight: "medium", style: "italic", fill: rgb("#475569"))[#exp.company – #exp.location]
|
||
)
|
||
]
|
||
#v(0.05em)
|
||
// Missions avec puces soignées
|
||
#for task in exp.tasks [
|
||
#grid(
|
||
columns: (7pt, 1fr),
|
||
gutter: 4pt,
|
||
align: (center + top, left + top),
|
||
text(size: 7pt, fill: primary)[▸],
|
||
text(size: 7.2pt)[#task]
|
||
)
|
||
]
|
||
#if "languages" in exp [
|
||
#v(-0.15em)
|
||
#pad(left: 11pt)[
|
||
#text(size: 6.9pt, fill: text-muted)[
|
||
_Technologies :_ #text(fill: text-dark)[#exp.languages]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
#v(0.28em)
|
||
]
|
||
|
||
// Formation
|
||
#section-title("Formation")
|
||
#for edu in data.education [
|
||
#let is-rncp = edu.degree.contains("RNCP 7")
|
||
#grid(
|
||
columns: (2.3cm, 1fr),
|
||
gutter: 6pt,
|
||
align: (left, left),
|
||
text(size: 7pt, fill: text-muted, weight: "medium")[#edu.dates],
|
||
[
|
||
#if is-rncp [
|
||
#text(size: 7.2pt, weight: "bold", fill: primary-dark)[#edu.degree]
|
||
] else [
|
||
#text(size: 7.2pt, weight: "bold")[#edu.degree]
|
||
] \
|
||
#v(-0.35em)
|
||
#text(size: 6.8pt, fill: text-muted)[#edu.school]
|
||
]
|
||
)
|
||
#v(0.12em)
|
||
]
|
||
]
|
||
)
|