🏴‍☠️

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's 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 have fast iteration loops.

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.

Riot should make the preferred path obvious. A golden path is not a prison. It is the route that has been designed, tested, documented, and supported end to end. You can step off it when you need to, but the core experience should not make every developer assemble their own workflow from pieces.

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.

Usually, if you make it beautiful, it will already be fast.

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

Direct, useful, joyful.

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

Direct

Say the thing. Avoid hedging, vague platform words, and narrative that hides the action.

Useful

Every sentence helps 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 · 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.

The 5px unit is small enough to tune dense interfaces without inventing one-off values, and large enough to stay memorable.

It keeps Riot surfaces rhythmic: rows, gutters, controls, and examples snap to the same cadence, so dense pages feel authored instead of arbitrary.

Typography can still be optical. The grid does its work in spacing, sizing, rhythm, and repeated structure.

u1 5px
u2 10px
u3 15px
u4 20px
u5 25px
u6 30px
u8 40px
u10 50px
UnitThe base spacing unit is 5px.
SizesControls, rows, icons, and marks follow named size tokens.
SpacersThe common rhythm is 5, 10, 15, 20, 25, 30, 40, and 50.
BordersUse 1px borders. Borders do not need to become grid units.

/03 · 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.

Typography has three jobs. Display type gives Riot its authored voice. Body type makes longer explanations comfortable. Code type keeps commands, paths, signatures, and numeric metadata precise.

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.
SizesUse the token scale for headings, labels, body text, and compact metadata.

/04 · 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.

Color clarifies what kind of information the reader is looking at. Paper carries reading, ink carries structure, coal carries code and terminal gravity, and Riot Red marks identity or action.

Paper #FFF8ED · Default reading surface
Paper-2 #F5ECDC · Recessed surface
Ink #15171B · Text, borders, structure
Coal #15171B · Terminal and code
Riot #EF233C · Flag, identity, primary action
Mint #24C08D · Success state
Amber #F0B429 · Warning state
Blue #2777FF · References and links
PrimaryRiot Red, paper, ink, and coal define identity, reading, structure, and terminal gravity.
SecondaryMint, amber, and blue are functional state colors for success, caution, and references.
Deliberate redUse Riot Red for identity, action, danger, and real emphasis.
Never color aloneState must also have labels, icons, text, or structure.

/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

Default geometry for docs, tables, panels, and command surfaces.

2px

Small radius is an exception for controls and terminal shells.

offset

Hard offset shadows create emphasis without making surfaces float softly.

heavy

Heavy emphasis belongs to major identity moments, not every card.

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.

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.

Primary actions use Riot Red and direct imperative labels.

Default actions keep the ink border and paper surface.

Dark actions carry terminal gravity when the context is technical.

Ghost command actions show copyable terminal intent without becoming a full code block.

Small primary actions are for compact tool surfaces.

Small secondary actions stay quiet but remain clearly clickable.

Badges

Small status and metadata markers. Use them for state, version, category, and stability labels.

stdlib

Default badges mark neutral categories and tags.

v0.6.0

Solid badges carry strong version or status metadata.

unstable

Riot badges are reserved for identity, risk, or attention.

stable

Mint badges communicate success, readiness, or passing checks.

beta

Amber badges communicate caution, beta state, or review needed.

Code

Code examples are product UI: command, output, signature, and copy affordance.

Code snippet
λ riot fmt --check
ok 128 files formatted
json: {"event":"fmt.checked","files":128}
curl -sSL https://get.riot.ml | sh
riot add krasny http postgres

Tables

Use table layout when users need to compare dense records quickly.

Data table

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

Numeric table

Run Mean P95 Delta
baseline42ms68ms0%
current37ms54ms-12%

Cards

Use cards for one bounded object. Do not use cards to replace comparison tables.

Basic card

Docs report

One object, one status, one next action. Cards frame a thing; they should not hide data that wants rows.

Status card

Build surface review

Use status cards for repeated summaries, settings groups, and focused previews.

Forms

Inputs are compact, labeled, and bordered in ink.

Fields keep labels close to values so compact forms still scan cleanly.

Version values stay mono and exact.

Short fields read like configuration, not marketing copy.

Groups are named by the decision they support, not by visual placement.

Callouts

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

/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 names the surface, the reader's task, the required information, the traps to avoid, and the components that carry it.

https://riot.ml

riot.ml

Many OCaml stacks.
/ One flies a flag.

The Riot website makes the stack legible fast. 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.

Signature
type ('a, 'e) t = Ok of 'a | Error of 'e
val map : ('a -> 'b) -> ('a, 'e) t -> ('b, 'e) t
terminal surfaces

CLI

Plain text still carries the brand.

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

CLI output
λ riot publish --dry-run
ok package checked
package: krasny
version: 0.2.1
next: run riot publish
https://pkgs.ml/not-found

Error pages

Useful dead ends.

Error pages are useful dead ends. Explain what failed, whether it is local or remote, and what the user can do next.