Skip to main content
Build a brand style guide generator that automatically extracts colors, typography, spacing, and visual identity from any website and compiles it into a professional PDF document. Brand style guide PDF generator using Firecrawl branding format to extract design system

What You’ll Build

A Node.js application that takes any website URL, extracts its complete brand identity using Firecrawl’s branding format, and generates a polished PDF style guide with:
  • Color palette with hex values
  • Typography system (fonts, sizes, weights)
  • Spacing and layout specifications
  • Logo and brand imagery
  • Theme information (light/dark mode)
Example of generated brand style guide PDF with colors typography and spacing

Prerequisites

  • Node.js 18 or later installed
  • A Firecrawl API key from firecrawl.dev
  • Basic knowledge of TypeScript and Node.js
1

Create a New Node.js Project

Start by creating a new directory for your project and initializing it:
mkdir brand-style-guide-generator && cd brand-style-guide-generator
npm init -y
Update your package.json to use ES modules:
package.json
{
  "name": "brand-style-guide-generator",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "npx tsx index.ts"
  }
}
2

Install Dependencies

Install the required packages for web scraping and PDF generation:
npm i @mendable/firecrawl-js pdfkit
npm i -D typescript tsx @types/node @types/pdfkit
These packages provide:
  • @mendable/firecrawl-js: Firecrawl SDK for extracting brand identity from websites
  • pdfkit: PDF document generation library
  • tsx: TypeScript execution for Node.js
3

Build the Brand Style Guide Generator

Create the main application file at index.ts. This script extracts brand identity from a URL and generates a professional PDF style guide.
index.ts
import Firecrawl from "@mendable/firecrawl-js";
import PDFDocument from "pdfkit";
import fs from "fs";

const API_KEY = "fc-YOUR-API-KEY";
const URL = "https://firecrawl.dev";

async function main() {
  const fc = new Firecrawl({ apiKey: API_KEY });
  const { branding: b } = (await fc.scrape(URL, { formats: ["branding"] })) as any;

  const doc = new PDFDocument({ size: "A4", margin: 50 });
  doc.pipe(fs.createWriteStream("brand-style-guide.pdf"));

  // Fetch logo (PNG/JPG only)
  let logoImg: Buffer | null = null;
  try {
    const logoUrl = b.images?.favicon || b.images?.ogImage;
    if (logoUrl?.match(/\.(png|jpg|jpeg)$/i)) {
      const res = await fetch(logoUrl);
      logoImg = Buffer.from(await res.arrayBuffer());
    }
  } catch {}

  // Header with logo
  doc.rect(0, 0, 595, 120).fill(b.colors?.primary || "#333");
  const titleX = logoImg ? 130 : 50;
  if (logoImg) doc.image(logoImg, 50, 30, { height: 60 });
  doc.fontSize(36).fillColor("#fff").text("Brand Style Guide", titleX, 38);
  doc.fontSize(14).text(URL, titleX, 80);

  // Colors
  doc.fontSize(18).fillColor("#333").text("Colors", 50, 160);
  const colors = Object.entries(b.colors || {}).filter(([, v]) => typeof v === "string" && (v as string).startsWith("#"));
  colors.forEach(([k, v], i) => {
    const x = 50 + i * 100;
    doc.rect(x, 195, 80, 80).fill(v as string);
    doc.fontSize(10).fillColor("#333").text(k, x, 282, { width: 80, align: "center" });
    doc.fontSize(9).fillColor("#888").text(v as string, x, 296, { width: 80, align: "center" });
  });

  // Typography
  doc.fontSize(18).fillColor("#333").text("Typography", 50, 340);
  doc.fontSize(13).fillColor("#444");
  doc.text(`Primary Font: ${b.typography?.fontFamilies?.primary || "—"}`, 50, 370);
  doc.text(`Heading Font: ${b.typography?.fontFamilies?.heading || "—"}`, 50, 392);
  doc.fontSize(12).fillColor("#666").text("Font Sizes:", 50, 422);
  Object.entries(b.typography?.fontSizes || {}).forEach(([k, v], i) => {
    doc.text(`${k.toUpperCase()}: ${v}`, 70, 445 + i * 22);
  });

  // Spacing & Theme
  doc.fontSize(18).fillColor("#333").text("Spacing & Theme", 320, 340);
  doc.fontSize(13).fillColor("#444");
  doc.text(`Base Unit: ${b.spacing?.baseUnit}px`, 320, 370);
  doc.text(`Border Radius: ${b.spacing?.borderRadius}`, 320, 392);
  doc.text(`Color Scheme: ${b.colorScheme}`, 320, 414);

  doc.end();
  console.log("Generated: brand-style-guide.pdf");
}

main();
For this simple project, the API key is placed directly in the code. If you plan to push this to GitHub or share it with others, move the key to a .env file and use process.env.FIRECRAWL_API_KEY instead.
Replace fc-YOUR-API-KEY with your Firecrawl API key from firecrawl.dev.

Understanding the Code

Key Components:
  • Firecrawl Branding Format: The branding format extracts comprehensive brand identity including colors, typography, spacing, and images
  • PDFKit Document: Creates a professional A4 PDF with proper margins and sections
  • Color Swatches: Renders visual color blocks with hex values and semantic names
  • Typography Display: Shows font families and sizes in an organized layout
  • Spacing & Theme: Documents the design system’s spacing units and color scheme
4

Run the Generator

Run the script to generate a brand style guide:
npm start
The script will:
  1. Extract the brand identity from the target URL using Firecrawl’s branding format
  2. Generate a PDF named brand-style-guide.pdf
  3. Save it in your project directory
To generate a style guide for a different website, simply change the URL constant in index.ts.

How It Works

Extraction Process

  1. URL Input: The generator receives a target website URL
  2. Firecrawl Scrape: Calls the /scrape endpoint with the branding format
  3. Brand Analysis: Firecrawl analyzes the page’s CSS, fonts, and visual elements
  4. Data Return: Returns a structured BrandingProfile object with all design tokens

PDF Generation Process

  1. Header Creation: Generates a colored header using the primary brand color
  2. Logo Fetch: Downloads and embeds the logo or favicon if available
  3. Color Palette: Renders each color as a visual swatch with metadata
  4. Typography Section: Documents font families, sizes, and weights
  5. Spacing Info: Includes base units, border radius, and theme mode

Branding Profile Structure

The branding format returns detailed brand information:
{
  colorScheme: "dark" | "light",
  logo: "https://example.com/logo.svg",
  colors: {
    primary: "#FF6B35",
    secondary: "#004E89",
    accent: "#F77F00",
    background: "#1A1A1A",
    textPrimary: "#FFFFFF",
    textSecondary: "#B0B0B0"
  },
  typography: {
    fontFamilies: { primary: "Inter", heading: "Inter", code: "Roboto Mono" },
    fontSizes: { h1: "48px", h2: "36px", body: "16px" },
    fontWeights: { regular: 400, medium: 500, bold: 700 }
  },
  spacing: {
    baseUnit: 8,
    borderRadius: "8px"
  },
  images: {
    logo: "https://example.com/logo.svg",
    favicon: "https://example.com/favicon.ico"
  }
}

Key Features

Automatic Color Extraction

The generator identifies and categorizes all brand colors:
  • Primary & Secondary: Main brand colors
  • Accent: Highlight and CTA colors
  • Background & Text: UI foundation colors
  • Semantic Colors: Success, warning, error states

Typography Documentation

Captures the complete type system:
  • Font Families: Primary, heading, and monospace fonts
  • Size Scale: All heading and body text sizes
  • Font Weights: Available weight variations

Visual Output

The PDF includes:
  • Color-coded header matching the brand
  • Embedded logo when available
  • Professional layout with clear hierarchy
  • Metadata footer with generation date
Side-by-side comparison of original website and generated brand style guide PDF

Customization Ideas

Add Component Documentation

Extend the generator to include UI component styles:
// Add after the Spacing & Theme section
if (b.components) {
  doc.addPage();
  doc.fontSize(20).fillColor("#333").text("UI Components", 50, 50);

  // Document button styles
  if (b.components.buttonPrimary) {
    const btn = b.components.buttonPrimary;
    doc.fontSize(14).text("Primary Button", 50, 90);
    doc.rect(50, 110, 120, 40).fill(btn.background);
    doc.fontSize(12).fillColor(btn.textColor).text("Button", 50, 120, { width: 120, align: "center" });
  }
}

Export Multiple Formats

Add JSON export alongside the PDF:
// Add before doc.end()
fs.writeFileSync("brand-data.json", JSON.stringify(b, null, 2));

Batch Processing

Generate guides for multiple websites:
const websites = [
  "https://stripe.com",
  "https://linear.app",
  "https://vercel.com"
];

for (const site of websites) {
  const { branding } = await fc.scrape(site, { formats: ["branding"] }) as any;
  // Generate PDF for each site...
}

Custom PDF Themes

Create different PDF styles based on the extracted theme:
const isDarkMode = b.colorScheme === "dark";
const headerBg = isDarkMode ? b.colors?.background : b.colors?.primary;
const textColor = isDarkMode ? "#fff" : "#333";

Best Practices

  1. Handle Missing Data: Not all websites expose complete branding information. Always provide fallback values for missing properties.
  2. Respect Rate Limits: When batch processing multiple sites, add delays between requests to respect Firecrawl’s rate limits.
  3. Cache Results: Store extracted branding data to avoid repeated API calls for the same site.
  4. Image Format Handling: Some logos may be in formats PDFKit doesn’t support (like SVG). Consider adding format conversion or graceful fallbacks.
  5. Error Handling: Wrap the generation process in try-catch blocks and provide meaningful error messages.