Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions packages/preview/modernpro-coverletter/0.0.7/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Typst-ModernPro-Coverletter

This is a cover letter template for Typst with Sans font. It is a modern and professional cover letter template. It is easy to use and customize. This cover letter template is suitable for any job application or general purpose.

If you want to find a CV template, you can check out [modernpro-cv](https://github.com/jxpeng98/Typst-CV-Resume/blob/main/README.md).

## How to use

### Use from the Typst Universe

It is simple and easy to use this template from the Typst Universe. If you prefer to use the local editor and `typst-cli`, you can use the following command to create a new cover letter project with this template.

```bash
typst init @preview/modernpro-coverletter
```

It will create a new cover letter project with this template in the current directory.

### Use from GitHub

You can also use this template from GitHub. You can clone this repository and use it as a normal project.

```bash
git clone https://github.com/jxpeng98/typst-coverletter.git
```

## Features

This package provides one **cover letter** template and one **statement** template.

### Cover Letter

```typst
#import "@preview/fontawesome:0.6.0": *
#import "@preview/modernpro-coverletter:0.0.7": *

#show: coverletter.with(
font-type: "PT Serif",
margin: (left: 2cm, right: 2cm, top: 3cm, bottom: 2cm),
name: [example],
address: [],
contacts: (
(text: [#fa-icon("location-dot") UK]),
(text: [123-456-789], link: "tel:123-456-789"),
(text: [example.com], link: "https://www.example.com"),
(text: [github], link: "https://github.com/"),
(text: [example\@example.com], link: "mailto:[email protected]"),
),
recipient: (
start-title: [],
cl-title: [],
date: [],
department: [],
institution: [],
address: [],
postcode: [],
),
)

#set par(justify: true, first-line-indent: 2em)
#set text(weight: "regular", size: 12pt)
```

| Parameter | Description |
| --- | --- |
| `font-type` | The font type of the cover letter, e.g. "PT Serif" |
| `margin` | Optional page margins as a single value or directional tuple |
| `name` | The name of the sender |
| `address` | The address of the sender |
| `contacts` | The contact information of the sender(text:[], link: []) |

| Parameter in Recipient | Description |
| --- | --- |
| `start-title` | The start title of the letter |
| `cl-title` | The title of the letter (i.g., Job Application for Hiring Manager) |
| `date` | The date of the letter(If "" or [], it will generate the current date) |
| `department` | The department of the recipient, can be "" or [] |
| `institution` | The institution of the recipient |
| `address` | The address of the recipient |
| `postcode` | The postcode of the recipient |

### Statement

```typst
#import "@preview/fontawesome:0.6.0": *
#import "@preview/modernpro-coverletter:0.0.7": *

#show: statement.with(
font-type: "PT Serif",
margin: (left: 2cm, right: 2cm, top: 3cm, bottom: 2cm),
name: [],
address: [],
contacts: (
(text: [#fa-icon("location-dot")]),
(text: [#fa-icon("mobile") 123-456-789], link: "tel:123-456-789"),
(text: [#fa-icon("link") example.com], link: "https://www.example.com"),
(text: [#fa-icon("github") github], link: "https://github.com/"),
(text: [#fa-icon("envelope") example\@example.com], link: "mailto:[email protected]"),
),
)

#v(1em)
#align(center, text(13pt, weight: "semibold")[#underline([Title])])
#set par(first-line-indent: 2em, justify: true)
#set text(11pt, weight: "regular")

// Main body of the statement
```

| Parameter | Description |
| --- | --- |
| `font-type` | The font type of the cover letter, e.g. "PT Serif" |
| `margin` | Optional page margins as a single value or directional tuple |
| `name` | The name of the sender |
| `address` | The address of the sender |
| `contacts` | The contact information of the sender(text:[], link: []) |

### Icons

The new version also integrates the FontAwesome icons. You can use the `#fa-icon("icon")` function to insert the icons in the cover letter or statement template as shown above.

You just need to import the FontAwesome package at the beginning of the document.

```typst
#import "@preview/fontawesome:0.6.0": *
```

## Preview

### Cover Letter

![Cover Letter Preview](https://img.pengjiaxin.com/2024/08/79decf8975b899d31b9dc76c5466a01a.png)

### Statement

![Statement Preview](https://img.pengjiaxin.com/2024/08/0483a06862932e1e9a9f1589676ce862.png)
256 changes: 256 additions & 0 deletions packages/preview/modernpro-coverletter/0.0.7/modernpro-coverletter.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
///////////////////////////////
// modernpro-coverletter.typ
// A cover letter template with modern Sans font for job applications and other formal letters.
// Copyright (c) 2025
// Author: Jiaxin Peng
// License: MIT
// Version: 0.0.7
// Date: 2025-10-14
// Email: [email protected]
///////////////////////////////


#let coverletter(
font-type: "",
margin: none,
name: "",
address: none,
contacts: (),
recipient: (start-title: "", cl-title: "", date: "", department: "", institution: "", address: "", postcode: ""),
supplement: none,
mainbody,
) = {
set text(font: font-type, weight: "regular")

let date-colour = rgb("#666666")
let primary-colour = rgb("#000000")
let headings-colour = rgb("#2b2b2b")
let subheadings-colour = rgb("#333333")

let sectionsep = {
[#v(5pt)]
}
let subsectionsep = {
[#v(2pt)]
}

let resolved-margin = if margin != none {
margin
} else {
(left: 1.25cm, right: 1.25cm, top: 3cm, bottom: 1.5cm)
}

// set the recipient
let recipient-generate(start-title, cl-title, date, department, institution, address, postcode) = {
align(
left,
{
if department != [] {
text(10pt, font: font-type, fill: subheadings-colour, weight: "bold")[#department]
}
h(1fr)
if date != [] {
text(10pt, font: font-type, fill: primary-colour, weight: "light")[#date\ ]
} else {
text(
10pt,
font: font-type,
fill: primary-colour,
weight: "light",
)[ #datetime.today(offset: auto).display("[day] [month repr:long] [year]")\ ]
}

if institution != [] {
text(10pt, font: font-type, fill: subheadings-colour, weight: "bold")[#institution\ ]
}

if address != [] {
text(10pt, font: font-type, fill: headings-colour, weight: "light")[#address\ ]
}
if postcode != [] {
text(10pt, font: font-type, fill: headings-colour, weight: "light")[#postcode ]
}
},
)
if cl-title != [] {
align(left, text(12pt, font: font-type, fill: primary-colour, weight: "bold")[#upper(cl-title)])
v(0.1em)
}
if start-title != [] {
set text(11pt, font: font-type, fill: primary-colour, weight: "regular")
[#start-title]
}
}

// show contact details
let contact-display(contacts) = {
v(-5pt)
set text(10pt, fill: headings-colour, weight: "regular")
contacts
.map(contact => {
if ("link" in contact) {
link(contact.link)[#{
contact.text
}]
} else [
#{
contact.text
}
]
})
.join(" | ")
}

set page(
margin: resolved-margin,
header: {
// Head Name Section
text(
20pt,
fill: primary-colour,
weight: "bold",
top-edge: "baseline",
bottom-edge: "baseline",
baseline: 12pt,
)[#align(center, [#name])]
// address
if address != none {
v(5pt)
text(
11pt,
fill: primary-colour,
weight: "regular",
top-edge: "baseline",
bottom-edge: "baseline",
baseline: 2pt,
)[#align(center, [#address])]
}
v(2pt)
align(center)[#contact-display(contacts)]
line(length: 100%, stroke: 0.2pt + primary-colour)
},
header-ascent: 1em,
)

// Add recipient details
recipient-generate(
recipient.start-title,
recipient.cl-title,
recipient.date,
recipient.department,
recipient.institution,
recipient.address,
recipient.postcode,
)

set par(justify: true, first-line-indent: 2em)

set text(11pt, font: font-type, fill: primary-colour, weight: "regular")

mainbody

set text(11pt, font: font-type, fill: primary-colour, weight: "regular")

v(1pt)
[Sincerely,]
v(1pt)
[*#name*]
if supplement != none {
v(1pt)
[#supplement]
}
}



#let statement(
font-type: "",
margin: none,
name: "",
address: none,
contacts: (),
supplement: none,
mainbody,
) = {
set text(font: font-type, weight: "regular")

let date-colour = rgb("#666666")
let primary-colour = rgb("#000000")
let headings-colour = rgb("#2b2b2b")
let subheadings-colour = rgb("#333333")

let sectionsep = {
[#v(5pt)]
}
let subsectionsep = {
[#v(2pt)]
}

let resolved-margin = if margin != none {
margin
} else {
(left: 1.25cm, right: 1.25cm, top: 3cm, bottom: 1.5cm)
}

// show contact details
let contact-display(contacts) = {
v(-5pt)
set text(10pt, fill: headings-colour, weight: "regular")
contacts
.map(contact => {
if ("link" in contact) {
link(contact.link)[#{
contact.text
}]
} else [
#{
contact.text
}
]
})
.join(" | ")
}

set page(
margin: resolved-margin,
header: {
// Head Name Section
text(
20pt,
fill: primary-colour,
weight: "bold",
top-edge: "baseline",
bottom-edge: "baseline",
baseline: 12pt,
)[#align(center, [#name])]
// address
if address != none or address != [] {
v(5pt)
text(
11pt,
fill: primary-colour,
weight: "regular",
top-edge: "baseline",
bottom-edge: "baseline",
baseline: 2pt,
)[#align(center, [#address])]
}
align(center)[#contact-display(contacts)]
line(length: 100%, stroke: 0.2pt + primary-colour)
},
header-ascent: 1em,
)

set par(justify: true, first-line-indent: 2em)

set text(11pt, font: font-type, fill: primary-colour, weight: "regular")

mainbody

set text(11pt, font: font-type, fill: primary-colour, weight: "regular")

if supplement != none {
v(1pt)
[#supplement]
}
}
Loading