Compare commits
3 Commits
c1cbdaf7f9
...
e50b6e70b7
Author | SHA1 | Date |
---|---|---|
Bauke | e50b6e70b7 | |
Bauke | d86e1eff02 | |
Bauke | a07d00e66d |
|
@ -0,0 +1,26 @@
|
||||||
|
# CV
|
||||||
|
|
||||||
|
> **Bauke's CV template.**
|
||||||
|
|
||||||
|
## Setup & Building
|
||||||
|
|
||||||
|
This project uses [Typst](https://typst.app) as the main language to render the CV, targeting PDF specifically. For the development environment [Nix flakes](https://nixos.wiki/wiki/Flakes) and [direnv](https://direnv.net/) is used to load in the required packages (see [`shell.nix`](shell.nix) for the list).
|
||||||
|
|
||||||
|
To make building easy, [cargo-make](https://sagiegurari.github.io/cargo-make/) is used, the available tasks are all described in the [`Makefile.toml`](Makefile.toml) configuration.
|
||||||
|
|
||||||
|
### Data-driven
|
||||||
|
|
||||||
|
All the information used in the CV is located in the git-ignored `data/` directory. With the main file being `data/info.toml`. You can find a full example in [DATA.md](./DATA.md).
|
||||||
|
|
||||||
|
### Images
|
||||||
|
|
||||||
|
Various images are used in the CV that all belong to their respective owners.
|
||||||
|
|
||||||
|
- The [email](images/email.svg), [home](images/home.svg), [launch](images/launch.svg) and [phone](images/phone.svg) icons are from the [Carbon Design System](https://carbondesignsystem.com/).
|
||||||
|
- The [Forgejo logo](images/forgejo.svg) belongs to [Forgejo](https://forgejo.org/).
|
||||||
|
- The [GitHub logo](images/github.svg) belongs to [GitHub](https://github.com).
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Distributed under the [Unlicense](https://spdx.org/licenses/Unlicense.html) license, see [LICENSE](https://git.bauke.xyz/Bauke/cv/src/branch/main/LICENSE) for more information.
|
|
@ -0,0 +1,56 @@
|
||||||
|
#import "../utilities/localize.typ": localize
|
||||||
|
|
||||||
|
#let skill(data, language) = {
|
||||||
|
// Determine the color to use for the proficiency percentage bar.
|
||||||
|
let proficiency_color = if "color" in data {
|
||||||
|
// If there is a color defined in the data, use `eval()` to convert it to a
|
||||||
|
// Typst color.
|
||||||
|
eval(data.color)
|
||||||
|
} else {
|
||||||
|
// Otherwise if no color is defined default to black.
|
||||||
|
black
|
||||||
|
}
|
||||||
|
|
||||||
|
let proficiency_percentage = str(data.proficiency) + "%";
|
||||||
|
|
||||||
|
// Use a stack to position the text and percentage bar from top to bottom.
|
||||||
|
stack(
|
||||||
|
dir: ttb,
|
||||||
|
// And use a grid to position the skill text and percentage number from
|
||||||
|
// left to right.
|
||||||
|
grid(
|
||||||
|
columns: (1fr, auto),
|
||||||
|
localize(data, "name", language),
|
||||||
|
proficiency_percentage,
|
||||||
|
),
|
||||||
|
// Add a tiny amount of vertical space so the bar doesn't overlap the text.
|
||||||
|
v(4pt),
|
||||||
|
// Then render the bar as two lines where the grid columns determine how
|
||||||
|
// long they both are. Initially I tried to do this with a linear gradient
|
||||||
|
// and using the percentage as a color stop, but it left a small fade in
|
||||||
|
// between the colors which I didn't want.
|
||||||
|
grid(
|
||||||
|
// We have to use `eval()` for the percentage here as it's still a string
|
||||||
|
// at this point and Typst expects a ratio type to calculate with.
|
||||||
|
columns: (1fr, 100% - eval(proficiency_percentage)),
|
||||||
|
line(length: 100%, stroke: 1pt + proficiency_color),
|
||||||
|
line(length: 100%, stroke: 1pt + silver),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let skills_section(data, language) = {
|
||||||
|
block(stroke: (left: 1pt + black), outset: (left: 0.75em), [
|
||||||
|
== #localize(data.locale, "skills", language)
|
||||||
|
|
||||||
|
// Define all the sections to render, the order here also determines the
|
||||||
|
// render order.
|
||||||
|
#for skill_section in ("language", "general", "software", "platforms") [
|
||||||
|
=== #localize(data, skill_section, language)
|
||||||
|
|
||||||
|
#for data in data.skills.at(skill_section) {
|
||||||
|
skill(data, language)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Get the localized item of a given key inside a data dictionary, using the
|
||||||
|
// provided language.
|
||||||
|
#let localize(data, key, language) = {
|
||||||
|
// If the key is directly available in the data then it hasn't been localized.
|
||||||
|
if key in data {
|
||||||
|
return data.at(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then check for the key with the wanted language appended to it.
|
||||||
|
let key_with_language = key + "_" + language
|
||||||
|
if key_with_language in data {
|
||||||
|
return data.at(key_with_language)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a locale dictionary exists in the data, then run this function again
|
||||||
|
// using that dictionary for the same key and language.
|
||||||
|
if "locale" in data {
|
||||||
|
return localize(data.locale, key, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And if the key nor locale exists in any way then panic, as we won't have
|
||||||
|
// any text to return.
|
||||||
|
panic("Data does not contain key in any localized way: " + key, data)
|
||||||
|
}
|
Loading…
Reference in New Issue