marquee-logo

Marquee (Markup)

Marquee is a markup language!

It's designed to be a mash-up of Markdown, BBCode, RST, and the old web.

For more information about dumb questions like "why it exists", "what it does", or "how to integrate with it", you should check the GitHub.

This? This is the DEMO.

Let's take a look at how to write Marquee!

Writing Marquee

This page is, of course, written in Marquee, at WRITING.mq.

Paragraphs and line breaks

This paragraph is written
across several source lines,
and none of those breaks survive — it flows.

Roses are red\
violets are blue,\
these lines end in backslashes\
so each one renders new.

This paragraph is written across several source lines, and none of those breaks survive — it flows.

Roses are red
violets are blue,
these lines end in backslashes
so each one renders new.

And no blank line is needed to start a block — here's my list:
- it starts anyway
- jammed right against the prose

And no blank line is needed to start a block — here's my list:

Headings

### A heading with *emphasis*, [color=goldenrod]color[/color], and [blink]blink[/blink]

######## Eight hashes still works — deeper than HTML dares.

######### Nine hashes is just a paragraph, rendered literally.

A heading with emphasis, color, and blink

Eight hashes still works — deeper than HTML dares.

######### Nine hashes is just a paragraph, rendered literally.

Emphasis and friends

*emphasis*, **strong**, **strong with *emphasis* inside**, ~~struck through~~, and
`inline code with *stars* nobody parses`.

Underscores are safe in snake_case_names, ***three stars are literal***, and a
lone * asterisk just sits there. Escapes work too: \*not emphasis\*, and a line that starts with

\- an escaped dash, which is not a list.

[blink]forgot the closer — so this whole run renders literally, brackets and all.

emphasis, strong, strong with emphasis inside, struck through, and inline code with *stars* nobody parses.

Underscores are safe in snake_case_names, ***three stars are literal***, and a lone * asterisk just sits there. Escapes work too: *not emphasis*, and a line that starts with

- an escaped dash, which is not a list.

[blink]forgot the closer — so this whole run renders literally, brackets and all.

Links, embeds, turbolinks

A [plain link](https://example.org), a [relative link](other-page), and a
[parenthetical Wikipedia link](https://en.wikipedia.org/wiki/Hat_(disambiguation)).

![a picture, by description](example-media/picture.jpg)

![the demo song](example-media/song.mp3)

![a tiny film](example-media/clip.mp4)

A bare URL alone in a paragraph becomes a turbolink:

https://example.org/interesting-post

https://commons.wikimedia.org/wiki/Special:FilePath/Cat03.jpg

https://commons.wikimedia.org/wiki/Special:FilePath/Meow.ogg

https://www.youtube.com/watch?v=kiTpHaShznE

%% You can also trigger link expansion directly, like this: 
:::turbolink target=https://www.youtube.com/watch?v=kiTpHaShznE level=title:::

A plain link, a relative link, and a parenthetical Wikipedia link.

One embed syntax — the file's own type decides whether it's a picture, a song, or a film:

(I apologize for the generative nature of these media assets, but it keeps them public domain so that I can distribute them freely as examples.)

a picture, by description

A bare URL alone in a paragraph becomes a turbolink:

Link kinds get their own expanders, plugged in by the host. An image link unfurls into the picture, a sound into play controls, a video link arrives playable:

Sizing media

Wrap embeds in a :::media block — its knobs apply to everything inside. Width and height take exact pixels (like 200) or the tokens small / medium / large / full. One dimension scales (aspect ratio kept); both dimensions make a frame the media fills, cropped, never squashed:

:::media width=200 height=300
![a picture, resized](example-media/picture.jpg)
:::

:::media width=200 height=300
![a tiny film, in a tiny frame](example-media/clip.mp4)
:::

:::media width=full
![the demo song, stretched full width](example-media/song.mp3)
:::

a picture, resized

Embeds are inline, so pictures sharing one paragraph flow and wrap like words — a gallery with no gallery element:

:::media width=150
![pasta](example-media/pasta_1.jpg)
![pasta](example-media/pasta_2.jpg)
![pasta](example-media/pasta_3.jpg)
![pasta](example-media/pasta_4.jpg)
:::

pasta pasta pasta pasta

Emoji

Known shortcodes resolve — :tophat: :sparkles: :blobcat: — and a host can map a slug to a
little image: :angry-burger: is a custom image emoji this host provides, worn at character
size right in the line. Unknown ones stay literal, like :thisoneisnotreal:, typed emoji 🎩
are just text, and 3:30 is just a time.

Known shortcodes resolve — 🎩 ✨ :blobcat: — and a host can map a slug to a little image: :angry-burger: is a custom image emoji this host provides, worn at character size right in the line. Unknown ones stay literal, like :thisoneisnotreal:, typed emoji 🎩 are just text, and 3:30 is just a time.

Typography spans

E = mc[sup]2[/sup], H[sub]2[/sub]O, [small]the fine print[/small], colors by
[color=goldenrod]name[/color] or [color=#f06]hex[/color], and
asides[sidenote]like this one[/sidenote] that never interrupt the sentence
(and [footnote]this[/footnote] and [aside]this[/aside] are the same tag —
sidenote, aside, footnote: whichever word your fingers find).

The size dial goes [size=1]one[/size] [size=2]two[/size] [size=3]three[/size]
[size=4]four[/size] [size=5]five[/size] [size=6]six[/size] [size=7]SEVEN[/size],
and [big]big[/big] is [small]small[/small]'s mirror.

When numbers feel cold, the rungs have names: [teeny]teeny[/teeny],
[tiny]tiny[/tiny], [huge]huge[/huge], [enormous]ENORMOUS[/enormous].

Fonts are picked from a closed list, never uploaded: [font=orbitron]SPACE STATION[/font],
[font=vt323]terminal green[/font], [font=im-fell-english]ye olde booke[/font],
[font=press-start]INSERT COIN[/font], [font=lobster]a fancy lobster[/font],
[font=creepster]the abomination approves[/font], [font=comic]the people's classic[/font],
[font=kablammo]KABLAMMO[/font], and [font=rye]howdy, partner[/font].

E = mc2, H2O, the fine print, colors by name or hex, and asides1 that never interrupt the sentence (and 2 and 3 are the same tag — sidenote, aside, footnote: whichever word your fingers find).

The size dial goes one two three four five six SEVEN, and big is small's mirror.

When numbers feel cold, the rungs have names: teeny, tiny, huge, ENORMOUS.

Fonts are picked from a closed list, never uploaded: SPACE STATION, terminal green, ye olde booke, INSERT COIN, a fancy lobster, the abomination approves, the people's classic, KABLAMMO, and howdy, partner. (Thirty-one real, self-hosted faces ship in the @cube-drone/marquee-fonts package, each with its license riding along — your host serves them itself, or inlines just the ones a page wears; no reader ever pings a font CDN.)

The full menu makes a fine specimen chart — and a fine use of a nested list. Every name renders as itself:

- standard stacks (no files needed)
  - [font=sans]sans[/font]
  - [font=serif]serif[/font]
  - [font=mono]mono[/font]
  - [font=comic]comic[/font]
- clean sans
  - [font=radio-canada]radio-canada[/font]
  - [font=atkinson-hyperlegible]atkinson-hyperlegible[/font]
  - [font=lexend]lexend[/font]
  - [font=quicksand]quicksand[/font]
- serif & slab
  - [font=playfair-display]playfair-display[/font]
  - [font=cormorant]cormorant[/font]
  - [font=zilla-slab]zilla-slab[/font]
- old book & gothic
  - [font=im-fell-english]im-fell-english[/font]
  - [font=uncial-antiqua]uncial-antiqua[/font]
  - [font=unifraktur]unifraktur[/font]
- mono & terminal
  - [font=jetbrains-mono]jetbrains-mono[/font]
  - [font=vt323]vt323[/font]
  - [font=major-mono]major-mono[/font]
  - [font=special-elite]special-elite[/font]
- pixel
  - [font=press-start]press-start[/font]
  - [font=silkscreen]silkscreen[/font]
  - [font=bitcount]bitcount[/font]
- display & neon
  - [font=orbitron]orbitron[/font]
  - [font=audiowide]audiowide[/font]
  - [font=bungee]bungee[/font]
  - [font=monoton]monoton[/font]
  - [font=creepster]creepster[/font]
- loud & weird
  - [font=kablammo]kablammo[/font]
  - [font=oi]oi[/font]
  - [font=henny-penny]henny-penny[/font]
  - [font=rye]rye[/font]
- script & hand
  - [font=lobster]lobster[/font]
  - [font=pacifico]pacifico[/font]
  - [font=caveat]caveat[/font]
  - [font=fredericka]fredericka[/font]
  - [font=comic-neue]comic-neue[/font]

Effects

[marquee]THE CLASSIC — scrolling since 1995, stoppable since Marquee[/marquee]

[blink]blink[/blink] · [rainbow]rainbow[/rainbow] · [bounce]bounce[/bounce] ·
[jitter]jitter[/jitter] · [wave]wave[/wave] · [rubber]rubber[/rubber] ·
[typewriter speed=30]typewriter — it types itself out, letter by letter, in pure CSS[/typewriter]

Nesting is the point: [marquee][blink]still open at 3am[/blink][/marquee]

[rainbow by=letter]EVERY LETTER ITS OWN HUE[/rainbow] ·
[wave by=letter]a true undulating wave[/wave] ·
[bounce by=word]each word takes its turn[/bounce] ·
[jitter by=letter]scattered nerves[/jitter] ·
[rubber by=letter]SQUASH AND STRETCH[/rubber]

[rainbow by=letter phase=scatter]CONFETTI HUES[/rainbow] ·
[bounce by=letter phase=scatter]popcorn letters[/bounce] ·
[jitter by=letter phase=ramp]an orderly shudder[/jitter]

[fadein]a ghostly fade-in[/fadein] ·
[fadein by=letter]drifting in letter by letter[/fadein] ·
[fadein by=word phase=scatter]apparition weather: words materializing in no particular order at all[/fadein]

[blink by=letter]CHASE LIGHTS[/blink] ·
[blink by=letter phase=scatter rate=2]twinkle twinkle[/blink]

:::media width=48
[bounce]![an angry burger, bouncing](example-media/angry-burger-emoji.png)[/bounce]
:::

THE CLASSIC — scrolling since 1995, stoppable since Marquee

blink · rainbow · bounce · jitter · wave · rubber · typewriter it types itself out, letter by letter, in pure CSS

Nesting is the point: still open at 3am

And by=letter / by=word give every unit its own offset cycle:

EVERY LETTER ITS OWN HUE · a true undulating wave · each word takes its turn · scattered nerves · SQUASH AND STRETCH

Offsets sweep smoothly by default; phase=scatter scrambles them (and phase=ramp can smooth even jitter):

CONFETTI HUES · popcorn letters · an orderly shudder

The reveals enter once and then behave — a ghostly fade, whole or per-unit:

a ghostly fade-in · drifting in letter by letter · apparition weather: words materializing in no particular order at all

And blink splits too — sweeping in order is a theater marquee, scattering is a night sky:

CHASE LIGHTS · twinkle twinkle

And effects wrap anything — transforms carry their children, so an embed inside a bounce is simply a bouncing picture:

an angry burger, bouncing

If your reduced-motion setting is on, everything above is calmly standing still — yes, even the burger.

Lists

- unordered
* with mixed markers (same list)
+ because markers are synonyms

1. ordered
7. with numbers that don't matter —
3. the renderer counts

- outer
  - inner
    - deeper (three or four spaces would also have worked)

- an item with a second paragraph

  which is indented to stay inside the bullet, image included:

  ![indented, so it stays in the bullet](blob:bullet-pic)

- and a normal next item

1. this ordered list is about to be interrupted
2. by an unindented line
oops — this line ends the list
1. and the numbering restarts, which is how you *see* the mistake
  1. ordered

  2. with numbers that don't matter —

  3. the renderer counts

Items hold whole paragraphs — even images — as long as they stay indented:

  1. this ordered list is about to be interrupted

  2. by an unindented line

oops — this line ends the list

  1. and the numbering restarts, which is how you see the mistake

Where indentation matters

Only lists mean anything by indentation (see above). Everywhere else, an indent demotes structure to prose — visibly, fixably, on purpose:

  # an indented heading is just text

  ::: and an indented closer closes nothing

# an indented heading is just text

::: and an indented closer closes nothing

So keep directive bodies flat, and use named closers when nesting gets deep.

Tables

Rows are paragraphs, cells are [c]-spans, and the blank line between rows is the ordinary paragraph break. header=row makes the first row real header cells (screen readers get the association, not just the bold):

:::table header=row
[c]dish[/c]                    [c]price[/c]

[c]*Spaghetti* al Limone[/c]   [c]$12[/c]

[c]Linguine alle Vongole[/c]   [c]$18 :sparkles:[/c]
:::
dishprice
Spaghetti al Limone$12
Linguine alle Vongole$18 ✨

Line the source up into columns or don't — spacing between cells is yours, and it renders the same either way. There is deliberately no way to put a table inside a table.

Quotes

> Every line of the quote is marked,
> line by line — no lazy continuation.
> > And quotes nest.

Every line of the quote is marked, line by line — no lazy continuation.

And quotes nest.

Code

```python
for hat in attic.hats():
    print(hat.vibe, hat.dampness)
```

```
:::page layout=basic
*none of this* is parsed [blink]in here[/blink] — fences are raw
:::
```
for hat in attic.hats():
    print(hat.vibe, hat.dampness)
:::page layout=basic
*none of this* is parsed [blink]in here[/blink] — fences are raw
:::

Need literal triple-backticks inside a block? Fence with four:

````
``` even literal triple-backticks, inside a longer fence ```
````
``` even literal triple-backticks, inside a longer fence ```

Comments and metadata

%% notes to self — readers never see this
%% consecutive comment lines merge into one comment

\%% but escape the marker and the line stays in prose

:::meta title="Writing Marquee, live" date=2026-07-10 tags="guide, demo":::

There is a live comment in this very spot — and rendering it as nothing is the demo:

%% but escape the marker and the line stays in prose

The live :::meta is at the top of this file, describing the document to any tool that asks, rendering nothing.

Alignment

The <center> tag is back, and it brought company — :::right, and :::left for un-aligning inside the other two:

:::center
**CENTERED**, like the old gods intended

:::left
(and this line ducks back to the left rail)
:::
:::

:::right
signed, ~~the management~~
:::

CENTERED, like the old gods intended

(and this line ducks back to the left rail)

signed, the management

Layout, sections, schemes

:::section scheme=terminal
sys.status: [color=#33ff66]COZY[/color] · uptime 11,315 days
:::

sys.status: COZY · uptime 11,315 days

Hark — the same markup, an entirely different mood (this one says scheme=parchment).

THE CONDIMENT KING'S OWN COLORWAY (scheme=hotdog-stand)

And backgrounds tile images, exactly as the old web intended — any image target, honoring the host's media policy like every other embed:

:::section background=tile:example-media/angry-burger-emoji.png color=#fff8dc
**WELCOME TO THE BURGER ZONE.** We regret nothing.
:::

WELCOME TO THE BURGER ZONE. We regret nothing.

And a whole page-in-miniature, wrapping slotted sections:

:::page layout=nav-footer scheme=noir
:::section slot=nav
**a tiny demo page** — layout picked from a list, never authored
:::
:::section slot=main
Nav above, footer below, this in the middle.
:::
:::section slot=footer
[small]est. 2026 · probably part of a webring[/small]
::: section
:::

(That footer closed with ::: section — a named closer: :::name opens, ::: name closes. Pure readability in deep nesting, plus a safety net — a named closer that doesn't match the innermost open block gets flagged right at that line instead of silently mis-nesting the rest of the page. And notice the sections inside the noir page needed no scheme of their own: styling flows down by containment, and whichever knob sits closest wins — that's the entire cascade.)

Widgets and placeholders

These depend on where you're posting — here they render labeled placeholder boxes, the graceful deal that lets old clients meet new vocabulary:

:::counter theme=retro:::

:::webring ring=cozy-comics:::

And one deliberate mistake — spaces around = — so you can see "strict but never destructive":

:::oops key = broken

The abomination

You have now seen each effect alone: polite, purposeful, stoppable. The spec says effects nest freely — that this "is not an edge case, it is the point." We are therefore obligated to conclude that the following is the point. All seven effects, one heading, eight levels deep — which used to ride the inline nesting cap exactly, until v0 sealed the cap at sixteen. There is now officially room for a second abomination inside this one. We mention this fact and immediately regret it:

:::section scheme=terminal
# [marquee][blink][wave][bounce][jitter][typewriter][rainbow by=letter phase=scatter]**WELCOME TO MY HOMEPAGE**[/rainbow][/typewriter][/jitter][/bounce][/wave][/blink][/marquee]
:::

WELCOME TO MY HOMEPAGE

(The typewriter is in there too — but its content is a whole nested effect rather than bare letters, so it sits this one out: the per-letter machinery splits text, not other spans. On its own it types in pure CSS now; see the effects section. And if your reduced-motion setting is on, the abomination is a perfectly still green-on-black heading — the exit is contractual, even from this.)


This document is CC0, like everything in vectors/ — copy it, break it, preview what happens. You cannot hurt it. ✨