Repository navigation
design patterns
Audience: contributors to Blacksmith and authors of closely-integrated modules.
How the stylesheets and scripts are organized, what the naming conventions are, and the cross-cutting layers -- stacking order and motion.
Token values are on the Design tokens page; component classes are on the Design components page.
module.json loads two entries: styles/default.css and styles/notes-gm.css. Everything else arrives
through default.css, which @imports 50 stylesheets. vars.css is the first import, so tokens are
defined before any component stylesheet consumes them.
notes-gm.css is the exception -- Foundry loads it directly, not through the import chain.
Every stylesheet on disk is reached by one of the two mechanisms. node tools/check-styles-loaded.mjs
verifies that, and the reverse -- that every @import names a file that exists.
styles/ holds 54 files, grouped by the surface they style.
| Family | Covers |
|---|---|
vars.css |
Global design tokens. Loads first. |
default.css |
The import manifest. No rules of its own. |
common.css |
Root layout variables, chat and scene cleanup |
cards-*.css |
Chat cards -- -common-layout is structure, -common-themes is color only |
window-*.css |
Application windows, dialogs, form controls, tooltips |
menubar*.css |
Primary menubar, secondary bars, combat bar |
toolbar*.css |
Toolbars and zones |
timer-*.css |
Combat, planning, and round timers |
sidebar-*.css |
Sidebar surfaces |
journal-*.css |
Journal tools, pins, toolbars |
pins.css |
Canvas pins |
The rest are per-feature: toast.css, settings.css, combat-tools.css,
latency.css, loading-progress.css, menu-context-global.css,
tabs-scenes.css, utility-quickview.css, notes-gm.css, widget-tags.css.
Two conventions hold across the tree. cards-common-themes.css contains color declarations only -- no
radius, spacing, typography, or layout -- which is what makes a new theme a pure color exercise. And
vars.css carries no component-specific rules (styles/vars.css:16).
Two prefixes are in live parallel use: .blacksmith-* (345 distinct classes) and .cpb-* (141). Broadly
blacksmith- covers canvas, menubar, window and card surfaces while cpb- covers dialog chrome and
skill-check UI, but the split is historical rather than semantic. New components use blacksmith-.
A third prefix, .bsw-* (15 classes), is used only by the tag widget --
templates/partials/tag-widget.hbs and styles/widget-tags.css. It is scoped to that one component.
The .bh- prefix appears nowhere in styles/, templates/, or scripts/.
BEM double-underscore syntax is not used -- there are no .block__element selectors. Variants and states
are separate classes applied alongside the base: .is-active, .active, .collapsed, .expired,
.dragover, .hidden, .tool-active, .has-secondary, .section-dark, .label-dimmed,
.label-highlighted.
Global tokens are --blacksmith-[category]-[step] and live in vars.css. Component-scoped properties
follow --blacksmith-[component]-[property] and live with their component: --blacksmith-card-* (16),
--blacksmith-menubar-* (32), --blacksmith-pin-* (6). One family predates the convention and keeps
its own shape: --secondary-bar-* (6).
Nine legacy Hungarian-notation properties remain in styles/common.css:8-16 --- --intChatSpacing,
--strHideRollTableIcon, and the --strScene* set. They are written by the scene-title and chat settings
handlers.
scripts/ holds 94 files in these families:
| Prefix | Count | Covers |
|---|---|---|
manager-* |
24 | Long-lived subsystem controllers |
utility-* |
13 | Shared helpers |
api-* |
12 | Public API surfaces |
window-* |
11 | ApplicationV2 window classes |
ui-* |
8 | UI injection and augmentation |
timer-* |
4 | Combat, planning, round timers |
registry-* |
4 | Registries, including JSON import |
stats-*, pins-*, asset-*
|
2 each | Statistics, canvas pins, asset loading |
The remainder are single-purpose files named for what they are: blacksmith.js (entry point and API
assembly), const.js, xp-manager.js, token-movement.js, theme-request-roll.js, widget-tags.js,
canvas-layer.js, compendium-types.js, pin-permission-icons.js, prompt-builder-actors.js.
Function naming follows get[Thing], set[Thing], and build[Component], all well populated. Variables
carry Hungarian prefixes in older code -- str, int, bln.
templates/ holds 41 root templates plus templates/partials/. Cards are card-*.hbs with shared
structure in cards-common.hbs; windows are window-*.hbs; timers are timer-*.hbs. Feature templates
are named for their feature (menubar.hbs, encounter-toolbar.hbs). Three windows used to be named the
other way round -- movement-window.hbs, vote-window.hbs, journal-tools-window.hbs -- and were renamed
to the prefix form so the convention has no exceptions to remember.
Partials are registered with Handlebars.registerPartial under an explicit alias rather than by path --
the shared header is registered as partial-unified-header (scripts/blacksmith.js:841-844). Template
preloading uses foundry.applications.handlebars.loadTemplates; the bare v1 globals are deprecated in
v13 and are not used in this module.
Stacking is assigned in tiers rather than ad hoc.
| Layer | Used by |
|---|---|
-1 |
Menubar backdrop (styles/menubar.css:463) |
1 - 5
|
In-card and in-pin layering |
10, 99
|
Local element stacking, menubar internals |
100 |
Primary menubar (styles/menubar.css:115), tag widget, JSON import window |
120 |
Combat bar (styles/menubar-combatbar.css:464) |
999 - 1000
|
Cinematic roll overlay, encounter toolbar, quick view |
10000 |
Global context menu, loading progress, toasts, cinematic top layer |
10001 |
Above the toast stack (styles/toast.css:143) |
100000 |
Pin interaction layer (styles/pins.css:90) |
999999 |
Global context menu top layer (styles/menu-context-global.css:21) |
Layer 100 is shared by three components rather than reserved for the menubar, and the combat bar sits
above it at 120.
Transitions cluster on a small set of durations -- 0.15s ease and 0.2s ease dominate, with
0.2s ease-out, 0.3s ease, 0.3s ease-out, and 0.4s ease-out also common. Other combinations appear
in smaller numbers across the tree, so this is the prevailing set rather than an enforced one.
Named animations are defined near the components that use them: pin animations in styles/pins.css:366-569
(ten blacksmith-pin-* keyframes), card and roll animations in styles/cards-parts.css,
cinematic roll animations in styles/window-roll-cinematic.css, and per-feature keyframes in
styles/menubar.css, styles/menubar-combatbar.css, styles/timer-planning.css,
styles/window-token-movement.css, styles/combat-tools.css, styles/loading-progress.css,
styles/window-common.css, styles/window-json-import.css, and styles/window-skillcheck.css.
- Ai assistance
- Applicationv2 traps
- CSS traps
- dnd5e conditions
- Documentation standard
- Token actor identity
- Campaign
- Canvas
- Chatcards
- Compendiums
- Contextmenu
- Core
- Create journal entry
- Dialog
- Active Effects
- Entity list
- Equiplocations
- Geography
- GM request
- Gmnotes
- Health
- Hookmanager
- Importer
- Inventory
- Menubar
- Notes
- Party
- Pins
- Quantity split
- Requestroll
- Rolls
- Scene config
- Sockets
- Stats
- Tags
- Toast
- Tokens
- Toolbar
- Window
- Worldclock