Skip to content

Commit ff4e49e

Browse files
committed
Versatile APA 7.1.0 update
1 parent b047b29 commit ff4e49e

32 files changed

+1689
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Versatile APA 7th Edition Template for Typst
2+
3+
A comprehensive Typst template following the official APA 7th Edition style guidelines. This template includes all necessary components for academic research papers, essays, theses, and dissertations.
4+
5+
## Usage
6+
7+
You can use this template through the Typst Universe page via the `typst` command locally or directly through the Typst Web App.
8+
9+
Alternatively, clone this repository as a git submodule. For detailed instructions, refer to this repository's main README.
10+
11+
### Features
12+
13+
This template supports, mainly, both student and professional versions of APA 7th Edition papers with:
14+
15+
- Title page
16+
- Abstract
17+
- Headings (levels 1-5, further levels will be formatted as level 5)
18+
- Raw/computer code blocks (See `codly` package for better code formatting)
19+
- Mathematical equations
20+
- References
21+
- Quotations (short and block formats based on word count)
22+
- Lists (ordered and unordered)
23+
- Footnotes
24+
25+
#### Figures
26+
27+
The template provides robust support for figures (images, tables, and equations) with:
28+
29+
- General notes
30+
- Specific notes
31+
- Probability notes
32+
33+
For appendix figures, use the `appendix-figure` function (which accepts the same parameters as `apa-figure`).
34+
35+
#### Language Support
36+
37+
- English
38+
- Spanish
39+
- Portuguese
40+
- German
41+
- Italian
42+
- French
43+
- Dutch
44+
45+
#### Appendices
46+
47+
- Figure appendices with independent numbering
48+
- Appendices outline
49+
- Smart appendix numbering (automatically disabled for single appendices)
50+
51+
#### Authoring
52+
53+
- Automatic footnote numbering for author affiliations
54+
- Author notes
55+
- ORCID integration
56+
57+
## Regarding LaTeX
58+
59+
**LaTeX `apa7` class inspiration**: This template draws heavily from the `apa7` class in LaTeX (which itself builds on the `apa6` class). The `journal` and `document` formats are not included due to styling and formatting variations. While technically possible to port these formats from LaTeX, they serve limited use cases beyond academic submission, as most APA-compliant journals implement their own specific styling requirements.
60+
61+
## Known Issues and Limitations
62+
63+
### Appendix Figure Numbering
64+
65+
The `appendix-figure` function works reliably but has some formatting inconsistencies. While the numbering functions correctly, the figure supplement uses the heading number instead of the figure number, causing outline inconsistencies. For example, a figure labeled `Figure B2` appears as `Figure B 2` in the outline.
66+
67+
**Solution**: You can manually adjust the outline entries, or set `joined-figure-numbering` to `false` in the `appendix` settings to consistently use the `B 2` format in both figures and outline.
68+
69+
> Note: The separation between letter and number in the figure supplement occurs because setting "A" for the first figures caused numbering issues where the letter was interpreted as the numbering value, resulting in figures displaying only as "A, B, ..." without proper sequence numbers.
70+
71+
## License
72+
73+
This package is licensed under the MIT License. See the repository for complete license information.
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
#import "utils/to-string.typ": *
2+
#import "utils/languages.typ": *
3+
#import "utils/authoring.typ": *
4+
#import "utils/orcid.typ": *
5+
#import "utils/appendix.typ": *
6+
#import "utils/apa-figure.typ": *
7+
8+
/// The APA 7th edition template for academic and professional documents.
9+
///
10+
/// - title (content): The title of your document.
11+
/// - authors (dictionary): The authors of the document.
12+
/// - For each author you must specify their name and their affiliations.
13+
/// - affiliations (dictionary): The affiliations of the authors.
14+
/// - For each affiliation you must specify its ID and its name.
15+
/// - custom-authors (content): The custom authors of the document.
16+
/// - You can manually specify the authors of the document.
17+
/// - custom-affiliations (content): The custom affiliations of the document.
18+
/// - You can manually specify the affiliations of the document.
19+
/// - course (content): The academic course for the document.
20+
/// - instructor (content): The instructor for the document.
21+
/// - due-date (content): The due date for the document.
22+
/// - running-head (content): The running head for the document.
23+
/// - author-notes (): The author notes for the document.
24+
/// - keywords (array | str): The keywords for the document metadata and abstract.
25+
/// - abstract (content): The abstract of the document.
26+
/// - journal (bool): Whether to use journal format.
27+
/// - font-family (): The font family for the document. APA 7th edition recommended fonts are:
28+
/// - Sans Serif fonts such as 11-point Calibri, 11-point Arial, or 10-point Lucida Sans Unicode
29+
/// - Serif fonts such as 12-point Times New Roman, 11-point Georgia, or 10-point Computer Modern (LaTeX)
30+
/// - font-size (length): The font size for the document.
31+
/// - APA 7th edition recommends a 10-12 point font size.
32+
/// - region (str): The region for the document (e.g., "us", "uk", "au").
33+
/// - language (str): The language for the document (e.g., "en", "es", "fr").
34+
/// - paper-size (str): The paper size for the document (e.g., "us-letter", "a4").
35+
/// - implicit-introduction-heading (bool): Wether to include the paper title at the top of the first page of the text, which acts as a de facto Level 1 heading.
36+
/// - abstract-as-description (bool): Whether to use the abstract as the document description.
37+
/// - body (content): The body of the document.
38+
/// -> content
39+
#let versatile-apa(
40+
title: [Paper Title],
41+
// Authoring fields
42+
authors: (:),
43+
affiliations: (:),
44+
custom-authors: [],
45+
custom-affiliations: [],
46+
// Student-specific fields
47+
course: [],
48+
instructor: [],
49+
due-date: [],
50+
// Professional-specific fields
51+
running-head: [],
52+
author-notes: [],
53+
keywords: (),
54+
abstract: [],
55+
// Common fields
56+
font-family: "Libertinus Serif",
57+
font-size: 12pt,
58+
region: "us",
59+
language: "en",
60+
paper-size: "us-letter",
61+
implicit-introduction-heading: true,
62+
abstract-as-description: true,
63+
body,
64+
) = {
65+
let double-spacing = 1.5em
66+
let first-indent-length = 0.5in
67+
68+
authors = validate-inputs(authors, custom-authors, "author")
69+
affiliations = validate-inputs(affiliations, custom-affiliations, "affiliation")
70+
71+
set document(
72+
title: title,
73+
author: if type(authors) == array {
74+
authors.map(it => to-string(it.name))
75+
} else {
76+
to-string(authors).trim(" ", at: start).trim(" ", at: end)
77+
},
78+
description: if abstract-as-description { abstract },
79+
keywords: keywords,
80+
)
81+
82+
set text(
83+
size: font-size,
84+
font: font-family,
85+
region: region,
86+
lang: language,
87+
)
88+
89+
set page(
90+
margin: 1in,
91+
paper: paper-size,
92+
numbering: "1",
93+
number-align: top + right,
94+
header: context {
95+
upper(running-head)
96+
h(1fr)
97+
str(here().page())
98+
},
99+
)
100+
101+
set par(
102+
leading: double-spacing,
103+
spacing: double-spacing,
104+
)
105+
106+
show link: set text(fill: blue)
107+
108+
show link: it => {
109+
underline(it.body)
110+
}
111+
112+
if running-head != none and running-head != [] and running-head != "" {
113+
if to-string(running-head).len() > 50 {
114+
panic("Running head must be no more than 50 characters, including spaces and punctuation.")
115+
}
116+
}
117+
118+
align(center)[
119+
#for i in range(4) {
120+
[~] + parbreak()
121+
}
122+
123+
#strong(title)
124+
125+
~
126+
127+
#parbreak()
128+
129+
#print-authors(authors, affiliations, language)
130+
131+
#print-affiliations(authors, affiliations)
132+
133+
#if type(course) == content and course != [] {
134+
course
135+
} else if type(course) != content {
136+
panic("Course must be of type content: ", type(course))
137+
}
138+
139+
#if type(instructor) == content and instructor != [] {
140+
instructor
141+
} else if type(instructor) != content {
142+
panic("Instructor must be of type content: ", type(instructor))
143+
}
144+
145+
#if ((type(due-date) == content and due-date != []) or (type(due-date) == str and due-date != "")) {
146+
due-date
147+
} else if type(due-date) != content {
148+
panic("Due date must be of type content or string: ", type(due-date))
149+
}
150+
151+
#if author-notes != [] and author-notes != none {
152+
v(1fr)
153+
154+
strong(get-terms(language).at("Author Note"))
155+
156+
align(left)[
157+
#set par(first-line-indent: first-indent-length)
158+
#author-notes
159+
]
160+
}
161+
162+
#pagebreak()
163+
]
164+
165+
show heading: set text(size: font-size)
166+
show heading: set block(spacing: double-spacing)
167+
168+
show heading: it => emph(strong[#it.body.])
169+
show heading.where(level: 1): it => align(center, strong(it.body))
170+
show heading.where(level: 2): it => par(
171+
first-line-indent: 0in,
172+
strong(it.body),
173+
)
174+
175+
show heading.where(level: 3): it => par(
176+
first-line-indent: 0in,
177+
emph(strong(it.body)),
178+
)
179+
180+
show heading.where(level: 4): it => strong[#it.body.]
181+
show heading.where(level: 5): it => emph(strong[#it.body.])
182+
183+
set par(
184+
first-line-indent: first-indent-length,
185+
leading: double-spacing,
186+
)
187+
188+
show figure: set figure.caption(position: top)
189+
190+
show table.cell: set par(leading: 1em)
191+
192+
show figure: set block(breakable: true)
193+
194+
show figure: it => {
195+
it.caption
196+
align(center, it.body)
197+
}
198+
199+
set figure(
200+
gap: double-spacing,
201+
placement: none,
202+
)
203+
204+
show figure.caption: it => {
205+
set par(first-line-indent: 0in)
206+
align(left)[
207+
*#it.supplement #context it.counter.display(it.numbering)*
208+
209+
#emph(it.body)
210+
]
211+
}
212+
213+
set table(stroke: none)
214+
215+
set list(
216+
marker: ([•], [◦]),
217+
indent: 0.5in - 1.75em,
218+
body-indent: 1.3em,
219+
)
220+
221+
set enum(
222+
indent: 0.5in - 1.5em,
223+
body-indent: 0.75em,
224+
)
225+
226+
set raw(
227+
tab-size: 4,
228+
block: true,
229+
)
230+
231+
show raw.where(block: true): block.with(
232+
fill: luma(250),
233+
stroke: (left: 3pt + rgb("#6272a4")),
234+
inset: (x: 10pt, y: 8pt),
235+
width: auto,
236+
breakable: true,
237+
outset: (y: 7pt),
238+
radius: (left: 0pt, right: 6pt),
239+
)
240+
241+
show raw: set text(
242+
font: "Cascadia Code",
243+
size: 10pt,
244+
)
245+
246+
show raw.where(block: true): set par(leading: 1em)
247+
248+
set math.equation(numbering: "(1)")
249+
250+
show figure.where(kind: raw): it => {
251+
set align(left)
252+
it.caption
253+
it.body
254+
}
255+
256+
show quote: set pad(left: 0.5in)
257+
show quote: set block(spacing: 1.5em)
258+
259+
show quote: it => {
260+
let quote-text = to-string(it.body)
261+
let quote-text-words = to-string(it.body).split(" ").len()
262+
263+
if quote-text-words <= 40 {
264+
set quote(block: false)
265+
[
266+
"#quote-text.trim(" ")"~#it.attribution.
267+
]
268+
} else {
269+
set quote(block: true)
270+
set par(hanging-indent: 0.5in)
271+
[
272+
#quote-text.trim(" ")~#it.attribution
273+
]
274+
}
275+
}
276+
277+
set bibliography(style: "apa")
278+
show bibliography: set par(first-line-indent: 0in)
279+
280+
if ((type(abstract) == content or type(abstract) == str) and (abstract != [] and abstract != "")) {
281+
heading(level: 1, get-terms(language).Abstract, outlined: false)
282+
283+
par(first-line-indent: 0in)[
284+
#abstract
285+
]
286+
287+
emph(get-terms(language).Keywords)
288+
[: ]
289+
keywords.map(it => it).join(", ")
290+
291+
pagebreak()
292+
} else {
293+
panic(
294+
"Invalid abstract, abstract must be content or string, and not be empty. Type is " + type(abstract),
295+
"Abstract input is: " + abstract,
296+
)
297+
}
298+
299+
if implicit-introduction-heading {
300+
heading(level: 1, title)
301+
}
302+
303+
body
304+
}
Loading
Loading
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def main():
2+
print("Hello, world!")
3+
4+
if __name__ == "__main__":
5+
main()

0 commit comments

Comments
 (0)