- Python 99.2%
- Shell 0.8%
Resolves its own location (following symlinks), walks up to the repo root that holds the blocky package, puts it on PYTHONPATH and runs the CLI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| bin | ||
| blocky | ||
| examples | ||
| tests | ||
| .gitignore | ||
| pyproject.toml | ||
| README.md | ||
blocky
Generate attractive block diagrams from a simple text format.
blocky turns a small, human-friendly text description into a clean SVG (or PNG) block diagram. It takes care of the tedious, easy-to-get-wrong parts — consistent sizing and spacing, sensible default colours, nesting, arrow routing that avoids overlaps — so that a few lines of input produce something that looks designed rather than dumped.
diagram pipeline {
block input "Input"
block parse "Parser"
block layout "Layout"
block render "Renderer"
block output "SVG / PNG"
input -> parse
parse -> layout
layout -> render
render -> output
}
Why it looks good by default
- Consistent layout & sizing. Blocks are sized to their labels and children with uniform padding; a layered layout assigns them to ranks along a flow direction and orders them to minimise crossings.
- Consistent spacing. Gaps between blocks are uniform and widen automatically where more arrows need to pass between them.
- Sensible default colours. Every block gets a coordinated fill, border and readable text colour with no configuration. Distinct classes are auto-assigned from a calm palette.
- Grid snapping. Every edge snaps to a configurable grid (default 10×10) so the diagram lines up crisply.
- Arrow routing. Arrows route orthogonally with right-angled bends around blocks, and are pushed into separate lanes so they don't overlap each other.
Install
pip install -e . # SVG output, no dependencies
pip install -e '.[png]' # add PNG output (cairosvg)
Usage
Run it straight from a checkout with the bundled launcher (no install needed):
./bin/blocky diagram.blk # finds the package relative to itself
Or, once installed (pip install -e .), use the blocky command:
blocky diagram.blk # -> diagram.svg (last diagram)
blocky diagram.blk -o out.svg
blocky diagram.blk -o out.png # PNG inferred from extension
blocky diagram.blk -f png -o out.png
blocky diagram.blk -d core # render a specific named diagram
blocky diagram.blk --list # list diagrams in the file
blocky diagram.blk -g 8 # override the grid size
blocky diagram.blk --theme sharp # render with a different theme
If you don't name a diagram, the last one in the file is rendered — which is usually the largest, composed from the earlier ones.
The format
The format is line oriented and forgiving. A whole diagram can be a handful of
block and arrow lines; everything else is optional.
Blocks
block id # id is also the label
block id "A nicer label"
block id : someclass "Label" # id : class "label"
block group "Container" { # blocks nest; containers size to fit children
block child1 "Child 1"
block child2 "Child 2"
child1 -> child2 # arrows can live inside a body, near their blocks
}
A block with children is a container: it is sized automatically to wrap them with padding and a band at the top for its own label.
Labels and descriptions
Long labels wrap automatically; use \n to force a line break. A block can also
carry a desc: shown beneath the label in a smaller, muted font — handy for a
one-line note like a technology or responsibility. This works on leaves and
containers alike.
block db : data "Primary Database" {
desc: "PostgreSQL 16\nmulti-AZ, read replicas"
}
Arrows
a -> b # forward
a <- b # backward
a <-> b # bidirectional
a -- b # plain line, no heads
a -> b : bus # give it a class
a -> b : bus "payload" # ...and a label
Endpoints can reference any block by id. A uniquely-named nested block can be
referenced by its bare id; otherwise use a dotted path (group.child1).
Classes
Classes let you style a whole category of block or arrow once. display is the
name shown in a legend.
blockclass cpu {
display: "CPU core"
fill: #4c78a8 # everything else (border, text colour) is derived
text: #ffffff # ...but can be overridden
}
arrowclass bus {
display: "System bus"
color: #2f4b66
width: 2.4
style: dashed # solid | dashed | dotted
}
Block class properties: display, fill, stroke, text, stroke_width,
radius, legend (off to hide from auto legends).
Arrow class properties: display, color, width, style, legend.
Any property can also be set inline on a single block or arrow with the same
key: value syntax inside a { ... } body.
Constraints
Constraints are optional hints honoured by the layout when set:
a left-of b # ordering along / across the flow
a right-of b
a above b
a below b
a near b # pull two blocks closer
a far b # push them apart
a near b 20 # ...with an explicit gap in pixels
Flow direction
diagram d {
flow: down # right (default) | down
...
}
Composition
Define a small diagram, then drop it into a larger one as a self-contained unit. The inner diagram is laid out on its own and then placed as a block:
diagram node {
block soc "SoC" { block cpu : logic "CPU" block ram : mem "RAM" cpu -> ram }
block flash : mem "Flash"
soc <-> flash
}
diagram board {
use node as left
use node as right
block switch "Switch"
left <-> switch
right <-> switch
}
Legend
legend auto # list every block & arrow class actually used
legend off # (default) no legend
legend manual "Title" { # list exactly what you choose
block cpu
arrow bus
}
Themes
A theme sets the defaults for a whole diagram — the palette used to auto-colour classes, the default block and arrow styling, the font, and whether corners are rounded. Two themes ship built in:
default— the calm palette with softly rounded corners (what you see throughout this README).sharp— the same palette and styling with square corners on blocks and arrow bends.
Select a theme in the file or on the command line; the CLI wins:
theme sharp # in the .blk file (top level)
blocky diagram.blk --theme sharp
blocky diagram.blk --theme path/to/custom.theme
Themes are plain text files. A custom theme can include a built-in one and
override just what it needs:
# ocean.theme
include: default
palette: #2a6f97, #468faf, #61a5c2, #89c2d9, #014f86
block.fill: #e8f1f5
arrow.color: #014f86
Theme properties: palette, font_family; block.fill, block.stroke,
block.text, block.stroke_width, block.radius; container.fill,
container.stroke, container.text; arrow.color, arrow.width,
arrow.corner_radius. Set block.stroke/block.text to auto to derive a
coordinated border / legible text colour from the fill, and set the radii to 0
for square corners. Class and inline properties always override the theme. The
built-in themes live in blocky/themes/.
Examples
The examples/ directory contains the source .blk files and
their rendered output:
| Example | Shows |
|---|---|
minimal.blk |
the smallest useful diagram |
soc.blk |
classes, nesting, arrow classes, auto legend |
compose.blk |
reusing a sub-diagram, named selection |
webapp.blk |
flow: down, spacing, a manual legend |
scheduler.blk |
arrow labels, dense fan-out, lane separation |
services.blk |
descriptions and wrapped multi-line labels |
Library use
from blocky import parse_file, render_document, write_output
doc = parse_file("diagram.blk")
svg = render_document(doc, name=None) # None -> last diagram
write_output(svg, "out.png", "png")
How the layout works
- Compose.
usedirectives expand into nested container blocks holding a copy of the referenced diagram, with its internal arrows carried along. - Size, bottom-up. Leaf blocks are sized to their label; containers are sized to wrap their laid-out children plus a label band.
- Layer. At each nesting level, blocks are assigned to ranks along the flow axis (from arrow direction and ordering constraints) and ordered within each rank by a barycenter heuristic to reduce crossings.
- Align. Connected blocks in adjacent ranks are pulled toward a common cross-axis position (via isotonic projection that respects order and spacing) so that single connections come out as straight lines.
- Place & snap. Ranks are spaced with consistent gaps that widen for busy channels, then every edge is snapped to the grid.
- Route. Each arrow leaves and enters its blocks with a straight stub (clearing any container border it crosses) so it always meets an arrowhead head-on. Ports sharing a side are fanned out with even spacing, and where two blocks' ranges overlap their ports are aligned to keep the link straight. The path itself is an A* search that rewards straight runs and penalises grid cells used by other arrows, so parallel arrows separate into their own lanes; straighter arrows route first and claim the direct lanes.






