🚧 This case study is under construction 🚧
TL;DR — The product launched with a set of generic visual themes for displaying marketing assets, but customers needed the assets to match their brand. Rather than build an in-app theme design editor, I built a system to allow customers to design custom themes in Figma and then import them to the platform. Since launch, we haven’t built a single new generic theme.
My role — I identified the approach, designed the workflow, built the initial prototype, and shipped the production version of the feature. I also owned a year of iteration after.
1. Context: turning data into marketing assets
UserEvidence helps B2B companies distribute surveys and convert the responses into verified social proof. A marketer runs a survey, a customer says something great, and the platform transforms that quote into a polished testimonial that a sales rep can drop into a deck, an email, or a LinkedIn post.
Consistently rendering polished testimonials is more complex than it sounds. Quotes can be any length, and potentially include a combination of additional visual elements. Attribution might include a name, a job title, a company, a photo, an NPS score — or any combination. A single theme has to finesse all of that variability into a ready-to-use graphic.

2. The problem: every customer wants their own theme
The platform launched with a fixed library of generic themes plus a panel of parameterized controls. The controls could toggle various characteristics of a theme, but this allowed only a limited amount of flexibility.

We received a steady stream of requests to customize themes in different ways, and each request became an engineering headache or a disappointed customer, or both. The team was constantly choosing between stacking more convoluted controls onto the existing themes, or adding entirely new static themes. Both options increased complexity that then needed to be maintained forever.
Our customers’ design teams already had a stockpile of on-brand designs and graphics. But they had no way to use them with our content.
3. A first class design experience with zero lines of code
We had been adding controls to expand the capabilities of generic themes, but we were running out of runway: every new request added a knob to the UI, the options matrix grew into a confusing mess, and we still couldn’t express an arbitrary layout. The pile of parameterized controls dangled the promise of design freedom without ever delivering it.

It became clear that any cookie-cutter solution would never satisfy our customers. What they wanted most was flexibility. We needed customizable themes.
The standing assumption was that we eventually had to build an in-app WYSIWYG theme editor so that users could design their own themes. That project would occupy our small team for a long time and the end state would inevitably be a strictly worse design tool than the industry-standard tools our customers’ designers already mastered. The “white whale” dream was to somehow offer a design experience as powerful as Figma, but without reinventing the wheel.
It turned out we didn’t need to build Figma at all. We only needed its output. I knew that our testimonials could be rendered from SVGs, which are natively supported by Figma. The key question was how to use Figma’s static output as a template without a cumbersome post-processing step. The breakthrough was when I remembered that layer names in Figma are preserved as an id on the corresponding node in the exported SVG. This was the missing puzzle piece. If a designer’s layer names survive the export, then the file already carries its own data-binding map. So I designed a vocabulary of layer names and a service layer to parse the SVG elements and substitute the placeholders in the SVG template with attributes from the testimonial being presented. This was the crux of the solution.
| Solution | Effort to build | Flexibility |
|---|---|---|
| Keep extending generic themes | Low | Low |
| Build an in-app WYSIWYG editor | High | Medium |
| Design themes in Figma | Low | High |
4. Trade-offs
This approach involved some trade-offs that I recognized from the outset.
It requires a manual. A raw SVG contract offers none of the hand-holding of a GUI. The author has to read and understand our documentation. That rules out the casual, drive-by user. But the target user was never that person; it was the professional designers working for our enterprise customers. They didn’t want hand-holding. They wanted us out of their way.
I still wanted to shorten the on-ramp as much as possible. It was obvious that we should provide some kind of starter template, but distributing it as a separate file would mean we also needed to update and redistribute it whenever the feature changed. I went a step further and built the starter into the product instead. Every new theme is initialized with a simple SVG template containing the most common placeholders, ready to copy/paste straight into Figma and edit. The starter example can never get lost or fall out of sync, because it’s built into the feature.

It dropped pagination. The first generation of generic themes paginated long quotes across multiple pages. Faithfully reproducing that pattern would have added a lot of complexity to any custom-theming solution, both in terms of the implementation and the end user experience. I already had doubts about whether multi-page testimonials were worth supporting at all: the power of a testimonial quote is that it’s short and impactful. Meanwhile, we already had other asset types designed for long-form content. Stakeholders were hesitant to remove existing functionality, but I made the argument and got buy-in to ship the first iteration without pagination. Multi-page testimonials were never missed and a ton of complexity was sidestepped. Knowing what not to solve was part of the design.
5. Proving it in two days
At the time, we were a small team with a long backlog of other feature requests and little appetite for speculative experiments. When the company organized a two-day hackathon in which engineers were encouraged to experiment and showcase ambitious ideas at an upcoming company meetup, I chose to tackle this problem.
The goal was to demo a real solution, not a someday-useful idea. Before writing any code, I gathered real branded marketing graphics that design teams were already making outside our platform to serve as the baseline for the initial prototype. I also designed a few deliberately ambitious fictional graphics to test capabilities well past anything our HTML themes could do, such as diagonal text or overlapping elements.

In two days I designed and built a working prototype that included SVG template ingestion, a placeholder substitution service, and a live preview UI that applied the theme authored in Figma to a real customer quote. Instead of churning on requirements, leadership saw the solution in action.
The company founders immediately gave the green light to expand the prototype into the production-ready feature. This problem had haunted us for years. Two days of tinkering and a five-minute demo moved it to the front of our roadmap. The prototype wasn’t a sock puppet. Its architecture became the spine of what is still in production today.
6. Early challenges
Devising the general system to ingest SVG templates was relatively easy. The harder, more inventive work was “fooling” a raw SVG file into behaving like a dynamic template without leaking complexity into the authoring experience. Several essential capabilities don’t natively exist in SVG, and closing those gaps invisibly was the real challenge.
Wrapping text. SVG’s native <text> element renders in a straight line. Wrapping is essential for displaying testimonial quotes of unknown lengths. The solution I landed on synthesizes this behavior by allowing the author to (optionally) include a rectangle as a sibling layer in Figma. When the exported template is parsed, the <text> element communicates the typographic styles and the <rect> defines its bounds. At render time, the system swaps these elements for a <foreignObject> with an HTML <div> inside, resulting in real text flow while preserving the designer’s text styles.
Long quotes. I purposefully did not include pagination in the spec of this feature. But we still needed a way to handle long quotes. Truncation was one option, but it could not be our only option because it meant the marketer would have to edit many of the quotes to fit elegantly. I wanted to give them more flexibility in the form of auto-scaling. The system detects if the quote text overflows the container, and then reduces the font size so that it fits. A consequence of this approach is that the font size of long quotes will sometimes be too small, and then need to be shortened. But those long quotes generally are not usable as-is anyway. Designing around runaway lengths would mean optimizing for testimonials that have already stopped being good testimonials.
Font files. Typography is a critical component of any brand. The prototype rendered system fonts faithfully, but most brands use proprietary fonts. We can’t export the fonts from Figma as outlines, because the text is dynamic (a different quote on every asset). The platform needs the actual font files to typeset arbitrary content at render time. Uploading the font files is straightforward; the real design challenge was making that step obvious to authors. The fonts were already installed on the computer where the design was created in Figma, so it was not intuitive to most authors that they needed to provide the font files separately for the theme to work correctly on our platform. So we added an automated prompt on import. The system scans the imported SVG for every font family it references and immediately prompts the author to upload any files that are missing. What was initially an easy-to-forget manual step became obvious and unmistakable.

7. What shipped: the final workflow
The shipped system is a documented set of Figma layer naming conventions and a simple interface to receive the exported design. All the design decisions have been outsourced to Figma, so the interface only has a few simple inputs: the theme name, attached font files, and the SVG template from Figma.
A designer creates the design in Figma, naming layers systematically with our placeholder variables to identify where to insert the corresponding attributes of the rendered testimonial: QUOTE_TEXT, TITLE, NPS_SCORE, etc. The design is exported or copied as an SVG, which gets pasted into our platform. A split-screen live preview immediately renders a testimonial with the imported theme. Simple adjustments can be made by manually editing the SVG code, but it’s generally much more convenient to adjust in Figma and then re-export the new SVG.

The new theme propagates everywhere that theme selection occurs, available to render any testimonial in the customer’s account.
8. Engineered for compatibility
The custom theme experience is very different from its predecessors, but I designed it from the outset to plug into our existing architecture. The generic themes were Vue components. They received testimonial data as props. To match this interface, I implemented a custom theme Vue component to act as a thin wrapper around the exported SVG design. The component receives the SVG template as an additional prop and renders it with the testimonial data inserted. The interface matched the legacy architecture, so all the downstream logic for printing assets as PNGs or PDFs kept working seamlessly.
The interface stayed the same, even though the philosophy was the opposite. In the generic themes, every visual decision lived in the application code as complex templating logic threaded with conditionals to handle all the theme options and differences between testimonial shapes. The SVG contract pulls those visual decisions out of the component code and makes the template editable by designers. The component became simpler and much more powerful because the complexity now lives in the template exported from Figma, not the code.
<template>
<div>
<!-- Loading state -->
<div v-if="isLoading">
...
</div>
<!-- Render the compiled svg -->
<div v-else class="testimonial-svg" v-html="svgContent" />
</div>
</template>
...
9. Loose ends
We shipped the first version deliberately lean, and piloted the feature with a handful of customers who had volunteered to give us feedback. The feedback was positive, but we also quickly identified a few loose ends.
Conditional rendering. Many attributes of a testimonial asset are optional. Some have a title and some don’t. They may or may not include an associated NPS score. Some quotes are anonymized and others include a name and job title attribution. Designers wanted to handle these variations with different visuals. I was hesitant to add more complexity, so I considered encouraging authors to create multiple themes to handle different “shapes” of testimonials. For example, a version for testimonials with headshots, and a version for those without. But this wasn’t a good match for the user’s mental model of a theme. The winning solution was to introduce a conditional syntax to the placeholder vocabulary. Adding an IF_ prefix to any of the existing placeholder variables makes a group appear conditionally, only if the corresponding attribute is not empty. This simple convention allows a single theme to handle multiple shapes.
Dynamic images. The first version only handled textual data from testimonials, but headshots were becoming a popular addition to testimonial assets and customers wanted to display them in their custom themes. Dynamically inserting images into a template is a different flavor of problem than substituting a text string. Images are two-dimensional and there is no guarantee that the testimonial photo’s dimensions and aspect ratio match the design. Many theme designers also crop the image into a circle or add other effects that make it more complicated than simply plopping an image file.
I added a HEADSHOT placeholder variable that is applied to a shape layer in Figma. At render time, the frontend creates an SVG <pattern> wrapping an <image> element pointed at the headshot’s URL, then sets the placeholder shape’s fill to reference that pattern. The shape gets “painted” with the headshot photo — and because it’s a pattern fill, it works on any shape (circle, rect, custom path).
Alternating content. Testimonials arrive from different sources with different fields populated. Some testimonials have an associated question that the quote answers. Some testimonials have a custom title added by the marketer. It is also possible to have both. Many of the early custom themes included a single headline that designers intended to contain the title or the question. A similar result could be achieved with the conditional groups described above, but it was cumbersome and unintuitive for designers. So I extended the placeholder variable vocabulary to include fallback chains that correspond to common use cases. For example, TITLE_OR_QUESTION resolves to whichever exists, in a defined priority order. This provides a simple, intuitive way for a theme designer to define a headline, and it will reliably render correctly regardless of which combination of attributes a testimonial has.
Auto-spacing. SVG element positions are effectively absolute. Every element sits at fixed coordinates. But some designers wanted the spacing between text elements to stay consistent regardless of their lengths, similar to how an HTML paragraph can have standard margin between the following paragraph. SVG does not work this way natively. The solution, which is currently in progress, extends the layer name directives to let authors declare how a group’s child layers should be arranged when rendered. These directives will bring the power of HTML’s flexbox into the custom theming ecosystem.
Notably, every one of these extensions was added to the original implementation with no breaking changes. The architecture from the two-day prototype absorbed a year of new requirements without a rewrite.
10. Outcome: the queue went quiet
Since this system launched, we have not built a single new static theme. The stream of ad hoc customization requests dried up. We still get occasional requests for additional SVG-theme capabilities, but these requests are welcome because they improve the product for all our users at once, instead of pleasing one customer at a time at the expense of others.
11. Reflection
This problem sat on our backlog for years, framed as a multi-quarter research project, until two days of building turned it into something stakeholders could see and decide on in a single meeting. A rough, working artifact advances a hard problem faster than any specification by replacing argument with evidence.