ChequoDocs
chequo.com

SVG animation format

This page is written to be pasted into an AI chat such as Claude or ChatGPT, together with the SVG animation prompt. It describes the one file the kiosk accepts as an animated welcome screen: a single animated SVG, with a loop that repeats without a jump. Follow every rule below exactly, and reply with only the SVG file.

For the whole picture (photos, videos, layouts and where to upload), see Welcome screen: photos, videos and animations.

Shape: which screens it is for

The shape of the drawing (its viewBox) says which kiosk screens it is made for:

ShapeExample viewBoxShown on
Square0 0 1000 1000landscape and portrait screens
Wide, at least 1.6 times as wide as tall0 0 1600 1000landscape screens only
Tall, at least 1.6 times as tall as wide0 0 1000 1600portrait screens only

A shape in between, such as 0 0 1200 1000, is refused. Draw for the screen your kiosk actually has: a wide drawing uses a landscape screen fully.

Positions: where the logo and buttons go

The welcome screen is split into three zones. On a portrait screen they are Top, Middle and Bottom; on a landscape screen Left, Centre and Right. The logo, the Start button and the language button each go in one zone; things in the same zone stack, logo first. Zones with nothing in them are free for the animation.

You choose the positions when you add the animation, and can change them any time in QR Menu → Kiosk. So tell the AI which zones to keep clear: for "logo at the top, buttons at the bottom" the subject belongs in the middle; for "everything on the left" of a landscape screen, keep the left third of the drawing quiet and put the action on the right.

Two layouts

Subject. A single thing, such as a dish, a drink or your mascot, on a transparent background. The kiosk fits it into the free zones, as large as they allow. If no zone is free, it sits behind the middle zone.

Full screen. A scene or pattern that fills the whole screen behind the logo and buttons, which get a soft shade so they stay readable. The kiosk scales the drawing to cover the screen and crops what does not fit, so keep anything that matters away from the edges. A square drawing loses the most: on both screen shapes only its central 56% is sure to show (560 Ɨ 560 of a 1000 Ɨ 1000 canvas). A wide or tall drawing made for one screen shape loses very little.

Colours

The kiosk tells the animation its five colours, the ones you set under QR Menu → Kiosk → Colours:

VariableColour
--kiosk-bgbackground
--kiosk-titletitles
--kiosk-textbody text
--kiosk-smallsmall text
--kiosk-accentaccent (buttons, highlights)

Use them for anything that should follow your branding, and keep natural colours for food. Always give a fallback colour, so the file also looks right when you open it on its own: var(--kiosk-accent, #c8553d).

If the Start button is hidden

You can hide the Start button. A tap anywhere still starts an order, but nothing on screen says so, so your animation should invite guests to tap: a pointing hand, a pulsing ring, or words such as "Tap to order".

Specification

These are the rules a file must follow. The back office checks all of them except the timing rules (durations that divide the loop, no positive delays) and the performance rules, which are up to you. A file that breaks a checked rule is refused with a list of the problems; nothing is removed or fixed for you.

The file

  • One SVG file (<svg xmlns="http://www.w3.org/2000/svg" …>), at most 500 KB.
  • At most 5,000 elements, nested at most 256 deep.
  • A viewBox that is square (0 0 1000 1000, for landscape and portrait screens), wide (at least 1.6 : 1, such as 0 0 1600 1000, landscape only) or tall (at least 1 : 1.6, such as 0 0 1000 1600, portrait only). Width and height are optional; the kiosk sizes it.

Root attributes

All on the <svg> element:

AttributeRequiredValue
data-kiosk-layoutyessubject (transparent background) or fullscreen (paint the whole canvas; keep what matters away from the edges)
data-kiosk-loopyesthe loop length in seconds, a plain number from 2 to 60 without a unit, such as 4 or 7.5
data-kiosk-stillnothe second of the loop to show as a still picture when the tablet is set to reduce motion; from 0 up to (not including) the loop length; default 0. Pick a moment where the picture looks complete.

Timing

The kiosk sets every animation in the file to the same point in the loop, so that kiosks side by side play in step. For that to work:

  • Every CSS animation's duration is data-kiosk-loop or a length that fits into it a whole number of times, and it repeats forever: animation: rise 4s ease-in-out infinite for a 4-second loop, or 2s for something that happens twice in each loop.
  • Every SMIL animation (<animate>, <animateTransform>, <animateMotion>, <set>) has a dur equal to the loop or a length that divides it evenly, and repeatCount="indefinite".
  • To make things happen at different moments, use keyframe percentages (40% { … }), or a negative animation-delay (-1.5s) to shift a copy of the same animation. Never a positive delay.
  • The end of the loop must match its start, so it repeats without a jump.
  • A CSS transform animation replaces the element's own transform="…" attribute. Animate a plain <g> and put any fixed transform on an element inside it.

Colours

  • Use the five variables --kiosk-bg, --kiosk-title, --kiosk-text, --kiosk-small and --kiosk-accent, each with a fallback colour: var(--kiosk-bg, #fff8f0).
  • Write them in CSS, in a <style> element or a style="…" attribute, not in fill="…" or stroke="…" attributes.

Allowed elements

svg, g, defs, symbol, use, path, circle, ellipse, rect, line, polyline, polygon, text, tspan, linearGradient, radialGradient, stop, clipPath, mask, pattern, filter, feGaussianBlur, feOffset, feFlood, feComposite, feMerge, feMergeNode, feColorMatrix, feDropShadow, image, style, title, desc, animate, animateTransform, animateMotion, set, mpath.

Any other element is refused. Use ordinary presentation, geometry, text, gradient, filter and animation attributes; id, class, style, role, aria-… and data-… attributes are fine (tabindex is not).

Not allowed

  • Scripts, <foreignObject>, links (<a>), and any element not in the list above.
  • Event attributes: anything starting with on, such as onclick or onload.
  • References to anything outside the file. href and xlink:href may only point at an id in the same file (href="#leaf"). In CSS and attributes, a url(…) may only point at an id in the same file (url(#glow)).
  • Pictures (<image>) only as an embedded PNG, JPEG or WebP: href="data:image/png;base64,…". Not SVG, not GIF, not a web address.
  • In CSS: no @import, no @font-face, no image-set, image, src, cross-fade or expression functions, and no backslash escapes (\).
  • No elements inside <style>; only CSS text (a CDATA section is fine).
  • No <!ENTITY …> declarations.
  • Fonts: no web fonts. Use generic families such as sans-serif, serif or system-ui.
  • The check is cautious and reads all text in the file, titles and visible words included. Do not write url(, image(, src(, @import, @font-face or a backslash anywhere, even in a <title>, in visible text or in a CSS comment.

Keep it smooth on kiosk tablets

Kiosk tablets have little graphics memory. Each moving group is kept as its own picture, and only the part near the screen is ready to draw. If big pieces jump far at the moment the loop restarts, the tablet must redraw several screens at once and the screen flashes. Follow these rules:

  1. Keep every moving group small. Any element or <g> that has an animation must fit within 1.5 times the canvas in each direction (at most 1500 Ɨ 1500 on a 0 0 1000 1000 canvas), including all its children. Never build one long strip (2, 3 or 4 screens wide or tall) and slide it across the screen.
  2. Keep moves short. No moving group may move more than half the canvas (500 units) in one animation cycle. For endless motion, such as a scrolling row of trees, falling snow or drifting clouds, draw a pattern that repeats every P units (P at most 500, for example trees every 250 units), make the group just wide or tall enough to cover the canvas plus one repeat (canvas + P), and move it by exactly P per cycle. At the end of each cycle it looks identical to the start, so the jump back is invisible and cheap.
  3. Animations may be shorter than the loop, as long as they fit into it a whole number of times. With data-kiosk-loop="60", the allowed durations are 60, 30, 20, 15, 12, 10, 6, 5, 4, 3 and 2 seconds (all with infinite). Use this for fast motion: snow that falls 250 units every 6 s instead of 3000 units every 60 s. Never use a duration that does not divide the loop.
  4. Prefer many small moving things over a few huge ones. Dozens of small shapes (snowflakes, leaves, toppings) are fine; avoid more than 4 moving groups that are each larger than the canvas.
  5. Static scenery (sky, ground, a still background) can be any size and does not count against these rules, as long as it does not move.

Before answering, check every moving group against rules 1 to 3: its size (width and height including children), its travel per cycle, and that its duration divides the loop.

Good to know

  • Keep it light: a few KB to a few hundred KB. Shapes and gradients are smaller and sharper than embedded pictures.
  • Test it by opening the .svg file in a web browser. It should loop smoothly.

Worked example

A subject, looping every 4 seconds: a cup that gently bobs while steam rises. The cup follows the kiosk's accent colour, the steam its small-text colour.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400"
     data-kiosk-layout="subject" data-kiosk-loop="4" data-kiosk-still="1">
  <title>A steaming cup of coffee</title>
  <style>
    .cup    { fill: var(--kiosk-accent, #c8553d); }
    .handle { fill: none; stroke: var(--kiosk-accent, #c8553d); stroke-width: 16; }
    .shadow { fill: var(--kiosk-text, #3b2f2a); opacity: 0.15; }
    .coffee { fill: #5a3825; }
    .steam  { fill: none; stroke: var(--kiosk-small, #9a8f85); stroke-width: 10;
              stroke-linecap: round; opacity: 0;
              animation: rise 4s ease-in-out infinite; }
    .s2 { animation-delay: -1.3s; }
    .s3 { animation-delay: -2.7s; }
    .bob { animation: bob 4s ease-in-out infinite; }
    @keyframes rise {
      0%   { opacity: 0;   transform: translateY(30px); }
      30%  { opacity: 0.9; }
      100% { opacity: 0;   transform: translateY(-40px); }
    }
    @keyframes bob {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-6px); }
    }
  </style>
  <ellipse class="shadow" cx="200" cy="340" rx="130" ry="20"/>
  <g class="bob">
    <path class="cup" d="M110 200 h180 v60 a70 70 0 0 1 -70 70 h-40 a70 70 0 0 1 -70 -70 z"/>
    <path class="handle" d="M290 220 a35 35 0 0 1 0 70"/>
    <ellipse class="coffee" cx="200" cy="200" rx="90" ry="14"/>
  </g>
  <path class="steam"    d="M160 170 c-20 -25 20 -45 0 -75"/>
  <path class="steam s2" d="M200 160 c-20 -25 20 -45 0 -75"/>
  <path class="steam s3" d="M240 170 c-20 -25 20 -45 0 -75"/>
</svg>

Every animation lasts 4 s, the same as data-kiosk-loop. The three wisps share one animation and are shifted with negative delays. Each animation ends where it starts, so the loop has no jump.