Modules › Presentation
Presentation
The projection, on whatever screen it lands on.
40 of 40 working
5 delighted · 19 verified · 16 tested
- verified ๐ Light and dark themes /help/personalize
- verified ๐ A font choice including monospaceSystem, serif or monospace, chosen in settings and stored on the user node like every other preference. /help/personalize
- verified Works on a phone, with touch targets that fit a thumbpersona: thumb /features/
- verified Indentation equals edge depth, exactly pandoPando projection principle 4.persona: first
- tested The UI holds no state the graph does not pandoPando projection principle 2.test/presentation.test.js
- verified Every action is reachable from the keyboardEvery command carries its bindings in the registry, and the keyboard handler looks them up rather than spelling them out. Ctrl-on-both-platforms bindings carry both spellings, because Ctrl is `mod` on Windows and `ctrl` on a Mac, and listing one of them left the key dead on the other.persona: keyboard /help/hotkeys
- verified ๐ A command paletteOne mechanism behind both Jump-to and the slash commands. The ranking lives in the kernel so it can be tested without a browser: a prefix beats a word start beats initials beats a scattered match. /features/
- verified ๐ Slash commandsA slash at the start of a word opens the commands for the bullet you are in. Mid-word it is an ordinary slash, because dates and paths have them in. /features/
- tested Instant presentation from an outlineEach child of the focus is a slide and its children are the bullets on it. No new data and no mode in the graph: the same projection at a different size, which is why leaving needs no cleanup. test/e2e/parity.mjs opens it from the app menu, asserts it starts at the first slide, that an arrow moves to the next, that one key leaves it and that the outline is where it was. Leaving is falsified at the key handler: an outline is already a deck, so what it costs is that going back has to be as cheap as arriving.test/e2e/parity.mjs /features/
- verified Every command is reachable with every input the device offers pandoSPEC.md Principle 4. One command registry; the keyboard map, the bullet menu, the palette, the slash commands and the mobile toolbar are all projections of it. test/commands.test.js fails the build if any command lacks a keyboard, pointer or touch path, so the rule is checked rather than remembered.persona: thumbs
- verified Adjust text sizepersona: mobile:sizer /help/navigate-around
- verified A gesture draws its outcome while your finger is still down pandoSwipe right and the green field with the check grows with the travel, locking when it will fire. A gesture you cannot see until it has happened is a guess.persona: traystack, persona: mobile:animator
- tested A drag shows the depth it would land at pandoThe line you are shown is the landing: one landing() computes both, so the drop cannot disagree with the picture. The depth is clamped to what is legal between those two rows. test/e2e/drag.mjs asserts the drop line is drawn while the bullet is in the air and that its left edge moves with the depth it would land at, so the depth is chosen before the drop rather than discovered after it. Falsified twice: by not drawing the line, and by drawing it without reading the pointer across the row.test/e2e/drag.mjs
- verified One guide line per ancestor pandoVisual isomorphism made literal: depth is drawn, not merely spaced, so it can be read back off the screen instead of counted. test/structure.test.js asserts the guide gradient repeats at exactly one indent so a line means an ancestor, that it hangs from the bullet by derivation from --guidex rather than a typed offset, and that its clamp is the SAME expression as the row padding in app.js. The stylesheet names that coupling as a hazard in as many words: the two have to agree or the guides stop landing on their bullets.persona: look
- tested The bullet says folded, mirrored and shared at a glance pandoA halo when a subtree is folded, a diamond for a mirror, a ring for shared. Shape, not colour, so it survives a dark theme and colour-blindness. test/structure.test.js asserts folded, mirrored and shared are drawn with no declaration in common, so four independent facts get four independent marks rather than one mark with a coin flip in it, and that each is decided from the row being drawn rather than read back off the element.test/structure.test.js
- verified ๐ A bottom bar on a phone, in thumb reach pandoSidebar, new bullet, today, search. It swaps with the bullet toolbar: one bar, whichever job is in front of you.
- verified On a phone the first tap chooses a bullet, the second writes in it pandoThe first tap on a phone is very often a scroll being stopped. It still does everything except raise the keyboard. The one-hand persona now measures it against the deployed site with real touch, over the sequence that actually broke it: open a bullet, leave, come back, tap once. It had shipped three times with nothing watching, and each time the memory of which bullet was tapped outlived the writing session.persona: again
- verified The bullet toolbar follows the keyboard away pandopersona: behindbar, persona: mobile:interrupted
- verified Every menu entry has a symbol pandoOne drawing per command, keyed by id, so a command declared once is drawn the same on every surface.persona: look
- tested Full screenA browser will not go full screen without a gesture, so it cannot be on by default however much one would like it to be. It is a mode like any other: Escape and Back leave it, and it is outermost, so the menu, the search and the pane all close before it does. It was missing from the mode registry because the browser exits on Escape by itself, which hid the fact that a phone has no Escape.test/e2e/tabs.mjs /help/navigate-around
- tested A design audit that runs against the deployed page pandoContrast, tap targets including hit area, no text glyphs standing in for icons, transition timing, scrim opacity, safe areas, 375px and landscape. 88 findings the first time it ran. test/tools.test.js asserts the audit is runnable by name, still has its sections, can report something as wrong, and reaches the site through the harness rather than naming a URL - so PYFLOW_BASE can point it at a local build instead of silently measuring production. What it finds is the running of it: contrast, tap targets measured with elementFromPoint, alignment, and the rest, each falsified today by breaking the product.test/tools.test.js
- verified One bar at the bottom whose buttons change with what you are doing pandoFour buttons change, More and Undo never move. Two bars stacked was a fifth of a phone screen spent on chrome.persona: thumb, persona: mobile:interrupted
- tested One button that scrolls to the top, then goes up a level pandoTwo intentions that arrive at different moments and mean the same thing: take me back out of here. test/e2e/parity.mjs presses it from both states: at the top it goes up a level, scrolled down it returns to the top and stays on the page it was on. What earns it one slot instead of two is that it never does nothing, so both verbs are driven.test/e2e/parity.mjs
- verified The bar stays above the keyboard while the page scrolls pandoPositioned against the visual viewport, which is the part you can see, rather than the layout one, which a soft keyboard does not shrink.persona: behindbar, persona: mobile:interrupted
- verified A quiet highlight on the bullet you last touched pandopersona: behindbar, persona: mobile:returner
- verified Text size and line spacing chosen together, over the outline pandoSize and spacing over the outline, chosen together because they only mean anything against each other. Spacing 0 is genuinely 0: the row floor scales with it, and the tap targets keep 44px through padding that grows outside the row rather than inflating it. Andreas set it to zero, saw nothing move and said there was an invisible element; there were two.persona: mobile:sizer
- verified A gesture does one thing per touch pandoThe first movement past the slop decides what the touch is and it stays that until the finger lifts.persona: onehand, persona: mobile:scroller
- tested Every menu has a handle to pull and a button to close pandoA rule over every menu rather than a property of one. test/e2e/parity.mjs opens three different sheets - the app menu, a bullet menu and a chooser opened from a menu - and asserts each has something to take hold of, a button beside it for a pointer, and that the way out is a thumb-sized target. Swipe down to close is not discoverable and is not available to a mouse at all.test/e2e/parity.mjs
- tested A second panel that judges how it feels to hold pandoTwelve personas: layout, thumb, eye, sizer, animator, scroller, interrupted, long-sighted, dark, impatient, fat-fingered, returner. A green behaviour panel says nothing about whether the thing is any good on a phone. test/tools.test.js holds the mobile panel to the same bar: sixteen critics, a severity it can report, and the site read from the harness. A panel that has been emptied still prints a clean verdict, which is worse than not having one, because a green report is read as evidence. Falsified by emptying it, by taking away its severities, and by pointing it at a hardcoded URL.test/tools.test.js
- verified A field for finding a bullet is not offered your passwords pandoautocomplete=off has not been honoured for years. type=search is the part that settles it, plus the three spellings the extensions read.persona: look
- tested A panel that measures what a real job costs pandoFourteen journeys, ten mobile, three desktop, one agent. Every journey carries a budget set to what it costs today, so the panel fails the moment anything gets more expensive. Counting is done by the page, and typing the thought is free because that is the job rather than the cost of it. test/tools.test.js the same, for the panel that measures what a job costs. It runs against production and reports each journey against its budget - capture one thought 2/3, undo a deletion 1/1, compound search 1/3.test/tools.test.js
- verified Atkinson Hyperlegible and OpenDyslexic, and a heavier weightLoaded only when an account has chosen one. Heavier text is a weight rather than a different family, so nothing moves and only the ink changes.persona: look blog 2026.05
- verified The count on a folded bullet can be turned off pandoIt was drawn on every folded bullet with no way to stop it. At tight spacing it is the loudest thing on the line.persona: mobile:sizer Andreas
- tested Every menu is grouped and ordered by what people do most pandoThe order was the order the commands happened to be declared in, which is the order they were built in. Groups rather than a number per command: writing before formatting, formatting before sharing, and the app menu opens on Settings, which was fourteenth. The registry test fails when a command has no group, no symbol, or a symbol icons.js cannot draw. test/menus.test.js asserts the declaration - every command in a known group, every group named or deliberately unnamed, the app menu a reordering of the same set rather than its own, the destructive group last in both, and no two entries a reader cannot tell apart. test/e2e/parity.mjs asserts the half that matters: the drawn menu follows the declared order, on the bullet menu and the app menu, and the two lead with different things because you came for different things. Three mutations red.test/menus.test.js Andreas
- verified A long outline arrives a page at a time pandoSPEC.md says the projection is f(Graph, FocusNode, Viewport), and this is the Viewport half doing its job. Every row was drawn however long the outline was, so folding one bullet rebuilt eighteen hundred of them: about 150ms of work for a change affecting one branch, which is what Andreas felt as lag. 250 rows at a time now, 30ms to fold, and the end of the drawn part says how many are left and brings them when you reach it.persona: packing Andreas
- tested The outline is a tree to a screen reader, not a run of sentences pandoIndentation is the whole meaning of an outline and it was carried entirely by a padding and a CSS variable, so by eye it was an outline and read aloud it was a flat list. The rows host is a tree, each row a treeitem carrying its level, and a row with children says whether it is expanded. aria-level is one-based where depth is zero, which is what the attribute means.test/e2e/reach.mjs Andreas asked for a UI rule set to be applied; this is the half of it a machine can check that nothing was checking.
- tested Every acknowledgement is heard as well as seen pandoCompleting paints green, a move flashes the row, a refusal shakes what said no. All three are silence for a screen reader, and the refusal grammar makes it worse: words come only on the third attempt, so the first two refusals were nothing at all. A polite live region carries the same one line the mark carries, and a refusal is announced on the first try rather than the third.test/e2e/reach.mjs Axiom 9 and axiom 7, applied to somebody who cannot see the mark
- tested A bullet says at rest what is true of it pandoFinished is grey and struck through, protected is a padlock, mirrored is a ring round the dot. Measured, a protected bullet and an ordinary one produced the identical sentence and the only way to find the lock was to type into it and be refused. Each row now ends with the words for whatever is true of it, in a span that is off screen and in the accessibility tree. A hidden span rather than a label on the row, because a label replaces what is inside it and the row would lose its link, its tag and its date.test/e2e/reach.mjs Axiom 9, third column
- tested A board, a dashboard and a timeline group what they arrange pandoWhich column a card is in is the whole content of a board: it is the status. Which panel a line is in is what a dashboard says. Which day a bullet is on is the only thing a timeline is for. All three said it by position alone, which is exactly what does not survive being read aloud, so each is a named group carrying its count in words. The table needed nothing: it has always been a real table element.test/e2e/reach.mjs The same question asked of every view whose meaning is its layout
- tested The whole app is reachable without a mouse, and focus is visible pandoEvery control on screen has something to announce, the keyboard reaches the chrome including search, a focused control looks different from an unfocused one, the menu opens from the keyboard and Escape leaves it with the caret back in the outline. An arrow with nothing to fold left the accessibility tree: a childless bullet keeps its arrow so every dot stays on one line, and it was announcing Collapse on every leaf in the outline.test/e2e/reach.mjs Axiom 6: every intent needs a visible, pressable path