BrandArtisan How it works AI skills CLI & API GitHub
The next/og engine, outside Next

Change it once,
every visual follows.

Change your tagline once, and every visual that carries it is rebuilt. Your Open Graph image, your LinkedIn and X posts, your banners and carousels drift a little further from your brand each time you touch one, and every fix is a round trip through Figma. BrandArtisan makes them code: palette, logotype and type in one place, one command re-exports every PNG. No Figma, no browser, no server.

$npx create-brand-artisan my-visuals

Node.js ≥ 22. Nothing else. · Source · npm · MIT

templates/calame/og.tsx
import type { Template } from "brand-artisan";
const SIZE = { width: 1200, height: 630 };  // Open Graph
const INK = "#1c1917";  // brands/calame/brand.md

function render() {
  return (
    <div style={{ display: "flex", height: "100%", color: INK }}>
      <div style={{ fontFamily: "Sora", fontSize: 70 }}>
        Write while you build
      </div>
    </div>
  );
}
export default { size: SIZE, render } satisfies Template;
npm run build
The Open Graph image produced by the source alongside: the Calame logotype, the headline Write while you build, and the mark bleeding off the top right corner.

out/calame/calame-share-preview.png · 1200 × 630 · source abridged, full file

01 / WHY

Three dependencies, removed.

Under the hood it is Satori + resvg, the next/og engine taken out of its Next wrapper: JSX → SVG → PNG. react only provides the JSX runtime; there is no react-dom and no client-side rendering.

No browser

Nothing headless to install, boot, or keep in step with CI. Rendering is a plain Node process.

No design tool

No file to open, export and re-upload by hand. A visual is a .tsx you diff, review and revert like any other file.

No server

Nothing to deploy or pay for. Images are built once, land in out/, and are yours to upload anywhere.

02 / HOW IT WORKS

A file, a URL, a PNG.

A .tsx under templates/<project>/ default-exports a Template. There is no registry to edit: dropping the file in is the whole registration.

the project it generates
my-visuals/
├─ brands/calame/        guidelines: brand.md, project.md
├─ templates/calame/     the visuals: one .tsx, one image
│  ├─ og.tsx
│  ├─ banner.tsx
│  └─ linkedin-carousel/ a folder of slides → one PDF
├─ tools/calame/         logo and favicon generators
├─ fonts/                Sora-700.ttf, Geist-400.ttf…
└─ out/                  what you upload
  • Write it. Flexbox and inline styles, the subset Satori renders. Any element with several children must be display: flex. That single rule causes most render errors.
  • Watch it. npm run dev serves a browsable preview at localhost:4000. Every visual gets its own URL, like Next routes, and re-renders on each refresh, the files it imports included.
  • Ship it. npm run build exports everything to out/<project>/, ready to upload.
dev server
localhost:4000/                lists the projects
localhost:4000/calame/og       preview page
localhost:4000/calame/og?raw   the raw PNG
03 / OUTPUT

Every image on this page came out of the repo.

These are the demo brand’s own templates, rendered by npm run build, not mockups, not screenshots of a design tool. Clone the repository and you get the same files back.

04 / CAROUSELS

A folder of slides, one PDF.

Each slide renders to its own PNG, which is what a carousel ad takes. An organic LinkedIn carousel is another animal: the swipeable one in the feed is a document post, and what you upload is a single PDF that LinkedIn paginates into slides. The same images attached to a post come out as a mosaic of thumbnails instead.

assembling the document post
$ npm run pdf -- calame/linkedin-carousel
 out/calame/linkedin-carousel.pdf
  • One slide per page, each at its slide’s exact size, in natural order: card-2 before card-10.
  • The command refuses to assemble slides of mixed ratios: LinkedIn takes the whole document’s ratio from its first page, so a stray page would come out letterboxed.
  • In dev, a folder’s page offers its PDF as soon as it exists, and flags it out of date when a slide is more recent.
Carousel slide 1.
card-1.tsx
Carousel slide 2.
card-2.tsx
Carousel slide 3.
card-3.tsx
Carousel slide 4.
card-4.tsx
Carousel slide 5.
card-5.tsx · 1080 × 1350
05 / DISCIPLINE

No visual without guidelines.

One project, one set of guidelines: its reference in brands/<project>/, its visuals in templates/<project>/.

brand.md sets the look

Palette, logotype, typography, do and don’t. Blocking: nothing gets composed without it. This is what stops humans and AI alike from inventing a color or rebuilding a logo by eye.

project.md sets the voice

Pitch, audience, value proposition, editorial voice, and the claims that are allowed. Read before a single word goes onto a visual, so no figure gets made up.

tools/ makes what rendering cannot

A separate toolchain generates what a template cannot: an SVG logotype traced from the font’s glyphs, multi-size favicons, .ico. Each script runs on its own, into out/<project>/brand/.

06 / DEVICE FRAMES

72 frames, screens already measured.

Phones, tablets, laptops, TVs and a few industry terminals, each carrying the exact coordinates of its screen, so a mockup sits inside a visual with nothing left to eyeball.

MacBook Pro 16 frame with a Calame visual sitting in its screen, the screen area outlined in amber.

screen: 3456 × 2170 @ 394, 130, from the catalog, not from the eye

frame(slug, width) scales a frame and its screen area together: you give a width, you get back the picture and the rectangle to draw into. The outline on the frame is not eyeballed for this page: it is that catalog entry, drawn from the same numbers.

assets/devices/catalog.ts
"apple-macbook-pro-16-2021": {
  image:    { width: 4244, height: 2594 },
  viewport: { width: 1728, height: 1085 },
  screen:   { x: 394, y: 130,
              width: 3456, height: 2170, radius: 0 },
}

The frames do not ship with the engine: copy assets/devices/ into your project, and read its NOTICE.md first: it covers the scale: 2 rule that keeps edges clean, and the rights, since most of these devices carry registered trademarks.

07 / WITH AN AI AGENT

Skills that already know the specs.

BrandArtisan ships skills: each platform’s official dimensions, its safe zones, and the nothing-outside-the-guidelines discipline. They install into whichever agent you use: Claude Code, Cursor, Copilot and twenty-odd others.

$npx skills add roslove44/brand-artisan -s "*" -y
/new-project my-brandSets up the guidelines through an interview.
/import-referenceDerives the guidelines from your existing visuals, measured to the pixel rather than guessed.
/og-image my-brandA 1200 × 630 Open Graph image.
/linkedin-post, /x-post…Visuals cut to each platform’s specs: posts, page headers, carousels.
/campaign my-brandA multi-platform kit from a single brief.
/brand-assets my-brandGenerates the logo, the favicon and their variants.

The agent reads your guidelines, asks the questions it is missing, and produces verifiable .tsx files: npm run typecheck, npm run build. Everything can still be done by hand. AI is an accelerator, not a requirement.

08 / SURFACE

Four commands, five exports.

Commands work from any subfolder of the project.

THE CLI

devRender server at localhost:4000.
buildExports templates/ to out/.
pdf <folder>Assembles a folder of slides into a PDF.
colors <image>An image’s palette, measured rather than guessed.

THE API

type TemplateA visual’s contract: { size, title?, render }. scale: 2 for retina.
brand(path)Absolute URL into brands/, ready for readFile.
root(path)Resolved from the project root.
toPng(node, size)JSX → PNG, as a Buffer.
renderToFile(…)The same, but writes out/<out>.png.

Two subpaths for brand tooling: brand-artisan/brandkit (glyph outlines, SVG → PNG, .ico) and brand-artisan/colors (palette and painted-area measurement).

Your first image before you have written anything.

The generated project holds only your brand: guidelines, visuals, asset scripts, fonts. The engine lives in node_modules and updates with npm update. A complete example ships with it: Calame, the fictional brand you have been looking at all the way down this page.

$npx create-brand-artisan my-visuals