brand book · v0.1
> curlo
Developer tools that feel native to the terminal — Pythonic, open, and one line away. The mark is how devs meet a tool: a curl line in the docs.
$ curl -sSL https://curlo.dev/install.sh | sh ✓ curlo installed # ready in one line
01 — logo
Logo
The mark is the >_ prompt: a green chevron (“run it”) and an amber cursor. It holds from a 16px favicon to a billboard.
Primary mark
logos/mark.svg
Wordmark
logos/lockup.svg
Favicon
SVG + PNG · holds at 16px
on dark
on light
02 — color
Palette
Dark by default. One signal green does the heavy lifting; cyan and amber support. Everything ships as --curlo-* tokens.
brand
#4ADE80–curlo-primary#38BDF8–curlo-secondary#FBBF24–curlo-accentstatus
#4ADE80–curlo-success#FBBF24–curlo-warning#FB7185–curlo-dangersurfaces · adapt to the active theme
03 — type
Typography
JetBrains Mono for anything code-shaped (display, commands, labels). Inter for body. Both self-hosted as variable fonts.
JetBrains Mono · display + code
ABCDEFG abcdefg 0123456789
> _ | { } [ ] < > $ ~ =>
Inter · body
Body copy — 16 / 400, comfortable for docs and portals.
Caption — 13 / 500, muted.
sans vs mono roles
product chrome · .curlo-page-title
~/curlo · dashboard
mono display for app / tool / product page titles
editorial · default h1
How we built the pipeline
Long-form prose keeps the sans heading default — right for WordPress posts, docs and marketing copy.
04 — components
Buttons
Monospace, CLI-flavored. Solids for actions, ghost/outline for quiet ones, and a copy-the-command --cli variant.
05 — components
Badges & tags
Solid badges for build/CI status. Outlined tags for metadata — versions, licenses, counts.
secondary
accent
passing
building
failing
python 3.13
MIT
★ 2.1k
open source
06 — components
Panels
Flat, hairline-bordered — they read like repo / README panels, not dashboard tiles. No drop-shadow bounce.
v1.4.0
The runtime behind every curlo command. Zero-config, single binary.
MIT
python 3.13
note
Accent callout
A thin accent stripe plus a // comment eyebrow. For tips, warnings and “what’s new” — the docs voice, not a marketing box.
A panel with a terminal title bar. Drop any content under the traffic lights — logs, results, previews.
README.md
A file-tab header for docs and code panels. Mono, muted, hairline divider — quiet chrome so content is loud.
07 — components
Terminal & code
The signature component. The hero of every page is the command itself. Code blocks add a line-number gutter.
# install curlo (the eponymous one-liner) $ curl -sSL https://curlo.dev/install.sh | sh # or, the Pythonic way $ pip install curlo $ curlo init --template "api" ✓ project ready in ./my-api # happy hacking
# curlo.toml[project]name = "my-api"template = "api"runtime = "python3.13"
08 — components
Forms & alerts
Inputs sit on surface with a green focus ring. Alerts use a left status stripe.
requests is outdated.
09 — components
Steps
Keycap-style markers for quickstarts and status lists.
Pipe the one-liner into your shell.
Scaffold a project from a template.
Deploy with a single command.
Informational step.
Completed successfully.
Failed / errored.
10 — components
Loaders
The signature busy state. Instead of a spinning ring, the six os of >curloooooo sit up front dimmed, then fill in left-to-right — a progress bar shaped like the wordmark. The os are real glyphs, so they match curl exactly and pick up its colour (dark on light, white in the terminal). Pure CSS, no JS, and the width is reserved from the start so nothing around it jumps.
>curloooooo
On surface
.curlo-curl
>curloooooo
On terminal
adapts to any surface
>curloooooo
Sizes
--sm · default · --lg
>curloooooo
>curloooooooooo
–curlo-curl-o: 3 · 6 · 10
label + loader (no offset CSS)
inline row · same size tier
>curloooooo
fetching packages…
stack · label above, left-aligned
>curloooooo
try it
$ curlo add curlo-core >curloooooo # ← resolving dependencies (this comment marks the end)
pause to freeze a frame and see where the fill ends
<!-- fills up on a loop; role=status announces it to screen readers --><span class="curlo-curl" role="status" aria-label="Loading"> <span class="curlo-curl__chev">></span>curl<span class="curlo-curl__o">oooooo</span></span><!-- print & fill any number of o's: set --curlo-curl-o + that many o's --><span class="curlo-curl" style="--curlo-curl-o:3"><span class="curlo-curl__chev">></span>curl<span class="curlo-curl__o">ooo</span></span>
full-page overlay
Put the whole page in a busy state: a modal terminal with the loader, over a blurred backdrop. Open it with curlo.loading() or a data-loading button. With no data-title the title bar picks a random dev task (compiling, resolving dependencies, …). This one closes itself after 3.5 s (or press Esc / click the backdrop).
Omit data-title and the bar shows one of these at random:
<!-- declarative: opens on click, auto-closes after data-duration ms --><!-- no data-title → the title bar shows a random dev task --><button class="curlo-btn curlo-btn--primary" data-loading data-duration="3500">test overlay</button>// programmatic: hold it open across an async task, then close by handvar h = curlo.loading();await doWork(); h.close();
form submit → overlay until navigation
Add data-loading to a <form> and the overlay covers the wait until the browser leaves the page (no auto-close). Native validation is respected — an empty field blocks submit and the overlay never opens. Submitting below reloads this page (a real navigation) to demonstrate; hit Back and you won’t be stuck behind the loader.
<!-- overlay opens on valid submit, stays until navigation (POST or GET) --><form action="/checkout" method="post" data-loading="processing…" data-title="~/curlo · checkout"> <!-- fields… --> <button class="curlo-btn curlo-btn--primary" type="submit">pay</button></form><!-- slow same-origin link: primary click shows the overlay, then navigates --><a class="curlo-btn" href="/reports/export" data-loading="exporting…">export</a>
11 — components
Command runner
Turn a button into an action. Click it and a popover opens, types the command into a terminal, then redirects when it finishes — the one-liner motion of the hero, wired to a real destination. This niche behaviour ships in the optional curlo-cli.js add-on (load it after curlo.js): any element with data-command becomes a trigger.
In this brand book the buttons use
data-redirect="self", so when the command finishes the page reloads. In production point data-redirect at any URL (e.g. a customer dashboard). The last button omits it, so the popover just stays open.
<!-- any element with data-command becomes a trigger --><button class="curlo-btn curlo-btn--primary" data-command="curlo launch --customer abc" data-result="✓ launched customer abc" data-redirect="https://app.example.com/abc">launch</button># once per page — core, then the command-runner add-on:<script src="…/curlo/curlo.js"></script><script src="…/curlo/curlo-cli.js"></script>
12 — integrate
Integrate
Pick the layer your stack needs. Fonts are self-hosted, so there are zero third-party calls.
Bootstrap stacks · WordPress
One file: tokens + base + components + a lean Bootstrap 5.3 variable bridge.
<link rel="stylesheet" href="…/curlo/curlo.bootstrap.min.css">
Everything else · Django / plain
Framework-agnostic tokens + curlo-* components. No Bootstrap.
<link rel="stylesheet" href="…/curlo/curlo.min.css">
Just the tokens · any design system
Consume palette / type / spacing as CSS variables and build your own components.
<link rel="stylesheet" href="…/curlo/tokens.css"><link rel="stylesheet" href="…/curlo/fonts.css">
13 — easter eggs
Easter eggs
A little brand delight, tucked away for the curious. Two secrets ship in the optional curlo-eggs.js add-on (self-contained — load it after curlo.js, or standalone). Both respect prefers-reduced-motion.
01 · secret commands
Type the secret
Punch in the Konami code, or just type sudo anywhere outside a text field — a cheeky line prints into the status bar down below (or a corner toast if a page has none).
↓↓
←→
←→
ba
02 · live focus prompt
The caret follows you
Press Tab to walk the page by keyboard and a >_ prompt glides to sit just left of whatever has focus — like the cursor is following you. Mouse users never see it, so it reads as a nav aid, not clutter.
The two buttons above just call
curlo.eggs.celebrate() so you can preview the lines without the keystrokes. In the wild there are no buttons — that’s the point.
<!-- self-contained; load after curlo.js (or on its own) --><script src="…/curlo/curlo-eggs.js"></script>// print your own status line, or turn the focus caret offcurlo.eggs.celebrate(">_ shipped it");curlo.eggs.focusPrompt(false);