burgee
API reference

burgee/brand

Every export of burgee/brand, with its signature and doc comment: opposedField, burgeeFlagPath, fieldId, chargeTransform, chargeRotation, placeCharge and 8 more, plus 7 types.

burgee/brand — a brand declares itself once; every identity surface is that declaration read by a different reader.

The same thesis as the rest of the package, applied to identity instead of argv. You declare a field and a mark; out come the flag, the favicon, the OG card and the article cover, every one a projection of that declaration, so none of them can drift from the others.

THE FLAG. A burgee is the swallowtail flag a boat flies to say which club it belongs to — a flag of identity, not of instruction. This one is composed the way real club burgees are: a field, and one charge upon it. The field is a gradient run BACKWARDS along the axis the two Interlace bars are stacked on, so the leading bar sits over the following bar's colour and the reverse. The charge is the Interlace mark itself.

WHY THE MIDPOINT STOP IS LOAD-BEARING. Deep rock on deep juniper measures 1.16:1 — invisible. The middle stop drops the field to near-black exactly where the charge sits, lifting the two bars to 3.50:1 and 3.02:1, both clearing the 3:1 floor WCAG sets for a graphical object. Remove that stop and the mark disappears. It is contrast, not decoration.

NOT in the core entry point. import { defineCommand } from "burgee" must stay one import of one file with no build step; this is a separate subpath and costs that path nothing.

DETERMINISM. No timestamps, no randomness, coordinates rounded to 2dp. The one id in the output — a gradient cannot be anonymous — is derived from the declaration itself, so the same brand always produces the same bytes and two different brands can share a page without colliding.

import { opposedField, burgeeFlagPath, fieldId, … } from 'burgee/brand';

Functions

burgeeBody

Field, charge and optional bordure — everything inside the viewBox.

function burgeeBody(brand: BurgeeBrand, id?: string, moving?: boolean): string;
ParameterType
brandBurgeeBrand
id (optional)string
moving (optional)boolean

Returns string

burgeeFlagPath

The flag silhouette, as SVG path data.

function burgeeFlagPath(): string;

Returns string

chargeGroup

The two Interlace bars, rotated and placed as the charge.

function chargeGroup(colors: BurgeeColors, scale?: number): string;
ParameterType
colorsBurgeeColors
scale (optional)number

Returns string

chargeRotation

The angle the charge is rotated by, as an SVG transform.

function chargeRotation(): string;

Returns string

chargeTransform

Where the charge sits, as an SVG transform. Exported because consumers that hand-write the flag (a React component, say) must place it identically, and recomputing it at the call site is how the two drift apart.

function chargeTransform(scale?: number): string;
ParameterType
scale (optional)number

Returns string

defineBurgee

Declare a brand. Everything on the returned object is a projection of it.

const brand = defineBurgee({
  name: 'burgee',
  mark: { lead: '#a84c17', follow: '#0a6b47' },
  field: [
    { offset: 0, color: '#0a6b47' },
    { offset: 0.5, color: '#0a0a0a' },
    { offset: 1, color: '#a84c17' },
  ],
});
writeFileSync('icon.svg', brand.favicon());
function defineBurgee(brand: BurgeeBrand): Burgee;
ParameterType
brandBurgeeBrand

Returns Burgee

fieldId

A stable id for the field gradient, derived from the declaration.

A gradient is the one thing in SVG that cannot be anonymous. Deriving the id from the brand keeps output byte-identical across runs, and keeps two different brands from colliding when they share a page. Two instances of the SAME brand do share an id, which is harmless — the definitions are identical — and a React caller can pass its own useId() value instead.

function fieldId(brand: BurgeeBrand): string;
ParameterType
brandBurgeeBrand

Returns string

opposedField

The field two colours imply.

Reversed on purpose: the leading colour goes at the FAR end, so the leading half of the charge sits against the following colour and the reverse. Through a dark midpoint, because the charge sits at the centre and two saturated colours of similar weight cannot be told apart — the reason this is a default rather than something each caller re-derives.

function opposedField(colors: BurgeeColors, ground?: string): FieldStop[];
ParameterType
colorsBurgeeColors
ground (optional)string

Returns FieldStop[]

placeCharge

Place any charge markup where the charge belongs, at the charge's scale.

function placeCharge(markup: string, scale?: number): string;
ParameterType
markupstring
scale (optional)number

Returns string

Constants

BURGEE_ANGLE

const BURGEE_ANGLE: number;

BURGEE_FLAG

const BURGEE_FLAG: readonly Point[];

CHARGE

The charge occupies this fraction of the flag, and sits here within it.

const CHARGE: {
    readonly scale: 0.4;
    readonly x: 42;
    readonly y: 50;
};

DEFAULT_GROUND

The midpoint a field falls through when none is given. Near-black.

const DEFAULT_GROUND = "#0a0a0a";

FIELD_AXIS

The gradient axis: the direction the two bars are stacked on, traversed backwards — hoist-top to fly-bottom, in mark-space coordinates.

const FIELD_AXIS: {
    readonly x1: 25;
    readonly y1: 6.7;
    readonly x2: 75;
    readonly y2: 93.3;
};

Interfaces

Bordure

One band of the outline.

interface Bordure {
    color: string;
    /** Visible thickness, in mark-space units. */
    width: number;
}

Burgee

Everything one brand declaration projects into.

interface Burgee {
    /** The flag alone, square, at any size. */
    flag(size?: number): string;
    /**
     * The same mark with its sheen sweeping across it, for a page that can afford
     * motion — a site header, a docs hero. Identical to {@link Burgee.flag} when
     * no `sheen` is declared, and parked still under `prefers-reduced-motion`.
     *
     * Not the favicon and not the README: a tab icon that shimmers is a tab icon
     * that distracts.
     */
    alive(size?: number): string;
    /** Favicon master. One file serves both themes — the flag carries its own field. */
    favicon(size?: number): string;
    /** Social card, 1200×630. */
    og(options?: CardOptions): string;
    /** Article cover, 1000×420. */
    cover(options?: CardOptions): string;
    /** Flag and wordmark, laid out horizontally. */
    lockup(options?: CardOptions): string;
    /** The gradient id this brand emits, for callers that need to match it. */
    fieldId(): string;
}

BurgeeBrand

interface BurgeeBrand {
    /** Accessible name for the flag. Without one the flag is decorative. */
    name?: string;
    /**
     * The charge, as a two-colour bar pair. Ignored when {@link BurgeeBrand.charge}
     * is given — that is the escape hatch for a CLI bringing its own glyph.
     */
    mark: BurgeeColors;
    /**
     * Your own charge instead of the bars: SVG markup drawn in a 0 0 100 100 box,
     * which burgee places and scales for you. Everything else — the swallowtail,
     * the reversed field, the sizes — still comes from this one declaration.
     *
     * The markup is emitted verbatim, so it is yours to trust: this runs at build
     * time on a file you wrote, not on anything a user supplies at runtime.
     */
    charge?: string;
    /**
     * Your own silhouette instead of the swallowtail: SVG path data in the same
     * `0 0 100 100` box, filled with the field and carrying the charge exactly as
     * the flag does. For a sibling brand whose name is not a flag — a roundel is
     * rings, a parrot is a parrot — the shape is the whole point, and drawing it
     * here keeps every other projection (favicon, lockup, OG, cover) intact.
     *
     * Filled `evenodd`, so a subpath drawn inside another cuts a hole through it:
     * that is how a ring gets its centre and an eye gets its white. Subpaths that
     * are meant to read as one solid body must not overlap.
     *
     * Emitted verbatim, like {@link BurgeeBrand.charge}: a build-time value you
     * wrote, never anything a user supplies at runtime.
     */
    shape?: string;
    /**
     * A sheen: a soft highlight laid across the field, `0` to `1`, where the
     * number is how bright its brightest point is. Depth, not decoration — a flat
     * gradient reads as printed ink, and one light source makes the same shape
     * read as an object with a front.
     *
     * It is drawn INSIDE the silhouette (clipped to it), so it never softens the
     * outline the mark is recognised by, and it sits under the charge, so it never
     * touches the contrast the charge was measured at.
     *
     * The same layer is what moves in {@link Burgee.alive}.
     */
    sheen?: number;
    /**
     * A bevel: how strongly the mark's own edge catches the light, `0` to `1`.
     *
     * The whole of the third dimension a logo can afford. Two copies of the
     * silhouette stroked and clipped to itself — light offset up toward the light
     * source, dark offset away — so the edge lifts and the face stays flat. No
     * extrusion, no renderer, and nothing that stops it being a 16px favicon: the
     * bevel is sub-pixel there and simply disappears, which is the correct
     * behaviour rather than a compromise.
     */
    bevel?: number;
    /**
     * Markings: SVG markup in the same `0 0 100 100` box as {@link BurgeeBrand.shape},
     * drawn over the field and under the charge.
     *
     * One path can hold one fill, and some marks are not one colour — a roundel is
     * concentric rings, a caique has a black cap over an orange throat over a white
     * belly. Those are markings ON the body, not the body, and they are declared
     * here rather than by stacking whole brands on top of each other.
     *
     * Emitted verbatim, like {@link BurgeeBrand.charge}: a build-time value you
     * wrote, never anything a user supplies at runtime.
     */
    markings?: string;
    /**
     * The field, as gradient stops along {@link FIELD_AXIS}. One stop is a flat
     * field. Keep a dark stop under the charge or the mark will not read.
     */
    field: readonly FieldStop[];
    /**
     * The outline, outermost band first.
     *
     * One band is enough when you control the ground. Two is what you want when
     * you do not: no single flat colour clears 3:1 against both a near-black and a
     * white page, so a dark outer band and a light inner band are given, and
     * whichever one the ground does not match is the one carrying the silhouette.
     * That is one asset that holds its outline anywhere — which a favicon, having
     * no stylesheet to read a theme from, actually needs.
     *
     * `width` is the visible thickness of each band in mark-space units.
     */
    bordure?: Bordure | readonly Bordure[];
}

BurgeeColors

interface BurgeeColors {
    /** Leading bar, hoist side. */
    lead: string;
    /** Following bar, fly side. */
    follow: string;
}

CardOptions

interface CardOptions {
    width?: number;
    height?: number;
    /** Large line. Defaults to the brand name. */
    title?: string;
    /** Small line under it; wraps. */
    subtitle?: string;
    theme?: 'light' | 'dark';
    background?: string;
    foreground?: string;
    muted?: string;
}

FieldStop

One gradient stop: how far along the axis, and what colour.

interface FieldStop {
    offset: number;
    color: string;
}

Types

Point

A point in the mark space.

type Point = readonly [number, number];

On this page