Patterns
Decks
A slide is read in seconds, from the back of a room, while somebody talks over it. Product rules tuned for desk distance are what make a deck boring — so this surface loosens the ones that only make sense up close, and holds every one that carries the brand.
Why decks differ
Same tokens. Different distance.
A slide is not a small web page. It is read in seconds, from the back of a room, while somebody talks over it. The rules that keep a dense operational interface legible — one ground, restrained colour, a bounded type scale, no decoration — are the same rules that make a deck boring, because they are tuned for a surface nobody is looking up at.
So the deck layer relaxes the rules that only make sense at desk distance, and holds every rule that carries the brand. What loosens is scale, ground, asymmetry and bleed. What never loosens is tokens, Lexend at two weights, JetBrains Mono as the second voice, and contrast.
Decks have their own agent contract at /decks/llms.txt. Product rules and slide rules disagree deliberately, and an agent given both at once averages them into something that is neither.
Setting one up
Two stylesheets and one wrapper. A deck is a standalone document.
<!doctype html>
<html lang="en" data-flux-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Deck title</title>
<link rel="stylesheet" href="https://flux.kaptio.com/tokens/v2/flux.css">
<link rel="stylesheet" href="https://flux.kaptio.com/tokens/v2/flux-deck.css">
</head>
<body>
<main class="flux-deck">
<!-- slides go here -->
</main>
<script src="https://flux.kaptio.com/tokens/v2/flux-deck.js" defer></script>
</body>
</html>The deck layer derives the root font size from the viewport, so the 80 × 45rem composition always fits and every rem token — spacing, type, radius — scales with it as one piece. Leftover viewport becomes the current slide’s ground, not a frame of the page behind it. That is the one place Flux overrides the fluid root, and it is safe only because a deck is a whole document. Never load flux-deck.css into product UI.
Slides advance to the right. Snapping is CSS, so a swipe, a trackpad and a presenter remote move one slide with no script at all; flux-deck.js adds the two things CSS cannot — previous and next arrows, clicking to advance, and the keys. Click anywhere to go forward, click the left quarter to go back, use the arrows at the edges, or the arrow keys, space, Page Up and Down, or Home and End.
Diagrams are inline SVG inside flux-slide__figure. Appearance comes from theflux-figure__* classes so the drawing re-themes with its slide; geometry stays in the markup as SVG attributes, because a viewBox is in user units and a rem token resolves to the wrong size inside one. Wrap the parts influx-stage to reveal them in the order you explain them.
A deck with no script is still a working deck, which is why the navigation lives in a separate file rather than inside the stylesheet. The staged reveal degrades the same way: without it, every stage simply shows.See a full deck, or print one to PDF at 1280 × 720 — one slide per page.
What loosens
Each product rule, and what replaces it on a slide.
| Product rule | On a slide |
|---|---|
| The type scale stops at --flux-text-6xl. | Decks add --flux-display-sm through --flux-display-xl. These are deck-only: a display size in product UI is a defect, not a bold heading. |
| A view has one ground. | Every slide sets its own data-flux-theme. A deep teal statement between two light slides is the cheapest visual event in the system, and contrast is already audited for all three themes. One extra ground exists for decks only: forest, set with data-ground="forest" on a deep slide. |
| Teal carries the system; other hues appear only when they carry meaning. | A slide may take any product accent with data-accent. On a slide the accent is identity rather than status, so it needs no meaning — but it stays graphic. |
| One yellow CTA element per view. | Yellow is not a slide ground. The closing ask uses a full-bleed photograph or muted background video under a deep-teal overlay. |
| No centred body copy. | Statement and quote slides centre. There is no body copy on them to centre. |
| No three-column grid of icon, heading and paragraph as a default layout. | The grid slide is exactly that, and it is right here — a taxonomy held on screen for twenty seconds is not a landing page. |
| Constrain reading columns to --flux-measure-prose. | Slides bleed to the edge. Images fill the canvas, and marks are cropped by it deliberately. |
| Never exceed --flux-duration-slow. | Content settles on arrival using a scroll-driven timeline, so it tracks the presenter rather than a clock. Still opacity and transform only. |
| No decorative marks, bars or dots. | A slide may carry one oversized typographic mark, or one hairline dot field. One. Two is decoration, and a coloured bar on the edge of anything is still banned. |
What holds
Unchanged from product UI. A slide is not an exemption.
- Every value comes from a token. No raw hex, no raw dimension, no Tailwind arbitrary value.
- Lexend at 300 or 700. JetBrains Mono at 400, 500 or 700. Never a third family.
- Never load flux-prototype.css in a deck. Google Sans Flex and Manrope belong to product prototypes; a deck always sets in Lexend and JetBrains Mono.
- Load flux.css and flux-deck.css from the Flux CDN, exactly as in the setup. Never copy token values into the deck, never redefine --flux-* variables, and never write var(--flux-…, #fallback): a deck that still renders without flux.css is a private copy of Flux that no theme or update reaches.
- Do not load fonts yourself. flux.css loads Lexend at 300 and 700 and JetBrains Mono; a font link that adds Lexend 400, 500 or 600 is a defect.
- Grounds are light, dark and deep, plus forest (data-ground="forest" on a deep slide). Contrast is WCAG 2.2 AA at minimum (4.5:1 text, 3:1 non-text) and stays audited. Never a hand-picked colour, never a ground set with style or a data-*-bg hex.
- A story, a chapter or a persona never gets its own hue. Tell people apart with the avatar, the name and the role, and chapters apart with the title and the ground rotation. Status colours — green, yellow, blue, orange — mean status, not identity.
- Product accents colour type and data: the oversized mark, the list numerals, chart bars, a donut arc. Never decoration — no bar on the edge of a card, no rule beside a label — and never a word you are meant to read.
- Interface radius stays --flux-radius-sm. A slide is not a reason for a soft corner.
- No gradient fills, no glassmorphism, no glow, no neon, no blur behind content, no gradient mesh.
- No emoji as an icon system.
- Animate opacity and transform. Never width, height, top, left, margin or padding.
- Deck videos are decorative and muted. They pause off-slide, respect reduced motion, and always have the Flux pause control. Background video with the deep-teal overlay is allowed on the cover and the closing ask; the agenda keeps its own video-backed layout. Nowhere else.
- The agenda video shows its complete frame: never crop it. Bottom-align it inside a full-height media region that occupies at least two-fifths of the slide, so the visual remains substantial beside the agenda.
- The blooming-flower video, flower-blooming-dark.mp4, belongs to the agenda slide and nowhere else. Never use it on the cover, the closing ask or any other slide; give those their own footage or a photograph.
- Real text in real elements. A slide is not an image of a slide.
- One idea per slide. If it needs two sentences to state, it is two slides.
A product accent colours type and data: the oversized mark on a section slide, the numerals on a list, the bars of a chart, the filled arc of a donut. It never draws decoration — no bar on the edge of a card, no rule beside a label — and it never carries a word, because it has no guaranteed contrast on any ground. Colour behind text comes from a theme. The closing slide uses the deep theme over a full-bleed photograph or muted background video, with the built-in teal overlay preserving contrast. The cover may use the same treatment.
Pacing
This is what makes a deck interesting. Not more colour.
A boring deck is almost always a deck where every slide is the same shape. Variety of rhythm does more than variety of decoration, and it costs nothing.
- Pick the layout from the sentence, not the sentence from the layout. Write the line first, then choose the slide that fits it.
- Never two consecutive slides of the same layout. If two facts want the same shape, one of them is the wrong shape.
- Change ground at least every third slide. Light, dark and deep in rotation is the rhythm. Forest is a beat, not a rotation: use it for a section or statement slide, at most once every few slides.
- Give the audience a beat every four to six slides: a section divider, or a statement with nothing else on it.
- Twenty-five words per slide, counting every visible word — kickers, labels and numerals included. Twelve on a statement slide.
- The data layouts — chart, donut, timeline, journey, infographic — and the agenda are outside that count, because their repeated text is labels rather than prose. They get their own budget: one label and at most one line of explanation per item, and nothing else.
- A labelled diagram earns its words by taking them off the slide. If the lede still explains what the figure already shows, cut the lede, not the labels.
- An agenda belongs near the front of a deck long enough to have parts, and again before each part with aria-current on the item you are reaching. In a deck of eight slides it is a slide nobody needed.
- When an agenda is needed, use the agenda-video layout. There is no text-only agenda alternative: the quiet looping visual gives the room a paced opening without competing with the list. Keep the whole video frame visible, bottom-aligned, and at least two-fifths of the slide wide.
- Five items in a list, eight words each. A sixth item is a second slide.
- Use kickers and numbering sparingly. A kicker earns its place only when it adds context the title does not, so it is not on every slide. Numerals mark real order — the agenda, steps, a timeline, a section mark — and never decorate tiles, columns or content slides.
- At most one accent per slide. None is fine — a statement carries itself. A second accent on the same slide is a defect.
- The final ask uses a full-bleed photograph or muted background video with the deep-theme overlay. Never a flat yellow closing ground.
- Open on a cover and close on the ask. Everything between is yours.
- A number worth saying out loud is worth a metric slide of its own.
- Every third slide carries a photograph, screenshot, or portrait. Three consecutive text-only slides is a defect.
- A founding, scale, or company-facts slide is never metrics alone. Pair it with an editorial image — use split-photo.
Images
Photography is part of the argument, not decoration you add if there is room.
Decks go dull when every slide is type on a flat ground. The image and split-photo layouts exist so agents reach for a photograph when the slide mentions a place, a product, or people in context — not only when the photo is the entire point.
- Every deck needs at least three photographs: one on the cover or opening beat, one grounding a company or product claim, one showing people or customers in context.
- Portraits for people. Photography for place and product. If the slide mentions where you are or what you do, show it.
- Use flux-slide--image when the photograph carries the point on its own. Use flux-slide--split-photo when copy and metrics share the slide with it.
- The closing ask always uses a meaningful full-bleed photograph or muted background video beneath the deep-teal overlay. The visual supports the action; it is not generic texture.
- Cover and closing may use a muted looping background video under that same deep-teal overlay. Put it in flux-slide__media with muted loop playsinline data-flux-video, keep the Flux pause control as a sibling, and do not put background video on any other layout.
- The agenda uses the video-backed agenda archetype. Keep its visual quiet, decorative and dark enough that the list remains the point. Use the complete uncropped frame, align it to the bottom, and retain at least two-fifths of the slide for the media.
- src may be a URL or a path under /decks/assets/. Alt text is mandatory and must describe what is in the frame, not repeat the slide title.
- Never leave a slide that mentions a place, product, or team without a visual of that thing.
The layouts
18 shapes. Pick the one that fits the sentence you have already written.
Each is a live slide at full size, framed to 16:9. If none of them fits what you are trying to say, say so and propose one — inventing a layout in a deck is the same defect as inventing a component in product UI.
One is already known to be missing: there is no layout for a command or a code snippet. A deck about tooling or adoption wants one, and until it exists, carry the command as a list item or a flux-slide__note rather than building a slide for it.
Cover
Use when The deck opens. Once, at the front. A full-bleed photograph or muted background video under the deep-teal overlay is allowed; type-only on deep remains valid.
Not when Never for a section break — that is the section layout, and it looks different on purpose.
<section class="flux-slide flux-slide--cover" data-flux-theme="deep" data-accent="core">
<p class="flux-slide__kicker">Platform review · Q3</p>
<h1 class="flux-slide__title">Connect ships to every tenant</h1>
<p class="flux-slide__note">Vera Lindqvist · 18 August 2026</p>
</section>Agenda
Use when Near the front, to tell the room the shape of the next twenty minutes. This video-backed layout is the agenda type for every deck. Its complete video frame is bottom-aligned in a media region at least two-fifths of the slide wide. Repeat it before a part begins, with aria-current on the item you are reaching.
Not when For content. An agenda names the parts of the deck; a list of arguments is the list layout. A four-slide deck does not need one.
<section class="flux-slide flux-slide--agenda flux-slide--agenda-video" data-flux-theme="deep" data-accent="core">
<div class="flux-slide__agenda-copy">
<p class="flux-slide__kicker">Agenda</p>
<h2 class="flux-slide__title">What we’ll cover</h2>
<ol class="flux-slide__agenda">
<li>
<div class="flux-slide__agenda-item">
<h3>Where we are today</h3>
<p>Product status, go-lives, and key milestones from the year.</p>
</div>
</li>
<li aria-current="step">
<div class="flux-slide__agenda-item">
<h3>What customers told us</h3>
<p>Themes from advisory boards, support, and the field.</p>
</div>
</li>
<li>
<div class="flux-slide__agenda-item">
<h3>The roadmap ahead</h3>
<p>Priorities and innovation themes for the next four quarters.</p>
</div>
</li>
<li>
<div class="flux-slide__agenda-item">
<h3>How we get there</h3>
<p>Teams, timelines, and what we need from each other.</p>
</div>
</li>
</ol>
</div>
<div class="flux-slide__agenda-media">
<video class="flux-slide__agenda-video" muted loop playsinline preload="metadata" data-flux-video aria-hidden="true">
<source src="https://flux.kaptio.com/decks/assets/flower-blooming-dark.mp4" type="video/mp4">
</video>
<button class="flux-slide__video-toggle" type="button" data-flux-video-toggle>Pause video</button>
</div>
</section>Section
Use when A divider between parts of the argument. It exists to give the room a beat.
Not when For a slide that carries content. A section slide says where you are, nothing else.
<section class="flux-slide flux-slide--section" data-flux-theme="dark" data-accent="edge">
<span class="flux-slide__mark" aria-hidden="true">02</span>
<p class="flux-slide__kicker">Part two</p>
<h2 class="flux-slide__title">What the migration cost</h2>
</section>Statement
Use when One sentence that deserves the whole room. The most underused slide in any deck.
Not when For a sentence that needs support. If it needs a caption, it is a split.
<section class="flux-slide flux-slide--statement" data-flux-theme="deep">
<p class="flux-display">Every booking now clears in under four seconds.</p>
</section>Metric
Use when A number worth saying out loud. One, or up to three abreast.
Not when For four or more numbers. That is a table. For founding year, headcount, revenue, or scale — use split-photo, not metric alone. Metrics without a photograph read as a spreadsheet.
<section class="flux-slide flux-slide--metric" data-accent="pex">
<p class="flux-slide__kicker">Throughput</p>
<div class="flux-slide__metrics">
<div class="flux-metric">
<span class="flux-metric__figure">3.9s</span>
<span class="flux-metric__label">Median clear time</span>
<span class="flux-metric__note">Down from twenty-one seconds at the start of the quarter.</span>
</div>
<div class="flux-metric">
<span class="flux-metric__figure">99.98%</span>
<span class="flux-metric__label">Availability</span>
<span class="flux-metric__note">Two minutes of degradation, all of it planned.</span>
</div>
</div>
</section>Chart
Use when One series, four to seven bars, where the shape of the change is the point. Each bar carries its value as --flux-bar, a percentage of the plot.
Not when For two series, or for a trend with many points. A deck bar chart compares a handful of things; anything denser belongs in the document behind it.
<section class="flux-slide flux-slide--chart" data-accent="pex">
<p class="flux-slide__kicker">Median clear time · seconds</p>
<h2 class="flux-slide__title">Every release took time out</h2>
<div>
<!-- The value is data, so it arrives in the markup. It is the one thing the
stylesheet cannot know, and the only sanctioned inline property. -->
<ol class="flux-slide__plot">
<li>
<p class="flux-slide__bar-value">21.4</p>
<div class="flux-slide__bar flux-slide__bar--muted" style="--flux-bar: 100%"></div>
</li>
<li>
<p class="flux-slide__bar-value">16.2</p>
<div class="flux-slide__bar" style="--flux-bar: 76%"></div>
</li>
<li>
<p class="flux-slide__bar-value">9.8</p>
<div class="flux-slide__bar" style="--flux-bar: 46%"></div>
</li>
<li>
<p class="flux-slide__bar-value">5.1</p>
<div class="flux-slide__bar" style="--flux-bar: 24%"></div>
</li>
<li>
<p class="flux-slide__bar-value">3.9</p>
<div class="flux-slide__bar" style="--flux-bar: 18%"></div>
</li>
</ol>
<ol class="flux-slide__axis">
<li>Q2 '25</li>
<li>Q3 '25</li>
<li>Q4 '25</li>
<li>Q1 '26</li>
<li>Q2 '26</li>
</ol>
</div>
</section>Donut
Use when One proportion, stated next to what it means. The ring is drawn on a circumference of exactly 100, so a segment is written as its percentage.
Not when For four or more segments, or for anything that is not a share of a whole. A donut sliced five ways is a table nobody can read.
<section class="flux-slide flux-slide--donut" data-accent="agents">
<svg class="flux-donut" viewBox="0 0 42 42" role="img"
aria-label="Sixty-eight per cent of tenants are on the new engine.">
<circle class="flux-donut__track" cx="21" cy="21" r="15.9155"></circle>
<!-- Circumference is 100, so the dash pair is the split itself. -->
<circle class="flux-donut__value" cx="21" cy="21" r="15.9155" stroke-dasharray="68 32"></circle>
</svg>
<div class="flux-slide__donut-body">
<p class="flux-slide__kicker">Migration</p>
<h2 class="flux-slide__title">68% of tenants are on the new engine</h2>
<p class="flux-slide__lede">The rest are scheduled before the end of the quarter.</p>
</div>
</section>Infographic
Use when One figure that carries the claim, with two or three that support it. The hierarchy is the point.
Not when For figures of equal weight — that is the metric layout. If you cannot say which number leads, you have a metric slide.
<section class="flux-slide flux-slide--infographic" data-accent="core">
<p class="flux-slide__kicker">Twelve months of Connect</p>
<div class="flux-slide__cluster">
<div class="flux-metric">
<span class="flux-metric__figure">1.2M</span>
<span class="flux-metric__label">Bookings cleared</span>
<span class="flux-metric__note">Through one engine, on one contract, in one currency model.</span>
</div>
<div class="flux-slide__cluster-support">
<div class="flux-metric">
<span class="flux-metric__figure">41</span>
<span class="flux-metric__label">Tenants live</span>
</div>
<div class="flux-metric">
<span class="flux-metric__figure">0</span>
<span class="flux-metric__label">Rollbacks</span>
</div>
</div>
</div>
</section>Split
Use when An argument on one side and its evidence on the other. Use --split-wide when the text outweighs the figure.
Not when For two competing positions. That is compare, and the seam between them matters.
<section class="flux-slide flux-slide--split" data-accent="agents">
<div class="flux-slide__pane">
<p class="flux-slide__kicker">Before</p>
<h2 class="flux-slide__title">Six systems, one booking</h2>
<p class="flux-slide__lede">Every handoff was a place a reservation could quietly stop existing.</p>
</div>
<figure class="flux-slide__figure">
<!-- Diagrams are inline, so currentColor follows the slide's theme.
A linked image would have to name a colour and would only suit one. -->
<svg viewBox="0 0 260 150" role="img" aria-label="Six systems joined by nine handoffs."
fill="none" stroke="currentColor" stroke-width="2">
<rect x="4" y="4" width="68" height="40" rx="2" />
<rect x="96" y="4" width="68" height="40" rx="2" />
<rect x="188" y="4" width="68" height="40" rx="2" />
<rect x="4" y="106" width="68" height="40" rx="2" />
<rect x="96" y="106" width="68" height="40" rx="2" />
<rect x="188" y="106" width="68" height="40" rx="2" />
<path d="M72 24h24M164 24h24M72 126h24M164 126h24" />
<path d="M38 44v62M130 44v62M222 44v62" stroke-dasharray="4 6" />
<path d="M72 44l58 62M188 44l-58 62" stroke-dasharray="4 6" />
</svg>
</figure>
</section>Split with photo
Use when Copy on one side and an editorial photograph on the other. The company-facts shape: founding story, scale metrics, or product context beside a place, landscape, or operators in the field.
Not when For a diagram or annotated screenshot — that is split with an inline SVG. For a photograph that carries the whole point alone — that is image.
<section class="flux-slide flux-slide--split-photo" data-accent="core">
<div class="flux-slide__pane">
<p class="flux-slide__kicker">About Kaptio</p>
<h2 class="flux-slide__title">Built for multi-day travel. Nothing else.</h2>
<p class="flux-slide__lede">Founded in Reykjavík to give multi-day travel brands software that fits the industry.</p>
<div class="flux-slide__metrics">
<div class="flux-metric">
<span class="flux-metric__figure">2014</span>
<span class="flux-metric__label">Founded in Iceland</span>
</div>
<div class="flux-metric">
<span class="flux-metric__figure">100</span>
<span class="flux-metric__label">People · 17 nationalities</span>
</div>
<div class="flux-metric">
<span class="flux-metric__figure">20+</span>
<span class="flux-metric__label">Travel brands</span>
</div>
<div class="flux-metric">
<span class="flux-metric__figure">€1.6B</span>
<span class="flux-metric__label">2025 booking volume</span>
</div>
</div>
</div>
<img class="flux-slide__photo"
src="https://images.unsplash.com/photo-1504829857797-ddff29c27927?w=960&q=80"
alt="Coastal mountains rising from the sea in Iceland at golden hour.">
</section>List
Use when Genuinely sequential or enumerable points. Five at most.
Not when As the default slide. If every slide is a list, the deck is a document read aloud.
<section class="flux-slide flux-slide--list" data-accent="quest">
<p class="flux-slide__kicker">What changed</p>
<ol class="flux-slide__list">
<li>One reservation record, owned by Connect.</li>
<li>Every price resolved at write time.</li>
<li>Failures surface in the channel, not the log.</li>
</ol>
</section>Compare
Use when Two positions held against each other. The seam is the point.
Not when For three or more options. Three columns of prose is a table nobody will read.
<section class="flux-slide flux-slide--compare" data-accent="edge">
<div>
<p class="flux-slide__kicker">Today</p>
<p class="flux-slide__lede">Each tenant runs its own pricing rules, and no two agree on rounding.</p>
</div>
<div>
<p class="flux-slide__kicker">September</p>
<p class="flux-slide__lede">One engine, one rounding rule, and a tenant override that has to be justified.</p>
</div>
</section>Timeline
Use when When. Three to five dated milestones on one axis. Mark the one you are standing on with aria-current.
Not when For stages without dates — that is the journey layout. For more than five milestones: a timeline read from the back of a room holds about four.
<section class="flux-slide flux-slide--timeline" data-accent="pex">
<p class="flux-slide__kicker">Rollout</p>
<h2 class="flux-slide__title">Every tenant by March</h2>
<ol class="flux-slide__timeline">
<li>
<span class="flux-slide__timeline-date">Sep 2026</span>
<span class="flux-slide__timeline-label">Pilot</span>
<span class="flux-slide__timeline-note">Four tenants, both currency models, one region.</span>
</li>
<li aria-current="step">
<span class="flux-slide__timeline-date">Nov 2026</span>
<span class="flux-slide__timeline-label">General availability</span>
<span class="flux-slide__timeline-note">Opt-in for everyone already on Connect.</span>
</li>
<li>
<span class="flux-slide__timeline-date">Jan 2027</span>
<span class="flux-slide__timeline-label">Default</span>
<span class="flux-slide__timeline-note">New tenants provision on the engine automatically.</span>
</li>
<li>
<span class="flux-slide__timeline-date">Mar 2027</span>
<span class="flux-slide__timeline-label">Legacy off</span>
<span class="flux-slide__timeline-note">The old pricing path stops accepting writes.</span>
</li>
</ol>
</section>Journey
Use when Who, in order. Stages of a process and the hand-off between them. The seams are where the work changes hands.
Not when For dated milestones — that is the timeline. For a set of points with no order, use list or grid.
<section class="flux-slide flux-slide--journey" data-accent="circle">
<p class="flux-slide__kicker">One booking, end to end</p>
<ol class="flux-slide__journey">
<li>
<span class="flux-slide__journey-stage">Quote</span>
<span class="flux-slide__journey-note">Prices resolve once, against the live rate card.</span>
<span class="flux-slide__journey-owner">Connect</span>
</li>
<li>
<span class="flux-slide__journey-stage">Hold</span>
<span class="flux-slide__journey-note">Inventory is reserved for the length of the quote.</span>
<span class="flux-slide__journey-owner">Connect</span>
</li>
<li>
<span class="flux-slide__journey-stage">Confirm</span>
<span class="flux-slide__journey-note">One record, written once, owned by one system.</span>
<span class="flux-slide__journey-owner">Salesforce</span>
</li>
<li>
<span class="flux-slide__journey-stage">Settle</span>
<span class="flux-slide__journey-note">Payment clears against the same record it quoted.</span>
<span class="flux-slide__journey-owner">Kaptio Pay</span>
</li>
</ol>
</section>Quote
Use when Somebody else’s words, where whose words they are matters.
Not when To dress up your own point. A quote from nobody is a statement slide with extra punctuation.
<section class="flux-slide flux-slide--quote" data-flux-theme="deep" data-accent="voyage">
<blockquote class="flux-slide__quote">We stopped reconciling bookings by hand in week three, and nobody has asked to go back.</blockquote>
<p class="flux-slide__attribution">Operations lead, Tauck</p>
</section>Image
Use when A photograph or screenshot that carries the point on its own.
Not when As background texture behind unrelated text. If the image is decoration, drop it.
<section class="flux-slide flux-slide--image">
<img class="flux-slide__media" src="/decks/assets/console.svg" alt="The Connect console showing a reservation timeline.">
<div class="flux-slide__plate">
<h2 class="flux-slide__title">One timeline</h2>
<p class="flux-slide__note">Every state change, in order, with the actor who caused it.</p>
</div>
</section>Grid
Use when A taxonomy: four to six things of the same kind, held on screen together.
Not when For a sequence. Order in a grid is ambiguous — use the list.
<section class="flux-slide flux-slide--grid" data-accent="circle">
<p class="flux-slide__kicker">The shape of it</p>
<div class="flux-slide__tiles">
<article class="flux-slide__tile">
<h3>Ingest</h3>
<p>Supplier feeds land once, and are versioned on arrival.</p>
</article>
<article class="flux-slide__tile">
<h3>Resolve</h3>
<p>Price and availability settle before anything is written.</p>
</article>
<article class="flux-slide__tile">
<h3>Publish</h3>
<p>One record, one shape, every channel.</p>
</article>
</div>
</section>Closing
Use when The ask. One per deck, at the end. A full-bleed photograph or muted background video under the deep-teal overlay makes the action tangible.
Not when Anywhere else. This is a closing action, not a reusable image treatment.
<section class="flux-slide flux-slide--closing" data-flux-theme="deep">
<img class="flux-slide__media"
src="https://images.unsplash.com/photo-1521737711867-e3b97375f902?w=1600&q=85"
alt="Colleagues working together around a table.">
<div class="flux-slide__closing-content">
<h2 class="flux-slide__title">Pilot it with two tenants in September.</h2>
<p class="flux-slide__note">Decision needed by 29 August.</p>
</div>
</section>For agents
The check to run before a deck is finished.
- Every slide uses one of the layouts. An invented layout is a missing one — say which.
- No two consecutive slides share a layout.
- The ground changes at least every third slide.
- There is a beat — a section or statement slide — every four to six slides.
- The last slide is the ask, over a full-bleed photograph or muted background video with the deep-teal overlay.
- No slide exceeds twenty-five words, the data layouts aside.
- One accent per slide, and no accent carries text.
- Every chart bar carries a --flux-bar percentage, and a donut’s two dash values add up to 100.
- A chart, a donut or a timeline has a caption saying what it shows. A figure without a claim is decoration.
- No raw hex, no raw dimension, no font weight outside 300 and 700.
- The deck links flux.css and flux-deck.css, loads no fonts of its own, and has no var(--flux-…) with a fallback value.
- Every slide ground is a theme or forest. No story, chapter or persona has its own colour.
- npx flux-check passes on the deck file.
- Every readable pairing uses a theme ground, so contrast stays at WCAG 2.2 AA.
- Every image has real alt text, and no slide is a picture of text.
- At least three slides carry a photograph or screenshot.
- If the deck needs an agenda, it uses flux-slide--agenda-video with a muted video and its pause control.
- The agenda video is uncropped, bottom-aligned, and occupies at least two-fifths of the slide.
- The blooming-flower video appears only on the agenda slide.
- Kickers are not on every slide, and numerals appear only where order is the meaning.
- No company-facts slide without an accompanying image — use split-photo.
- The deck reads correctly with the browser at any window size, because the composition scales as one piece and leftover viewport takes the slide’s ground.
The full machine contract — premise, layouts, markup and all of the above — is at/decks/llms.txt. It is written to be sufficient on its own: an agent should be able to build a correct deck from that file without fetching anything else.
For a Slack teammate whose configuration is a single text box and cannot fetch a URL,/decks/brief.txt is the same contract condensed to fit a system prompt. It is generated from the same source, so it is a shorter statement of these rules and never a different one.