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: how the library is organized, what content units are called, and how the reader and player screens are composed. This page covers what's configurable today via brand.json and the direction for the future.

What ships today

The app is a fixed-shell PWA (app/src/) with these screens and one persistent tab bar:

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

The tab bar (app/src/components/TabBar.tsx) is fixed: Home · Library · Now Playing · Settings. The routes are defined in app/src/router.ts (/, /library, /library/<bookId>, /read/<bookId>/<chapterId>, /now-playing, /settings, /about). This structure is not currently config-driven — changing nav arrangement or screen composition means editing the components.

What you configure via brand.json

Two fields drive presentation without touching code:

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 app/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 brand.json nouns — the app never hardcodes "story", "chapter", or "book". Consumed via app/src/brand.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 app/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.

The direction (planned)

The layout + nouns fields are the first slice of a larger idea: a presentation-layer template that is distinct from a theme — the theme carries identity (colors/fonts/icons), the presentation template carries structure (nav arrangement, screen composition, library organization). Today the shell is fixed and only layout/nouns vary it.

The planned distribution model mirrors themes:

Swappable presentation templates and a config-driven shell are planned, not available today. What you can rely on now is layout and nouns; deeper changes require editing the app components.


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