Skip to content

Autoflow rule: bare-image-position-variation

Priority 70 · A bare ![](src) varies position across the deck: inline → left → right → ...

This is the only rule that uses cross-slide state. Every other rule treats each slide independently. This one looks at the position used for the previous bare image in the deck and picks the next position in the cycle.

The name says “position variation” because it varies the image position across slides — it doesn’t rotate the image itself.

  • The slide has exactly 1 image with no layout modifier (no right / left / inline / qr / fit / filtered / bg)
  • The slide also has at least 1 non-image content line (heading or text)

The rule reads ctx.state.lastBareImagePosition and picks the next position in the cycle:

1st bare image in deck → inline (image in flow, text above)
2nd bare image in deck → left (split, image left + text right)
3rd bare image in deck → right (split, image right + text left)
4th → inline, 5th → left, 6th → right, ...

All three are existing parser primitives — the rule rewrites the bare ![](src) into one of ![inline](src) / ![left](src) / ![right](src) and the parser handles the rest.

The variation persists across the entire deck via ctx.state, not just within the current slide. Even if other rules fire between two bare-image slides, the variation continues from where it left off.

Cross-slide observation: explicit images count too

Section titled “Cross-slide observation: explicit images count too”

The autoflow also observes explicit ![left] / ![right] / ![inline] images on every slide — even on slides that the autoflow itself skips (because of #[fit], code blocks, etc). The state is updated as if the variation had picked that position.

Why: a user-placed ![left](photo.jpg) and a bare ![](next-photo.jpg) on the next slide should not both end up on the left. Without this observation, the bare-image variation would happily ignore the explicit one and could pick the same position. With it, the rule treats explicit and bare images as one continuous variation history.

Concrete example:

slide 1: ![](a.jpg) → rule rewrites to ![inline](a.jpg) (lastPosition = inline)
slide 2: ![left](b.jpg) → autoflow skips (explicit), but observes (lastPosition = left)
slide 3: ![](c.jpg) → rule picks NEXT after left = right (lastPosition = right)

Slide 3 would have been left again without the observation step.

Without it, an author with several plain ![](src) slides would have to manually decide a position for each one. Forgetting to vary results in every photo on the same side, which feels mechanical. The variation gives natural rhythm without any thinking from the author.

The fixture below has 4 bare-image slides in a row. Watch the position cycle through inline → left → right → inline as you flip through.

footer: autoflow rules · bare-image-position-variation
slidenumbers: true
autoflow: true
theme: nordic
scheme: 1
# Cover
(slide 0 — sets up the deck before the variation begins)
---
<!--
RULE: bare-image-position-variation (priority 70)
TRIGGERS WHEN:
- The slide has exactly 1 image with NO layout modifier
(no right/left/inline/qr/fit/filtered/bg)
- The slide also has at least 1 non-image content line
EFFECT (the only history-based rule):
- Picks position by varying across deck: inline → left → right → ...
- The position is based on ctx.state.lastBareImagePosition, NOT slide index
- All three rewrite the bare ![](src) into a parser primitive:
![inline](src), ![left](src), ![right](src)
- State is also updated when an EXPLICIT ![left]/![right]/![inline]
image appears on a skipped slide, so the variation never repeats the
same position as the previous slide.
The name says "position variation" because it varies the IMAGE POSITION
across slides — it doesn't rotate the image itself.
The 4 slides below show one full cycle + wrap:
slide 1: 1st bare image → inline (variation starts)
slide 2: 2nd bare image → left
slide 3: 3rd bare image → right
slide 4: 4th bare image → inline (cycle wraps)
-->
![](/demo/images/vibe-coding/karpathy-vibe.webp)
# First image of the deck
This one becomes inline (image in flow, text above).
---
![](/demo/images/vibe-coding/seven-languages-book.webp)
# Second image
This one varies to left split.
---
![](/demo/images/vibe-coding/pragmatic-programmer-tweet.webp)
# Third image
And this one varies to right split.
---
![](/demo/images/vibe-coding/bravenewgeek-you-are-not-paid.webp)
# Fourth image
Cycle wraps: back to inline.

A planned upgrade: measure the image at render time (naturalWidth/Height) and prefer portrait → split vs landscape → inline hero instead of strict round-robin variation. The CSS classes would be applied via a small JS helper in js/render.js, not in autoflow itself (which is sync markdown→markdown). Tracked in the roadmap.