This isn't just a portfolio piece
It's a working product used by a real design team. The skills it required span the full content design + engineering stack:
Content reviews were a bottleneck nobody talked about
At Birdeye, design moves fast. Designers finish a flow, need copy validated, and send a Slack message. The content designer reviews it async. Comments land in Figma hours later. Designers have moved on. Copy sometimes ships as-is.
The problem wasn't attention or intent. It was friction. Reviews happened outside the design tool, after the design was already done, in a format that required context-switching for everyone involved.
I was already running content reviews manually. I knew the rules. I knew the patterns. And I knew that most of the errors I caught were the same ones, repeated across components, across teams. That's a system problem. Systems can be automated.
One panel. Three steps. Instant feedback.
The plugin lives inside Figma's plugin panel. A designer selects one or more text layers, picks the component type from a dropdown, and clicks Validate content. That's it.
The interface is intentionally minimal. The cognitive load belongs in the feedback, not the tool.
Same errors. Different designers. Every sprint.
After auditing three months of copy reviews, the failure modes were predictable:
Not imperative. Too long. Passive construction. No urgency.
Verb-first. Concise. Action-oriented. Matches the design system.
What's invalid? Which field? What do I do? No context. No next step.
Specific. States the fix. Sentence case. No blame.
Instructions in a placeholder. Disappears on focus. Inaccessible.
An example, not an instruction. Scannable. Accessible.
Vague. No example. Users have no idea what to type or why.
Specific. Actionable. Explains the what and the why.
These weren't judgment calls. They were rules. Rules that could be encoded, applied consistently, and returned in milliseconds.
Six layers. One continuous loop.
-
Selection layer
The plugin reads whichever text node a designer has selected via the Figma Plugin API, capturing the text, layer name, parent frame, sibling nodes, and page name as context signals.
-
Semantic classification
The designer picks a component type: CTA, tooltip, error message, helper text, empty state, placeholder, and 11 more. Each has a hardcoded ruleset with checks for length, voice, tone, structure, and punctuation.
-
Prompt orchestration
The selected text, component metadata, heuristic rules, and Birdeye's global style principles are compiled into a structured prompt. The model evaluates each check individually and returns structured JSON.
-
Proxy / API layer
Figma plugins can't store API keys safely client-side. I deployed a Deno proxy server that receives requests from the plugin, injects credentials securely, routes to the model, and normalises the response.
-
AI evaluation
The model returns: status (aligned or not aligned), a 0–100 quality score, which specific checks failed, an explanation, and suggested rewrites. Explore mode skips the verdict and returns 4–5 simpler alternatives.
-
Canvas annotation rendering
The JSON converts back into readable markdown and attaches to the selected Figma node as a native annotation. Designers see the feedback exactly where the copy lives, on the canvas itself.
Heuristics, not hunches
Every component type has explicit checks derived from Birdeye's UX writing guidelines. These aren't suggestions, they're tests. The model evaluates each one pass/fail and the score reflects the ratio.
| Component | Checks run |
|---|---|
| CTA | Starts with imperative verb · ≤3 words · no ending punctuation · no vague verbs like "Submit" or "OK" |
| Tooltip | ≤120 characters · one sentence · explains why or how, not restating the label · no jargon |
| Error message | States issue + context + next step · plain language · ≤90 chars · active voice · no blame |
| Form hint | Short guidance or format rule · example where helpful · does not repeat the label · ≤90 chars |
| Toaster | Confirms the result in one short sentence · friendly tone · no extra interaction required |
| Placeholder | Example, not instruction · ≤5 words · no critical info · no period |
| Empty state | Positive tone · one clear next step · benefit-oriented headline · no filler |
Global checks apply across all components: US English, sentence case, active voice, contractions encouraged, no jargon.
Strictness is configurable. Light mode allows up to 2 failed checks before returning "not aligned." Strict allows none. Teams dial up rigour depending on the surface, onboarding vs internal admin screens.
The result lives where the copy lives
When the plugin returns a result, it writes it as a native Figma annotation, attached directly to the selected text node on the canvas. Not in a side panel. Not in a comment thread. Exactly where the designer is already looking.
The annotation format mirrors Figma's native Dev Mode annotation categories, Development, Interaction, Accessibility, and Content, so it integrates visually with how design teams already work.
What I built, and why
-
Deno over Node for the proxy
Deploys in seconds, built-in TypeScript, cold-start-fast on the free tier. For a plugin proxy handling bursts during design reviews, that mattered more than ecosystem size.
-
Structured JSON output from the model
I constrained output to a strict JSON schema using OpenAI's
response_format: { type: "json_object" }. No regex. No parsing heuristics. Deterministic annotation rendering. -
Two modes: validate and explore
Validate returns a pass/fail verdict with rewrites. Explore skips the verdict and returns 4–5 simpler alternatives. Designers told me they sometimes want options, not a grade.
-
Context signals from surrounding layers
The plugin reads parent frame name, sibling text nodes, page name, and layer name, all fed into the prompt. A CTA labelled "Save" inside a "Delete account" frame is different from the same word in onboarding.
-
Consistency check across multiple selections
When a designer selects more than one text node, the plugin checks them against each other, length, case, punctuation, voice, in addition to each against the style guide.
What changed
Designers validate copy without contacting the content team for routine reviews.
Component types covered, each with specific linguistic heuristics.
Validate for verdicts. Explore for options. One tool, two intent states.
Time from joining Birdeye to Employee of the Month, plugin cited as a direct reason.
Beyond the numbers: this changed how content design was perceived at Birdeye. When a tool that automates expert judgment ships inside the design tool itself, content design stops being a bottleneck. It becomes infrastructure.
Content systems are software
The plugin didn't just automate a review task. It encoded years of UX writing judgment into something another person, or a whole design team, can access without me in the room. That's what a real content system does. It scales the author, not just the output.
Rishi Sharma · Lead Content Designer · Birdeye
Building this taught me the line between content design and product engineering is thinner than either discipline admits. The hard part wasn't the code, it was knowing what to check, how to phrase the rules, and what level of strictness serves a designer's intent without annoying them. That's a content problem wearing a developer costume.
The planned next version adds screenshot-based consistency checking: upload a production screen, compare new Figma copy against what's already live. Same architecture. New context layer.