Visual Elements
Callouts highlight critical information. Images bring pages to life. This chapter covers both — from basic syntax to full-bleed artwork and print-safe image requirements.
Callouts
Callouts draw attention to important information. Gutterpress ships GitHub's
alert syntax as the bundled Callouts feature (gutterpress-gfm-alerts,
issue #237) — turn it on from the recommended-features list. It is off by
default, so a book that never asked for callouts keeps rendering > [!NOTE]
exactly as it always did: a plain blockquote whose first line is the marker.
Five types, the GitHub set, matched case-insensitively:
> [!NOTE]
> General information, context, or explanations.
> [!TIP]
> Helpful advice, shortcuts, or best practices.
> [!IMPORTANT]
> Things the reader must not skip.
> [!WARNING]
> Cautions, important considerations, potential issues.
> [!CAUTION]
> Critical warnings — data loss, irreversible actions.
The marker goes alone on its own line; the body follows on the next
line(s). Each renders as unbranded structure in core's own vocabulary — a
.gp-alert box carrying .gp-alert-<type>, with a .gp-alert-title
paragraph holding the label — so any theme can restyle it, and an unthemed
book still prints a legible rule and label.
Anything that is not one of those five ([!DANGER], [!INFO], a typo) does
not match and prints as an ordinary blockquote. Branded extras such as the
Dimm City plugin's [!DM] / [!VIBE] / [!ORIGIN] come from that plugin,
layered on top of .gp-alert, not from core.
Titles and multi-paragraph callouts
The marker line carries only the marker — > [!WARNING] Read this is
not recognised. Put a title in the body instead, and prefix every
continuation line with >:
> [!WARNING]
> **Read this carefully.** This callout spans multiple paragraphs.
>
> Each paragraph needs the `>` prefix.
>
> - Lists work inside callouts
> - **Bold**, *italic*, and `code` formatting too
Callout guidance
Use at most 2–3 callouts per page — overuse reduces impact. Match the type to the severity:
| Type | Use for |
|---|---|
| Note | Context, background, explanations |
| Tip | Optional improvements, shortcuts |
| Important | Things the reader must not skip |
| Warning | Things that could go wrong |
| Caution | Critical issues, data loss, irreversible actions |
Colours come from the theme: core sets one accent per type as an
author-overridable --gp-alert-<type>-color custom property.
Images
Basic image syntax


Always provide meaningful alt text. For purely decorative images, use empty alt text — leave the brackets empty: ![].
Where images live: put the image file inside your project folder and
reference it with a path relative to the manifest — Gutterpress copies exactly
the images your markdown (or CSS) references, keeping your own folder
structure in the output. There's no directory to declare. A reference that
points outside the project (../shared/logo.png) fails the build with a
message telling you to copy the file in instead.
Image sizing
Use markdown-it-attrs (bundled — no install step needed) for precise sizing:
{width="300px"}
{width="80%"}
Common image classes
Core Gutterpress ships a small, composable gp-* vocabulary for placing
images — plain CSS rules in GUTTERPRESS_CSS, always present, no plugin or theme
required. markdown-it-attrs (also bundled) is what lets you attach
{.gp-right} and friends to an image. Pick one position word, and
optionally add a size, a spacing, and a shape word — the
classes compose:
{.gp-right .gp-small}
{.gp-center .gp-medium}
{.gp-left .gp-loose}
{.gp-full}
Positions (text wraps around the floats; the image's vertical spot on the page is simply where it appears in your markdown):
| Class | What it does |
|---|---|
.gp-left |
Floats left with clearance margins, capped at 50% width |
.gp-right |
Floats right with clearance margins, capped at 50% width |
.gp-center |
Centers a block-level image (display: block; margin: 0 auto) |
.gp-full |
Fills the page's content width (width: 100%) |
.gp-bleed |
Own page, edge-to-edge — see Full-bleed artwork |
.gp-pin |
Pins to a spot on the page instead of flowing — see Pinned images |
.gp-flush |
With .gp-pin + an edge: sits on the paper's edge, not the margin |
Sizes (work with any position, including .gp-pin; an explicit size
overrides the floats' 50% cap):
| Class | Width |
|---|---|
.gp-small |
25% of the column |
.gp-medium |
50% |
.gp-large |
75% |
Spacing (how much room a float leaves for the text wrapping around it — no class means the normal 1em):
| Class | Clearance |
|---|---|
.gp-tight |
0.5em |
.gp-loose |
2em |
Under the hood the presets set the --gp-gap custom property, so a
stylesheet can also tune the clearance directly (img { --gp-gap: 0.75em }).
Migrating from the old class names? The pre-vocabulary utilities —
.center,.float-left,.float-right,.full-width,.full-bleed— were removed when thegp-*vocabulary shipped. Rename them in your markdown (.center→.gp-center,.float-left→.gp-left,.float-right→.gp-right,.full-width→.gp-full,.full-bleed→.gp-bleed); the desktop editor's image menu rewrites the old name for you when you edit an image's position. Seedocs/migrations/2026-08-gp-image-classes.md.
Shaped text wrap
.gp-shape makes wrapping text follow a floated image's actual visible
silhouette instead of its rectangular box — for cut-out art with a
transparent background (PNG or SVG with an alpha channel):
{.gp-right .gp-shape .gp-loose}
The spacing words (.gp-tight/.gp-loose) set how far the text stays from
the visible silhouette, the same way they set float clearance. Details worth
knowing:
- Floats only.
.gp-shapeneeds.gp-leftor.gp-right; on anything else it does nothing (this is how CSSshape-outsideworks, and it makes the class safe to leave on while trying layouts). - The image needs real transparency. A JPEG has no alpha channel, so its "shape" is just its rectangle.
- You only type the class. Gutterpress mirrors the image's own file into the CSS shape automatically, and the build inlines it so the printed PDF wraps exactly like the preview.
Pinned images
.gp-pin takes an image out of the text flow entirely and pins it to a spot
on the page — centered by default, or against an edge with .gp-top,
.gp-bottom, .gp-left, and .gp-right (the same position words the
floats use):
@page
# A Title Page
{.gp-pin .gp-medium}
{.gp-pin .gp-bottom .gp-right .gp-small}
One horizontal word + one vertical word gives nine positions; sizes compose the same way they do in flow.
Two rules keep pinning predictable:
-
A pinned image must live inside an
@page(or@spread) block. The pin is anchored to that container. Outside one there is nothing on the page to anchor to — the image would resolve against the whole document and can print on a completely different sheet, so the build and preview warn (pin_outside_page) when you do it. -
The pin anchors to the nearest enclosing frame. Normally that is the
@pagecontainer: Gutterpress sizes it to the page's own content box — the sheet less its margins — so.gp-bottomsits on the bottom margin edge even when the page holds two lines of text. The two only part company if one@pageblock runs long and fragments across several sheets: there.gp-bottommeans the bottom of that whole block, not of each sheet. -
A styled block between the image and the page can become the frame instead — on purpose. If your theme positions something closer than the page (a card, a callout, a component shell), the pin anchors to that, which is how you pin art to a card rather than to the sheet. It also means an image pinned inside an
@section(or any such block) will sit at the corner of that block, not the page corner — which looks like the pin "not reaching the page". If you want the sheet, author the image outside the block, as page furniture:@section .intro ...the card's own text... @end-section {.gp-pin .gp-bottom .gp-right .gp-small}Only a pin with no enclosing frame at all is an error — that is the
pin_outside_pagewarning above.
Pinning art to the paper's edge
.gp-bottom stops at the bottom margin, because that margin is where the
page's printable area ends. Add .gp-flush and the art sits on the paper
instead:
@page
## A page whose art runs off the bottom edge
...text...
{.gp-pin .gp-bottom .gp-flush .gp-behind}
.gp-flush works with any edge, including corners — {.gp-pin .gp-bottom .gp-right .gp-flush} puts the art in the bottom-right corner of the paper. A
centred pin touches no edge, so the class does nothing there. In the desktop
app it is the "Sit flush with the page edge" checkbox in an image's
properties; you never write the class by hand.
Under the hood, Gutterpress frees that one page's margin on the edges the pin uses — the only way anything reaches the paper — and takes care of what that would otherwise cost you:
- Your page numbers and running heads keep printing. A margin box lives in the margin, so freeing the margin would normally erase that edge's folio; Gutterpress re-draws it inside the page at exactly its usual spot, with the same values, on top of the art. Furniture on every other edge is untouched.
- Your page's own setup is preserved. A named page's margins, background, and furniture all still apply on the flushed page.
One consequence remains yours to design for: the printable area really is bigger on that edge, so on a full page, text can flow into the freed strip — on a short page (the usual case for pinned art) nothing changes.
This is trim-edge flush, not printer bleed: nothing in Gutterpress extends art past the trim. If your printer wants bleed overage, build it into the artwork and your PDF export settings, the same as for full-bleed artwork.
Full-bleed artwork
.gp-bleed forces the image onto its own page and cancels that page's left/right margins so the image spans the page edge-to-edge horizontally:
{.gp-bleed}
.gp-bleed produces the edge-to-edge result this way:
- Forces a page break before the image (
break-before: page). - It assigns the image's page to a core-owned named page (
@page gp-full-bleed) with zero left/right margins, so the page's own content box already is the sheet — no negative margin needed.
It does not: cancel the top/bottom margins, remove headers or footers, or add printer bleed overage past the trim edge. The running head and folio move onto the trim line on the bleed page itself (margin boxes are positioned by the page's own margins, which are zero here) — if you need to keep them, suppress the ones that would land on the trim edge with a small override targeting @page gp-full-bleed (e.g. @top-center { content: none }); see the native engine styling guide. If you need true bleed — content that extends past the trim line for a print shop to cut through — design the artwork to the bled dimensions and add that overage via your PDF export/preflight settings; see Bleed for full-page images below.
Image galleries
Wrap a gallery in a named section so your own stylesheet can choose its layout
and fragmentation policy. A plain @section is structural and may split; this
guide's .figure class is the opt-in keep-together treatment:
@section .figure
{width="30%"}
{width="30%"}
{width="30%"}
@end-section
Figure with caption
Wrap the image and its caption in @section .figure so they stay together on
one page, and tag the caption paragraph with the .figcaption class (via
markdown-it-attrs, always available — see Chapter 5):
@section .figure

Figure 1: System architecture overview {.figcaption}
@end-section
Print-Safe Images
Resolution requirements
| Use | Minimum DPI | Recommended |
|---|---|---|
| Body illustrations | 300 | 300–600 |
| Line art / diagrams | 600 | 600+ |
| Full-bleed artwork | 300 | 300 |
To calculate pixel dimensions: print width (inches) × DPI = pixel width. An 4-inch image at 300 DPI needs 1,200 pixels.
File formats
- JPEG — photographs (quality 80–90%)
- PNG — graphics with transparency
- SVG — logos and diagrams (scales perfectly)
Color space
Use RGB color space. Ghostscript handles CMYK conversion automatically during the PDF/X pipeline — you do not need to pre-convert images to CMYK.
Pre-sizing images
Pre-size images to their final print dimensions before adding them to the project:
# Resize to 2400px wide (8-inch image at 300 DPI)
convert input.jpg -resize 2400x output.jpg
# Optimize JPEG quality
convert input.jpg -quality 85 output.jpg
Bleed for full-page images
Add 0.125 inches (3.175 mm) to all edges for full-bleed images:
Page size: 6in × 9in
Image with bleed: 6.25in × 9.25in
At 300 DPI:
Width: 6.25 × 300 = 1,875 pixels
Height: 9.25 × 300 = 2,775 pixels
Common Image Issues
| Problem | Cause | Fix |
|---|---|---|
| Pixelated in print | Resolution below 300 DPI | Re-export at 300 DPI minimum |
| Wrong aspect ratio | No object-fit set |
Use object-fit: cover or contain in CSS |
| Splits across pages | No break-inside rule | Apply break-inside: avoid or use full-bleed |
| White border on bleed | Image doesn't extend to edge | Add 0.125in bleed on all sides |
| Color shift in print | RGB vs CMYK mismatch | Test the CMYK conversion; adjust RGB values |
Image Best Practices
Organization
assets/
├── images/
│ ├── chapter-01/
│ │ ├── hero.jpg
│ │ └── diagram-1.png
│ └── chapter-02/
│ └── portrait.jpg
├── icons/
│ └── badge.svg
└── diagrams/
└── flowchart.svg
Print testing checklist
Before final submission:
- [ ] All images are at least 300 DPI
- [ ] Full-bleed images include 0.125in bleed on all sides
- [ ] Images are pre-sized to print dimensions (not scaled in markdown)
- [ ] Grayscale conversion tested if printing black and white
- [ ] No pixelation visible when zoomed to 300% in the preview
- [ ] Alt text provided for all meaningful images