Beautiful theorem-like environments with 9 distinctive styles and a French math preset.
Gallery
| Classic |
Modern |
| Elegant |
Colorful |
| Boxed |
Minimal |
| Academic |
QED Symbols |
| BW (French B&W course) |
Cours (French course) |
| French Math Preset & New Features |
|
| Trous (student and instructor builds) |
|
| Header Layout & Per-Environment Colors |
|
Features
- 9 distinct styles: classic, modern, elegant, colorful, boxed, minimal, academic, bw, cours
- 6 variants per style: prominent, standard, subtle, accent, minimal, inline
- Flexible mapping: Assign any variant to any environment type
- Independent counters: Each environment type has its own counter
- Customizable labels: Change “Theorem” to “Théorème”, “Satz”, etc.
- QED symbol presets: □, ■, ∎, CQFD, //, Q.E.D.
- Color themes: Pre-built themes (ocean, forest, sunset, lavender)
- Language presets: French, German, Spanish
- French Math Preset: one-call setup for French secondary math courses
- QR sidebar: attach a QR code column to any environment
- Environment references: label theorem-like blocks and link back to their page
- Section-linked numbering: LaTeX
\numberwithin-style “Theorem 2.1.3” with per-section reset (opt-in) - Instructor mode: one source, two documents — corrections and instructor-only blocks hidden in the student build
- Student fill space: blank, ruled lines, or dot grid appended inside any environment
- Per-environment colors everywhere:
env-colors: truemakes every variant of every style followtheorem-color,example-color, … (not just theaccentvariants);label-color: "base"paints the header in the same colour - Perceptual background tints:
background-tint: autolightens each colour until it reaches the same perceived lightness, so a yellow tint reads as strongly as a green one - Header layout:
header-layout: "title-first"/"title-abbrev"/"title-only"/"prefix"swaps the emphasis so the title leads and the label follows (What is analysis? (Rem 2),Rem 2: What is analysis?), in every style - Trous:
#trou[...]reserves space sized to the hidden content (scaled by a handwriting factor, snapped to the ruling) in the student build and prints that content in the instructor build;#trou-inline[...]blanks a single word - Print-friendly modes: color, grayscale, black & white
Quick Start
#import "@preview/beautiframe:0.4.5": *
#theorem(name: "Pythagorean")[
In a right triangle: $a^2 + b^2 = c^2$
]
#definition[
A *limit* is the value that a function approaches.
]
#proof[
The proof is left as an exercise.
]
Environments
| Environment | Default Variant | Counter | Notes |
|---|---|---|---|
theorem |
prominent | Optional | Main results |
definition |
standard | Optional | Foundational concepts |
lemma |
standard | Optional | Supporting results |
proposition |
standard | Optional | Secondary results |
corollary |
standard | Optional | Consequences |
remark |
subtle | Optional | Commentary |
example |
accent | Optional | Illustrations |
proof |
(special) | No | Ends with QED |
All environments support optional numbering via the number parameter.
All environments accept title: as a synonym for name: (backward compat).
All environments accept label: for cross-references with env-ref.
French Math Environments
The following environments are available after #preset-french-math() or #preset-french-math-bw():
| Environment | Label | Base | Numbered |
|---|---|---|---|
theoreme |
Théorème | theorem | Yes |
definitionfr |
Définition | definition | Yes |
propositionfr |
Proposition | proposition | Yes |
exemplefr / exemple |
Exemple | example | Yes |
remarque |
Remarque | remark | No |
corollaire |
Corollaire | corollary | Yes |
preuve |
Preuve | proof | No |
pratique |
En pratique | example | Yes |
guided-example |
Exemple guidé | example | Yes |
propriete |
Propriété | corollary | No |
formule |
Formule | lemma | Yes |
formules(...) |
Formules (plural) | lemma | Yes |
methode |
Méthode | proposition | Yes |
notation(...) |
Notation | remark | No |
discussion(...) |
Discussion | remark | No |
Numbering Control
// Automatic numbering (default for most)
#theorem[Theorem 1]
#theorem[Theorem 2]
// No numbering
#theorem(number: none)[Unnumbered theorem]
// Custom number
#theorem(number: "A")[Special theorem A]
// title: alias for name:
#theorem(title: "Pythagorean")[...]
Section-linked numbering (LaTeX \numberwithin style) is opt-in and also applies
to new-env custom environments such as formule:
#set heading(numbering: "1.1.")
#beautiframe-setup(
link-to-section: true, // true = 1 heading level; an int N = N levels ("2.1.3")
counter-reset: "section", // restart counters at each heading up to that depth
)
= Première section
#formule[$a^2 + b^2 = c^2$] // Formule 1.1
#formule[$e^(i pi) = -1$] // Formule 1.2
= Deuxième section
#formule[$sin^2 + cos^2 = 1$] // Formule 2.1
env-ref/env-refs display the same section-linked numbers.
References
Add a Typst label to any environment, then reference it with #env-ref(<label>).
The reference text includes the environment label, number, and target page, and the whole text links to the labelled block.
#theorem(label: <thm-pythagore>, title: "Pythagore")[
Dans un triangle rectangle: $a^2 + b^2 = c^2$.
]
Voir #env-ref(<thm-pythagore>).
// -> Théorème 1 (p. 3)
#remark(label: <rem-unites>)[Attention aux unités.]
Voir #env-ref(<rem-unites>).
// -> Remark (p. 3)
Use page: false to hide the page number: #env-ref(<thm-pythagore>, page: false).
Use page-style: "comma" when the reference already sits inside parentheses:
(voir #env-ref(<thm-pythagore>, page-style: "comma")).
Use env-refs for several environments. Consecutive references with the same label are compacted:
#pratique(label: <prac-3>)[...]
#pratique(label: <prac-4>)[...]
#pratique(label: <prac-5>)[...]
#pratique(label: <prac-6>)[...]
Voir #env-refs(<prac-3>, <prac-4>, <prac-5>, <prac-6>, page: false).
// -> En pratique 3-6
Voir #env-refs(<prac-3>, <prac-4>, <prac-5>, <prac-6>, page-style: "comma").
// -> En pratique 3-6, pp. 4-5
#definition(label: <def-limite>)[...]
#proposition(label: <prop-limite>)[...]
Voir #env-refs(<def-limite>, <prop-limite>, page: false).
// -> Définition 1 et Proposition 2
Style Selection
#beautiframe-setup(style: "modern")
// Available: classic, modern, elegant, colorful, boxed, minimal, academic, bw, cours
French Math Preset
One-call setup for French secondary math courses:
#import "@preview/beautiframe:0.4.5": *
// Color version (cours style, blue accent, bold labels, QED square)
#preset-french-math()
// Black-and-white version (bw style, 8.4pt labels, luma palette)
#preset-french-math-bw()
// Reset all counters (including custom French envs)
#beautiframe-reset-french-math()
// Use French environments
#theoreme(name: "Pythagore")[Dans un triangle rectangle: $a^2 + b^2 = c^2$]
#definitionfr[Une fonction continue préserve les limites.]
#pratique[Calculer la dérivée de $f(x) = x^3 - 2x$.]
#worked-exercise(correction: [On obtient $f'(x)=3x^2$.])[
Calculer la dérivée de $f(x)=x^3$.
]
#guided-example(title: "Méthode guidée")[On détaille chaque étape.]
#formule[Les solutions de $a x^2 + b x + c = 0$ sont $x = (-b plus.minus sqrt(b^2 - 4 a c)) / (2 a)$.]
#preuve[Par définition de la continuité.]
worked-exercise displays its correction: only when beautiframe-setup(instructor-mode: true) is active. Configure correction-renderer: (title, body) => ... to use a custom correction style.
QR Sidebar
Attach a QR code (or any content) in a right sidebar to any environment:
// Configure once in your preamble (using tiaoma or any renderer):
#beautiframe-setup(
qr-renderer: url => image(tiaoma.qrcode(url), format: "svg", width: 1.85cm),
qr-width: 1.85cm,
)
// Then use qr: on any environment:
#theorem(qr: "https://example.com/proof")[
In a right triangle: $a^2 + b^2 = c^2$
]
The qr-renderer receives the URL string and returns content placed in a right sidebar column of width qr-width.
Student Fill Space
Append blank space for students to write in, inside any environment:
// Blank area
#pratique(space: "empty", space-height: 3cm)[Solve for x.]
// 8mm ruled lines
#pratique(space: "lines", space-height: 4cm)[Show your work.]
// 5mm dot grid
#exemple(space: "grid", space-height: 5cm)[Sketch the function.]
space: values: "empty" (blank), "lines" (8mm ruled lines), "grid" (5mm dot grid).
Default space-height is 3cm.
Trous
Where space: appends anonymous blank space, a trou carries the content the
class is meant to produce there. It prints as reserved space in the student
build and as the content itself in the instructor build, from one source file:
#trou[La suite 1/n tend vers 0 sans jamais l'atteindre.]
#trou(hint: [contre-exemple])[La fonction de Dirichlet.]
#trou(fill: "lines", height: 4cm)[Esquisse du graphe.]
Une fonction #trou-inline[continue] sur [a; b] atteint ses bornes.
// The instructor build is one switch away
#beautiframe-setup(instructor-mode: true)
The reserved height is measured from the hidden content and multiplied by
trou-scale (default 2.0), because a hand needs about twice the room typeset
text occupies; with fill: "lines" it then snaps up to a whole number of
trou-line-gap rules. An explicit height: is used as given. Every style
renders trous in its own visual language.
Header Layout
Which half of the header carries the emphasis:
#beautiframe-setup(header-layout: "label-first") // Remark 2 (What is analysis?) [default]
#beautiframe-setup(header-layout: "title-first") // What is analysis? (Remark 2)
#beautiframe-setup(header-layout: "title-abbrev") // What is analysis? (Rem 2)
#beautiframe-setup(header-layout: "title-only") // What is analysis?
#beautiframe-setup(header-layout: "prefix", label-abbrev: true) // Rem 2: What is analysis?
Only environments that have a title are affected, and every style follows.
Variant Mapping
Assign any variant to any environment type:
#beautiframe-setup(
theorem-variant: "prominent", // Strongest emphasis
definition-variant: "standard", // Normal styling
remark-variant: "inline", // Flows with text
example-variant: "accent", // Uses environment color
)
Set all 7 variants at once with default-variant; individual params override it:
// All environments use boxed, except theorems which stay prominent
#beautiframe-setup(default-variant: "boxed", theorem-variant: "prominent")
Available variants: prominent, standard, subtle, accent, minimal, inline
BW style has additional variants: boxed (light rect), prominent (thicker rect), accent (env-color)
Boxed style has 4 additional variants: titled, centered, corner, corner2
QED Symbols
#qed-square() // □ (default)
#qed-filled() // ■
#qed-tombstone() // ∎
#qed-cqfd() // CQFD
#qed-slashes() // //
#qed-text() // Q.E.D.
#qed-none() // (none)
// Custom symbol (use size: 1.4em for consistency)
#beautiframe-setup(qed-symbol: text(size: 1.4em, fill: green, sym.checkmark))
Language Presets
#preset-french() // Théorème, Définition, Preuve...
#preset-german() // Satz, Definition, Beweis...
#preset-spanish() // Teorema, Definición, Demostración...
#preset-english() // Back to the built-in Theorem, Definition, Proof...
Color Themes
#theme-ocean() // Blue tones
#theme-forest() // Green tones
#theme-sunset() // Red/orange tones
#theme-lavender() // Purple tones
Print-Friendly Modes
#beautiframe-setup(color-mode: "color") // Full color (default)
#beautiframe-setup(color-mode: "grayscale") // Grayscale
#beautiframe-setup(color-mode: "bw") // Pure black and white
Configuration Reference
See the full manual for complete API documentation.
#beautiframe-setup(
style: "classic", // classic, modern, elegant, colorful, boxed, minimal, academic, bw, cours
// Variant mapping (default-variant sets all 7; individual params override)
default-variant: none,
theorem-variant: "prominent",
definition-variant: "standard",
lemma-variant: "standard",
remark-variant: "subtle",
example-variant: "accent",
// Colors
accent-color: rgb("#2980b9"),
theorem-color: rgb("#c0392b"),
definition-color: rgb("#2980b9"),
env-colors: false, // true = every variant follows the per-environment colors
label-color: auto, // auto | "base" | a color
background-tint: auto, // auto = perceptual, or a ratio like 92%
background-lightness: 0.93, // target lightness of tints, 0..1
// Header layout
header-layout: "label-first", // label-first, title-first, title-abbrev,
// title-only, prefix
label-abbrev: false, // demote labels to theorem-abbrev, remark-abbrev, ...
prefix-separator: ":",
// Trous
trou-fill: "empty", // empty, lines, grid
trou-scale: 2.0, // handwriting factor on the measured height
trou-line-gap: 8mm,
trou-frame: true,
trou-min-height: 1cm,
// Typography
label-size: 1em, // Defaults to body font size
label-weight: "bold",
// Layout (classic style)
line-position: 2cm,
label-extra: 1cm,
border-width: 1pt,
// Labels
theorem-label: "Theorem",
proof-label: "Proof",
// QED
qed-symbol: sym.square.stroked,
// Print mode
color-mode: "color",
// QR sidebar
qr-renderer: none, // url => content function, or none
qr-width: 1.85cm, // Width of the QR sidebar column
)
Utility Functions
#beautiframe-reset() // Reset all built-in counters to 0
#beautiframe-reset-french-math() // Reset built-in + French env counters
#reset-env("Conjecture") // Reset a specific custom env counter
#beautiframe-reset-config() // Reset every setting to its default value
Changelog
[0.4.5] - 2026-08-19
Added
- Trous (
trou,trou-inline): fill-in space that carries its content. The student build prints reserved, correctly sized blank space; the instructor build (instructor-mode: true) prints the content itself, flagged in the accent colour. The reserved height is measured from the hidden content and multiplied bytrou-scale(default2.0, a handwriting factor), then snapped to a whole number oftrou-line-gaprules whenfill: "lines"; an explicitheight:is used as given. Per-callheight,scale,fill("empty"/"lines"/"grid"),frame,hint,min-height,padding; configtrou-fill,trou-scale,trou-line-gap,trou-frame,trou-color,trou-padding,trou-min-height,trou-max-height,trou-hint-size,trou-mark-instructor,trou-mark-color. Every style renders trous in its own visual language, and a trou inside an environment body drops the label-column layout since that column is already taken. header-layoutredistributes the two halves of an environment header:"label-first"(default, Remark 2 (What is analysis?)),"title-first"(What is analysis? (Remark 2)),"title-abbrev"(What is analysis? (Rem 2), the label always abbreviated whateverlabel-abbrevsays),"title-only"(What is analysis?, label and number dropped from the header while the counter still advances, soenv-refkeeps working),"prefix"(Rem 2: What is analysis?, separator configurable viaprefix-separator). It applies to every style, since the redistribution happens before the style is called, and only to environments that have a title.label-abbrev: truedemotes labels to the short formstheorem-abbrev,definition-abbrev,lemma-abbrev,proposition-abbrev,corollary-abbrev,remark-abbrev,example-abbrev,proof-abbrev. Plural forms andnew-envcustom labels are never abbreviated.preset-french()sets the French forms and a narrow non-breaking space before the"prefix"colon.env-colors: truemakes every variant of every style follow the per-environment colours (theorem-color,example-color, …), not just theaccentvariants. Styles now read the resolvedbase-colorinstead ofaccent-color, so the setting is honoured everywhere, boxed-only variants (titled,centered,corner,corner2) included.beautiframe-reset-config()restores every setting to its default. Configuration is global and cumulative, so this is the way back after a style, preset or theme has been applied — each chapter of a document can start from a known state.preset-english()restores the built-in English labels, plurals and abbreviations — the way back frompreset-french(),preset-german(),preset-spanish()orpreset-french-math().label-colorcontrols header ink:auto(each style’s own choice, default),"base"(follow the environment colour), or an explicit colour.
Fixed
- Proof label and body sat on different baselines. The QED symbol is set at
1.4em, taller than the text it ends, which grew the line box and dropped the body line relative to the label standing in its own column. The symbol is now placed in a zero-height box: it still sits on the baseline but no longer dictates the height of the line.
Changed
- Background tints of filled boxes are now perceptual by default (
background-tint: auto): each colour is lightened by however much it takes to reachbackground-lightness(default0.93), so a yellow tint no longer vanishes where a green shouts. Setbackground-tintto a ratio (e.g.92%) for the previous fixed lightening.
[0.4.0] - 2026-07-14
Added
- References:
env-ref(<label>)andenv-refs(<a>, <b>, ...)link to any labelled environment, displaying its label, number and page (“théorème 2, p. 5”). Consecutive references of the same type are compacted into ranges (“formule 1.1-1.2”). Options:page,page-style,page-prefix,lower-label,missing,separator,last-separator. Aliasesenvref/envrefs. - Section-linked numbering (LaTeX
\numberwithinstyle), opt-in:link-to-sectionnow accepts an integer depth in addition totrue:true= one heading level (“Théorème 2.3”),N= first N levels (“Formule 2.1.3” withlink-to-section: 2).counter-reset: "section"is now implemented: every environment counter restarts after each heading up to thelink-to-sectiondepth (level 1 when the prefix is off). Default"manual"unchanged.- Both settings now also apply to
new-envcustom environments (formule,methode,pratique, …), which previously ignored them. env-ref/env-refsdisplay the same section-linked numbers.
instructor: falseparameter on every environment (built-ins andnew-envcustoms): the whole block is only rendered wheninstructor-mode: true, in addition to the existing per-correctiongating.
Fixed
bwstyle: boxed/prominent variants now honour the configuredinsetinstead of a hardcoded value.
[0.3.1] - 2026-05-27
Added
worked-exercise: new environment for instructor-controlled correction reveal — shows correction only whenbeautiframe-setup(instructor-mode: true)is active; customizable viacorrection-rendererguided-example: new “Exemple guidé” environment for step-by-step demonstrationsinstructor-mode/correction-label/correction-renderer: newbeautiframe-setupparameters for worked exercise supportlower-labelparameter onenv-ref/env-refs: renders environment label in lowercase (e.g. “le théorème 3” vs “Théorème 3”)- Manual: document
defi/défichallenge callout with parameter table and live examples - Manual: document
formule-end/formules-recapformula recap workflow - Manual: document
objectifs,concepts,glossairecourse meta-environments
Fixed
- Replace deprecated
patternwithtiling(removed in Typst 0.15.0) - Remove “Typst” from package description (redundant on Typst Universe)
v0.3.0
default-variant: new parameter onbeautiframe-setup()— sets all 7 environment variants at once; individual*-variantparams override it- Documentation: comprehensive manual expansion — full API reference with all spacing params,
title:alias documentation,notation/discussion/pratiquelive examples,default-variantsection with live gallery
v0.2.0
- New styles:
bw(Gymnomath black-and-white two-column) andcours(French course style with margin overhang) - French Math Preset:
#preset-french-math()and#preset-french-math-bw()for one-call setup - French environments:
theoreme,definitionfr,propositionfr,exemplefr,remarque,corollaire,preuve,pratique,propriete,formule,formules,methode,notation,discussion - QR sidebar:
qr-rendererconfig +qr:parameter on all environments - Student fill space:
space: "empty"|"lines"|"grid"andspace-height:on all environments title:alias: synonym forname:on all environments- Default label-size: changed from
11ptto1em(inherits document body font) beautiframe-reset-french-math(): resets all counters including French custom envs- Bug fix: fill-space lines calculation with length arithmetic
v0.1.0 (2026-01-28)
- Initial release
- 7 styles: classic, modern, elegant, colorful, boxed, minimal, academic
- 6 core variants: prominent, standard, subtle, accent, minimal, inline
- Boxed style extras: titled, centered, corner, corner2
- QED symbol presets: square, filled, tombstone, CQFD, slashes, Q.E.D.
- Language presets: French, German, Spanish
- Color themes: ocean, forest, sunset, lavender
- Print modes: color, grayscale, bw
- Optional numbering for all environments
License
MIT