Write your page

A frwrd.to page is one Markdown document: a short header with settings, then the text. You can write it by hand, in the web app, or let an assistant write it from this page. Every link on it becomes a short link with its own click numbers.

What a page is

A page is for the one link people tap in your profile: a logo, a name, a few buttons and your social icons. It has no custom code, no columns and no forms. What it can show is listed on this page, and nothing else is needed to make it look like yours: a logo, a colour, a button style, a shape and a font.

Where it lives

Your main page is at https://frwrd.to/@handle, where handle is your workspace's id (it starts with w-), a handle you claimed, or your domain. Further pages sit under it: https://frwrd.to/@handle/jobs is the page jobs. In the API, the MCP tools and the command-line tool the main page is called -.

A page in another language than your workspace's sits under that language's code: https://frwrd.to/@handle/de/jobs. Pair the versions with translations (below) and the page shows a language switcher.

Add .md to any page's address to read it as Markdown, for example https://frwrd.to/@frwrd.to/help-pages.md is this page.

The header

The header is the first block of the document, between two lines that hold only ---. It is YAML. Always put colours in quotes: without them # starts a comment, so the page is refused until you add them.

FieldWhat it isAllowed values
titleRequired. The page's name, the browser tab and link previews1 to 120 characters
descriptionOne sentence for search results and link previewsup to 300 characters
avatarYour logo or photo, shown above the text. A web address, never a filehttps address, no user name or password
langThe page's language. Leave it out: it follows the page's addressa language code such as en or de-AT; must match the address
translationsThe same page in other languages{ de: de/jobs }: language code, then the page's address; "-" for a main page
utmTracking parameters added to every link on the pageany of source, medium, campaign, term, content, each 1 to 100 characters without spaces
socialYour profiles, shown as a row of icons under the logoa list of up to 12 https addresses
theme.accentThe colour of links, icons, outlines and filled buttons"#rrggbb"; must read on the background (see the contrast rule)
theme.backgroundThe page's backgroundlight, dark, or "#rrggbb"
theme.buttonHow link buttons lookoutline (an outline in the accent, the default) or filled (the accent as background)
theme.shapeThe corners of buttons and iconsrounded (the default), pill or square
theme.fontThe typefacesans (the default), serif or mono. System fonts only: nothing is loaded from elsewhere
campaignsNamed time windows for timed blocks (below){ black-friday: { from: 2026-11-27, until: 2026-11-30 } }; names of a-z, 0-9 and -; dates in UTC

Anything else in the header is refused with the name of the key.

Everything under theme is optional. A page that sets nothing there looks the same as it always did: a light background, a blue accent, outlined buttons with rounded corners and a sans serif font.

Social icons

social lists full https addresses. frwrd.to recognises the network from the address and draws its icon: Instagram, LinkedIn, X (also twitter.com), YouTube, TikTok, GitHub, Facebook, Threads, Bluesky, Mastodon, Twitch, Pinterest, Spotify, SoundCloud, Behance, Dribbble, Medium, Substack, WhatsApp (wa.me), Telegram (t.me) and Discord. A Mastodon server is recognised by its /@name path. Anything else gets a globe icon, named after its host.

social:
  - https://www.instagram.com/milafischer
  - https://www.linkedin.com/in/milafischer
  - https://milafischer.example

Each icon is a short link like every other link: its clicks are counted and the page's utm applies. The icons are in your accent colour, never the network's brand colours, so they stay readable on your background. Each has an accessible name, the network's. An e-mail address or a mailto: link is not possible (only http and https addresses are links); write the address as text in the body.

Social profiles come after the page's own links in the page's numbers, in the order you wrote them. A page can have 200 links in all, social ones included.

The contrast rule

A page whose text cannot be read is refused when you save it, with a message that names the fields and the measured ratio, for example:

theme.accent #ff5a1f on theme.background light (#ffffff) measures 3.11:1; links in the accent colour need at least 4.5:1 (WCAG AA). Pick a darker accent for a light background, a lighter one for a dark background, or change theme.background

You choose the background and the accent. frwrd.to works out the rest: the text colour is the near black or near white that reads best on your background, cards and secondary text are derived from it, and the label on a filled button is black or white, whichever reads better on your accent. Those always pass. What you can get wrong is the accent, so the rule is about it:

In short: on a light background pick a dark accent, on a dark background a light one. Accents that work:

BackgroundAccents that read
light (white)"#1f6feb", "#c2410c", "#0f766e", "#7c3aed"
dark"#ff7a59", "#7fb2ff", "#4ade80", "#fbbf24"
"#fff8e7" (cream)"#8a3b00", "#0f766e"
"#10243e" (navy)"#7fb2ff", "#fbbf24"

A page you saved before this rule existed keeps working as it is; saving it again asks for a colour that reads.

The body

Everything after the header is Markdown, a safe subset of it.

You writeYou get
# NameThe page's heading. ## and ### are smaller headings; deeper levels count as ###
a link alone on its line, or alone in a list itemA button: - [Book a call](https://example.com/call)
a link inside a sentenceAn ordinary link: See [the docs](https://example.com/docs).
<https://example.com> or a bare https://example.comA link with its own address as text
**bold** and *emphasis*Bold and italic
a list with - or 1.A list; lists and quotes nest up to 12 levels
`code` and fenced blocksInline code and code blocks; line breaks are kept
> textA quote
a GFM table: a header row, a row of dashes under it, then the rowsA table that scrolls sideways when it is wide
![alt text](https://example.com/a.png)An image, https only. Always write the alt text
--- alone on a lineA dividing line

Raw HTML is dropped. A link whose address is not http or https stays text, and so do www. addresses and e-mail addresses written without a scheme. A link to another page of yours starts with /@handle/: it is counted like every link, without tracking parameters.

A page can be 64 KiB at most. Links are checked when you save: an address that points inside a network (such as localhost) is refused, and so is one with a user name and password in it.

Timed blocks

A block can show only during a campaign. Put it between a line that says when and a line that holds only ::::

::: from 2026-11-27 until 2026-11-30
- [Black Friday: 30 % off](https://example.com/sale)
:::

Either end can be left out: ::: from 2026-12-24 shows from then on, ::: until 2026-12-31 until then. For a campaign that appears in several places, name it once in the header and use the name:

---
title: Acme
campaigns:
  black-friday: { from: 2026-11-27, until: 2026-11-30 }
---

::: black-friday
- [Shop the sale](https://example.com/sale)
:::

::: not black-friday
- [Shop](https://example.com/shop)
:::

::: not black-friday shows whenever the campaign is not running, so the regular button comes back by itself when the sale ends.

All times are UTC. A date means the whole day in UTC: until 2026-11-30 shows the block through the end of November 30, UTC. For a time of day, write it in UTC with a Z: from 2026-11-27T08:00Z is 9:00 in Vienna in winter. A time with another zone, such as +01:00, is refused.

The links in a block work all the time, also before the block shows and after it ended. Every link gets its short link when you save, so you can test the campaign's links, QR codes and tracking before it starts.

Blocks do not nest, a page holds at most 20, and the ::: lines start at the beginning of the line. Before a block shows, the page's Markdown version (.md) leaves it out, and the campaign names in the header too.

Check before you save

The web app's editor has a Check button, the API POST /v1/pages/{path}/check, MCP the tool check_page and the command-line tool frwrd page check <path> < page.md. Each runs every check a save runs, saves nothing, and lists warnings: things that are allowed but probably not meant, such as a missing description, a campaign no block uses, a timed block that has already ended, a ::: that starts with spaces (so it is shown as text), a skipped heading level, a second # title, an http link or an image without a description. Warnings never stop a save; errors do, and they say what to fix.

Three complete examples

Each one is a whole document, ready to paste. Replace the addresses with yours.

---
title: Mila Fischer
description: Illustrator in Vienna. Commissions open.
avatar: https://example.com/mila.jpg
social:
  - https://www.instagram.com/milafischer
  - https://www.behance.net/milafischer
  - https://www.youtube.com/@milafischer
theme: { background: "#fff8e7", accent: "#8a3b00", button: filled, shape: pill, font: serif }
utm: { source: bio, medium: social }
---

# Mila Fischer

Illustrator in Vienna. I draw books, posters and the odd mural.

- [Book a commission](https://example.com/commissions)
- [Shop prints](https://example.com/shop)
- [Newsletter, once a month](https://example.com/newsletter)

An event

---
title: Frwrd Summit 2027
description: Two days on links, pages and numbers. Vienna, 14 and 15 May 2027.
social: [https://www.linkedin.com/company/example, https://bsky.app/profile/example.com]
theme: { background: dark, accent: "#7fb2ff", button: outline, shape: square, font: sans }
utm: { source: summit, medium: qr, campaign: summit-2027 }
---

# Frwrd Summit 2027

**14 and 15 May 2027**, Museumsquartier, Vienna.

- [Get your ticket](https://example.com/tickets)
- [See the programme](https://example.com/programme)
- [Become a speaker](https://example.com/speakers)

## Good to know

> Doors open at 08:30. Lunch is included.

| Day | Topic |
|:--|:--|
| 14 May | Links and pages |
| 15 May | Numbers |

Getting there: [the venue on the map](https://example.com/map).

A portfolio

---
title: Jonas Weber, product designer
description: Selected work, writing and contact.
avatar: https://example.com/jonas.jpg
social: [https://github.com/jonasweber, https://dribbble.com/jonasweber, https://www.linkedin.com/in/jonasweber]
theme: { background: light, accent: "#0f766e", button: outline, shape: rounded, font: mono }
---

# Jonas Weber

Product designer. I make checkout flows less painful.

## Selected work

- [Checkout redesign for Acme](https://example.com/work/acme)
- [A design system for Beta](https://example.com/work/beta)

## Writing

My latest piece is [Why buttons need labels](https://example.com/blog/buttons).
All of it is at https://example.com/blog.

## Contact

Write to hello at example dot com, or use [the contact form](https://example.com/contact).

Let an AI write it

Copy this prompt into any assistant (Claude, ChatGPT or another one that can read a web address). It reads this page, asks you what it needs and gives you the finished document.

Read https://frwrd.to/@frwrd.to/help-pages.md. It describes the Markdown format of a frwrd.to page: the header (title, description, avatar, social, theme) and the body.

Ask me, one question at a time:
1. My brand or name.
2. One sentence about what I do.
3. The links I want on the page, each with a label.
4. My social profiles, as full https addresses.
5. The address of my logo.
6. My colours: a background (light, dark or a #rrggbb colour) and an accent (#rrggbb).

Then write the whole page as one Markdown document, header included, in a single code block. Follow the format exactly. Check the contrast rule: the accent needs a contrast ratio of at least 4.5 to 1 against the background, so on a light background use a dark accent and on a dark background a light one. Put every colour in quotes. Do not invent links, profiles or facts. Use the three examples on that page as a guide for the structure.

Then save the result: paste it into the page editor in the web app, or let your assistant publish it through MCP or the command-line tool (frwrd page push). The save tells you exactly what to fix if something in the document is not allowed.

Read on