Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

LaTeX & Typst (papers, theses, resumes and slides)  Module 2 – Document structure, text and page layout

Sections, titles and tables of contents

Number headings in LaTeX and Typst, list them in a table of contents of the depth you want, and put the document title on the page and in the PDF.

  • Beginner
  • 15 minutes
  • Examples run with TeX Live 2026, TinyTeX 2026.10, Typst 0.15.1, Python 3.14.8, Pyodide 314.0.7 and poppler 26.09.0
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Structure a document with numbered and unnumbered headings in both systems
  • Generate a table of contents and control how many levels it lists
  • Set the document title and author so that they also appear in the PDF's metadata
  • Use Typst's title element instead of a heading for the document title, with no skipped heading levels

Before you start

What you’ll learn · Before you start

What you will learn

  • Structure a document with numbered and unnumbered headings in both systems
  • Generate a table of contents and control how many levels it lists
  • Set the document title and author so that they also appear in the PDF's metadata
  • Use Typst's title element instead of a heading for the document title, with no skipped heading levels

Before you start

On this page

This lesson’s report has seven headings, and after its first pdfLaTeX run the contents list shows none of them. Its PDF has no title either, although “Monsoon Notes” is printed at the top of the page.

Below you will number headings in both systems and leave some unnumbered, list them in a contents list of the depth you choose, put the title on the page and into the PDF’s metadata, and keep the heading levels in the order screen readers rely on. A second pdfLaTeX run and one package fix the report; Typst gets all of it right in one compile.

Headings are commands in LaTeX and equals signs in Typst

Here is the report in both systems: an unnumbered preface, two sections, subsections under them and one heading a level deeper. It uses A5 pages, so its contents list reads back in short lines:

The same report in LaTeX and Typst

LaTeX: sections.tex · sections/sections.tex

% sections.tex: a short report with an unnumbered preface and three levels of headings.
\documentclass[a5paper]{article}
\title{Monsoon Notes}
\author{A. Rao}
\date{}% no date under the title
\setcounter{tocdepth}{2}% list sections and subsections, not subsubsections
\begin{document}
\maketitle
\tableofcontents
\section*{Preface}
\addcontentsline{toc}{section}{Preface}
Why these notes exist.
\section{Rainfall}
\subsection{Gauges}
\subsubsection{Daily readings}
Two days are missing.
\subsection{Totals}
\section{Rivers}
\subsection{Flow}
\end{document}

Typst: sections.typ · sections/sections.typ

// sections.typ: the same report in Typst.
#set page(paper: "a5")
#set document(title: [Monsoon Notes], author: "A. Rao")
#set heading(numbering: "1.1")
#title()
#outline(depth: 2)

#heading(numbering: none)[Preface]
Why these notes exist.

= Rainfall
== Gauges
=== Daily readings
Two days are missing.
== Totals
= Rivers
== Flow

In LaTeX a heading is a sectioning command: A LaTeX command that starts a heading at one level, such as \section or \subsection. Its level number decides whether the heading is numbered (secnumdepth) and whether the table of contents lists it (tocdepth).. The LaTeX2e reference manual gives each a level number: \section is 1, \subsection 2 and \subsubsection 3, then come \paragraph and \subparagraph; above them, \chapter is 0 in report and book, which alone have it, and \part is −1 there and 0 in article. Whether a level is numbered depends on the counter secnumdepth, which the manual gives as 3 in article and 2 in report and book: an article numbers three levels.

Typst writes headings with equals signs, = for level 1, == for level 2 and so on. Its reference adds the difference that surprises people coming from LaTeX: headings are not numbered until you ask, because numbering defaults to none. #set heading(numbering: "1.1") asks for numbers like 1, 1.1 and 1.1.1. This session builds both files; read the headings in the first PDF and at the end of the Typst one:

Two pdfLaTeX runs and one Typst compile of the report Python · sections_session.py
from session import files, run

# The report's two pdfLaTeX runs, with the .toc file written between them, then the same report in Typst. pdftotext
# reads the start of each PDF, keeping the layout; rows of leader dots are shortened to " …" so that lines fit.
files("sections/sections.tex", "sections/sections.typ")
run(r"""
pdflatex -interaction=nonstopmode sections.tex | grep -E "Warning|Output"
pdftotext -layout sections.pdf - | head -14
cat sections.toc
pdflatex -interaction=nonstopmode sections.tex | grep -E "Warning|Output"
pdftotext -layout sections.pdf - | head -14
typst compile sections.typ sections-typst.pdf
pdftotext -layout sections-typst.pdf - | head -16
""", mask=[(r"( \.){3,}", " …")])

Output

$ pdflatex -interaction=nonstopmode sections.tex | grep -E "Warning|Output"
Output written on sections.pdf (2 pages, 58284 bytes).
$ pdftotext -layout sections.pdf - | head -14
                   Monsoon Notes
                          A. Rao


Contents

Preface
Why these notes exist.


1       Rainfall
1.1     Gauges
1.1.1    Daily readings
Two days are missing.
$ cat sections.toc
\contentsline {section}{Preface}{1}{}%
\contentsline {section}{\numberline {1}Rainfall}{1}{}%
\contentsline {subsection}{\numberline {1.1}Gauges}{1}{}%
\contentsline {subsubsection}{\numberline {1.1.1}Daily readings}{1}{}%
\contentsline {subsection}{\numberline {1.2}Totals}{2}{}%
\contentsline {section}{\numberline {2}Rivers}{2}{}%
\contentsline {subsection}{\numberline {2.1}Flow}{2}{}%
$ pdflatex -interaction=nonstopmode sections.tex | grep -E "Warning|Output"
Output written on sections.pdf (2 pages, 60239 bytes).
$ pdftotext -layout sections.pdf - | head -14
                   Monsoon Notes
                            A. Rao


Contents
Preface                                                        1

1 Rainfall                                                     1
  1.1 Gauges …   1
  1.2 Totals …   2

2 Rivers                                                       2
  2.1 Flow …   2

$ typst compile sections.typ sections-typst.pdf
$ pdftotext -layout sections-typst.pdf - | head -16
Monsoon Notes
Contents
Preface … ⁠1
1 Rainfall … ⁠1
  1.1 Gauges … ⁠1
  1.2 Totals … ⁠1
2 Rivers … ⁠1
  2.1 Flow … ⁠1

Preface
Why these notes exist.

1 Rainfall
1.1 Gauges
1.1.1 Daily readings
Two days are missing.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 sections_session.py

Both systems numbered the same way: 1 Rainfall, 1.1 Gauges, 1.1.1 Daily readings, then 1.2, 2 and 2.1. Step through the headings to see each count go up, start again or stay where it was:

Interactive · step by step How the report's headings get their numbers Step through the seven headings in order and watch the count of each level go up, start again or stay put.
How the report's headings get their numbers, step 7 of 7. Showing Preface (no number), Rainfall (1), Gauges (1.1), Daily readings (level 3, 1.1.1), Totals (1.2), Rivers (2), Flow (2.1). In focus: Flow.Prefaceno numberRainfall1Gauges1.1Daily readingslevel 3, 1.1.1Totals1.2Rivers2Flow2.1

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. An unnumbered heading. \section*{Preface} in LaTeX and #heading(numbering: none)[Preface] in Typst print the heading with no number, and no count changes: both are still at zero.
  2. The first section. \section{Rainfall}, or = Rainfall with numbering 1.1, adds one to the count of level 1. Its number is 1.
  3. One level down. \subsection{Gauges}, or == Gauges, adds one to level 2 under section 1, so its number is 1.1.
  4. A third level. \subsubsection{Daily readings}, or === in Typst, is numbered 1.1.1. The article class numbers three levels by default, as its secnumdepth of 3 says.
  5. Back up to level 2. Totals is the second subsection of section 1, so it is 1.2. The level 3 count starts again from zero.
  6. A new section. Rivers adds one to level 1, which starts every deeper count again from zero. Its number is 2.
  7. A fresh subsection count. Flow is the first subsection of section 2, so it is 2.1, not 1.3. Both systems printed exactly these numbers.

Bold text instead of a heading

Wrong: \textbf{Rainfall} on a line of its own, or #text(weight: "bold")[Rainfall] in Typst, to make something look like a heading. It gets no number, no entry in the contents and no bookmark. Right: use a sectioning command or =, and change how headings look with the class, a package or a show rule.

An unnumbered heading can still be listed in the contents

A preface, an abstract or acknowledgements usually carry no number. LaTeX’s starred form, \section*{Preface}, does this, and the reference manual says what else it does: it does not number the heading and does not make an entry in the table of contents. The report adds the entry by hand with \addcontentsline{toc}{section}{Preface}, in the form the manual gives for that command. Typst’s #heading(numbering: none)[Preface] is unnumbered too, but the reference says headings are listed in the outline unless outlined is false, so Typst needs no extra line.

Both contents lists in the session start with “Preface”, with no number in front. The heading after it was numbered 1, not 2, in both systems: an unnumbered heading changes no count. For a long heading, LaTeX’s optional argument, \section[Rain]{Rainfall at the three stations}, gives the contents and the page headers a shorter title. The three cases side by side:

What you want LaTeX Typst
A numbered heading \section{Rainfall} = Rainfall, with #set heading(numbering: "1.1")
No number, listed in the contents \section*{Preface} and \addcontentsline{toc}{section}{Preface} #heading(numbering: none)[Preface]
No number, not listed \section*{Preface} #heading(numbering: none, outlined: false)[Preface]

Key idea

A heading’s number and its entry in the contents are separate choices: a starred LaTeX heading drops both until \addcontentsline brings the entry back, and a Typst heading without numbering keeps its entry unless outlined is false.

Checkpoint

Check: Unnumbered headings and the contents · Heading levels and numbering

  1. Question 1 of 2 In sections.typ, the preface is #heading(numbering: none)[Preface]. Is it in the outline?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Yes, because headings are outlined unless outlined is false

    numbering: none removes the number only. The heading stays in the outline, and in the PDF's bookmarks, unless outlined: false is added.

  2. Question 2 of 2 The report's headings are Preface (unnumbered), Rainfall, Gauges, Daily readings, Totals, Rivers and Flow. What number does Flow get?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 2.1

    A numbered heading adds one to its level and starts every deeper level again from zero; an unnumbered heading changes nothing. Both systems printed 2.1 Flow.

The contents list takes two runs in LaTeX and one compile in Typst

\tableofcontents prints the contents where it stands, and the reference manual explains why it needs a second run: LaTeX stores the entries in a .toc file during one run and reads them back on the next, so after the first run of a new document only the heading appears. That is what the session’s first PDF showed: the word “Contents” and nothing under it. The .toc file printed after that run holds one \contentsline for every heading, Daily readings included, and the second run turned those lines into the list. Follow one heading through both runs:

Interactive · stage by stage How Gauges reaches the contents list Step through the two pdfLaTeX runs that put 1.1 Gauges into the contents, and the shorter way Typst takes.
How Gauges reaches the contents list, step 7 of 7. Showing \subsection{Gauges}, The count goes up, An entry for .toc, Run 2 reads .toc, tocdepth decides, The contents line. In focus: typst.Typst: in the same compile\subsection{Gauges}run 1, page 1The count goes upnumber 1.1 printedAn entry for .tocwritten at the end of run 1Run 2 reads .tocat \tableofcontentstocdepth decideslevel 2 is within depth 2The contents line1.1 Gauges, page 1

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. The heading. In run 1, pdfLaTeX typesets \subsection{Gauges} on page 1 while the contents list above it is still empty, because no .toc file exists yet.
  2. Its number. The subsection count goes up, and 1.1 Gauges is printed in the text. The number is known at once, because everything before it has been typeset.
  3. A note for later. The heading also makes a line \contentsline {subsection}{\numberline {1.1}Gauges}{1}, which LaTeX writes into sections.toc when the run ends.
  4. The next run. In run 2, \tableofcontents reads sections.toc before any heading of this run is typeset, so it can print entries for headings that come later in the file.
  5. The depth filter. The file lists every heading, Daily readings at level 3 too. With \setcounter{tocdepth}{2}, levels 1 and 2 are printed and level 3 is left out.
  6. The line. The contents list now shows 1.1 Gauges with its page number, 1. Run 1 had printed only the word Contents.
  7. Typst's way. Typst lays the document out, finds every heading, and lays it out again within the same compile, so the outline was complete after one typst compile.

The depth is a separate setting. The tocdepth: The LaTeX counter that sets the deepest heading level the table of contents lists, as in \setcounter{tocdepth}{2}. Its partner secnumdepth sets the deepest level that is numbered. counter names the deepest level listed: \setcounter{tocdepth}{2} in the report lists sections and subsections, so the third-level heading Daily readings was numbered in the text but left out of the contents. Typst’s #outline(depth: 2) does the same, and the reference says that with the default, none, every heading is included.

Count the levels before you choose a depth. In article, \section is level 1, as = is in Typst, so depth 2 lists the same headings in both. In report and book, \chapter is level 0, so tocdepth 2 reaches \subsection, three levels down, while Typst’s depth 2 still stops at ==.

Lists of figures and tables

\listoffigures and \listoftables work like \tableofcontents, with their entries in .lof and .lot files. In Typst, #outline(target: figure.where(kind: table)) lists the tables, as the outline reference shows.

Checkpoint

Check: The contents list and its depth

  1. Question 1 of 2 What did the report's contents list show after the first pdfLaTeX run?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Only the word Contents, with no entries under it

    LaTeX writes the contents entries into the .toc file during a run and reads them back on the next, so a new document needs a second run before its contents list fills in.

  2. Question 2 of 2 A thesis uses the report class with \setcounter{tocdepth}{2}. Which headings does its contents list show?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Chapters, sections and subsections

    Count the levels before you choose a depth: in article, \section is level 1; in report and book, \chapter is level 0, so the same number reaches one level further down.

The title belongs on the page and in the PDF’s metadata

A PDF has two places for a title: the page, where readers see it, and its PDF metadata: What a PDF records about itself, such as its title and author, which viewers show in the window title and pdfinfo prints. LaTeX writes it through hyperref, Typst through set document., which search tools read and a viewer can show in its title bar. In LaTeX, \title, \author and \maketitle fill only the first. The reference manual adds a detail about the date: if you omit \date, LaTeX prints the current one, and \date{} prints none, which is why the report has it. This session reads the metadata of each PDF with pdfinfo:

Where the title went: the page or the PDF's metadata Python · title_session.py
from session import files, read, run, write

# Where the title of the report goes: pdfinfo reads the PDF's metadata, first of the plain LaTeX build, then of a copy
# that loads hyperref with pdfusetitle, then of the Typst build.
files("sections/sections.tex", "sections/sections.typ")
write("sections-meta.tex", read("sections.tex").replace(
    "% no date under the title\n",
    "% no date under the title\n\\usepackage[pdfusetitle]{hyperref}% the title and author into the PDF's metadata\n"))
run(r"""
pdflatex -interaction=nonstopmode sections.tex | grep -E "Output"
pdfinfo sections.pdf | grep -E "^(Title|Author|Pages)"
grep -n "hyperref" sections-meta.tex
pdflatex -interaction=nonstopmode sections-meta.tex | grep -E "Output"
pdflatex -interaction=nonstopmode sections-meta.tex | grep -E "Output"
pdfinfo sections-meta.pdf | grep -E "^(Title|Author|Pages)"
typst compile sections.typ sections-typst.pdf
pdfinfo sections-typst.pdf | grep -E "^(Title|Author|Pages)"
""")

Output

$ pdflatex -interaction=nonstopmode sections.tex | grep -E "Output"
Output written on sections.pdf (2 pages, 58284 bytes).
$ pdfinfo sections.pdf | grep -E "^(Title|Author|Pages)"
Pages:           2
$ grep -n "hyperref" sections-meta.tex
6:\usepackage[pdfusetitle]{hyperref}% the title and author into the PDF's metadata
$ pdflatex -interaction=nonstopmode sections-meta.tex | grep -E "Output"
Output written on sections-meta.pdf (2 pages, 58807 bytes).
$ pdflatex -interaction=nonstopmode sections-meta.tex | grep -E "Output"
Output written on sections-meta.pdf (2 pages, 61348 bytes).
$ pdfinfo sections-meta.pdf | grep -E "^(Title|Author|Pages)"
Title:           Monsoon Notes
Author:          A. Rao
Pages:           2
$ typst compile sections.typ sections-typst.pdf
$ pdfinfo sections-typst.pdf | grep -E "^(Title|Author|Pages)"
Title:           Monsoon Notes
Author:          A. Rao
Pages:           1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 title_session.py

The plain LaTeX build printed “Monsoon Notes” on the page, yet pdfinfo found no Title and no Author. The copy that loads hyperref with the option pdfusetitle, which its manual says derives the PDF’s title and author from \title and \author, had both. You can also set them yourself with \hypersetup{pdftitle={…}, pdfauthor={…}}.

Typst keeps one title for both places. #set document(title: [Monsoon Notes], author: "A. Rao") writes the metadata, and the reference says that by default the title is not shown on the page; #title(), the title element, prints it. Typst’s accessibility guide asks for that set rule before any content, and pdfinfo found the title and author in the Typst PDF.

Where LaTeX Typst
On the page \title{…}, \author{…}, \date{}, then \maketitle #title()
In the PDF’s metadata \usepackage[pdfusetitle]{hyperref}, or \hypersetup{pdftitle={…}} #set document(title: […], author: "…")
PDF Metadata Viewer & Editor Open a PDF in your browser and read the title and author it carries, and how many bookmarks it has.

Screen readers follow the headings, so levels must not skip

Heading levels are a map: screen readers list the headings to jump between them, and tagged PDF stores each one with its level. Typst’s accessibility guide gives two rules for that map. Never use a heading for your document title; use the title element instead. Never skip a heading level when going deeper.

Choosing a level for its size

Wrong: === under = because a level 3 heading looks the right size. Right: choose the level from the structure, == under =, and change the size with a show rule such as #show heading.where(level: 2): set text(size: 11pt). LaTeX’s tagged PDF, which the accessibility module sets up, needs the same care.

Each of these three files breaks one rule. This session puts them through Typst’s export for PDF/UA-1, the standard for accessible PDF, which --pdf-standard ua-1 asks for:

A title typed as a heading, twice, and a skipped level

title/heading-title.typ

// heading-title.typ: the title typed as a level 1 heading, and no document title set.
#set text(lang: "en")
= Monsoon Notes
== Rainfall
Gauges were read every morning.

title/heading-meta.typ

// heading-meta.typ: the title set for the PDF's metadata, and still typed as a level 1 heading.
#set document(title: [Monsoon Notes])
#set text(lang: "en")
= Monsoon Notes
== Rainfall
Gauges were read every morning.

title/skipped.typ

// skipped.typ: a title, then a heading two levels below the one before it.
#set document(title: [Monsoon Notes])
#set text(lang: "en")
#title()
= Rainfall
=== Daily readings
Two days are missing.
Three files through Typst's PDF/UA-1 export Python · structure_session.py
from session import files, run

# Typst's PDF/UA-1 export against the two rules of its accessibility guide: heading-title.typ types its title as a
# level 1 heading and sets no document title, heading-meta.typ sets the document title and still types it as a
# heading, and skipped.typ goes from = straight to ===. Last, skipped.typ compiled without the standard.
files("title/heading-title.typ", "title/heading-meta.typ", "title/skipped.typ")
run(r"""
!typst compile --pdf-standard ua-1 heading-title.typ
typst compile --pdf-standard ua-1 heading-meta.typ
!typst compile --pdf-standard ua-1 skipped.typ
typst compile skipped.typ
""")

Output

$ typst compile --pdf-standard ua-1 heading-title.typ
error: PDF/UA-1 error: missing document title
 = hint: set the title with `set document(title: [...])`

[exit status 1]
$ typst compile --pdf-standard ua-1 heading-meta.typ
$ typst compile --pdf-standard ua-1 skipped.typ
error: PDF/UA-1 error: skipped from heading level 1 to 3
  ┌─ skipped.typ:6:0
  │
6 │ === Daily readings
  │ ^^^^^^^^^^^^^^^^^^
  │
  = hint: heading levels must be consecutive

[exit status 1]
$ typst compile skipped.typ

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 structure_session.py

heading-title.typ, whose title is a level 1 heading and which sets no document title, stopped with PDF/UA-1 error: missing document title. heading-meta.typ sets the document title and still types it as a level 1 heading, and the export accepted it without a word: the check finds a missing title, not a heading used as one, so the first rule is yours to keep. skipped.typ, which goes from = straight to ===, stopped with skipped from heading level 1 to 3 and the hint that heading levels must be consecutive. Compiled without the standard, the same file built without a word, so a skipped level is easy to miss.

Key idea

Headings carry the structure: a title element for the title, consecutive levels under it, and the look left to set and show rules.

Checkpoint

Check: The title on the page and in the metadata · Heading structure for screen readers

  1. Question 1 of 2 sections.tex prints "Monsoon Notes" with \maketitle, but pdfinfo shows no Title. Which change puts the title into the PDF's metadata?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Load hyperref with the option pdfusetitle

    \maketitle prints on the page; hyperref writes the metadata, from \title and \author with pdfusetitle or from \hypersetup{pdftitle=…, pdfauthor=…}.

  2. Question 2 of 2 What did typst compile --pdf-standard ua-1 skipped.typ report?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: An error: skipped from heading level 1 to 3, with the hint that heading levels must be consecutive

    Typst's accessibility guide says never to skip a heading level when going deeper, and its PDF/UA-1 export enforces it. A plain compile does not check.

Practise: outline a report

Number the headings of a report and list them to a depth, exactly as both systems did above. The sample tests use this lesson’s report, and the first hint is open:

Exercise · Medium · Python

Number the headings and list them to a depth

Write contents(headings, depth) in contents.py. It returns the lines of a table of contents the way the lesson's report printed them, with heading numbers like 1, 1.1 and 2.1.

Each heading is a tuple (level, title, numbered): level 1 for a section, 2 for a subsection, and so on. The rules are the ones both LaTeX and Typst followed in the lesson:

  • A numbered heading adds one to the count of its level and starts every deeper level again from zero, so the subsection after 2 Rivers is 2.1.
  • An unnumbered heading gets no number and changes no count.
  • Only headings whose level is depth or less are listed.
  • A line is the number, a space and the title, or the title alone for an unnumbered heading.

The report's headings, listed to depth 2, give ["Preface", "1 Rainfall", "1.1 Gauges", "1.2 Totals", "2 Rivers", "2.1 Flow"].

Starter code · contents.py

def contents(headings, depth):
    """Return the contents lines for a list of (level, title, numbered) headings, down to the given depth."""
    # Replace this comment: count each level, number the numbered headings, and keep those at depth or less.
    return [title for level, title, numbered in headings]
The sample tests · test_contents.py
from contents import contents

REPORT = [
    (1, "Preface", False),
    (1, "Rainfall", True),
    (2, "Gauges", True),
    (3, "Daily readings", True),
    (2, "Totals", True),
    (1, "Rivers", True),
    (2, "Flow", True),
]


def test_the_report_to_depth_2():
    """lists the lesson's report as both systems did with depth 2"""
    assert contents(REPORT, 2) == ["Preface", "1 Rainfall", "1.1 Gauges", "1.2 Totals", "2 Rivers", "2.1 Flow"]


def test_the_report_to_depth_1():
    """lists only the top level with depth 1"""
    assert contents(REPORT, 1) == ["Preface", "1 Rainfall", "2 Rivers"]


def test_the_report_to_depth_3():
    """numbers a third level as 1.1.1"""
    assert contents(REPORT, 3)[3] == "1.1.1 Daily readings"


def test_unnumbered_heading_changes_no_count():
    """keeps counting across an unnumbered heading of the same level"""
    headings = [(1, "Rain", True), (2, "Gauges", True), (1, "Notes", False), (2, "Totals", True)]
    assert contents(headings, 2) == ["1 Rain", "1.1 Gauges", "Notes", "1.2 Totals"]


def test_deeper_counts_start_again():
    """starts the subsection count again after each section"""
    headings = [(1, "A", True), (2, "x", True), (2, "y", True), (1, "B", True), (2, "z", True)]
    assert contents(headings, 2) == ["1 A", "1.1 x", "1.2 y", "2 B", "2.1 z"]
A hint

Keep one count per level in a list, counts = [0] * 10. For a numbered heading of level n, add one to counts[n - 1] and set every count after it to 0; its number is the counts up to that level joined with dots. Number every heading, then decide whether its level is deep enough to list.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Then try the questions on headings, contents and titles. They start with the part you know least, and a question about levels or depth that you miss comes back once before the end:

Practice that adapts to you

Practise this lesson

11 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 11 In the article class, what level is \subsection?

    Choose one answer.

    Show the answer: Question 1 of 11

    Answer: 2

    The level numbers decide what is numbered (secnumdepth) and what the contents list shows (tocdepth).

  2. Question 2 of 11 A new Typst file has = Introduction and == Aims, and no set rule. How are they printed?

    Choose one answer.

    Show the answer: Question 2 of 11

    Answer: Without numbers, because heading numbering defaults to none

    LaTeX numbers sectioning commands by default; Typst does the opposite, and a set rule turns numbering on for the whole document.

  3. Question 3 of 11 What does \section*{Acknowledgements} do, according to the LaTeX2e reference manual?

    Choose one answer.

    Show the answer: Question 3 of 11

    Answer: Prints the heading without a number and leaves it out of the table of contents

    A starred heading drops its number and its contents entry; \addcontentsline brings the entry back.

  4. Question 4 of 11 A Typst report needs a heading "Notes for markers" with no number and no entry in the outline. Which line does it?

    Choose one answer.

    Show the answer: Question 4 of 11

    Answer: #heading(numbering: none, outlined: false)[Notes for markers]

    In Typst the number and the entry are two separate settings of the heading: numbering and outlined.

  5. Question 5 of 11 Which file did pdfLaTeX write the report's contents entries into?

    Choose one answer.

    Show the answer: Question 5 of 11

    Answer: sections.toc

    \tableofcontents reads the .toc file at the start of the next run, which is why the list fills in on run 2.

  6. Question 6 of 11 A Typst outline should list = and == headings but not === ones. What value of outline's depth does that?

    Type a number.

    A hint

    Typst counts = as level 1.

    Show the answer: Question 6 of 11

    Answer: 2 levels

    #outline(depth: 2) lists levels 1 and 2. With the default, none, every heading is listed.

  7. Question 7 of 11 Why does sections.tex contain \date{}?

    Choose one answer.

    Show the answer: Question 7 of 11

    Answer: Without \date, \maketitle prints the current date under the title

    \title and \author fill the title block, \date adds a date (today's when it is missing), and \maketitle prints them.

  8. Question 8 of 11 A Typst file has #set document(title: [Field Report]) and nothing else about the title. What do readers see?

    Choose one answer.

    Show the answer: Question 8 of 11

    Answer: The title in the viewer's window title, from the metadata, but not on the page

    set document writes the metadata; #title() prints the same title on the page, so one title serves both places.

  9. Question 9 of 11 heading-title.typ typed its title as = Monsoon Notes and set no document title. What did PDF/UA-1 export say?

    Choose one answer.

    Show the answer: Question 9 of 11

    Answer: PDF/UA-1 error: missing document title, with the hint to set it with set document

    Use set document for the title in the metadata and #title() to print it; keep = for the top-level sections.

  10. Question 10 of 11 heading-meta.typ sets #set document(title: [Monsoon Notes]) and still types = Monsoon Notes as its first heading. What did the PDF/UA-1 export do?

    Choose one answer.

    Show the answer: Question 10 of 11

    Answer: It built the PDF without a word

    The PDF/UA-1 export enforces what it can check, a title in the metadata and consecutive heading levels. Whether the first heading is really the title is for you to see: print it with #title() and start the sections at =.

  11. Question 11 of 11 A Typst report puts === Methods directly under = Study because a level 3 heading has the size the designer wants. What do you change?

    Choose one answer.

    Show the answer: Question 11 of 11

    Answer: Make it == Methods and give level 2 headings the wanted size with a show-set rule

    Choose heading levels from the structure, and change how a level looks with a show rule such as #show heading.where(level: 2): set text(size: 11pt).

Interview questions

Warm-up (fresher to 2–5 years): how does \section* differ from a Typst heading with numbering: none?

Both print a heading without a number, and neither changes the count, so the next numbered heading continues where the last one left off. They differ in the contents list. LaTeX’s starred form also leaves the heading out of the table of contents, and with hyperref it gets no bookmark; to list it, add \addcontentsline{toc}{section}{Preface} right after it. A Typst heading with numbering: none stays in the outline, because outlined is true by default; to drop it, add outlined: false. So a preface listed in the contents takes two lines in LaTeX and one in Typst, and a heading kept out of the contents takes one line in LaTeX and needs outlined: false in Typst.

Two harder questions follow, about a university’s rules for the contents and title of a thesis and about an accessibility check that fails. Decide where you would start before you open each first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A university's thesis rules say: the abstract and acknowledgements appear in the contents without numbers, the contents lists chapters, sections and subsections only, and the PDF's title field shows the thesis title. Set this up in LaTeX with the report class, and say how you check each rule in the finished PDF.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    An accessibility check rejects your Typst report with two findings: the document has no title, and a heading level is skipped. The PDF looks fine to you. Explain both findings, fix the source, and make sure neither comes back in later versions of the report.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • LaTeX headings are sectioning commands with level numbers, numbered down to secnumdepth; Typst headings are = signs, numbered only after #set heading(numbering: "1.1").
  • A starred LaTeX heading has no number and no contents entry until \addcontentsline; a Typst heading without numbering stays in the outline unless outlined is false, and neither changes the count.
  • \tableofcontents fills in on the second run, from the .toc file; tocdepth and Typst’s outline(depth:) set how many levels are listed, counted from level 0 for chapters in LaTeX.
  • \maketitle prints the title, but only hyperref (pdfusetitle or pdftitle) puts it into the PDF’s metadata; Typst’s set document writes the metadata and #title() prints it.
  • Use the title element for the title and never skip a heading level: Typst’s PDF/UA-1 export stops on a missing document title and on a skipped level, but a heading typed as the title is yours to catch.

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. How many heading levels does LaTeX number by default?

    Three in article (section, subsection and subsubsection), because secnumdepth is 3 there; in report and book it is 2.

  2. Are Typst headings numbered by default?

    No: numbering defaults to none. #set heading(numbering: "1.1") gives numbers such as 1, 1.1 and 1.1.1.

  3. How do you list an unnumbered LaTeX heading in the contents?

    Add \addcontentsline{toc}{section}{Preface} after \section*{Preface}: the starred form alone has no number and no contents entry.

  4. How do you keep an unnumbered Typst heading out of the outline?

    #heading(numbering: none, outlined: false)[Preface]. Without outlined: false, an unnumbered heading is still listed.

  5. Why is a new LaTeX contents list empty after the first run?

    \tableofcontents reads its entries from the .toc file that the previous run wrote, so the list appears on the second run.

  6. What does tocdepth 2 list in the report class, compared with Typst's outline(depth: 2)?

    In report, \chapter is level 0, so tocdepth 2 reaches \subsection, three levels down; Typst's depth 2 stops at ==.

  7. How do you put a document's title into the PDF's metadata?

    In LaTeX, load hyperref with pdfusetitle or set pdftitle with \hypersetup, because \maketitle only prints it. In Typst, #set document(title: ...) writes it and #title() prints it.

  8. What does Typst's PDF/UA-1 export refuse about headings?

    A missing document title and a skipped heading level, such as = followed by ===. A heading typed as the title passes, so that rule is yours to keep.

Terms in this lesson

PDF metadata
What a PDF records about itself, such as its title and author, which viewers show in the window title and pdfinfo prints. LaTeX writes it through hyperref, Typst through set document.
sectioning command
A LaTeX command that starts a heading at one level, such as \section or \subsection. Its level number decides whether the heading is numbered (secnumdepth) and whether the table of contents lists it (tocdepth).
tocdepth
The LaTeX counter that sets the deepest heading level the table of contents lists, as in \setcounter{tocdepth}{2}. Its partner secnumdepth sets the deepest level that is numbered.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.