Fewter

Fewter

Convert Figma signature frames into Gmail-ready HTML with AI refinement.

MarTech · Founder · 1 month · Team of 1

Problem

Email signatures designed in Figma look correct in the design file but cannot be pasted into Gmail as-is. Gmail strips flexbox, grid, <style> blocks, SVG, and base64 images — it expects nested <table> layout with inline style="" attributes and stable https:// image URLs. Teams either hand-code signatures from scratch (hours per design iteration) or use generic templates that do not match brand layouts. Figma's own export URLs expire, so even a manually built HTML file breaks when image links rot.

Context

The target workflow is a marketing or design team that already maintains signature frames in Figma — often with logos, social icons, and contact details laid out precisely. Gmail is the primary client constraint; other email clients share similar table-and-inline-CSS limitations. Fewter assumes the user owns or can view the Figma file and has imgbb and Gemini API keys for server-side image hosting and HTML generation. The product is intentionally scoped: one frame in, one signature fragment out — no team management, no template library, no bulk export.

Strategy

  1. Authenticated Figma access — Auth.js v5 with the Figma OAuth provider (PKCE, token refresh) so the server can read file content and export images on the user's behalf.
  2. Asset durability — Walk the frame subtree, discover up to 20 raster assets, download from Figma, and upload to imgbb for permanent CDN URLs before any HTML is generated.
  3. AI-first HTML generation — Send a trimmed node tree, asset map, and PNG screenshot to Gemini (gemini-2.5-flash) with a strict system prompt enforcing table layout, inline CSS, and Gmail-safe tags; validate and sanitize model output before returning it.
  4. Human-in-the-loop refinement — A streaming chat panel applies incremental edits to the current HTML (preserving imgbb URLs) with client-side undo — no second conversion pass required.

The copy step writes both text/html and text/plain to the clipboard so Gmail receives rendered content, not escaped tags.

Architecture

Web application — Next.js 16 App Router with React 19. A single-page converter (SignatureConverter) orchestrates URL input, progress UI, preview, chat, and copy. Route protection in Auth.js authorized callback gates only /api/convert and /api/chat; the landing page stays public.

Conversion pipeline (runConversion) — Four sequential stages surfaced to the UI:

  1. parse-url — Extract fileKey and nodeId from standard and branch Figma URLs.
  2. fetch-figma — Load frame node tree via Figma REST API; render a PNG screenshot for multimodal context.
  3. upload-assets — Discover node renders and image fills; upload each to imgbb; build an assetMap keyed by Figma node id or imageRef.
  4. generate-html — Gemini multimodal generateText with system prompt → extract HTML fragment → validateAndSanitizeHtml.

Refinement chatPOST /api/chat streams Gemini responses with the current HTML and asset map injected into the system prompt. Client-side onFinish extracts table HTML, re-validates, and pushes prior HTML onto an undo stack.

HTML validationvalidateAndSanitizeHtml strips forbidden tags (script, style, svg, etc.), event handlers, javascript: URLs, and data URIs; verifies image src values against the imgbb allowlist and href schemes (https, mailto, tel).

Execution

Sign-in and conversion

  • Figma OAuth with file_content:read and file_metadata:read scopes; automatic access token refresh
  • Paste a frame URL with node-id query parameter; branch URLs supported
  • Simulated four-step progress bar during the single POST request (~10–30 seconds typical)
  • Structured error responses per pipeline stage (access denied, asset limit, upload failure, empty HTML)

Asset handling

  • Walk visible nodes; collect vectors, components, instances, and image fills
  • Hard cap of 20 exportable images per conversion
  • imgbb upload on the server only — API key never exposed to the browser

Preview and copy

  • Toggle preview width between 600px (desktop) and 320px (mobile)
  • Sandboxed iframe renders the signature with clickable links
  • Collapsible HTML source view for debugging
  • Copy for Gmail writes ClipboardItem with both HTML and plain-text fallbacks; manual select-and-copy fallback when clipboard API is blocked

AI refinement

  • Streaming chat via Vercel AI SDK @ai-sdk/react
  • Quick prompts: "Make the text larger", "Add a phone link", "Fix alignment"
  • Undo reverts to previous HTML without another API call
  • Assistant responses containing <table> update the preview; conversational replies display as text

Gmail compatibility rules enforced in prompts and validation

  • Nested <table> layout only — no flexbox, grid, or layout <div>s
  • Inline style="" only — no classes, no <style> blocks
  • <img> with explicit dimensions and imgbb-hosted src
  • Web-safe font fallbacks (Arial, Helvetica, sans-serif)
  • ~600px max width on outermost table

Challenges

Gmail HTML constraints — Modern CSS that works in Figma and browsers fails silently in Gmail. The system prompt and post-generation validator must agree on what is allowed; model output occasionally includes forbidden tags or data URIs that need stripping with user-visible warnings.

Temporary Figma image URLs — Figma export links expire. The pipeline must download and re-host every asset before HTML generation, or signatures break days after paste.

Multimodal fidelity — Gemini sees both structured JSON (trimmed node tree) and a PNG screenshot. Complex effects (blur, shadows, gradients) simplify to solid colors; the prompt explicitly tells the model to match the screenshot within email constraints.

AI output extraction — Models sometimes wrap HTML in markdown fences or add prose. extractHtmlFromResponse pulls the <table> fragment; empty or non-table output fails the pipeline with a clear error.

Clipboard API variance — Safari and locked-down browsers block rich clipboard writes. The copy button falls back to instructing the user to select the rendered preview manually.

Session and token lifecycle — Figma access tokens expire mid-session. Auth.js refreshes tokens in the JWT callback; expired refresh tokens surface a session error on convert/chat routes.

Solution

A single server-side pipeline (runConversion) keeps Figma credentials and imgbb keys off the client. The Gemini system prompt encodes non-negotiable Gmail rules so the model generates table fragments from the start rather than requiring a full CSS rewrite pass. validateAndSanitizeHtml acts as a defense-in-depth layer regardless of prompt compliance.

Chat refinement injects the current HTML and asset map into every request so the model applies surgical edits — "make the name bolder" — without redesigning the entire signature or dropping imgbb URLs. Client-side HTML history enables undo without re-fetching Figma or re-uploading assets.

The preview iframe at 600px matches typical email signature width, giving designers confidence before paste. Dual-format clipboard copy bridges the gap between raw HTML (useless in Gmail's rich editor) and rendered content Gmail expects.

Measurable impact

  • Replace manual HTML coding — Designers paste a Figma URL instead of hand-building nested tables and inline styles for each signature iteration.
  • Cut conversion time from hours to minutes — Full pipeline (fetch, re-host, generate) completes in under five minutes including optional chat refinements.
  • Stable image hosting — imgbb CDN URLs survive beyond Figma's temporary export links, so pasted signatures keep working.
  • Iterate without re-conversion — AI chat and undo let teams tune spacing, links, and typography without restarting the Figma fetch pipeline.
  • Gmail-safe output by default — Validation strips forbidden tags and unapproved image sources before copy, reducing paste-and-debug cycles in Gmail Settings.

Tech & infrastructure

Tech Stack

Next.js 16React 19TypeScriptTailwind CSS 4shadcn/uiAuth.js v5Vercel AI SDKGoogle GeminiBiomeBun

Infrastructure

Vercel

Integrations

Figma OAuth APIimgbbGoogle Gemini

Gallery

Fewter screenshot