Riot's Jolly Roger

A modern design system for RiotML, unifying the experience across the whole stack: web, CLI, documentation, package registry, diagnostics, and release surfaces.

Why Riot?

There are many OCaml stacks, this one is mine.

I wanted an ML-family language and ecosystem that felt beautiful and practical, that was actor-based and multi-core ready, without sacrificing a bit of type-safety, and just all around delightful to use with vertically designed tooling. Something built for shipping.

Riot is my take on what an ML stack should feel like in 2026: opinionated unified tooling, a modern packaging story with built-in docs, actor-oriented by default, a standard library for 80% of everything you'll ever need, and one flag over the whole thing.

Riot is about building software that works, shipping it quickly, making it beautiful when it proves itself useful, and enjoying the work along the way.

It is the most fun I've had programming in my entire career.

Jolly Roger is Riot's design system, in many ways our very own pirate flag, and it describes the values this stack, and all the supporting systems around it, lives and dies by.

01

Optimize for Developer Joy

I get joy out of building good software. Especially when it works, but even more so when it looks beautiful and it is fast. Riot is optimized to help you write code just like that: every interaction with the system is designed to make information clear, feedback actionable, and iteration loops fast.

Riot gets out of the way, and helps you build good software.

A Riot surface optimizes for developer joy when it reduces friction without hiding truth. It should make the next useful action obvious, put commands near claims, put examples near explanations, make errors recoverable, and make package and docs metadata easy to scan.

Joy does not mean everything is soft, friendly, or simplified until it becomes useless. Riot can be sharp. It can be loud. It can be opinionated. It can say no. Joy comes from clarity, momentum, beauty, and trust, not from sanding every edge away.

02

A Cohesive Platform

Riot is one piece, not many. It is a cohesive platform that covers the development experience end to end.

The compiler, runtime, actor model, package manager, docs generator, registry, test runner, benchmark harness, formatter, diagnostics, install scripts, standard library, and website should feel like parts of the same whole.

This is not about making every page look identical. It is about making every interface feel like Riot: same judgment, same voice, same density, same sharpness, same recovery patterns, same respect for the person building.

Designing as one piece does not mean nobody else can build on Riot. The crew can bring packages, lint rules, foreign dependencies, C or Zig integrations, reports, tools, and libraries. That is good. Riot should grow. But the core model is not up for committee redesign. Riot is allowed to have its own shape.

03

Build for Shipping

Riot exists so you can ship real software.

That means the design system should not reward performative complexity, academic posturing, or elaborate setup rituals. It should help people understand, install, try, build, test, publish, debug, and keep going.

Shipping is not the opposite of quality. Shipping is how you learn whether the thing matters. The Riot path is: make it work, learn whether it is the right thing, make it beautiful, make it fast where it needs to be fast, and keep shipping.

Riot should not be moralistic about prototype code. A prototype can be ugly. It can be one giant module. It can be one actor doing too much. It can be a rough pile of functions proving a hypothesis. When the idea proves itself, the stack should help the builder make it beautiful.

04

Human-led, Agent-ready

Riot is designed by humans. Taste matters. Authority matters. Judgment matters.

But Riot is also built for the way software is built now: with agents, migrations, generated reports, structured diagnostics, typed documentation, and tools that need to understand the project without guessing.

Human-led, agent-ready means the design has a human point of view and machine-readable structure. The captain is human. The surfaces are structured enough for agents to help.

AI-friendly does not mean vague chat UX. It does not mean autopilot magic. It does not mean generated slop. It does not mean the AI owns the taste. It means Riot speaks clearly enough that agents can inspect, migrate, repair, and extend projects without guessing what happened.

05

Trust Your Crew

Riot trusts you with powerful tools.

The stack should be approachable, but it should not be timid. Riot should give builders sharp tools: actors, message passing, macros, compile-time power, foreign dependencies, low-level visibility, multi-core runtime behavior, generated docs, package metadata, and eventually multiple compiler backends.

Power is part of the joy. A builder should be able to start high-level and productive, then reach down when they need to understand performance, allocation, scheduling, generated code, or platform behavior.

Power tools need visible edges. Riot can expose advanced mechanisms without making the default experience painful. The trick is not to remove power. The trick is to make the path obvious: simple first, deeper when needed.

Voice and writing

A brilliant maintainer in a good mood.

Riot's Jolly Roger should sound direct, opinionated, useful, technical, and joyful. It should not sound corporate, academic, generic, or clever when a clear label would help more.

Direct

Say the thing. Do not wrap every claim in hedging language, vague platform words, or narrative that hides the action.

Useful

Every sentence should help someone install, find, fix, understand, compare, decide, or keep moving.

Joyful

Joy means momentum: beautiful code, fast feedback, good defaults, visible power, and the pleasure of building useful software.

Error voice

Clear, structured, recoverable.

  • Name the problem before explaining the system.
  • Show package, source, requested thing, expected or available thing, and why it happened.
  • End with one concrete command, edit, or docs link.
  • Keep the diagnostic readable as plain text.

Agent-ready voice

Stable labels. No guessing.

  • Use stable labels and predictable structure.
  • Prefer package, source, wanted, found, and fix fields.
  • Make examples reusable without hidden setup.
  • Avoid prose that forces tools to infer important state.

Better than vague

Bad:
Something went wrong while resolving dependencies.

Good:
Riot could not resolve Krasny because riot-cli does not depend on the package that exposes it.

Run riot add krasny.

Preferred structure

package: riot-cli
source:  src/publish.ri
wanted:  Krasny
fix:     riot add krasny

Tangibles

Visible decisions, not decoration.

Tangibles are the mark, typography, color, spacing, layout, edges, tables, code, and terminal output. They follow the beliefs. They are not arbitrary styling choices.

/01 · Mark and flag

A black flag with a white λ.

The Jolly Roger is the flag of Riot: sharp, simple, reproducible, and more like a symbol stamped on a tool than a mascot illustration.

Use as identityLet the mark sign a surface. Do not use it as repeated decoration.
High contrastPrefer simple placements where the mark survives at small sizes.
No mascotThe flag is enough. Do not over-illustrate the metaphor.
Never competeThe mark should not fight code, docs, or diagnostics for attention.

/02 · Typography

Technical, authored, readable.

Use a bold display mono for headings, navigation, labels, and data chrome; a readable sans for explanations; and a regular mono for code and terminal output.

DisplayMartian Mono · 800
Hoist the λ

Programming ecosystem. Headings, navigation, labels, data chrome.

BodyAtkinson Hyperlegible · 400

Long explanations should be comfortable enough to read without making dense technical pages feel padded.

Human explanation. Long docs prose. Editorial surfaces.

CodeMonospace stack · 500
$ riot add httpaf
→ resolved 12 packages in 84ms

Code is product UI. Commands, paths, signatures, table numerics.

RuleDo not use display mono for long code snippets.
RuleMake headings breathe, but keep the system compact.

/03 · Color

Cream, coal, ink, and one red.

Riot Red is identity and action. Paper is reading. Coal is terminal and gravity. Ink is text. Mint, amber, and blue are functional colors.

Paper #FFF8ED · Default reading surface
Paper-2 #F5ECDC · Recessed surface
Ink #151317 · Text, borders, structure
Coal #0E0D10 · Terminal and code
Riot #F5334D · Flag, identity, primary action
Mint #24C08D · Success state
Amber #F0B429 · Warning state
Blue #2777FF · References and links
Deliberate redUse Riot Red for identity, action, danger, and real emphasis.
Paper for readingDense docs and long-form prose live on warm paper surfaces.
Coal for gravityUse coal for terminals, code-heavy blocks, and hero-level contrast.
Never color aloneState must also have labels, icons, text, or structure.

/04 · Spacing

5px base unit. Memorable scale.

Use 5px increments for spacing and component rhythm. The common scale is 5, 10, 15, 20, 25, 30, 40, 50.

u1 5px
u2 10px
u3 15px
u4 20px
u5 25px
u6 30px
u8 40px
u10 50px
BordersUse 1px borders. Borders do not need to become grid units.
TypographyUse body type and line-height by eye when readability needs it.

/05 · Edges

Sharp, not inflated.

Square edges by default. Small radius only when hard edges create visual artifacts. Prefer borders and offset shadows over soft floating cards.

square
2px
offset
heavy
DefaultUse square geometry for cards, tables, docs shells, and command blocks.
ExceptionStatus tags and terminal shells may use small radii.

/06 · Density

Clean compression.

Riot surfaces carry packages, modules, types, versions, dependencies, diagnostics, reports, and docs. Density is a feature when hierarchy is clear.

Use tablesComparison belongs in rows, not decorative cards.
Use sidebarsLong references need orientation and anchors.
Keep metadata closeVersions, dates, owners, and docs status sit near the thing they describe.
Hierarchy firstReach for labels, rules, alignment, and type before adding empty air.

/07 · Layout

Chapter, body, anchors.

Use the shell to orient the reader, chapters to create long-page structure, and compact metadata to keep decisions local.

ShellPage frame plus flexible main column.
ContentReadable width with tokenized gutters.
ChapterMarker column plus body column.
SectionTokenized vertical padding and rule.

/08 · Code and terminal

Terminal output is UI.

Commands, package names, paths, signatures, diagnostics, and numeric output use the mono channel. Color always means state.

λ riot fmt --check
ok 128 files formatted
json: {"event":"fmt.checked","files":128}

Components

Recurring communication patterns.

Components are not a generic UI kit. They are repeated Riot communication patterns with a purpose, anatomy, rules, examples, and anti-examples.

Buttons

Direct, compact, imperative actions.

Command block

Executable commands with output when output confirms success or explains failure.

λ riot add krasny http postgres
ok resolved 12 packages
next: run riot build

Copy strip

One command close to the explanation, with an obvious copy affordance.

curl -sSL https://get.riot.ml | sh
riot add krasny http postgres

Diagnostic block

Help a blocked user recover.

error[R0087]: Type mismatch in fold accumulator

package: demo
source:  src/main.ri:42:13
wanted:  int
found:   string

42 │ let total = items |> List.fold (+) "zero"^^^^^^

fix: change "zero" to 0

Package row

Use table layout when users need to compare packages quickly.

Package Version Docs Owner Updated
std0.6.0readycoretoday
http0.7.1readynet2d
krasny0.2.1partialweb1d

Package card

Use cards for narrative package contexts, not comparison lists.

std@0.6.0

Datatypes, collections, IO, time, formats, crypto.

docs ready · updated today
krasny@0.2.1

Web framework on top of httpaf and the actor runtime.

source linked · 1d

API signature row

Make generated docs scannable.

Result.map('a -> 'b) -> ('a, 'e) t -> ('b, 'e) tstable
Package.searchquery:string -> package list promisebeta
Docs.renderartifact -> html resultstable

Callout

Interrupt only when the information changes what the reader should do.

Caution

Foreign dependencies need visible platform caveats and a concrete recovery path.

Forms

Inputs are compact, labeled, and bordered in ink.

Module index

Module indexes should scan like maps, not grids of teasers.

Release note

Lead with breaking changes, group by risk, and link to migration help.

0.6.0Static docs shell, package cards, and terminal diagnostics.
0.5.2Registry tables moved to the Jolly Roger density rules.
0.5.0First pass at voice rules for CLI output and agents.
/A · Primary mark

Riot · The Jolly Roger

/B · Context

Use the mark as identity, not decoration. Pirate energy can exist, but the system should not become costume. The mark is enough.

on cream
on coal
on amber
on red

In the wild

Real surfaces governed by the system.

Each application should explain what the surface is for, what the reader is trying to do, what must be visible, what should be avoided, and which components it uses.

https://riot.ml

riot.ml

Many OCaml stacks.
/ One flies a flag.

The Riot website should make people think: this might be the ML stack I wanted. Show the command, the stack, and the path to install, learn, and ship.

std@0.6.0

Standard library surface for system work.

published today
kernel@0.0.18

Core abstractions shared across Riot.

updated 6h ago
https://pkgs.ml
pkgs.ml
1,247 packages
Total packages1,247
Downloads8.4M
Publishers309
Median publish1.8s
Package Version Description Category Downloads Updated
std 0.4.0 Standard library. Datatypes, collections, IO, time, formats. core 1,247,890 today
httpaf 0.7.1 Fast, low-allocation HTTP/1.1 server and client. net 840,221 2d
kernel 0.0.18 Core abstractions shared across the Riot ecosystem. core 620,498 6h
krasny 0.2.1-rc Web framework on top of httpaf and the actor runtime. web 98,114 1d
https://docs.pkgs.ml/p/kernel/0.0.18/Kernel/Result
Riot Docs kernel / Kernel / Result v0.0.18

module · Kernel.Result

Result

Errors are values. Results carry them through pipelines, actor boundaries, and logs.

type ('a, 'e) t = Ok of 'a | Error of 'e
val map : ('a -> 'b) -> ('a, 'e) t -> ('b, 'e) t
Use let* for chains. Stop nesting binds when the next step can fail.
terminal surfaces

CLI

Plain text still carries the brand.

Prompts, aligned labels, diagnostic IDs, and fix commands should work without browser chrome.

λ riot publish --dry-run
ok package checked
package: krasny
version: 0.2.1
next: run riot publish