QnA Markup Syntax & Usage

Syntax & Usage (How-To Guide)

Flowchart from the quick start guide

Not up for a full syntax lesson? Download and print this three-page quick start guide. It has pretty flowcharts.

Quick Start Guide

QnA is a markup language for people with little or no programming experience. It was designed with attorneys in mind and transforms blocks of text into interactive question and answer sessions (QnAs). These QnAs can be used as stand-alone expert systems or in the aid of rule-based document construction. See example below. Plus, they can be fun, the entire project is open source, and as a bonus, every QnA can be turned into a flowchart.

Authors define behavior by placing text after one of ten tags† described below. On this page, you'll find everything you need to write your own QnA. Assuming an average reading speed of 275 words per minute, this entire page should only take a little over 20 minutes to read. Of course, you should probably open the QnA editor and play around as you read. So it might take an hour before you're an expert. ;)

If you're pressed for time, start with the Body section. That's where all the exciting stuff happens, and you can build a very respectable QnA with body tags alone. Alternatively, you could try our quick start guide.

If you're looking for some help structuring your thoughts, this video lesson on Thinking in Flowcharts has a lot of good practicle advice you can put to use building QnAs.

Sections

Header

The header is optional text appearing before the first non-header tag (those tags listed under Body below). The header can be empty or contain any of the following five header tags. The values of these tags are defined by the text between tags. The order of tags is unimportant as long as they appear before the first non-header tag. If you use a tag twice, the last value provided will be used to define the tag's value.

Title: text/html

If present, contents of the Title tag are visible in the credits at the bottom of the QnA output and as the title of any stand-alone HTML page. The contents of this tag are also used to define the title element in Open Graph metadata associated with any stand-alone HTML page. Contents may include HTML, but such styling will only appear in the credits, not in the HTML page's title or metadata.

Author: text/html

If present, contents of the Author tag are visible in the credits at the bottom of the QnA output. Contents may include HTML.

Description: text/html

If present, contents of the Description tag are visible in the credits at the bottom of the QnA output. They are also used to define the description element in Open Graph metadata associated with any stand-alone HTML page. Contents may include HTML, but such styling will only appear in the credits, not in the HTML page's metadata.

Before: text/html

If present, the contents of this tag are placed in the QnA output directly preceding the rendering of the first Q: tag, but after a definition of the QnA's CSS and the declaration of its FORM element. This tag should include anything you want to place in the HTML output at this point. For example, you could redefine style elements, define hidden form values, add a title... You can also define Javascript here with a <script> tag. It runs before the first question is shown, so it is the place to define functions that your A tags or later scripts call (see Adding Your Own Scripts).

After: text/html

If present, the contents of this tag are placed at the end of the QnA output directly following the rendering of its footer link(s). This is after the closing of the output's FORM element. This tag should include anything you want to place in the HTML output at this point, perhaps some Javascript. Any <script> tags found here are run once the QnA has been placed in the page (see Adding Your Own Scripts).
↑ Back to top

Body

The Body is where you define questions and answers along with the text of any would-be documents. This content can be plain text, or it can be formated as HTML. The output of a QnA is an interactive HTML document. Consequently, if you would like to include comments (text that doesn't show up in your output), you can hide them like this: <!-- comment text here -->, just as you would in HTML. To get a good feel for what's possible in QnA, read through the following tag descriptions.

Q(variable_name): text/html

The content of Q (question) tags are rendered inside left-aligned text bubbles. The first Q tag's content is visible after loading. The content of other Q tags become visible after a user selects its preceding A tag.

For example, the first question below is displayed after loading, along with its possible answers (those A tags in a vertical line below its Q). After a user selects an answer, the content of the Q following that answer is displayed, along with its possible answers (if any).

Q: first questionA: first answer to first question 	Q: first question under the first answer to the first questionA: second answer to first question	Q: first question under the second answer to the first question

The Q tag must start a new line or be indented one level deeper than the preceding A tag. Indent with tabs or spaces, whichever you prefer; what matters is that tags at the same level line up.

After rendering, by an interpreter every Q tag will have a unique variable_name placed in a parenthetical between its Q and colon based on its relation to other Q tags. If left in the default form of alternating numbers and periods (e.g., 1.1.1.3.4), these variable_name update with every rendering. That is, when you click Update Outputs in the editor. You can change these default names to any unique combination of letters, numbers, periods, dashes, and underscores (e.g., my_cool_variable). Regardless of the format, variable_names can be used in conjunction with the GOTO tag to direct users to Q tags that would otherwise be inaccessible given the linear nesting of questions and answers. A discussion of the GOTO tag can be found below.

Q(1): A: 	Q(1.1):A: 	Q(1.2):Q(2):

Formatting:

QnA outputs HTML files. So you can format questions in their text bubbles using standard HTML. If, however, you would like the text of a question to display in multiple text bubbles, use <br><br> to create a bubble break. This will split the current text bubble in two at the point where it is included. You may recognize this as two HTML line breaks. If you want to place two HTML line breaks in a bubble without triggering a bubble break, simply add a space between the two tags (<br> <br>), and they will not create a bubble break.

GOTO:variable_name

The GOTO tag moves a user to the location targeted by its variable_name.

For example, given the QnA below, a user answering "Red Sox" will find themselves presented with the reply "Cool." Whereas, a user answering "Yankees" will find him/herself presented with the text "Seriously..." followed by the original question, "Red Sox or Yankees?"

Q(1): Red Sox or Yankees?A: Red Sox	Q(1.1):GOTO:2 A: Yankees	Q(1.2): Seriously... GOTO:1Q(2): Cool.

GOTO calls can only appear at the end of a Q tag, and there can only be one GOTO call per tag. They are not allowed in A tags.

If the target of a GOTO is removed or the variable_name shows up more than once before the ids are recalculated: the GOTO is declared ambiguous, and an error is thrown. If the target of a GOTO is renumbered, in most cases, the GOTO is renumbered as well. Remember, such an update only happens if you click Update Outputs in the editor. It is not triggered by the live preview.

Note: DOC: tags (described below) are included when a user is moved to a target location.

To make the same kind of jump from JavaScript, see the goto() function (described below).

A(variable_value): some text/html, A(variable_value)[href]: some text/html, or A(variable_value):[href] some text/html

The A tag is rendered as a button following the preceding question's text. By default, clicking on this button will replace all buttons with a right-aligned word bubble containing the contents of the selected button and followed by the text of the next nested question in a left-aligned text bubble.

The A tag must line up with the preceding Q tag (i.e., have the same number of tabs between it and the start of the line). You can have as many A tags following a Q as you like. For example:

Q: A: A:A:		Q:		A:		A:
Advanced Usage

Variables

Answers are stored in variables with names defined by variable_name in the parent Q tag. By default, answer values are equal to the text/html in the A tag. However, if the A tag is written with parentheses (e.g., A():) the contents of these parentheses are used as the answer.

You can access an answer's values by enclosing its variable_name like so: <x>variable_name</x>. Instances of such enclosures will be replaced with that variable's value. For example, consider the following.

Q(drink): Coffee or tea?A: coffee	Q(1.1):GOTO:extrasA: tea	Q(1.2):GOTO:extras​Q(extras): Milk and sugar?A(milk and sugar): Yes. Milk and sugar.	Q(2.1):GOTO:gotitA(nothing added): No. I take it black.	Q(2.2):GOTO:gotitA(milk): Milk only.	Q(2.3):GOTO:gotitA(sugar): Sugar only.  	Q(2.4):GOTO:gotit​Q(gotit): Got it. You like <x>drink</x> with <x>extras</x>. 

Note: the replacement of <x>variable_name</x> with user values includes text inside DOC: tags (described below).

In addition to the replacement example above, user answers are stored in the interview itself. So if you use the submit2() function (described below) these variables will be passed along. Also, you can get at these values using Javascript's innerHTML property: document.getElementById("variable_name").innerHTML.

Links

If the A tag is written with brackets (e.g., A[]: or A:[]) the contents of the brackets will be passed to that button's href attribute. That is, the button can be turned into a link. If the brackets fall before the colon, the link will target the page the button is on. If the brackets follow the colon, the href will target a new blank page/window. In HTML, A[http://www.nasa.gov]: I love NASA effectively becomes <a href="http://www.nasa.gov">I love NASA</a>, whereas, A:[http://www.nasa.gov] I love NASA effectively becomes <a href="http://www.nasa.gov" target="_blank">I love NASA</a>.

A bracket may run over several lines, which makes longer scripts much easier to write and read. Each line is trimmed and // comments are removed before the code runs. If you need a literal ] inside a bracket, write it as \].

A[javascript:	// gather the answers so far	var s = transcript();	alert("Answers so far:\n" + s);]: Show me a summary.

When used in conjunction with QnA's predefined Javascript functions (described below), the href argument can do some neat stuff above and beyond linking to things because you can uses a href to run Javascript.

For example, you could use the save2() and transcript() functions to let a user save their conversation to a file.

Q(1): Do you want to see something neat?A: Yes.	Q(1.1): Cool. Click away.	A[javascript:save2('transcript.txt', transcript());]: Save conversation.		Q(1.1.1):GOTO:1A: No.	Q(1.2):GOTO:1

X:

Use the X tag in the place of an A tag when you would like users to type their own answer. Instead of a button, it will present as a input/text field. The contents of such a field is saved as the question's variable value, and its names is based on the Q tag's variable_name. Because the name comes from the Q tag, nothing needs to follow the colon; text placed there is ignored, and the editor shows a warning so you know it is having no effect.

To ask for a number, write X:number. The field then only accepts a number (with a decimal point if the user needs one) and, on phones, brings up the numeric keypad; what is saved in the variable is the number as typed. This is the one word that means something after the colon.

As with A tags, if you enclose a variable name like so <x>variable_name</x> it will be replaced by that variable's value.

For example, in the QnA below, if a user types in "David," the QnA's reply would read "Nice to meet you David."

Q(name): What is your name?X: 	Q: Nice to meet you <x>name</x>.

Note: as with A tags, the replacement of <x>variable_name</x> with user values includes text inside DOC: tags (described below).

Advanced Usage

When the X tag is used, as with the A tag, in addition to the replacement described above, user answers are stored in the interview itself. So if you use the submit2() function (described below) user variables will be passed along. Also, you can get at these values using Javascript's innerHTML property: document.getElementById("variable_name").innerHTML.

Running JavaScript

Like an A tag, an X tag can be written with brackets, but here they do one job only: they hold JavaScript to run once the user has submitted their text. There is no button link to fill, so the contents must start with the javascript: prefix, e.g., X[javascript:alert('Got it!');]:. The code runs after the user's text has been saved as the question's variable, so it can read the new value (and any earlier ones) and act on it. It runs whether the user presses Enter or clicks the button under the field, and it does not run if the field was left empty (the user is asked to type something instead).

The brackets may fall before or after the colon, X[javascript:]: or X:[javascript:]; unlike the A tag, it makes no difference which. Keep the bracket right against the colon, with no space between them. Empty brackets (X[]:), or brackets whose contents lack the javascript: prefix, are reported as errors, as is more than one set of brackets.

As with A tags, the bracket may run over several lines: each line is trimmed, // comments are removed, and a literal ] is written \]. In the QnA below, once a name is typed it is read back from the document and placed in the page's title.

Q(name): What is your name?X[javascript:	// "name" now holds what was typed	var n = document.getElementById("name").value;	document.title = "A QnA for " + n;]:	Q: Nice to meet you <x>name</x>.

The code is only run when the user answers. It is not run again when the conversation is redrawn, for example after GO BACK ONE or when saved progress is restored. It has the same reach as a script in an A tag: the predefined functions (described below) and anything defined by a script in your Before: tag can be called from it. In particular, goto() (described below) lets the code choose the next question based on what was typed.

DOC: text/html

You associate a DOC: tag with a Q: tag by placing it in line with and directly before the Q: tag. When a Q tag is displayed to a user, the content of its associated DOC: tag is added to a QnA document variable.

For example, in the QnA below, items are added to a shopping list based on the meals a user selects. You are then presented with two options for viewing the shopping list: (1) on screen by reading the contents of the document into an standard Javascript alert window; or (2) saving the shopping list as a text file using the save2() function (described below). Both methods access the document via the doc() function (described below).

DOC(1):SHOPPING LIST​Q(1): What would you like to cook?A: Garlic Chicken	DOC(1.1):Garlic Chicken	4 boneless skinless chicken breasts	4 garlic cloves, minced	4 tablespoons brown sugar	1 tablespoon olive oil	additional herbs and spices, as desired	Q(1.1):GOTO:2A: Mac and Cheese	DOC(1.2):Mac and Cheese		3/4 pound dried elbow macaroni	1 1/2 cups grated  sharp cheddar cheese	1/2 cup grated gruyere cheese	1/3 cup panko bread crumbs, toasted until golden	Q(1.2):GOTO:2Q(2): Okay. I have the shopping list ready. How would you like it?A[javascript:alert(doc());]: In an alert box.	Q(2.1):GOTO:3A[javascript:showdoc('Edit, then print, save, or copy.');]: As a file I can save.	Q(2.2):GOTO:3A[javascript:save2('list.txt',doc());]: As a file I can save.	Q(2.3):GOTO:3Q(3): Enjoy the grub.
Advanced Usage

You can make use of the submit2() function (described below) to pass your document to an editor. So instead of saving a document directly to ones computer, a user could have a chance to edit their document before saving.

Note: the DOC: content is just text. It doesn't matter if it's HTML, markdown, LaTeX, CommonAccord, whatever you like.

↑ Back to top

Form fields in questions

An X tag gives you one text (or number) field per question. For anything else a web form can ask for — a date, a checkbox, a set of radio buttons, a drop-down, a longer note, a colour, a range — write the ordinary HTML control in the question's text. A control with a name becomes a variable of that name, just like a question's: its value is stored, it can be placed in later questions and documents with <x>name</x>, read with getvar(), and it is included in json_str() and in what submit2() sends. The question still needs an A (or X) tag to move on; think of that button as the form's submit button.

Q(details): A few details, please.<br>Date of birth: <input type="date" name="dob" required><br><input type="checkbox" name="pets" value="cat"> I have a cat<input type="checkbox" name="pets" value="dog"> I have a dog<br>State: <select name="state"><option>MA</option><option>NY</option></select>A: Continue	Q: Born <x>dob</x>, in <x>state</x>, with: <x>pets</x>

How it behaves:

Two things the editor warns about: a control with no name (it is shown, but nothing entered in it is kept), and a name that is also a question's name or id (the two would write the same variable). Field names are otherwise shared across the whole interview, including QnAs brought in with loadQnA(), so use a fresh name for each thing you ask. File pickers and buttons are not recorded.

↑ Back to top

Predefined Javascript Functions

All interactive QnA documents come preloaded with a set of Javascript functions. As described above, these can be called from an A tag using the syntax: A[javascript:function_name();]:. Below we'll explain what each of these functions do.

transcript(format);

This function will return a transcript of the current QnA as it exists at the time the function is called. For example, when selected, the following tag will display a transcript in an alert window. A[javascript:alert(transcript());]: button text.

This function accepts an argument called format. When set equal to 1, the transcript will include HTML found in the original QnA questions and answers. Otherwise, all HTML will be removed from the presented transcript. For example, this call will include HTML A[javascript:alert(transcript('1'));]: , and this one will not A[javascript:alert(transcript());]: .

You may recall the use of this function from the links example above.

doc();

This function will return the DOC: content associated with rendered Q tags. For example, when selected, the following tag will display the DOC: content: A[javascript:alert(doc());]: button text.

You may recall the use of this function in the shopping list example above.

showdoc(instructions);

Open the DOC: contents in a the same document as the QnA with a simple editable overlay with Print, Save as HTML and Copy buttons. Note: This editor assumes the content is plain text or HTML.

You may recall the use of this function in the shopping list example above.

json_str();

This function will return a JSON string containg the QnA's variable names and values as key-value pairs. For example, when selected, the following tag will display the the QnA's variables as a JSON string: A[javascript:alert(json_str());]: button text.

getvar(variable_name);

This function returns the value saved for the Q tag named variable_name: the text typed into an X tag, or the value of the A tag chosen. If that question has not been answered it returns undefined. For example, X[javascript:alert('Hello ' + getvar('name'));]:.

goto(variable_name);

This function moves the user to the Q tag targeted by variable_name, just as a GOTO: tag does (described above), only from JavaScript. That lets a script decide where the conversation goes next. As with GOTO:, the target's DOC: tag is included, and if the target itself ends in a GOTO: that is followed too. For example, the QnA below checks a typed answer with getvar() (described below) and picks the next question accordingly.

Q(number): What's the answer to the ultimate question of life, the universe and everything?X[javascript:	var n = getvar('number');	if (n == 42) { goto('right') }	else if (n < 42) { goto('low') }	else if (n > 42) { goto('high') }	else { goto('nan') }]:Q(right): That's right!Q(low): Too low. GOTO:numberQ(high): Too high. GOTO:numberQ(nan): So it turns out the answer is a number, and that's not a number. GOTO:number

The target may be a name or a number, so goto(Math.floor(Math.random() * 20) + 2) jumps to one of questions 2 through 21 at random. Prefer names where you can: unlike the targets of GOTO: tags, a number inside your JavaScript is not updated when the editor renumbers your questions.

Where the jump lands depends on where goto() is called from:

  • In the script of an answer, A[javascript:…]: or X[javascript:…]:, the target takes the place of the question that would otherwise follow that answer. So an answer whose script always calls goto() needs no question of its own beneath it, and one that calls it only some of the time falls through to the question beneath it the rest of the time.
  • In a script inside a Q tag, the jump is made once that question has been displayed, as if its text had ended in GOTO:.
  • Called at any other time (from a timer, say, or once a request for data has come back), the target is added after the question then showing.

GO BACK ONE works as it does everywhere else: it undoes the user's last answer together with any jump that followed it, and returns them to the question they answered (with their text back in the field, if it was an X tag). The jumps a user actually made are remembered with their answers, so when the conversation is redrawn (after GO BACK ONE, or when saved progress is restored) it is redrawn as it was, without running your scripts again. A random jump, for instance, is not rolled a second time.

In the flowchart, a goto() whose target is written out, goto('right'), is drawn as a dashed line marked JS GOTO from the question whose script it is (or whose answer's script it is) to the target, to tell it from the dashed line of a GOTO tag. A target worked out at run time, like the random one above, cannot be drawn. Inside a QnA loaded with loadQnA() (described below), goto() and getvar() look in that QnA first.

loadQnA(url, find, replace);

This function brings another QnA into the conversation. Called from an answer's script, A[javascript:loadQnA('…')]: or X[javascript:loadQnA('…')]:, it fetches the QnA at url and shows its first question in place of the question that would otherwise have followed the answer. From there the visitor is in the loaded QnA: its questions, answers, GOTO tags, DOC: tags and scripts all work as they would on their own, and they are drawn in the style of the QnA that loaded them (the loaded QnA's Title:, Author:, Description:, Before:, After: and hidden Settings: tag are ignored). Because the loaded QnA takes the answer's place, an answer that calls loadQnA() may not have a Q tag nested beneath it; the editor reports one as an error. Where the loaded QnA ends, the conversation ends, unless you bring it back with find and replace (below).

url may point at a text file of QnA Markup (the kind the source parameter takes, described below), at a web page with a QnA embedded in it (the first <script type="text/qna"> on the page is used), or at a link made by the editor's Link output, which carries the QnA inside it. A relative URL is resolved against the page the QnA is on, or, for a call made from a loaded QnA, against that QnA's own URL. As with the source parameter, the file is fetched by the visitor's browser, so it must be served over HTTPS with CORS headers that allow cross-origin reads (this also applies in the editor's preview, even for files on the editor's own server). If the file cannot be fetched, or is not a well-formed QnA, the conversation shows a message saying so, and the visitor can go back.

Coming back: find and replace. The loaded QnA knows nothing about the one that loaded it, so the return trip is set up by the loading author. find names a question in the loaded QnA and replace a question in your own: whenever the visitor would arrive at find, by a GOTO, by goto(), or by answering the question above it, they arrive at replace instead. So a loaded QnA that ends in Q(done): Thanks. can be sent anywhere you like with loadQnA('…', 'done', 'next'). To redirect several questions, pass an object: loadQnA('…', {done: 'next', quit: 'bye'}).

In the QnA below, an intake asks for a name, then hands over to one of two other QnAs (housing or family), and picks up again at next when either of them reaches its done.

Title: IntakeQ(name): What is your name?X:	Q(help): Hi <x>name</x>. What do you need help with?	A[javascript:loadQnA('https://example.com/housing.txt', 'done', 'next')]: A housing problem	A[javascript:loadQnA('https://example.com/family.txt', 'done', 'next')]: A family matter	A: Something else		Q(other): Tell us more.		X:			Q(other2): GOTO:nextQ(next): Thanks, <x>name</x>.

And here is housing.txt, which works on its own as well:

Title: HousingQ(name): First, what is your name?X:	Q(issue): <x>name</x>, is this about an eviction or repairs?	A(eviction): An eviction		Q(eviction): Did you get a notice to quit?		A: Yes			Q(e1): GOTO:done		A: No			Q(e2): GOTO:done	A(repairs): Repairs		Q(r1): GOTO:doneQ(done): That is all we need about housing.

Sharing answers between QnAs. Both QnAs above ask for a name. With Q Sharing on (the default; it is on the editor's Settings screen), a variable name given by an author means the same thing in every QnA loaded into a conversation, so when the housing QnA reaches its own Q(name), the question is not asked again. It is filled in from the earlier answer, and shown to the visitor as the question followed by the answer prefixed with the Prior Answers text, "Earlier you entered:" (also on the Settings screen, so it can be changed or translated). The variable keeps the value it already had. This works in both directions: had the housing QnA been loaded first, the intake's Q(name) would have been filled in instead. Only names you have given count; the numbered names the editor makes up (1.2.1) are never shared. And a QnA re-asking one of its own questions, as a GOTO loop does, is still asked, as is a question that was filled in once and reached again later. With Q Sharing off, nothing is shared: every loaded QnA keeps its variables to itself.

For an X tag the earlier answer is simply entered. For a question with A tags, the answer whose value (the parenthetical, or the button text) is the same as the earlier one is chosen. If none is exactly the same, the values are compared again with case, spaces, punctuation and other symbols ignored (letters, numbers and emoji are all that count), so Yes. matches yes; because that is a guess, the visitor is asked first, in a dialog whose text is the Confirm text on the Settings screen, with <x>answer</x> standing for the button. OK takes the answer; Cancel asks the question. When no answer matches either way, or two answers match alike, the question is asked, without a dialog. A filled-in answer counts as an answer in every way: its [javascript:…] runs, its DOC: content is collected, and it appears in transcript().

GO BACK ONE treats a filled-in answer as one step: going back onto it asks the question, an X field with the earlier text in it, ready to edit, and the new answer is what the variable holds from then on. Everything a loaded QnA adds is remembered with the visitor's answers, as text, so GO BACK ONE and saved progress (Save visitor progress) redraw the conversation exactly as it happened, without fetching anything again.

In the flowchart, an answer that calls loadQnA() leads to a box marked External QnA (captioned with the file's name when the URL is written out). A loaded QnA can itself call loadQnA(), as many levels deep as you like; each loaded QnA gets its own numbered names, so nothing collides.

mail2(to, subject, body);

When called, this function will make use of the mailto URI scheme to open a new email in the user's default email program. This email will be addressed to to, with the subject line subject, and the body of the email will be body. For example, when selected, the following tag will draft an email with the transcript of the current QnA addressed to jdoe@example.com with the subject line QnA Transcript. A[javascript:mail2('jdoe@example.com','QnA Transcript',transcript());]: button text. The contents of the doc() function might also make for good reading.

Note: Due to a common security setting, this function may not work if the QnA is embedded in an iframe.

save2(filename,content);

This function will save a file to the user's computer with the name filename and content equal to content. For example, when selected, the following tag will save a file named QnA_document.txt with contents equal to the output of the doc() function. A[javascript:save2('QnA_document.txt',doc());]: button text.

You may recall the use of this function from the save conversation example above.

submit2(action, method, docAs, instructions, transcriptAs, jsonAs, target);

The entierty of a QnA conversation is wrapped in an HTML FORM tag. This function will set that tag's action to action, its method to method, and its target to target. Note: the target parameter is optional. It's default setting is: target = "_self".

It will send the QnA's document as a single variable named docAs along with an HTML transcript named transcriptAs and a JSON string named jsonAs.

It will send all variables defined with their names as defined by the Q tag's variable_name as well as any hidden variables placed inside the document's Before, After, Q and A tags.

Additionally, it will send a variable named i with a value equal to instructions. This last variable is intended specifically for use with the WYSIWYG editor described below.

↑ Back to top

Adding Your Own Scripts

A QnA can carry its own Javascript. Place it in <script> tags, just as you would in an HTML page, inside a Before: or After: tag or in the text of a Q. Scripts run in the order they are written, and anything they define is available to the rest of the page: to later scripts, and to the href of any A tag. You can load a library too (<script src="https://..."></script>); the scripts written after it wait until it has loaded.

Scripts in Before: and After: run once, before the first question is shown. A script in a Q runs each time that question is shown (including when the conversation is redrawn after GO BACK ONE). For example, the QnA below defines a function in its header, uses it in a question, and again from a button.

Before: <script>function greet(name) { return "Hello, " + name + "!"; }</script>​Q(name): What is your name?X:	Q(1.1): <span id="hi"></span>	<script>document.getElementById("hi").textContent = greet(JSON.parse(json_str()).name);</script>	A[javascript:alert(greet("again"));]: Say it again.

If you copy a QnA into a web page by hand, remember that the markup itself sits inside a <script type="text/qna"> tag, so any closing </script> within it must be written <\/script>. The editor's Embed Code and HTML outputs take care of this for you.

↑ Back to top

Embedding a QnA in Your Own Page

The interpreter is a single JavaScript file served from www.qnamarkup.org (or host it yourself). Put your markup in a <script type="text/qna"> tag and it renders in place:

<script src="https://www.qnamarkup.org/dist/2.5.0/qna.min.js"></script><script type="text/qna">Q: Would you like to embed a QnA?A: Yes.	Q: Then you already have.</script>

The markup goes in a script tag with type text/qna because the browser leaves the contents of such a tag exactly as written (tabs, HTML and all).

Advanced Usage Style options go on the same tag as data- attributes, e.g. data-comp-bg="336699" data-font-size="16" data-footer="false"; the option names match the editor's Settings tab. data-chat-style="llm" shows questions as plain text on the page, like a chat assistant's replies, instead of in speech bubbles, and the text of the built-in buttons and footer links can be changed with data-label-save, data-label-back, data-label-restart, data-label-credits, data-label-edit and data-label-code (e.g. data-label-back="Previous"). The buttons themselves are colored with data-btn-bg (background) and data-btn-txt (text), data-btn-bold="true" sets their text in bold, data-btn-border colors the buttons' outline, and data-btn-divider colors the thin dividing lines above the go back / start over buttons and above the footer. data-save-progress="true" remembers a visitor's answers in their browser so they can leave and come back (off by default). Use data-target="#some-id" to render somewhere other than right after the script tag. The one sequence that cannot appear inside a script tag is a closing </script>; write it as <\/script> if you ever need it. However, if you're working the editor, it will take care of this for you, escaping the closing script before providing you with code.

The editor's Embed Code output writes a version-pinned URL together with an integrity hash, so your page keeps loading exactly the library it was written against no matter what is released later. Copy that rather than typing the tag by hand.

↑ Back to top

Loading a Remote QnA

If you have a text file containing QnA Markup at a URL, you can pass that URL to the stand-alone viewer or the editor using the source parameter:

[QnA instance's URL]/i/?source=[QnA text file's URL]

For example:

https://www.qnamarkup.net/i/?source=https://colarusso.github.io/QnAMarkup/examples/source/first_q.txt (view link)

The file is fetched by the visitor's browser, so it must be served over HTTPS with CORS headers that allow cross-origin reads. GitHub Pages, raw GitHub URLs, and most static hosts do this. Style options may be appended (e.g., &font_size=16).

The same kind of file can be loaded into a running conversation with the loadQnA() function (described above).

You do not need a remote file to share a QnA, though. The editor's Link output packs the whole QnA into the link itself (compressed), and its HTML outputs give you a page you can host anywhere.

↑ Back to top

Document Parsers & Editors

My hope is that this section will grow into a list of parsers as people point me to various parsers around the web. The basic idea is that by using the submit2() function in conjunction with a document parser/editor, it is possible to hold up the document created by a QnA for review by a person. Imagine a QnA that used a combination of DOC: and X: tags to craft a custom document for a pro se litigant. At the end of the QnA, that document can be passed to a parser and the user can take some time to edit the text before printing or saving it. The thing is that the DOC: tag doesn't really care what format its content is in. It could be HTML, markdown, or LaTex. By passing that content to a parser, it can be rendered and placed in a form that's easier for a user to digest.

Local WYSIWYG Editor

The document editor page at doc/ opens a document in a full WYSIWYG editor (CKEditor 4) where a person can review and touch it up before printing or saving it. It reads two variables: t, the content to display, and i, a set of instructions shown above the editor. Because the page is static (there is no server) it can only read them from the URL, so send them with the GET method — submit2() takes i as its instructions argument:

A[javascript:submit2('https://www.qnamarkup.net/doc/', 'GET', 't', 'Proof read your letter.');]: Show me my letter.

If you would rather not leave the QnA, the predefined showdoc(instructions) function opens the same document in a simple editable overlay with Print, Save as HTML and Copy buttons:

A[javascript:showdoc('Proof read your letter, then print it.');]: Show me my letter.

Of course, submit2() can still POST a document, transcript and JSON string to any server-side parser or service of your own.

For example, the QnA below can be used to create a letter to Santa. Note: the document is in HTML. So you'll notice that line breaks are indicated by the HTML tag <br>. Also, it is sent to the document editor page with GET.

Q(1): Would you like to write a letter to Santa? A: Yes.	Q(myname): What is your name?	X:		DOC(1.1.1):Dear Santa,<br><br> ​		Q(naughty): Have you been naughty or nice?		A(I am sorry that I have been naughty. I will work hard to be nice in the new year.<br><br>): Naughty			Q(1.1.1.1):GOTO:whatiwant		A(): Nice			Q(1.1.1.2):GOTO:whatiwant		A: No.	Q(1.2): That's cool. Have a good day.​Q(whatiwant): What would you like for Christmas?X:	DOC(2.1):<x>naughty</x>I would like <x>whatiwant</x> for Christmas. I hope all is well with you up north.<br><br>			Sincerely,<br>			<x>myname</x>	Q(2.1): Alright, are you ready to see your letter?	A[javascript:submit2('https://www.qnamarkup.net/doc/', 'GET', 't', 'Proof read your letter. Print it out, and mail it to: Santa Clause, North Pole')]: Yes.		Q(2.1.1): Thank you.

Working with .docx Documents

Although the above parsers offer a good deal of flexability, sometime you want to control a document's format with greater percision than allowed by HTML et al. For such instances, you can make use of .docx templates such as this one.

Instead of constructing the document in QnA you can merge your QnA answers with an existing template. Below we'll do this with a standard .docx (Word) file with mail merge feilds. The service below will take in a JSON string, the URL of a .docx file from which to make a merged .docx file, and the name for the output file. The service we'll be using below is an instance of docx_webmerge.

However, the tool only accepts .docx files from a whitelist of servers. if you're a non-profit and would like me to add your website to the whitelist for the example service available at http://colarusso.pythonanywhere.com/ (the app behind the live example below), let me know. As long as you aren't expecting wicked crazy volume, I'll probably just add you to the list.

That being said, let us write another letter to Santa.

Before: <input type="hidden" name="name" value="Letter to Santa"/><input type="hidden" name="docx_uri" value="https://www.qnamarkup.org/docxmerge/templates/Santa-letter.docx"/>​Q(1): Would you like to write a letter to Santa? A: Yes.	Q(myname): What is your name?	X:		Q(1.1.1):GOTO:whatiwant		​Q(whatiwant): What would you like for Christmas? X:	Q(2.1):Alright, are you ready to see your letter?	A[javascript:submit2('https://colarusso.pythonanywhere.com/','GET','','','','json_doc')]:Yes.		Q(2.1.1): Thank you.

Note: if you'd like to include instructions along with your document, consider appending an instructions page to the begining of the document.

↑ Back to top

Bots Building Bots

An AI assistant can write QnA Markup for you, or turn something you already have into a QnA, once it has been taught the language. The teaching comes as a skill: a small folder of files, written for assistants, that describes the syntax, the rules the interpreter enforces, the predefined functions, a set of checked examples and a script that checks a QnA the way the editor does. Give the skill to an assistant that supports skills and you can ask for things like:

  • "Help me write a QnA that helps a tenant work out whether they can be evicted."
  • "Encode the process in this document as a QnA." (with the document attached)
  • "Turn this intake form into a QnA that assembles a letter at the end."
  • "Here is my QnA and the error the editor shows; fix it."

The skill is in the repository under skills/qna-markup/, and copies are served here:

Installing it

Claude (the app at claude.ai and the desktop app): open Customize → Skills, find Add, and upload qna-markup.zip. The skill is then available in every conversation, and Claude picks it up on its own whenever a request is about a QnA. Skills are a Claude feature that may not be on for every plan; if you don't see the option, the fallback below works everywhere.

Claude Code and Cowork: unzip the file into ~/.claude/skills/ (for yourself) or into a project's .claude/skills/ folder (for everyone working on that project), so that the file SKILL.md ends up at ~/.claude/skills/qna-markup/SKILL.md. Both tools can also run the skill's checker, node scripts/check.js my_qna.txt, which prints the interpreter's own error messages with line numbers, so they will usually check their work before handing it over.

The Claude API accepts the same zip through its Skills feature, for developers building their own tools; see Anthropic's documentation for the current upload call.

Any other assistant (ChatGPT, Gemini, a local model): skills are just text. Paste the contents of SKILL.md, followed by reference.md, into the assistant's custom instructions, a project's instructions, or simply the start of the conversation, and ask away. The examples can be pasted too when you want the output to look like them.

Using it

Describe the interview you want, or attach the document, checklist or policy you want encoded, and ask for a QnA. The assistant should answer with plain text indented with tabs (or a .txt file), plus a line on how to run it. Paste that text into the editor: the live preview shows the interview, Update Outputs reports any errors with line numbers, and the Flowchart output lets you see the whole tree at once, which is the quickest way to check that the assistant understood the process. Anything wrong, paste the error (or the flowchart's problem) back and ask for a fix.

Two things to keep in mind. The skill teaches the language, not your subject: an assistant can write a fluent QnA that gets the law, the policy or the procedure wrong, so read the questions and the endings as carefully as you would a colleague's draft. And the skill is written for the current interpreter; when the language gains a feature, the skill in the repository is updated with it, so re-download it now and then.

↑ Back to top

†The Hidden Tag: Settings: name=value; name=value; ...

Depending on how you count, there is an "eleventh" tag, which you should never need to write or even see. When you click Save to File under your QnA code, the editor adds a final line to the saved text file holding everything from its Settings screen (fonts, sizes, colors, chat style, button colors and text, footer, and so on), so your QnA's look is saved along with its words:

Q(1): Do you like my colors?A: Yes.	Q(1.1): Thanks!​Settings: fontSize=18; compBg=336699; compTxt=ffffff; footer=false

When you Load File, the editor removes this line from the text and uses its values to fill in the Settings screen, which is why you never see it in the editor. The same happens if you paste a saved file into the editor and click Update Outputs.

The tag only counts when it is the very last line of a QnA. It is never part of the conversation. If a QnA containing it is placed in a web page or loaded from a remote file, its values style that QnA, but anything set explicitly wins: a data- attribute on the <script type="text/qna"> tag (see above) overrides the same setting in the tag. Names match those attributes without the data- prefix (data-font-size becomes fontSize, or font_size if you prefer); only the options found on the Settings screen are recognized, and anything else is ignored.

↑ Back to top