Building content right: Markdown, HTML and animation
What works in the two formats of the editor, what the screen removes and how long animated content may run. With examples to copy.
Content with the Document template is text that you write in the editor as Markdown or as HTML. You choose between the two in the Format field. Markdown is enough for most texts, HTML is what you need for your own design and for motion.
This guide shows what works in both formats, what the screen removes and how long animated content may run. Every example can be copied and pasted into the editor; the preview next to it shows right away how it looks on the screen.
Markdown
Markdown is plain text with a few characters for the layout.
Format text
# Title,## Subtitlefor headings**bold**,*italic*- Start a line with
-for a bullet list,1.for a numbered one - New line: just press Enter
---on its own line draws a divider[Text](https://…)for a link- Insert an image: the Insert image button above the text field. To align it, put
"center"or"right"after the image URL, e.g.(left otherwise)
Icon shortcodes
A shortcode turns into a coloured icon button, the second form is also animated:
:phone:/:phone-ring:phone, rings:link:/:link-pulse:link, pulses:mail:/:mail-shake:letter, shakes:calendar:/:calendar-flip:calendar, flips:location:/:location-bounce:location, bounces
A time like 10:30 stays unchanged.
HTML inside Markdown
Markdown may contain raw HTML, such as a <div> or a <style> with an animation. The same rules apply as in the HTML format, including those on length. What the screen removes is described in the next section.
# Water outage
On **Tuesday** the water is off from 8 to 11 am.
- Please fill a jug beforehand.
- The property management answers questions :phone:
---
Thank you for your understanding.HTML
In the HTML format you write an HTML fragment, without <html>, <head> and <body>.
Allowed formatting
Common tags work: <h1> to <h3>, <p>, <strong>, <em>, <ul> / <ol> / <li>, <a href="…">, <br>, <mark>. For layout you have <div> and <span> with class and style, and your own CSS goes into a <style>.
Images
Images from the media library only, meaning addresses under /cdn/assets/…. In the media library the Copy URL button copies the address of an image. The screen removes an <img> with a foreign address entirely, as it does an <image> in an SVG graphic.
Addresses in CSS
CSS may only load what comes from the media library as well. This applies to url() in any property, to image-set() and to @import, in a style attribute as in a <style> block. In addition, you may embed images and fonts directly as data:, such as a font with src: url(data:font/woff2;base64,…) in @font-face. If the CSS contains any other address, the content cannot be saved, and the editor points this out beforehand in a note.
What is removed
Before showing content, the screen removes:
- every
<script> <iframe>,<object>and<embed>- event handlers such as
onclickoronload - links with
javascript: - images, videos and sounds from foreign addresses (
<img>,<video>,<audio>,<source>) - CSS with a foreign address: in a
styleattribute the whole attribute is dropped, in a<style>block the affected rule - in SVG graphics every reference to another file, such as
fill="url(https://…)"or<use href="https://…"> - anything that is not on the lists in the next section, such as
<input>,<form>or the attributebackgroundon a table
If your content contains any of this, the editor shows a note below the text field. Such content cannot be saved, and the rejection names the spot. The one exception is an image in Markdown notation with a foreign address: it is saved, but does not appear on the screen.
Icon badges
Shortcodes like :phone: work in Markdown mode only. In HTML you write the badge directly as a <span> with two classes, and a third class sets it in motion:
- Phone:
q-ico q-ico-tel, ringing withq-anim-ring - Link:
q-ico q-ico-link, pulsing withq-anim-pulse - Letter:
q-ico q-ico-mail, shaking withq-anim-shake - Calendar:
q-ico q-ico-cal, flipping withq-anim-flip - Location:
q-ico q-ico-pin, bouncing withq-anim-bounce
For example <span class="q-ico q-ico-tel q-anim-ring">☎</span>.
<div>
<h2>Laundry room</h2>
<p>The new schedule applies from <strong>1 November</strong>.</p>
<ul>
<li>Monday to Friday, 7 am to 9 pm</li>
<li>Saturday, 8 am to 6 pm</li>
</ul>
<p><mark>The laundry room stays closed on Sundays.</mark></p>
<p><span class="q-ico q-ico-tel">☎</span> The property management answers questions.</p>
</div>Allowed elements and attributes
The screen only shows elements and attributes that are on the lists below. It removes everything else before display, and the editor points this out to you beforehand with a note. For an element such as <font> or <center> only the element is dropped, the text inside it stays. The lists are the same ones the screen uses for cleaning up.
SVG graphics are allowed, including gradients, masks and shadows. A reference may only point within the content, such as fill="url(#gradient)" or <use href="#dot">. The screen removes references to other files and images from foreign addresses.
HTML elements
<h1><h2><h3><h4><h5><h6><p><br><hr><div><span><section><article><figure><figcaption><strong><b><em><i><u><s><mark><small><sub><sup><code><pre><blockquote><ul><ol><li><table><caption><thead><tbody><tfoot><tr><th><td><a><img><video><audio><source><track><style>
SVG elements
<svg><g><defs><symbol><use><title><desc><path><rect><circle><ellipse><line><polyline><polygon><text><tspan><clipPath><mask><pattern><marker><linearGradient><radialGradient><stop><image><filter><feDropShadow><feGaussianBlur><feOffset><feFlood><feComposite><feMerge><feMergeNode><feBlend><feColorMatrix>
Attributes on every element
classidtitlelangdirrolestylearia-…data-…
Additional attributes of individual HTML elements
<a>:hrefxlink:hreftargetrel<img>:srcaltwidthheight<video>:srcposterwidthheightcontrolsautoplayloopmutedplaysinlinepreload<audio>:srccontrolsautoplayloopmutedpreload<source>:srctype<track>:srckindsrclanglabeldefault<ol>:startreversedtype<li>:value<th>:colspanrowspanscope<td>:colspanrowspan
Attributes on every SVG element
xmlnsxmlns:xlinkviewBoxpreserveAspectRatiotransformxywidthheightcxcyrrxryx1y1x2y2fxfydxdydpointspathLengthfillfill-opacityfill-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-miterlimitstroke-opacityopacitycolordisplayvisibilityvector-effectpaint-orderfont-familyfont-sizefont-weightfont-styletext-anchorletter-spacingword-spacingtext-decorationdominant-baselinealignment-baselinebaseline-shiftclip-pathclip-ruleclipPathUnitsmaskmaskUnitsmaskContentUnitsfilterfilterUnitsprimitiveUnitsmarker-startmarker-midmarker-endmarkerWidthmarkerHeightmarkerUnitsrefXrefYorientpatternUnitspatternContentUnitspatternTransformgradientUnitsgradientTransformspreadMethodoffsetstop-colorstop-opacityinin2resultstdDeviationflood-colorflood-opacityoperatork1k2k3k4modetypevalues
Additional attributes of individual SVG elements
<use>:hrefxlink:href<image>:hrefxlink:href
Attributes with an address
<a href><a xlink:href>: A link with http, https, mailto, tel or a relative address. Any other target is removed, the link text stays.<img src><video src><audio src><source src><track src><image href><image xlink:href>: Only an address from the media library (/cdn/assets/…). Otherwise the whole element is removed.<video poster>: Only an address from the media library (/cdn/assets/…). Otherwise only the attribute is removed.<use href><use xlink:href>: Only a reference within the content such as #dot. Otherwise the attribute is removed.every SVG attribute: url() only as a reference within the content such as url(#gradient). If it holds any other address, the attribute is removed.style: Addresses only from the media library, plus images and fonts as data:. The details are in the HTML section under “Addresses in CSS”.
<div>
<svg viewBox="0 0 320 80" role="img" aria-label="Welcome">
<defs>
<linearGradient id="gradient">
<stop offset="0" stop-color="#12296b" />
<stop offset="1" stop-color="#1a5c3a" />
</linearGradient>
<circle id="dot" r="10" />
</defs>
<rect width="320" height="80" rx="12" fill="url(#gradient)" />
<use href="#dot" x="40" y="40" fill="white" />
<text x="64" y="47" fill="white" font-size="22">Welcome</text>
</svg>
</div>Animation and length
Motion comes from CSS animations alone, in a <style> or in the style attribute, because the screen removes scripts. These animations determine the length of a content item.
How it is measured
- An infinite loop (
infinite) counts with its duration. Its delay only shifts where in the loop it starts and does not count. - A finite animation counts with delay plus duration times iterations:
animation: title 5s 2s 3gives 17 seconds. - If the content has several animations, the longest one counts.
- The motion of the icon badges (classes starting with
q-anim-) does not count. - If the content has several language versions, the longest of them counts.
The limit
Animated content may be at most 30 seconds long, the same limit as for a video. The editor shows the measured length below the preview and blocks saving while it is above the limit. A video is trimmed to the limit on upload, whereas animated content that is too long is rejected on saving: trimmed, its ending would silently be missing on the screen.
If a time value uses var(), calc() or a related function such as min(), max() or clamp(), the length cannot be determined, and the content is rejected on saving as well. Write duration and delay as fixed values in s or ms, or state the length yourself with data-duration.
On the screen
If the measured length is longer than 12 seconds, the content holds its zone for exactly one pass, like a video, and then the next content follows. A shorter animation holds nothing: the content changes at the usual pace, and a small loop such as a pulsing arrow simply keeps running while it is visible. This applies in every zone of the screen.
<div>
<style>
.title { animation: fade-in 2s ease-out 1s both; }
.alternate { position: relative; }
.alternate p { animation: show 20s linear infinite; }
.alternate p + p { position: absolute; top: 0; animation-delay: -10s; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes show {
0%, 45% { opacity: 1; }
50%, 95% { opacity: 0; }
100% { opacity: 1; }
}
</style>
<h2 class="title">Autumn clean-up on Saturday</h2>
<div class="alternate">
<p>We meet at 9 am in the courtyard.</p>
<p>Gloves and bags are provided.</p>
</div>
</div>Stating the length with data-duration
With the attribute data-duration you set the length of a content item yourself, for example <div data-duration="20s">. The statement wins over the measured animations, even if they would be longer or could not be determined; var() and calc() in the animations then no longer matter for the length. It also applies to content without any animation, which then stays in its zone for exactly that long. Unlike a measured length, a stated length holds the zone even when it is below 12 seconds.
Where it has to go
On the outermost element, meaning the first element in the source. A <style>, a <script> and comments before it are skipped. In Markdown the first HTML element in the raw text counts, even if a heading with # stands above it. On an inner element the statement has no effect.
Valid values
A number with s or ms, for example 20s, 1500ms or 12.5s. A number without a unit, a space between number and unit, a comma instead of the point and the value zero are invalid. With an invalid value the editor blocks saving and names the value. The limit of at most 30 seconds applies to the statement as well.
<div data-duration="20s">
<h2>Residents' meeting</h2>
<p>Thursday, 7 pm, in the common room.</p>
</div><div data-duration="15s">
# Lift under maintenance
Please use the stairs until Friday.
</div>The trap: <style> as the first element
If a <style> is the first element in the source, the browser moves it into the head of the document while parsing, and the screen discards it there. The animations are then missing, and the editor measures no length either. The preview in the editor shows the same, so you see it before saving.
The remedy is a wrapping element: put the whole content including the <style> into a <div>. The same applies in Markdown when the text starts with a <style>.
<style>
.title { animation: fade-in 2s both; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
</style>
<h2 class="title">Welcome</h2><div>
<style>
.title { animation: fade-in 2s both; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
</style>
<h2 class="title">Welcome</h2>
</div>