Typography#
bootstack uses a token-based font system. Every widget that accepts a font=
argument takes a token string such as "body" or "heading-lg". Tokens
resolve to named fonts at startup and update automatically when the theme
changes, so you style text by role rather than by hard-coding families and
sizes. A small set of functions adjusts those tokens at runtime.
Platform defaults#
bootstack picks native UI and monospace families for each platform so an app looks at home on every desktop, and tunes the base point size so text appears the same visual size everywhere:
Platform |
UI family |
Monospace family |
Base size |
|---|---|---|---|
Windows |
Segoe UI |
Cascadia Mono |
11 pt |
macOS |
SF Pro Text |
Menlo |
13 pt |
Linux / other |
DejaVu Sans |
DejaVu Sans Mono |
11 pt |
The macOS base is two points larger because Tk converts points to pixels against a 72 DPI baseline there — versus roughly 96 DPI on Windows and Linux — which would otherwise render the same point size noticeably smaller. The bump keeps text at visual parity across platforms and aligns with the macOS control-text size.
Override the family with set_font_family() (see
Setting the application font family); if a requested family is not installed,
bootstack keeps the current fonts rather than falling back silently.
Font tokens#
All sizes shown are relative to the base size for the platform (see Platform defaults). The actual pixel size also varies with DPI scaling.
Token |
Approx size |
Weight |
Typical use |
|---|---|---|---|
|
base+17 |
bold |
Hero headings, splash screens |
|
base+11 |
bold |
Page titles, large callouts |
|
base+7 |
bold |
Section headings |
|
base+4 |
bold |
Sub-section headings |
|
base+2 |
bold |
Card titles, dialog headers |
|
base+1 |
bold |
Group labels, compact headings |
|
base+1 |
normal |
Emphasized body text |
|
base |
normal |
Default widget text |
|
base-1 |
normal |
Secondary text, hints |
|
base-2 |
normal |
Timestamps, metadata, fine print |
|
base-2 |
bold |
Form labels, table headers |
|
base (mono) |
normal |
Code, paths, identifiers |
|
base |
normal |
Inline links (underlined) |
Modifiers#
Append bracket modifiers to any token. Multiple modifiers can be chained.
Heading tokens are already bold — adding [bold] is a no-op.
Modifier |
Effect |
|---|---|
|
Bold weight |
|
Italic slant |
|
Underlined text |
|
Strikethrough |
|
Increase size by N points relative to the token base |
|
Decrease size by N points relative to the token base |
|
Set absolute size to N points |
|
Set absolute size to N pixels (negative points in Tk) |
bs.Label("Italic caption", font="caption[italic]")
bs.Label("Underlined body", font="body[underline]")
bs.Label("Slightly larger", font="body[+2]")
bs.Label("Slightly smaller", font="body[-1]")
bs.Label("Bold italic body", font="body[bold][italic]")
bs.Label("Heading italic", font="heading-md[italic]")
Size delta#
[+N] and [-N] shift the size relative to the token’s own base, not
the global base size. body[+2] is always 2 pt larger than body,
regardless of theme or DPI scaling.
Use absolute sizes ([14]) only when you need a specific measurement
independent of the token scale — for example, matching an external design spec.
Setting the application font family#
set_font_family switches every text token to one family in a single call.
The monospace code token keeps its own family so code stays readable; pass
mono_family= to change it too:
from bootstack.style import set_font_family
import bootstack as bs
with bs.App() as app:
set_font_family("Inter") # body, headings, labels…
set_font_family("Inter", mono_family="Fira Code")
app.run()
If the requested family is not installed, a warning is issued and the current fonts are left unchanged — text never silently falls back to an unexpected face.
Adjusting a font token#
update_font_token overrides individual attributes of a single token.
Only the attributes you pass change; the rest are left as is. The update
applies everywhere the token is used:
from bootstack.style import update_font_token
update_font_token("body", size=12) # larger default text
update_font_token("code", family="JetBrains Mono")
update_font_token("caption", slant="italic")
Listing installed families#
get_font_families returns the installed UI families, sorted and filtered of
system and emoji fonts — ready to populate a font picker or to validate input
before calling set_font_family:
from bootstack.style import get_font_families
with bs.App() as app:
families = get_font_families()
bs.Select(options=families, signal=bs.Signal(families[0]))
app.run()
Choosing a font interactively#
ask_font opens a font selector and returns the user’s choice as a
FontChoice — a plain namedtuple with family, size, weight,
slant, underline, and overstrike fields (or None if canceled):
choice = bs.ask_font(default_font="body")
if choice:
update_font_token("body", family=choice.family, size=choice.size)
See also#
Theming — colors, accents, and custom themes.
Font Dialog — the
ask_font()/FontDialogselector.Label— the primary widget for displaying text with font tokens.
API reference#
The complete reference for the runtime font functions lives in Theming (alongside the theme functions). At a glance:
Set the application font family for all non-code text. |
|
Override the attributes of a single font token. |
|
Return the installed UI font families available to the application. |
The FontChoice result type returned by ask_font() is documented with the
Font Dialog selector.