Authoring Bento decks
SkillFiles & storageUse when creating or editing Bento (`.bento.html`) slide decks, including any request for a single-file HTML slide deck.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Authoring Bento decks skill
What this skill tells your AI
The instructions your AI receives, as published by sammcj/agentic-coding in Skills/bento-slides/SKILL.md and read by ahel’s review.
A Bento deck is one self-contained .bento.html file: a compressed app runtime plus the document as plain JSON in one block:
<script type="application/bento+json" id="bento-doc"> { "format":"bento/slides", ... } </script>
- Edit that block only, in place. Leave the runtime untouched.
render_check.mjs --writewrites the block for you. A hand-written splice must escape every<in the JSON as its unicode escape (a backslash followed byu003c), so the block never contains a literal</script>and a build script can find the block's end at the first</script>after the opening tag.- Chat context (no filesystem): the user copies the JSON out (Save > Copy compact JSON) and pastes your replacement back (Save > Replace from JSON).
window.bento.loadDoc(json)does the same from the console.
Before touching an existing deck
- Ask the user to close the deck in their browser. A save from the open tab overwrites your edit. Autosave keeps a recovery snapshot in IndexedDB keyed by
docId, so a stale tab offers to "restore" the old version on the next open; tell the user to pick Discard. - If the
#bento-docblock contains"format":"bento/enc", the deck is password-encrypted. Stop and tell the user; plain JSON written over it destroys the ciphertext. - Leave
collabas found. Keys mint at creation and every save writes them, so nearly every saved deck carriesownerPriv: anyone with the file can join its live session.- Deleting
collabsevers the owner from their own room while sent copies keep the old one. - Tell the user once that the file contains session keys. Hand-outs: Share > View-only copy. Leaked file: Share > Reset access.
- Every save also stamps CRDT state into
collab.sync; the next open merges it with the room, so elements a script deleted come back.render_check.mjs --writedropscollab.sync. If edits still reappear, the owner's other tabs are in the room: setcollab.ontofalseand have the user Share > Reset access. - Otherwise set
collab.ontofalseonly when the user asks to stop sharing. Change that one field and keep the keys.
- Deleting
Starting from nothing
The user does not need Bento installed; the app ships inside every deck. Fetch the latest release and author into it:
# name the file after the deck's topic, e.g. Q4_Review.bento.html
curl -fsSL https://bento.page/releases/slides/Bento_Slides.bento.html -o "<Topic>.bento.html"
Verify the download contains id="bento-doc". The block is empty on disk (a browser mints a demo deck on first open), so there is nothing to discard. Omit docId and collab. render_check.mjs --write saves freshly minted keys with sharing off (collab.on:false); the user turns sharing on with Share. Never author a collab object: one without keys never gets any, so Share silently connects nothing.
Starting from a theme
When the user asks for a themed deck or names a theme from bento-themes, use the local clone at ~/git/sammcj/bento-themes if it exists (it may hold unreleased work). Otherwise clone it there: git clone https://github.com/sammcj/bento-themes ~/git/sammcj/bento-themes.
- Pick a theme from the repo README (one section per theme) and
themes/<name>/preview/*.png. - Shell: copy
themes/<name>/<Name>.bento.htmlto<Topic>.bento.html. - Document: start
doc.jsonfromthemes/<name>/<Name>.doc.json(full form). Keeptheme,fonts,assets,present,metaandlayouts. Deletecollab,docIdandtemplateso the deck is not a template and--writesaves it with sharing off. - Slides: copy the demo slide for each layout you need (its element ids match the layout), give it a unique slide
id, and replace its text, data and notes. A layout with a detail state needs its state slide copied too, withstateOfandlinkupdated. Theme layouts carry norole, so compact role placement does not apply.
Workflow
Create a task per step below, each with its completion criterion, then work them to completion.
- Read the references.
references/agents-1.2.3.md(element shapes, chart/state/hidden-slide rules, column arithmetic) andreferences/format-reference.md(compact form, fields since 1.0.18, runtime rules). Every key, generated from the runtime: https://bento.page/schema/slides.json. - Find the document (existing deck). Parse the
#bento-docJSON. Notedoc.size,doc.theme, elementids,docId, and whethertemplateorreadonlyis set. KeepdocIdunchanged. - Classify the source material. For each piece: a stat, a table, a process, a definition to expand, a photo, code?
- Map each piece to a feature. This step makes it a Bento deck:
- numbers to compare (trend, magnitude, share) -> a
chartelement - a comparison, spec, pricing or feature grid -> a
tableelement (columnsweights +rowsofcells+ astyleobject) - a point to drill into -> a state slide (
stateOf+ elementlink) - a hero or full-slide image -> full-bleed image + scrim rect + text
- a sequence, flow or timeline -> connectors (
from/to) or apath - a headline number -> big text, one plain number per box
- source code -> a
codeelement - a build across slides -> one slide per step with
unnumbered:trueon the continuations - repeated chrome or logo -> keep its
idstable across slides - a demo clip, recording or soundbite -> a
mediaelement
- numbers to compare (trend, magnitude, share) -> a
- Author
doc.jsonin compact form ("compact": true), per format-reference "Compact form". Its typography and role-placement rules fail silently: read both before writing text. Compact fillssize(1280x720) andthemewhen omitted.- Keep one accent colour, at most two typefaces, and 96px side margins (right-most x <= 1184).
- Write speaker notes on each slide.
- Size text with compact
h:"auto"; otherwisewindow.bento.measure({html, w, fontSize, fontFamily})viarender_check.mjs --eval, always passingfontFamily. - When fanning slides out to several agents, the shared spec lists the type scale (title/body/caption px) and the band tier per slide, as well as colours and columns.
- Render and write back:
render_check.mjs "<Topic>.bento.html" --doc doc.json --write "<Topic>.bento.html"(add--motionif the user asked for animation). In a chat context, return the replacement JSON instead. - Fix and re-run until there are no
droppedentries or errors, and no warnings on content you authored, then read every PNG. On an existing deck, report warnings about the user's own content (motion, notes, fonts) instead of rewriting it. Overflow, crowding and a dropped chart key are invisible in the JSON and obvious on screen. - Self-audit before finishing:
- any numbers rendered as text that should be a chart?
- a drill-down that would work better as a state slide?
- one accent colour?
- captions baked into raster images flagged to the user?
- on content slides, does the content reach the bottom of the band, with slack spread between blocks rather than pooled underneath?
Motion default
Static decks: transition:"none" or "fade", no fx, no loops, no ken-burns. Most decks are shown over a video call, where frame drops smear motion and loops pull attention from the speaker. Bento's templates lean on animation; ignore that. Only when the user asks for animation, read references/motion.md (morph, entrances, step reveals, count-up, ken-burns, loops).
Density rules
Bento's examples use a display band (y:72 h:84, content from y:208, 96px bottom margin) that leaves 416px of 720 for content. Pick a tier per slide:
- Display tier (cover, section divider, one-idea slide): that band, 88px headline, 40px+ body.
- Content tier (bullets, tables, charts, comparisons): title ~40px at
y:64, rule aty:132, content fromy:156toy:656(500px), 96px side margins kept. - Type floor: body 16px or larger, nothing below 14px, 20px+ body on content slides. Video calls downscale the canvas, so 13px on 1280 reaches the audience at about 7px. Text baked into a raster image cannot be fixed; flag it during classification (step 3).
- Fill the band, then choose the gaps. On a content slide, content reaches the band's bottom and right edges, with leftover height distributed between blocks. For a column: measure every block (or use compact
h:"auto"), sum, then split the remainder across the gaps.
Scripts
node scripts/render_check.mjs <deck> [--doc doc.json [--write <deck>]]boots the deck in headless Brave/Chrome with DNS off, prints the load report,validate()findings and the skill's own checks, and saves one PNG per page.--helplists the options.node scripts/inflate_runtime.mjs <deck>extracts the compressed runtime as searchable minified JS;rgit to settle a question about app behaviour.- The browser needs its profile directory under
$TMPDIR; a sandbox that blocks it kills the run, so run outside it. Outside the sandbox$TMPDIRdiffers: keepdoc.jsonand the deck on project paths.
Critical gotchas
- Bullets are
<ul><li>inhtml(nested<ul>for sub-bullets,<ol>for numbered). A typed "•" or "-" is inline, so a wrapped line returns under the bullet instead of hanging under the text. Compactmdbullets produce those glyph lines, so a bulleted element useshtml. Lists cost height and<ul>already breaks (no<br>beside it): re-measure after converting. Centre or right aligned lists lose the hanging indent. - Text
htmlkeeps tags only (b i u br p div span ul ol li h1 h2 a code strong em s). Every attribute except http(s)hrefis stripped, so inline colour or size needs a separate text element.$…$on one line renders as maths ($typst: …$for Typst); write\$for a literal dollar. - Charts implement a subset of ECharts: unimplemented keys are ignored silently, and
validate()misses ignored sub-keys. Write charts only from agents "chart" and format-reference "Chart rules". fit:"contain"draws smaller than its box. Margin and coverage checks read the box, so a portrait image in a landscape box passes while leaving dead space. Give the box the asset's aspect ratio.- Tables: the header row is always bold and
borderWidthapplies to every edge. For horizontal rules only, setborderWidth:0and drawrectshapes ofh:1at the row pitch. - Hairlines: a
lineshape draws at 2px minimum. A 1px rule is arectwithh:1. {{date}}renders the viewer's today.{{date:YYYY-MM-DD}}pins the format only. Write a literal date for a fixed event.template:true-> every open mints a fresh deck, so the runtime strips the flag on load;render_check.mjs --writerestorestemplateandlayoutsfrom the input JSON.readonly:true-> the file boots straight into the show with no editor (validate/measure absent).doc.layoutsinsert semantics: deep-clones, preserves element ids, clearsnotes,nameandstateOf. Leavelinkout of layouts.
Working examples of every technique: open any template at https://bento.page and read its #bento-doc block.
Signals
- GitHub stars
- 161
- Forks
- 24
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
bento-slides- Source
- github.com/sammcj/agentic-coding