On this page

Askdown 0.1

Status: Draft · License: CC BY 4.0 · JSON Schema: /schema/v0.1.json

Askdown is a plain-text language for writing forms. It looks like Markdown, reads well as a plain file, and renders sensibly on sites that show Markdown, such as GitHub. Every Askdown document maps to a JSON form model, described by a JSON Schema, so tools can work with either representation.

This document specifies both. The key words "MUST", "MUST NOT", "SHOULD" and "MAY" are to be interpreted as described in RFC 2119.

---
title: DevConf RSVP
submit: Send RSVP
---

? Full name *
___

? Will you attend? *
- ( ) Yes
- ( ) No -> #bye

-> submit

--- {#bye}

# Sorry to miss you

? Why not?
___
___

1. Documents#

An Askdown document is UTF-8 text. Implementations MUST treat \r\n and \r as line endings equivalent to \n. The recommended file extension is .ask.md, and the recommended media type is text/markdown.

A document consists of optional front matter followed by a body. The body is a sequence of lines, which are grouped into:

  • questions, which start with a ? line;
  • page breaks, which are lines of three or more dashes;
  • page jumps, which are lines starting with ->;
  • content: every other line, which is Markdown.

Parsers MUST NOT fail on any input. Problems are reported as diagnostics, each with a severity (error or warning), a message, and a line and column. A document with error diagnostics is invalid, but parsers SHOULD still produce the best possible form, so editors can show a preview while the user types.

2. Front matter#

If the first line of a document is exactly ---, the lines up to the next --- line are front matter. Front matter holds form settings as a small subset of YAML: one key: value per line.

---
title: Customer survey
description: |
  A few questions about your experience.

  It takes about **two minutes**.
submit: Send
confirmation: Thanks for your feedback!
collectEmail: true
limitOne: false
---
Key Type JSON Meaning
title string title Form title
description string description Markdown shown under the title
submit string settings.submitLabel Label of the submit button
confirmation string settings.confirmation Markdown shown after submitting
collectEmail boolean settings.collectEmail Ask for the respondent's email address
limitOne boolean settings.limitOne Allow one response per respondent (best-effort)

Values are plain text up to the end of the line. A value MAY be double-quoted (with JSON string escapes) or single-quoted (with '' for a quote). Text after # in an unquoted value is a comment. | starts a block of indented lines that keeps line breaks; > joins them with spaces. Lines starting with # are comments. Booleans are true and false.

Unknown keys produce a warning. Implementations MAY use keys starting with x- for extensions.

3. Questions#

A question starts with a line beginning with ? and a space, followed by the label:

? What is your name? * {#name}
  • A * at the end of the label makes the question required.
  • An attribute block {#id} at the end of the line sets the question's id.

The question continues on the following lines, up to the first blank line, page break, or next question. Leading whitespace on these lines is ignored. Each line is one of:

Line Meaning
> text Help text, shown below the label. Markdown. Consecutive lines are joined with line breaks.
[type attributes] The question type and its attributes. At most one per question.
___ A text answer. Three or more underscores.
- ( ) label A single-choice option.
- [ ] label A multiple-choice option.
- label An option without a marker.
| cell | cell | A table row, for grids.

Any other line is an error. A line that looks like part of a question, but is separated from it by a blank line, SHOULD produce a warning.

3.1 Ids#

Every question has an id. Answers are stored under this id, so it SHOULD stay the same once a form is in use.

Explicit ids match [A-Za-z0-9][A-Za-z0-9_-]*. A question without an explicit id gets one derived from its label:

  1. Decompose the label (Unicode NFKD) and remove combining marks.
  2. Lowercase it, and replace each run of characters other than a–z and 0–9 with -.
  3. Remove leading and trailing dashes, and truncate to 40 characters (removing any trailing dash again).
  4. If the result is empty, use q.
  5. If the id is already taken, append -2, -3, and so on, until it is unique.

Explicit ids are reserved first, so a derived id never collides with an explicit id anywhere in the document. Derived ids are then assigned in document order. Duplicate explicit ids are an error.

Tools that publish a form SHOULD pin ids by writing an explicit {#id} on every question, so that later edits to a label don't change the id.

3.2 Attributes#

A [type ...] line takes attributes separated by whitespace:

  • key=value, where the value is either a bare word or double-quoted with \ escapes: placeholder="Your name".
  • A bare flag: shuffle.
  • A positional value, used by scale and rating: 1..5, "Low".

Attribute names are case-insensitive. Unknown attributes produce a warning.

4. Question types#

The type is set explicitly with [type]. Without one, it is inferred from the answer lines:

  1. options with ( ) markers, or without markers: radio
  2. options with [ ] markers: checkbox
  3. a table: grid
  4. two or more ___ lines: paragraph
  5. otherwise: text

Mixing ( ) and [ ] markers in one question is an error.

Type Answer Attributes JSON properties
text Short text placeholder, minlength, maxlength, pattern placeholder, minLength, maxLength, pattern
paragraph Long text placeholder, minlength, maxlength placeholder, minLength, maxLength
email Email address placeholder placeholder
url http(s) URL placeholder placeholder
number Number placeholder, min, max, step placeholder, min, max, step
date YYYY-MM-DD min, max min, max
time HH:MM min, max min, max
radio One option shuffle options, other, shuffle
checkbox Several options shuffle, min, max options, other, shuffle, minSelected, maxSelected
select One option, from a dropdown shuffle options, shuffle
scale Integer in a range a..b, two quoted labels, minlabel, maxlabel min, max, minLabel, maxLabel
rating Integer from 1 to max max (positional), max max
grid One column per row — rows, columns
checkbox-grid Several columns per row — rows, columns
file Uploaded files accept, maxsize, maxfiles accept, maxSize, maxFiles

checkboxes, dropdown and textarea are accepted as aliases for checkbox, select and paragraph.

4.1 Text#

? Name *
___

? Bio
___
___

? Postcode
[text placeholder="1234 AB" pattern="\d{4} ?[A-Z]{2}"]

pattern is a regular expression that the whole answer MUST match, with the same semantics as the HTML pattern attribute. minlength and maxlength count Unicode code points.

___ lines are allowed after an explicit type and are ignored. This lets authors keep the visual hint: [email] followed by ___.

4.2 Choices#

? Favorite color
- ( ) Red
- (x) Green
- ( ) Other: ___

? Toppings
[checkbox max=2]
- [x] Cheese
- [ ] Olives

? Country
[select]
- Netherlands
- Canada
  • (x) or [x] marks an option as selected by default. A radio or select question SHOULD have at most one default.
  • An option whose label ends with ___ is the other option: it adds a free-text answer. The text before the ___ (and an optional :) is its label, or Other if empty. There is at most one other option, and it is not allowed on select.
  • Option labels within a question MUST be unique.
  • An option MAY end with a jump: -> #page-id.

4.3 Scale and rating#

? How likely are you to recommend us?
[scale 0..10 "Not likely" "Very likely"]

? Rate the food
[rating 5]

A scale defaults to 1..5 and has at most 11 steps. A rating defaults to 5, with a maximum between 2 and 10.

4.4 Grids#

? Rate each session
[grid]
|          | Poor | OK | Great |
|----------|------|----|-------|
| Keynote  |      |    |       |
| Workshop |      |    |       |

The first table row lists the columns, starting from its second cell. Every following row is a grid row, labelled by its first cell. Other cells and separator rows are ignored. Rows and columns MUST be unique and non-empty. For a required grid, every row must be answered.

4.5 Files#

? Resume *
[file accept=".pdf,.docx" maxsize=10MB maxfiles=2]

accept is a comma-separated list of extensions (.pdf) and media types (image/*, application/pdf), as in HTML. maxsize accepts B, KB, MB and GB, using binary multiples (1KB = 1024 bytes). In JSON, maxSize is in bytes. maxFiles defaults to 1.

5. Pages#

A line of three or more dashes starts a new page (a section). A page break MAY carry an id:

--- {#details}

A # heading at the start of a page (before anything else) is the page title. On the first page, a leading # heading is the form title instead, unless the front matter sets a title. To start a page with a level 1 heading that is not the title, escape it as \#.

A page break before any other body content doesn't create an empty first page. It sets the first page's id and jump instead.

Pages without an explicit id get an id derived from the title, the same way as question ids, or page-N (1-based) when there is no title.

Because --- is a page break, Markdown content uses *** for a horizontal rule.

5.1 Content#

Lines that are not part of a question, a page break or a page jump are Markdown content. Consecutive content lines, including blank lines, form one content block. Inside fenced code blocks (``` or ~~~), question, page-break and jump syntax isn't recognized. To start a content line with ? followed by a space, escape it as \?; to write a line that would be a page jump, escape it as \->.

Implementations MUST render Markdown safely: raw HTML MUST be escaped or sanitized, and links MUST NOT use javascript: or other script-capable URL schemes. Implementations SHOULD support at least paragraphs, headings, lists, blockquotes, code, emphasis, links and images.

5.2 Jumps#

A jump sends the respondent to another page. Its target is #page-id, or submit to end the form.

  • An option of a radio or select question MAY have a jump. It applies when that option is chosen. Jumps on checkbox options are ignored with a warning.

  • A page MAY have a page jump: a line containing only -> and a target, outside any question. It applies after that page when no option jump applies. It is conventionally written at the end of the page:

    ? Invoice number
    ___
    
    -> submit

    The page jump MAY instead be written on the page break that starts the page: --- {#billing} -> submit. A page has at most one page jump.

After a page, the next page is determined as follows:

  1. The last radio or select question on the page whose chosen option has a jump decides.
  2. Otherwise, the page's own jump applies.
  3. Otherwise, the next page follows, or the form is submitted after the last page.

A jump to an unknown page is an error. Jumping to the same or an earlier page produces a warning. Implementations MUST stop when a page would be visited twice.

6. JSON form model#

The JSON model is described by the JSON Schema. Here is the first example as JSON:

{
  "$schema": "https://askdown.dev/schema/v0.1.json",
  "askdown": "0.1",
  "title": "DevConf RSVP",
  "settings": { "submitLabel": "Send RSVP" },
  "pages": [
    {
      "id": "page-1",
      "next": "submit",
      "items": [
        { "id": "full-name", "type": "text", "label": "Full name", "required": true },
        {
          "id": "will-you-attend",
          "type": "radio",
          "label": "Will you attend?",
          "required": true,
          "options": [{ "label": "Yes" }, { "label": "No", "goto": "bye" }]
        }
      ]
    },
    {
      "id": "bye",
      "title": "Sorry to miss you",
      "items": [{ "id": "why-not", "type": "paragraph", "label": "Why not?" }]
    }
  ]
}
  • askdown is the spec version, "0.1".
  • A page's items are questions and content blocks ({ "type": "content", "markdown": "…" }).
  • Optional properties are omitted rather than null. Boolean properties are omitted when false.
  • Question ids and page ids are each unique within a form.
  • Objects MAY contain extension properties starting with x-, which implementations MUST ignore when they don't understand them.

A conforming parser MUST produce the JSON model described here, and a conforming serializer MUST produce text that parses back to an equal model. The examples directory contains pairs of .ask.md and .json files for conformance testing.

7. Responses#

A response maps question ids to answers:

Question type Answer
text, paragraph, email, url, date, time string
radio, select string: the chosen option label, or the other text
checkbox array of strings: chosen labels in option order, then the other text
number, scale, rating number
grid object: row label → column label
checkbox-grid object: row label → array of column labels
file array of { "name", "size", "type" } objects, plus implementation-specific keys
{
  "full-name": "Ada Lovelace",
  "will-you-attend": "Yes",
  "which-days": ["Day 1", "I'll decide later"],
  "rate-the-sessions": { "Keynote": "Great" }
}

When validating a response, implementations MUST:

  1. Trim answers. An empty string, empty array or empty object is no answer.
  2. Determine the pages the respondent visits, following jumps with the given answers.
  3. Only check questions on visited pages, and discard answers to questions on other pages.
  4. Reject a missing answer to a required question, and answers that don't satisfy the question's constraints.

8. Reserved for future versions#

The following are planned, and implementations SHOULD NOT use these names for other purposes: quiz mode (points, correct and feedback on questions), conditional display (if), and answer piping in labels.

Changelog#

  • 0.1 (2026-09): First draft.