Instrument x Seth Akkerman
Projects Figma Ad Resizer Plugin

Figma Ad Resizer Plugin

A custom Figma plugin that intelligently copies layout from a guide frame to multiple ad sizes — using a SafeZone system to anchor elements correctly across wildly different aspect ratios.

Ad production at scale has a dirty secret: resizing the same creative across 15+ formats is almost entirely manual. You move the logo, nudge the headline, pray the product shot still looks right. This plugin — built for a Crocs campaign — automates that entire pipeline inside Figma using a SafeZone-based layout system that understands intent, not just coordinates.

How the SafeZone system works

The core idea is a named rectangle layer called SafeZone that lives inside each frame. The guide frame’s SafeZone defines which elements should be constrained to the safe content area. Each target frame’s SafeZone defines where that content should land in its specific format — with appropriate margins for a 9:16 Story, a 1.91:1 banner, or a 300×600 display unit. Elements inside the guide’s SafeZone get anchor-aware repositioning: a bottom-right logo stays bottom-right, a centered product shot stays centered, a top-left badge stays top-left. Elements outside (backgrounds, bleed elements) scale to fill the full frame. When a constrained element is too large for the target’s SafeZone, it scales down uniformly rather than clipping or overflowing.

Wide format adaptation

The trickiest case is applying a square or portrait guide to a wide landscape target — a 1:1 social post becoming a 1.91:1 display banner, for example. V1.3 introduced a split-SafeZone layout that kicks in automatically when the guide aspect ratio is ≤ 1.1 and the target exceeds 1.5. Text elements move to the left half of the SafeZone, left-aligned; image elements anchor to the right half, centered. Text boxes switch to HEIGHT auto-resize so wrapped copy grows the container instead of clipping. It’s the closest thing to automated art direction I’ve built.

What this unlocks

The immediate win is time: what used to be an hour of manual resizing per creative becomes a single plugin operation. But the deeper implication is design system thinking applied to paid media. By encoding layout intent — not just pixel positions — into the Figma file structure, you can add a new ad size to a campaign without touching the guide. The plugin also ships with an AI tips feature via OpenRouter that can surface layout suggestions. The same SafeZone approach could extend to: email template systems where header modules adapt across mobile and desktop, localization workflows where RTL languages need mirrored layouts, or any design system where components need to live in containers of unpredictable proportions.

Applying this to your project

  1. Copy the plugin folder, discard the scratch files. Take code.ts, ui.html, manifest.json, package.json, and tsconfig.json. Skip PROJECT_CONTEXT.md (internal dev notes for this specific build) and don’t copy code.js or node_modules — regenerate those yourself with npm install then npm run build.

  2. Set up your own API key, and don’t hardcode it like this version does. The only external service is OpenRouter, used for the “AI Tips” feature. Get a key at openrouter.ai. Because a Figma plugin’s JS bundle ships to anyone who inspects it, a key hardcoded in code.ts (as OPENROUTER_API_KEY) is effectively public — route the request through your own small server/proxy instead, and only put your key there.

  3. Rebrand the obvious surface area. Update the name and id fields in manifest.json, replace any Crocs-specific copy in ui.html, and if your team uses different layer-naming conventions, update the SAFE_ZONE_LAYER_NAMES list and SAFE_AREA_BOUND_ELEMENTS list in code.ts to match.

  4. Know where your content actually plugs in. There’s no config file for content — the plugin works by scanning Figma layers for a rectangle named SafeZone inside a “guide” frame and inside each “target” frame (findSafeZoneLayer()). Everything else in the guide frame gets copied and repositioned relative to those two rectangles. In practice, this means your designers just need to name one rectangle SafeZone per frame — no code changes needed to adapt it to new content.

  5. Watch for these non-obvious gotchas:

    • manifest.json sets networkAccess.allowedDomains to ["none"], but analyzeWithAI() in code.ts fetches https://openrouter.ai anyway — in a real published plugin this call would be blocked. You need to add the correct domain to allowedDomains for AI Tips to actually work.
    • The manifest uses documentAccess: "dynamic-page", which requires figma.getNodeByIdAsync() instead of the older synchronous getNodeById() — code copied from older plugin tutorials will silently break here.
    • Component instances are intentionally not supported as guide or target containers (isContainerNode() only allows FRAME, COMPONENT, COMPONENT_SET) because Figma blocks structural edits inside instances.
    • The “wide format split” layout (text left, images right) only kicks in when guideAspect <= 1.1 && targetAspect > 1.5 — formats outside that range fall back to plain anchor-based positioning, which can look wrong if you expected the split behavior.

Other ways to repurpose this

Product Design

Web Design

Marketing

Branding

Dependencies

typescript 5.9.3 Compiles the ~2,300-line code.ts source (the SafeZone anchor-mapping and multi-format layout-copying engine) into code.js, the file manifest.json actually loads into the Figma plugin runtime.
@figma/plugin-typings 1.123.0 Supplies TypeScript type definitions for the Figma Plugin API (FrameNode, ComponentNode, figma.ui, SceneNode, etc.) so code.ts type-checks against real Figma objects during the build.
Role
Design Engineer
Tools
Figma Plugin API TypeScript OpenRouter API
Video thumbnail for Figma Ad Resizer Plugin