Skip to content

Brandbook generator #104

Description

@abdullah13599ak-rgb

Script Name: (e.g., Brandbook generator)

Author: (Abd or Bombaygrid)

Description: (It can create 12/24.40 pages comprehensive brandbook just by adding logo, colors & bried description?)

Preview image: (drag and drop 16x9 preview image)

Version: (Version of script)

Code:

Your javascript code

// ============================================================
// BOMBAYGRID — BRANDBOOK GENERATOR FOR AFFINITY DESIGNER
// Version 2.0 | bombaygrid.com
//
// HOW TO USE:
// 1. Open Affinity Designer (v2.x)
// 2. Go to File > Run Macro... (or View > Studio > Macros)
// Alternatively: use the built-in Script Editor (Help > Scripts)
// 3. Paste this entire file
// 4. Fill in the BRAND CONFIG section below
// 5. Run the script
//
// Generates: 12 / 24 / 40 page brandbooks
// Spec: A4 (210 x 297mm) | 300 DPI | CMYK
// ============================================================

// ╔══════════════════════════════════════════════════════════╗
// ║ ① BRAND CONFIG ║
// ║ Fill this section with your brand data ║
// ╚══════════════════════════════════════════════════════════╝

const BRAND = {

// ── Basic Info ──────────────────────────────────────────
name: "Brand Name", // e.g. "ROOM"
tagline: "Your brand tagline", // Short punchy line
year: "2025",

// ── Page Count ──────────────────────────────────────────
// Options: 12 | 24 | 40
pageCount: 24,

// ── Style Theme ─────────────────────────────────────────
// Options: "minimal" | "editorial" | "bold"
style: "editorial",

// ── Colors ──────────────────────────────────────────────
colors: {
primary: "#1A1A2E", // Main brand color
secondary: "#E94560", // Supporting color
accent: "#F5A623", // Highlight / CTA color
neutral: "#F4F4F0", // Light background
dark: "#0A0A0F", // Deep background / type
white: "#FFFFFF",
},

// ── Logo Files ──────────────────────────────────────────
// Paste the FULL file path to each exported logo asset
// Supported: PNG / SVG / PDF
logos: {
primary: "", // e.g. "/Users/you/Logos/brand-primary.png"
secondary: "", // Horizontal lockup
tertiary: "", // Icon / mark only
// Leave empty ("") if file doesn't exist — pages will use placeholder
},

// ── Typography ──────────────────────────────────────────
fonts: {
primary: "Helvetica Neue", // Display / Headings font
secondary: "Georgia", // Body / editorial font (optional — set "" to skip)
},

// Font size scale (in points)
fontSizes: {
display: 72, // Hero / cover
H1: 48,
H2: 36,
H3: 28,
H4: 22,
H5: 18,
H6: 14,
body: 11,
caption: 9,
label: 8,
},

// ── Brand Story ─────────────────────────────────────────
about: {
description: "A short, punchy brand description. What does this brand do in one sentence?",

research: "Describe your market research findings. Who is the target audience? What gap does the brand fill? Include competitive insights.",

aboutUs: "Tell the brand's story. The founder's journey, the mission, the vision. This is the heart of the brand — write with intention.",

},

// ── Brand Voice & Values ────────────────────────────────
voice: {
personality: ["Bold", "Witty", "Human", "Direct"], // 3–5 words
tone: "Confident but approachable. Never corporate. Always clear.",
avoid: ["Jargon", "Passive voice", "Clichés"],
},

};

// ╔══════════════════════════════════════════════════════════╗
// ║ ② SCRIPT ENGINE (Do Not Edit) ║
// ╚══════════════════════════════════════════════════════════╝

// ── Constants ───────────────────────────────────────────────

const PAGE_W = 595.28; // A4 width in pts (210mm)
const PAGE_H = 841.89; // A4 height in pts (297mm)
const MARGIN = 42.52; // ~15mm margin
const COL2 = (PAGE_W - MARGIN * 3) / 2; // Two-column width
const GRID = PAGE_H / 12; // Baseline grid unit

// Style maps per theme
const THEMES = {
minimal: {
coverBg: BRAND.colors.white,
coverText: BRAND.colors.dark,
accentLine: BRAND.colors.primary,
sectionBg: BRAND.colors.neutral,
bodyBg: BRAND.colors.white,
dividerH: 0.5,
},
editorial: {
coverBg: BRAND.colors.dark,
coverText: BRAND.colors.white,
accentLine: BRAND.colors.secondary,
sectionBg: BRAND.colors.primary,
bodyBg: BRAND.colors.white,
dividerH: 1,
},
bold: {
coverBg: BRAND.colors.primary,
coverText: BRAND.colors.white,
accentLine: BRAND.colors.accent,
sectionBg: BRAND.colors.secondary,
bodyBg: BRAND.colors.neutral,
dividerH: 3,
},
};

const THEME = THEMES[BRAND.style] || THEMES.editorial;

// ── Page Manifest (12-page core, extended for 24/40) ────────

function buildManifest(count) {
const core12 = [
{ id: "cover", title: "Cover", section: "INTRO" },
{ id: "toc", title: "Table of Contents", section: "INTRO" },
{ id: "about", title: "About the Brand", section: "01 BRAND STORY" },
{ id: "research", title: "Research & Positioning", section: "01 BRAND STORY" },
{ id: "logo_primary", title: "Primary Logo", section: "02 LOGO SYSTEM" },
{ id: "logo_variants", title: "Logo Variants", section: "02 LOGO SYSTEM" },
{ id: "logo_usage", title: "Logo Usage Rules", section: "02 LOGO SYSTEM" },
{ id: "color_system", title: "Color System", section: "03 COLOR" },
{ id: "typography", title: "Typography System", section: "04 TYPOGRAPHY" },
{ id: "dos_donts", title: "Do's & Don'ts", section: "05 BRAND RULES" },
{ id: "voice", title: "Brand Voice & Tone", section: "06 VOICE" },
{ id: "back", title: "Back Cover", section: "END" },
];

const ext24 = [
{ id: "mission", title: "Mission & Vision", section: "01 BRAND STORY" },
{ id: "values", title: "Brand Values", section: "01 BRAND STORY" },
{ id: "logo_clearspace", title: "Logo Clear Space", section: "02 LOGO SYSTEM" },
{ id: "logo_color", title: "Logo on Color", section: "02 LOGO SYSTEM" },
{ id: "logo_misuse", title: "Logo Misuse", section: "02 LOGO SYSTEM" },
{ id: "color_palette", title: "Extended Palette", section: "03 COLOR" },
{ id: "color_pairs", title: "Color Pairings", section: "03 COLOR" },
{ id: "type_scale", title: "Type Scale", section: "04 TYPOGRAPHY" },
{ id: "type_pairs", title: "Type Pairings in Use", section: "04 TYPOGRAPHY" },
{ id: "iconography", title: "Iconography", section: "07 ELEMENTS" },
{ id: "imagery", title: "Photography & Imagery", section: "07 ELEMENTS" },
{ id: "patterns", title: "Patterns & Textures", section: "07 ELEMENTS" },
];

const ext40 = [
{ id: "about_us", title: "Our Story", section: "01 BRAND STORY" },
{ id: "target", title: "Target Audience", section: "01 BRAND STORY" },
{ id: "competitive", title: "Competitive Landscape", section: "01 BRAND STORY" },
{ id: "logo_mono", title: "Monochrome Logo", section: "02 LOGO SYSTEM" },
{ id: "logo_reversed", title: "Reversed Logo", section: "02 LOGO SYSTEM" },
{ id: "logo_digital", title: "Logo for Digital", section: "02 LOGO SYSTEM" },
{ id: "logo_print", title: "Logo for Print", section: "02 LOGO SYSTEM" },
{ id: "color_code", title: "Color Codes Reference", section: "03 COLOR" },
{ id: "color_dark", title: "Dark Mode Palette", section: "03 COLOR" },
{ id: "type_hierarchy", title: "Type Hierarchy", section: "04 TYPOGRAPHY" },
{ id: "type_editorial", title: "Editorial Typography", section: "04 TYPOGRAPHY" },
{ id: "motion", title: "Motion Principles", section: "08 MOTION" },
{ id: "animation", title: "Animation Guidelines", section: "08 MOTION" },
{ id: "grid_system", title: "Grid System", section: "09 LAYOUT" },
{ id: "layout_ex", title: "Layout Examples", section: "09 LAYOUT" },
{ id: "social_media", title: "Social Media Guidelines", section: "10 APPLICATIONS" },
{ id: "business_card", title: "Business Card", section: "10 APPLICATIONS" },
{ id: "letterhead", title: "Letterhead", section: "10 APPLICATIONS" },
{ id: "merchandise", title: "Merchandise", section: "10 APPLICATIONS" },
{ id: "signage", title: "Signage & Environmental", section: "10 APPLICATIONS" },
{ id: "digital_ui", title: "Digital UI Examples", section: "10 APPLICATIONS" },
{ id: "packaging", title: "Packaging", section: "10 APPLICATIONS" },
{ id: "credits", title: "Credits", section: "END" },
{ id: "contact", title: "Contact", section: "END" },
{ id: "divider_motion", title: "— Motion", section: "DIVIDER" },
{ id: "divider_layout", title: "— Layout", section: "DIVIDER" },
{ id: "divider_app", title: "— Applications", section: "DIVIDER" },
{ id: "divider_brand", title: "— Brand Story", section: "DIVIDER" },
];

if (count === 12) return core12;
if (count === 24) return [...core12.slice(0, -1), ...ext24, core12[core12.length - 1]];
if (count === 40) return [...core12.slice(0, -1), ...ext24, ...ext40, core12[core12.length - 1]];
return core12;
}

// ── Utility: Hex → RGB ────────────────────────────────────

function hexToRGB(hex) {
const clean = hex.replace("#", "");
return {
r: parseInt(clean.substring(0,2), 16) / 255,
g: parseInt(clean.substring(2,4), 16) / 255,
b: parseInt(clean.substring(4,6), 16) / 255,
};
}

function makeColor(hex) {
const { r, g, b } = hexToRGB(hex);
return { red: r, green: g, blue: b, alpha: 1.0 };
}

function makeColorA(hex, alpha) {
const { r, g, b } = hexToRGB(hex);
return { red: r, green: g, blue: b, alpha: alpha };
}

// ── Utility: Safe text truncate ───────────────────────────

function safe(str, maxLen) {
if (!str) return "";
return str.length > maxLen ? str.substring(0, maxLen) + "…" : str;
}

// ── Affinity Designer API Wrappers ────────────────────────
//
// Affinity Designer 2 exposes a global app object.
// All drawing calls go through the active document & spread.

function getDoc() {
return app.activeDocument;
}

function getPage(index) {
return getDoc().pages[index];
}

function setFill(node, hexColor) {
node.fill = { type: "color", color: makeColor(hexColor) };
}

function setNoFill(node) {
node.fill = { type: "none" };
}

function setStroke(node, hexColor, width) {
node.stroke = { color: makeColor(hexColor), width: width || 1 };
}

function setNoStroke(node) {
node.stroke = null;
}

// Place a solid rectangle on a page layer
function addRect(layer, x, y, w, h, fillHex, strokeHex, strokeW) {
const rect = layer.addRectangle({ x, y, width: w, height: h });
if (fillHex) setFill(rect, fillHex);
else setNoFill(rect);
if (strokeHex) setStroke(rect, strokeHex, strokeW || 1);
else setNoStroke(rect);
return rect;
}

// Place styled text
function addText(layer, x, y, w, h, content, options) {
const opts = options || {};
const txt = layer.addTextFrame({
x, y,
width: w,
height: h,
text: content,
});
txt.fontFamily = opts.font || BRAND.fonts.primary;
txt.fontSize = opts.size || BRAND.fontSizes.body;
txt.color = makeColor(opts.color || BRAND.colors.dark);
txt.bold = opts.bold || false;
txt.italic = opts.italic || false;
txt.alignment = opts.align || "left";
txt.tracking = opts.tracking !== undefined ? opts.tracking : 0;
txt.leading = opts.leading || (opts.size || BRAND.fontSizes.body) * 1.45;
txt.capitalized = opts.caps || false;
return txt;
}

// Horizontal rule
function addRule(layer, x, y, w, hexColor, thickness) {
return addRect(layer, x, y, w, thickness || THEME.dividerH, hexColor);
}

// Place logo image from file path
function addImage(layer, filePath, x, y, w, h) {
if (!filePath || filePath.trim() === "") return null;
try {
const img = layer.addImage({ filePath, x, y, width: w, height: h });
return img;
} catch (e) {
// Fallback: placeholder box
return addPlaceholder(layer, x, y, w, h, "LOGO PLACEHOLDER");
}
}

// Dashed placeholder box
function addPlaceholder(layer, x, y, w, h, label) {
const box = addRect(layer, x, y, w, h, null, BRAND.colors.primary, 1);
box.dashPattern = [6, 4];
const lbl = addText(layer, x, y + h/2 - 8, w, 16, label, {
size: BRAND.fontSizes.caption,
color: BRAND.colors.primary,
align: "center",
caps: true,
tracking: 120,
});
return box;
}

// Color swatch block
function addSwatch(layer, x, y, w, h, hexColor, name, codes) {
addRect(layer, x, y, w, h, hexColor, null);
const isLight = isColorLight(hexColor);
const textCol = isLight ? BRAND.colors.dark : BRAND.colors.white;

addText(layer, x + 10, y + h - 68, w - 20, 20, name.toUpperCase(), {
size: BRAND.fontSizes.label,
color: textCol,
bold: true,
tracking: 120,
});
addText(layer, x + 10, y + h - 46, w - 20, 40, codes, {
size: BRAND.fontSizes.caption,
color: makeColorA(textCol.replace ? textCol : "#FFFFFF", 0.7),
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
}

function isColorLight(hex) {
const { r, g, b } = hexToRGB(hex);
return (r * 299 + g * 587 + b * 114) / 1000 > 0.5;
}

// Section label tag (chapter eyebrow)
function addEyebrow(layer, x, y, section, color) {
addText(layer, x, y, 300, 14, section, {
size: BRAND.fontSizes.label,
color: color || BRAND.colors.secondary,
caps: true,
tracking: 200,
bold: true,
});
}

// Page number
function addPageNum(layer, pageIndex) {
addText(layer, MARGIN, PAGE_H - MARGIN - 10, 50, 12,
String(pageIndex + 1).padStart(2, "0"), {
size: BRAND.fontSizes.caption,
color: BRAND.colors.primary,
tracking: 80,
});
}

// Footer line + studio mark
function addFooter(layer, pageIndex) {
addRule(layer, MARGIN, PAGE_H - MARGIN - 18, PAGE_W - MARGIN * 2,
BRAND.colors.primary, 0.5);
addPageNum(layer, pageIndex);
addText(layer, PAGE_W - MARGIN - 80, PAGE_H - MARGIN - 10, 80, 12,
BRAND.name.toUpperCase(), {
size: BRAND.fontSizes.caption,
color: BRAND.colors.primary,
tracking: 120,
align: "right",
});
}

// ╔══════════════════════════════════════════════════════════╗
// ║ ③ PAGE BUILDERS ║
// ╚══════════════════════════════════════════════════════════╝

function buildPage_cover(page) {
const L = page.addLayer({ name: "Cover" });

// Full-bleed background
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.coverBg);

// Accent strip
if (BRAND.style === "editorial") {
addRect(L, 0, 0, 8, PAGE_H, BRAND.colors.secondary);
}
if (BRAND.style === "bold") {
addRect(L, 0, PAGE_H * 0.6, PAGE_W, PAGE_H * 0.4, BRAND.colors.secondary);
}
if (BRAND.style === "minimal") {
addRect(L, MARGIN, PAGE_H * 0.55, PAGE_W - MARGIN * 2, 1, BRAND.colors.primary);
}

// Logo area
addImage(L, BRAND.logos.primary,
MARGIN, MARGIN + 10,
(PAGE_W - MARGIN * 2) * 0.55, 80
) || addPlaceholder(L, MARGIN, MARGIN + 10,
(PAGE_W - MARGIN * 2) * 0.55, 80, "PRIMARY LOGO");

// Brand name (display)
addText(L, MARGIN, PAGE_H * 0.38, PAGE_W - MARGIN * 2, 90,
BRAND.name.toUpperCase(), {
size: BRAND.fontSizes.display,
color: THEME.coverText,
bold: true,
tracking: BRAND.style === "bold" ? 60 : 0,
});

// Tagline
addText(L, MARGIN, PAGE_H * 0.38 + 100, PAGE_W - MARGIN * 2 - 40, 30,
BRAND.tagline, {
size: BRAND.fontSizes.H5,
color: makeColorA(THEME.coverText, 0.65),
font: BRAND.fonts.secondary || BRAND.fonts.primary,
italic: !!(BRAND.fonts.secondary),
});

// Document label
addText(L, MARGIN, PAGE_H - MARGIN - 32, PAGE_W - MARGIN * 2, 14,
"BRAND IDENTITY GUIDELINES", {
size: BRAND.fontSizes.label,
color: makeColorA(THEME.coverText, 0.5),
tracking: 200,
caps: true,
});
addText(L, MARGIN, PAGE_H - MARGIN - 16, PAGE_W - MARGIN * 2, 14,
Version 1.0 · ${BRAND.year} · Confidential, {
size: BRAND.fontSizes.caption,
color: makeColorA(THEME.coverText, 0.35),
});
}

function buildPage_toc(page, manifest) {
const L = page.addLayer({ name: "Table of Contents" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "INDEX");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 40,
"Table of\nContents", {
size: BRAND.fontSizes.H1,
bold: true,
color: BRAND.colors.dark,
});

addRule(L, MARGIN, MARGIN + 75, PAGE_W - MARGIN * 2, BRAND.colors.primary, 0.5);

let cy = MARGIN + 90;
let lastSection = "";
manifest.forEach((item, i) => {
if (item.id === "cover" || item.id === "toc" || item.id === "back") return;
if (item.section.startsWith("DIVIDER")) return;

if (item.section !== lastSection) {
  cy += 6;
  addText(L, MARGIN, cy, 200, 12, item.section, {
    size: BRAND.fontSizes.caption,
    color: BRAND.colors.secondary,
    caps: true,
    tracking: 150,
    bold: true,
  });
  cy += 14;
  lastSection = item.section;
}

addText(L, MARGIN + 10, cy, PAGE_W - MARGIN * 2 - 60, 14, item.title, {
  size: BRAND.fontSizes.body,
  color: BRAND.colors.dark,
});
addText(L, PAGE_W - MARGIN - 24, cy, 24, 14, String(i + 1).padStart(2, "0"), {
  size: BRAND.fontSizes.body,
  color: BRAND.colors.primary,
  align: "right",
});
// Dot leader
addRect(L, MARGIN + 10, cy + 10, PAGE_W - MARGIN * 2 - 60, 0.5,
  makeColorA(BRAND.colors.dark, 0.12));
cy += 18;
if (cy > PAGE_H - MARGIN * 2) return;

});

addFooter(L, 1);
}

function buildPage_about(page, pageIndex) {
const L = page.addLayer({ name: "About the Brand" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "01 — Brand Story");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 50,
"About the\nBrand", {
size: BRAND.fontSizes.H2,
bold: true,
color: BRAND.colors.dark,
});

addRule(L, MARGIN, MARGIN + 84, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH + 1);

// Two column layout
const bodyFont = BRAND.fonts.secondary || BRAND.fonts.primary;

addText(L, MARGIN, MARGIN + 96, COL2, 200,
safe(BRAND.about.description, 600), {
size: BRAND.fontSizes.body,
font: bodyFont,
color: BRAND.colors.dark,
leading: BRAND.fontSizes.body * 1.65,
});

addText(L, MARGIN + COL2 + MARGIN, MARGIN + 96, COL2, 200,
safe(BRAND.about.aboutUs, 600), {
size: BRAND.fontSizes.body,
font: bodyFont,
color: BRAND.colors.dark,
leading: BRAND.fontSizes.body * 1.65,
});

// Pull quote strip
const quoteY = MARGIN + 96 + 210;
addRect(L, 0, quoteY, PAGE_W, 80, BRAND.colors.primary);
addText(L, MARGIN, quoteY + 22, PAGE_W - MARGIN * 2, 40,
"${BRAND.tagline}", {
size: BRAND.fontSizes.H4,
color: BRAND.colors.white,
italic: true,
font: bodyFont,
});

addFooter(L, pageIndex);
}

function buildPage_research(page, pageIndex) {
const L = page.addLayer({ name: "Research" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "01 — Brand Story");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Research &\nPositioning", {
size: BRAND.fontSizes.H2,
bold: true,
color: BRAND.colors.dark,
});

addRule(L, MARGIN, MARGIN + 80, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

addText(L, MARGIN, MARGIN + 92, PAGE_W - MARGIN * 2, 400,
safe(BRAND.about.research, 1200), {
size: BRAND.fontSizes.body,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
color: BRAND.colors.dark,
leading: BRAND.fontSizes.body * 1.7,
});

// Audience tags
const tags = BRAND.voice.personality;
let tx = MARGIN;
const tagY = PAGE_H - MARGIN - 80;
addText(L, MARGIN, tagY - 16, 200, 14, "BRAND ATTRIBUTES", {
size: BRAND.fontSizes.caption,
color: BRAND.colors.secondary,
caps: true,
tracking: 150,
bold: true,
});
tags.forEach(tag => {
const tw = tag.length * 7 + 20;
addRect(L, tx, tagY, tw, 24, null, BRAND.colors.primary, 1);
addText(L, tx + 6, tagY + 5, tw, 14, tag.toUpperCase(), {
size: BRAND.fontSizes.caption,
color: BRAND.colors.primary,
tracking: 80,
});
tx += tw + 8;
});

addFooter(L, pageIndex);
}

function buildPage_logo_primary(page, pageIndex) {
const L = page.addLayer({ name: "Primary Logo" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "02 — Logo System");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Primary Logo", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 62, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

// Large centered logo display
const logoY = MARGIN + 80;
const logoH = PAGE_H * 0.35;
const logoW = PAGE_W - MARGIN * 4;
addRect(L, MARGIN * 2, logoY, logoW, logoH, BRAND.colors.neutral);
addImage(L, BRAND.logos.primary,
MARGIN * 2 + logoW * 0.15, logoY + logoH * 0.25,
logoW * 0.7, logoH * 0.5
) || addPlaceholder(L, MARGIN * 2, logoY, logoW, logoH, "PRIMARY LOGO");

// Color variations row
const varY = logoY + logoH + 28;
addText(L, MARGIN, varY, 200, 14, "LOGO VARIATIONS", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});

const varW = (PAGE_W - MARGIN * 2 - 16) / 3;
const varH = 70;
const varBoxY = varY + 18;

// Full color
addRect(L, MARGIN, varBoxY, varW, varH, BRAND.colors.neutral);
addText(L, MARGIN, varBoxY + varH + 4, varW, 12, "Full Color", {
size: BRAND.fontSizes.caption, color: BRAND.colors.dark,
});

// Reversed
addRect(L, MARGIN + varW + 8, varBoxY, varW, varH, BRAND.colors.dark);
addText(L, MARGIN + varW + 8, varBoxY + varH + 4, varW, 12, "Reversed", {
size: BRAND.fontSizes.caption, color: BRAND.colors.dark,
});

// One-color
addRect(L, MARGIN + (varW + 8) * 2, varBoxY, varW, varH, BRAND.colors.primary);
addText(L, MARGIN + (varW + 8) * 2, varBoxY + varH + 4, varW, 12, "One Color", {
size: BRAND.fontSizes.caption, color: BRAND.colors.dark,
});

addFooter(L, pageIndex);
}

function buildPage_logo_variants(page, pageIndex) {
const L = page.addLayer({ name: "Logo Variants" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "02 — Logo System");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Logo Variants", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 62, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

const logos = [
{ path: BRAND.logos.primary, label: "Primary Lockup", sub: "Full horizontal with tagline" },
{ path: BRAND.logos.secondary, label: "Secondary Lockup", sub: "Compact horizontal version" },
{ path: BRAND.logos.tertiary, label: "Icon Mark", sub: "Symbol / favicon usage" },
];

logos.forEach((logo, i) => {
const bx = MARGIN;
const by = MARGIN + 80 + i * 170;
const bw = PAGE_W - MARGIN * 2;
const bh = 130;
addRect(L, bx, by, bw, bh, BRAND.colors.neutral);
addImage(L, logo.path, bx + 20, by + 15, bw * 0.5, bh - 30)
|| addPlaceholder(L, bx, by, bw, bh, logo.label);
addText(L, bx + bw * 0.58, by + 16, bw * 0.38, 18, logo.label, {
size: BRAND.fontSizes.H6, bold: true, color: BRAND.colors.dark,
});
addText(L, bx + bw * 0.58, by + 36, bw * 0.38, 40, logo.sub, {
size: BRAND.fontSizes.caption, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
});

addFooter(L, pageIndex);
}

function buildPage_logo_usage(page, pageIndex) {
const L = page.addLayer({ name: "Logo Usage" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "02 — Logo System");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Usage Rules", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 62, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

// Clear space diagram
const csY = MARGIN + 78;
addText(L, MARGIN, csY, 200, 14, "CLEAR SPACE", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});
addText(L, MARGIN, csY + 16, PAGE_W - MARGIN * 2, 28,
"The logo must always have clear space equal to the height of\nthe 'x' character in the logotype on all sides.", {
size: BRAND.fontSizes.body, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});

// Clear space diagram box
const diagramY = csY + 52;
addRect(L, MARGIN, diagramY, PAGE_W - MARGIN * 2, 90, BRAND.colors.neutral);
addRect(L, MARGIN + 28, diagramY + 18, PAGE_W - MARGIN * 2 - 56, 54, null, BRAND.colors.primary, 0.5);
addPlaceholder(L, MARGIN + 56, diagramY + 28, PAGE_W - MARGIN * 2 - 112, 34, "LOGO");

// Minimum sizes
const msY = diagramY + 108;
addText(L, MARGIN, msY, 200, 14, "MINIMUM SIZES", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});

const sizes = [
{ label: "Print", value: "25mm wide" },
{ label: "Digital", value: "64px wide" },
{ label: "Favicon", value: "16px" },
];
sizes.forEach((s, i) => {
const sx = MARGIN + i * (PAGE_W - MARGIN * 2) / 3;
addRect(L, sx, msY + 18, (PAGE_W - MARGIN * 2) / 3 - 8, 44, BRAND.colors.neutral);
addText(L, sx + 8, msY + 24, 120, 14, s.label, {
size: BRAND.fontSizes.caption, bold: true, color: BRAND.colors.dark,
});
addText(L, sx + 8, msY + 38, 120, 14, s.value, {
size: BRAND.fontSizes.H5, bold: true, color: BRAND.colors.primary,
});
});

// File formats
const ffY = msY + 80;
addText(L, MARGIN, ffY, 200, 14, "FILE FORMAT GUIDE", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});
const formats = [
"SVG — Digital, scalable, preferred for web",
"PDF — Print, presentations, master file",
"PNG — Digital with transparency, social media",
"EPS — Large format printing",
];
formats.forEach((f, i) => {
addText(L, MARGIN, ffY + 18 + i * 16, PAGE_W - MARGIN * 2, 14, f, {
size: BRAND.fontSizes.body, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
});

addFooter(L, pageIndex);
}

function buildPage_color_system(page, pageIndex) {
const L = page.addLayer({ name: "Color System" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "03 — Color");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Color System", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 62, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

const palette = [
{ hex: BRAND.colors.primary, name: "Primary", role: "Brand core" },
{ hex: BRAND.colors.secondary, name: "Secondary", role: "Accent & highlight" },
{ hex: BRAND.colors.accent, name: "Accent", role: "CTA & energy" },
{ hex: BRAND.colors.neutral, name: "Neutral", role: "Backgrounds, space" },
{ hex: BRAND.colors.dark, name: "Dark", role: "Typography & depth" },
{ hex: BRAND.colors.white, name: "White", role: "Canvas & space" },
];

// Two rows × three columns
const sw = (PAGE_W - MARGIN * 2 - 16) / 3;
const sh = 130;
palette.forEach((s, i) => {
const col = i % 3;
const row = Math.floor(i / 3);
const sx = MARGIN + col * (sw + 8);
const sy = MARGIN + 80 + row * (sh + 8);
addSwatch(L, sx, sy, sw, sh, s.hex, s.name,
${s.hex.toUpperCase()}\nRGB ${hexToRGB(s.hex).r.toFixed(2).replace("0.","")} ${hexToRGB(s.hex).g.toFixed(2).replace("0.","")} ${hexToRGB(s.hex).b.toFixed(2).replace("0.","")}\n${s.role}
);
});

// Usage note
const noteY = MARGIN + 80 + 2 * (sh + 8) + 12;
addText(L, MARGIN, noteY, PAGE_W - MARGIN * 2, 28,
"Primary is used for 60% of all brand applications. Secondary for 30%. Accent for 10% maximum.", {
size: BRAND.fontSizes.body,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
color: BRAND.colors.dark,
italic: true,
});

addFooter(L, pageIndex);
}

function buildPage_typography(page, pageIndex) {
const L = page.addLayer({ name: "Typography" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "04 — Typography");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Type System", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 62, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

// Primary font specimen
addText(L, MARGIN, MARGIN + 80, PAGE_W - MARGIN * 2, 14, "PRIMARY TYPEFACE", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary, caps: true, tracking: 150, bold: true,
});
addText(L, MARGIN, MARGIN + 96, PAGE_W - MARGIN * 2, 60,
BRAND.fonts.primary, {
size: BRAND.fontSizes.H1, bold: true, color: BRAND.colors.dark,
font: BRAND.fonts.primary,
});
addText(L, MARGIN, MARGIN + 160, PAGE_W - MARGIN * 2, 24,
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz 0123456789", {
size: BRAND.fontSizes.caption, color: BRAND.colors.dark, font: BRAND.fonts.primary,
});

addRule(L, MARGIN, MARGIN + 192, PAGE_W - MARGIN * 2, BRAND.colors.primary, 0.5);

// Type scale table
const scales = [
{ label: "H1", size: BRAND.fontSizes.H1, usage: "Page titles, hero copy" },
{ label: "H2", size: BRAND.fontSizes.H2, usage: "Section headings" },
{ label: "H3", size: BRAND.fontSizes.H3, usage: "Sub-headings" },
{ label: "H4", size: BRAND.fontSizes.H4, usage: "Card titles, callouts" },
{ label: "H5", size: BRAND.fontSizes.H5, usage: "Labels, emphasis" },
{ label: "H6", size: BRAND.fontSizes.H6, usage: "Captions, metadata" },
{ label: "Body", size: BRAND.fontSizes.body, usage: "Running text, descriptions" },
];

let ty = MARGIN + 206;
scales.forEach((scale) => {
addText(L, MARGIN, ty, 36, 16, scale.label, {
size: BRAND.fontSizes.caption, bold: true, color: BRAND.colors.secondary,
});
addText(L, MARGIN + 44, ty, 80, 16, ${scale.size}pt, {
size: BRAND.fontSizes.caption, color: BRAND.colors.primary,
});
addText(L, MARGIN + 120, ty, PAGE_W - MARGIN * 2 - 120, 16, scale.usage, {
size: BRAND.fontSizes.caption, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
addRule(L, MARGIN, ty + 14, PAGE_W - MARGIN * 2, BRAND.colors.primary, 0.25);
ty += 22;
});

// Secondary font (if set)
if (BRAND.fonts.secondary) {
addText(L, MARGIN, ty + 8, PAGE_W - MARGIN * 2, 14, "SECONDARY TYPEFACE", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary, caps: true, tracking: 150, bold: true,
});
addText(L, MARGIN, ty + 24, PAGE_W - MARGIN * 2, 30,
BRAND.fonts.secondary, {
size: BRAND.fontSizes.H3, color: BRAND.colors.dark,
font: BRAND.fonts.secondary, italic: true,
});
}

addFooter(L, pageIndex);
}

function buildPage_dos_donts(page, pageIndex) {
const L = page.addLayer({ name: "Do and Donts" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "05 — Brand Rules");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Do's & Don'ts", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 62, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

const DOS = [
"Always use the approved logo files",
"Maintain clear space around the logo at all times",
"Use brand colors as specified in this guide",
"Use the primary typeface for all headings",
"Ensure sufficient contrast on all backgrounds",
"Download assets from the official brand portal",
];

const DONTS = [
"Do not stretch or distort the logo",
"Do not apply drop shadows or effects to the logo",
"Do not use unapproved color combinations",
"Do not place the logo on busy photography",
"Do not retype or recreate the logo in any font",
"Do not use outdated versions of the logo",
];

const colW = COL2;

// Do column
addRect(L, MARGIN, MARGIN + 78, 4, 200, "#22C55E");
addText(L, MARGIN + 12, MARGIN + 78, 40, 18, "DO", {
size: BRAND.fontSizes.H5, bold: true, color: "#22C55E",
});
DOS.forEach((item, i) => {
addText(L, MARGIN + 12, MARGIN + 100 + i * 28, colW - 12, 24, ✓ ${item}, {
size: BRAND.fontSizes.body, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
});

// Dont column
addRect(L, MARGIN + colW + MARGIN, MARGIN + 78, 4, 200, BRAND.colors.secondary);
addText(L, MARGIN + colW + MARGIN + 12, MARGIN + 78, 60, 18, "DON'T", {
size: BRAND.fontSizes.H5, bold: true, color: BRAND.colors.secondary,
});
DONTS.forEach((item, i) => {
addText(L, MARGIN + colW + MARGIN + 12, MARGIN + 100 + i * 28, colW - 12, 24, ✗ ${item}, {
size: BRAND.fontSizes.body, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
});

// Misuse examples label
const muY = MARGIN + 78 + 200;
addText(L, MARGIN, muY, 200, 14, "COMMON MISUSES", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});

const muW = (PAGE_W - MARGIN * 2 - 32) / 4;
const muH = 55;
const muLabels = ["Don't stretch", "Don't outline", "Don't rotate", "Don't recolor"];
muLabels.forEach((lbl, i) => {
const mx = MARGIN + i * (muW + 8);
addRect(L, mx, muY + 18, muW, muH, BRAND.colors.neutral);
addText(L, mx, muY + muH + 22, muW, 14, lbl, {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary, align: "center",
});
});

addFooter(L, pageIndex);
}

function buildPage_voice(page, pageIndex) {
const L = page.addLayer({ name: "Brand Voice" });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, "06 — Voice & Tone");
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 30,
"Brand Voice\n& Tone", { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 80, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

addText(L, MARGIN, MARGIN + 94, PAGE_W - MARGIN * 2, 40,
safe(BRAND.voice.tone, 300), {
size: BRAND.fontSizes.H5, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
italic: true,
});

// Personality traits
const traitY = MARGIN + 150;
addText(L, MARGIN, traitY, 200, 14, "PERSONALITY TRAITS", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});

BRAND.voice.personality.forEach((trait, i) => {
const ty2 = traitY + 20 + i * 50;
addRect(L, MARGIN, ty2, PAGE_W - MARGIN * 2, 40, BRAND.colors.neutral);
addText(L, MARGIN + 12, ty2 + 12, 200, 18, trait.toUpperCase(), {
size: BRAND.fontSizes.H5, bold: true, color: BRAND.colors.primary,
tracking: 60,
});
});

// Avoid list
const avoidY = traitY + 20 + BRAND.voice.personality.length * 50 + 16;
addText(L, MARGIN, avoidY, 200, 14, "WORDS & PHRASES TO AVOID", {
size: BRAND.fontSizes.caption, color: BRAND.colors.secondary,
caps: true, tracking: 150, bold: true,
});
BRAND.voice.avoid.forEach((a, i) => {
addText(L, MARGIN, avoidY + 18 + i * 16, PAGE_W - MARGIN * 2, 14, — ${a}, {
size: BRAND.fontSizes.body, color: BRAND.colors.dark,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});
});

addFooter(L, pageIndex);
}

function buildPage_sectionDivider(page, sectionTitle, sectionNum) {
const L = page.addLayer({ name: Section: ${sectionTitle} });
addRect(L, 0, 0, PAGE_W, PAGE_H, BRAND.colors.primary);

// Accent rectangle
addRect(L, 0, 0, PAGE_W * 0.5, PAGE_H, BRAND.colors.dark);

addText(L, MARGIN, PAGE_H * 0.4, PAGE_W * 0.45, 30, sectionNum, {
size: BRAND.fontSizes.H3, color: BRAND.colors.secondary, bold: true,
});
addText(L, MARGIN, PAGE_H * 0.4 + 36, PAGE_W * 0.45, 80, sectionTitle, {
size: BRAND.fontSizes.H1, color: BRAND.colors.white, bold: true,
});
}

function buildPage_generic(page, pageIndex, item) {
const L = page.addLayer({ name: item.title });
addRect(L, 0, 0, PAGE_W, PAGE_H, THEME.bodyBg);

addEyebrow(L, MARGIN, MARGIN, item.section);
addText(L, MARGIN, MARGIN + 22, PAGE_W - MARGIN * 2, 50,
item.title, { size: BRAND.fontSizes.H2, bold: true, color: BRAND.colors.dark });

addRule(L, MARGIN, MARGIN + 74, PAGE_W - MARGIN * 2, THEME.accentLine, THEME.dividerH);

// Generic content grid
const gw = (PAGE_W - MARGIN * 2 - 16) / 2;
const gh = (PAGE_H - MARGIN * 2 - 90 - 16) / 2;
for (let row = 0; row < 2; row++) {
for (let col = 0; col < 2; col++) {
addRect(L,
MARGIN + col * (gw + 8),
MARGIN + 90 + row * (gh + 8),
gw, gh, BRAND.colors.neutral
);
}
}

addFooter(L, pageIndex);
}

function buildPage_back(page) {
const L = page.addLayer({ name: "Back Cover" });
addRect(L, 0, 0, PAGE_W, PAGE_H, BRAND.colors.dark);

if (BRAND.style === "editorial") {
addRect(L, PAGE_W - 8, 0, 8, PAGE_H, BRAND.colors.secondary);
}

addImage(L, BRAND.logos.primary,
MARGIN, MARGIN,
(PAGE_W - MARGIN * 2) * 0.45, 60
) || addText(L, MARGIN, MARGIN, 200, 30, BRAND.name.toUpperCase(), {
size: BRAND.fontSizes.H3, bold: true, color: BRAND.colors.white,
});

addText(L, MARGIN, PAGE_H * 0.5, PAGE_W - MARGIN * 2, 40,
"${BRAND.tagline}", {
size: BRAND.fontSizes.H3, italic: true, color: BRAND.colors.white,
font: BRAND.fonts.secondary || BRAND.fonts.primary,
});

addText(L, MARGIN, PAGE_H - MARGIN - 50, PAGE_W - MARGIN * 2, 14,
"BRAND IDENTITY GUIDELINES", {
size: BRAND.fontSizes.label, color: makeColorA(BRAND.colors.white, 0.4),
caps: true, tracking: 200,
});
addText(L, MARGIN, PAGE_H - MARGIN - 34, PAGE_W - MARGIN * 2, 14,
© ${BRAND.year} ${BRAND.name}. All rights reserved. Confidential & Proprietary., {
size: BRAND.fontSizes.caption, color: makeColorA(BRAND.colors.white, 0.3),
});
addText(L, MARGIN, PAGE_H - MARGIN - 16, PAGE_W - MARGIN * 2, 14,
"Designed by BombayGrid · bombaygrid.com", {
size: BRAND.fontSizes.caption, color: makeColorA(BRAND.colors.white, 0.3),
italic: true,
});
}

// ╔══════════════════════════════════════════════════════════╗
// ║ ④ MAIN GENERATOR ║
// ╚══════════════════════════════════════════════════════════╝

function generateBrandbook() {
// Validate config
if (!BRAND.name || BRAND.name === "Brand Name") {
app.showMessage("⚠️ Please fill in BRAND.name before running.");
return;
}
if (![12, 24, 40].includes(BRAND.pageCount)) {
app.showMessage("⚠️ BRAND.pageCount must be 12, 24, or 40.");
return;
}

// Build page manifest
const manifest = buildManifest(BRAND.pageCount);

// Create new A4 document
const doc = app.addDocument({
width: PAGE_W,
height: PAGE_H,
pageCount: manifest.length,
dpi: 300,
colorFormat: "CMYK",
name: ${BRAND.name} — Brand Identity Guidelines,
});

// Build each page
manifest.forEach((item, i) => {
const page = getPage(i);

switch (item.id) {
  case "cover":          buildPage_cover(page);                       break;
  case "toc":            buildPage_toc(page, manifest);              break;
  case "about":          buildPage_about(page, i);                   break;
  case "research":       buildPage_research(page, i);                break;
  case "logo_primary":   buildPage_logo_primary(page, i);            break;
  case "logo_variants":  buildPage_logo_variants(page, i);           break;
  case "logo_usage":     buildPage_logo_usage(page, i);              break;
  case "color_system":   buildPage_color_system(page, i);            break;
  case "typography":     buildPage_typography(page, i);              break;
  case "dos_donts":      buildPage_dos_donts(page, i);               break;
  case "voice":          buildPage_voice(page, i);                   break;
  case "back":           buildPage_back(page);                       break;

  // Section dividers
  case "divider_brand":  buildPage_sectionDivider(page, "Brand Story",    "01"); break;
  case "divider_logo":   buildPage_sectionDivider(page, "Logo System",    "02"); break;
  case "divider_color":  buildPage_sectionDivider(page, "Color",          "03"); break;
  case "divider_type":   buildPage_sectionDivider(page, "Typography",     "04"); break;
  case "divider_motion": buildPage_sectionDivider(page, "Motion",         "08"); break;
  case "divider_layout": buildPage_sectionDivider(page, "Layout",         "09"); break;
  case "divider_app":    buildPage_sectionDivider(page, "Applications",   "10"); break;

  // All extended pages (24/40) get the generic builder
  default:               buildPage_generic(page, i, item);           break;
}

});

app.showMessage(
✅ Brandbook generated!\n +
${manifest.length} pages · Style: ${BRAND.style}\n +
Brand: ${BRAND.name}\n\n +
Tip: Review placeholder boxes and replace with\n +
your real logo and photography assets.
);
}

// ── Run ───────────────────────────────────────────────────
generateBrandbook();

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions