StoryLark
← All guides

Build Your Own Presentation

Where a theme changes how the app looks (colors, fonts, icons), the presentation layer is how it's structured: nav arrangement, Home's sections, library sorting/grouping, reader defaults, player controls, and what content units are called. It lives in its own file, presentation/<id>/presentation.json, separate from identity (brand.json) and from per-install config (deployment.json) — see build-your-own-theme.md for how the three relate. Example presentation templates live in the theme & template gallery.

It's a runtime file, like your brand

presentation.json is served and read on every request, the same way brand.json and theme.css are — not baked into the JavaScript bundle. Replace it on a deployed site and the next page load reflects the new arrangement: no rebuild, no redeploy of the app bundle. A missing key always falls back to StoryLark's built-in default, so a presentation file written today keeps working on every future engine version, and an unknown key is ignored with a warning rather than breaking the page. The easiest way to change it on a live site is a theme package that includes a presentation.json — installed from the admin portal's Brand & themes card or npm run import-theme, with the same validation, version history, and one-click rollback a theme gets.

What's configurable

Home        Continue card (resume where you left off) + new-releases carousel
Library     the shelf — sorting, search, cover art (list vs grouped by layout)
Book        a unit's detail / chapter list
Reader      read / listen / read+listen with word-synced highlighting
Now Playing full-screen audio player
Settings    account, typography, theme, read-along mode, downloads
About       app version, changelog, roadmap

presentation.json drives, without touching code:

Key Controls
nav Tab bar position, which entries appear, their order, their labels.
home.sections Which sections Home shows and in what order.
library Default sort, which sorts/groupings the picker offers, list vs. grid, whether search shows.
reader.defaultMode Read, Listen, or Read + Listen as the opening mode.
player Skip distance, the speed-control options.
cover.aspect Cover art aspect ratio across the app.
detail Which of cover / author / description / chapter list / length show on a unit's detail screen.
auth.required Whether an account is required to use the app.
settings Which controls the Settings screen offers.
download.mode Offline-download behavior.
emptyState Copy shown on an empty shelf.
about.links Links shown on the About screen.
layout / nouns See below — the original two knobs, now part of the same file.

Full key-by-key defaults live in storylark-core's DEFAULT_PRESENTATION; every value above defaults to the app's existing, shipped behavior, so an empty or partial presentation.json changes nothing you didn't explicitly set.

layout — flat vs series

"layout": "flat"    // standalone units in one flat list, no collection level
"layout": "series"  // units grouped into collections

The layout value also changes an auto-download default (see the autoDownload setting in packages/core/src/lib/types.ts): flat auto-downloads new units; series keeps a whole collection downloaded.

nouns — what a "unit" and "collection" are called

Every user-visible content word is pulled from presentation.json nouns — the app never hardcodes "story", "chapter", or "book". Consumed via packages/core/src/presentation.ts (NOUNS, countUnits()).

"nouns": {
  "unit": "story",      "unitPlural": "stories",
  "Unit": "Story",      "UnitPlural": "Stories",
  "collection": null,   "Collection": null
}
Key Meaning
unit / unitPlural Lowercase singular/plural of one content unit ("story"/"stories", "chapter"/"chapters").
Unit / UnitPlural Capitalized forms for sentence starts / headings.
collection / Collection The grouping level's name ("book", "series"), or null for a flat library.

Example for a chaptered-book library:

"layout": "series",
"nouns": {
  "unit": "chapter", "unitPlural": "chapters",
  "Unit": "Chapter", "UnitPlural": "Chapters",
  "collection": "book", "Collection": "Book"
}

countUnits(n) then renders "1 chapter" / "3 chapters" automatically, and layout: "series" groups those chapters under their book.

Content shape

The manifest and chapter schema the presentation renders is produced by the publish pipeline. Blocks the reader knows how to render (from packages/core/src/lib/types.ts / BlockRenderer):

paragraph (with em/strong spans) · scene-break · display-beat · message-block (speaker/time/text) · image · end-marker.

How markdown maps to these blocks is documented in content-pipeline.md; how the reader renders and highlights them is in read-along.md.

What still means editing components

Screens themselves — their internal composition beyond the toggles in the table above, and adding an entirely new screen — aren't config-driven. The shape of a screen (nav position, sections, sort options, defaults, what's shown) is presentation data; the code that renders a screen is still the engine. If a template you want isn't expressible with the keys above, that's the line: browse existing arrangements in the gallery for ones close to what you want, retune the JSON, and see the submission guide to share your own.


Found a gap? StoryLark is open source — improve these docs on GitHub.