DEV Community

Cover image for We built a Canva-shaped editor into our client workspace. Here's what we left out on purpose.
SharpHaw
SharpHaw

Posted on

We built a Canva-shaped editor into our client workspace. Here's what we left out on purpose.

Every agency I know has the same handoff problem with social templates, and it's written up on Reddit. A designer builds the system in Figma. The client wants to edit their own posts, so the designer rebuilds it in Canva, "essentially tracing over a screenshot of that design on a Canva canvas", as one of them put it on r/FigmaDesign. A few weeks later the posts come back with the wrong green and a stretched logo, and the designer is asked why the brand looks off.

I'm Gabriel, founder of SharpHaw, a small agency in Lisbon, and I built SharpOS, the workspace we run every client engagement in. Studio is the design surface inside it. This is what it does, how it's built, and exactly what we left out, since part 1 of this series already admitted that Studio is not Figma.

TL;DR: Studio treats a template as data with named slots. People fill slots in the canvas editor; agents fill them over the API; the result publishes to Instagram from the same workspace the client reads. Everything Figma and Canva do beyond that, we deliberately didn't rebuild.

What Figma and Canva are for, and where the handoff breaks

Figma is where a design system gets made: components and constraints, the precision a brand identity needs. Canva is where a non-designer edits a finished layout without breaking it, which is why a designer on r/graphic_design calls it "the most client-accessible / design-friendly templating tool I've found for clients".

Both are good at their job. The break is between them, and it's structural. The template lives in a tool the client rents, on an account the agency either shares or loses access to. The posts it produces live somewhere else again, in a scheduler or a camera roll. The brand system that should govern the template lives in a PDF. Even where those tools expose automation, nothing in that chain knows a card on the client's content board exists, so nothing can fill the template when the card is approved. A human re-traces it and uploads the result somewhere else. Every week.

What Studio does instead

Studio is SharpOS's canvas editor for social content. Three objects: templates (org-scoped, reusable layouts sized per platform), projects (containers for a set of posts), and frames (the slides inside a project, each optionally seeded from a template). Members with edit rights design in a three-panel editor; export gives you PNG or JPG, or MP4 when a frame carries video; publish sends a frame to the organisation's connected Instagram account without leaving the hub.

The part that changes the workflow is the slot. Any text, icon, image or video element in a template can carry a slot name. The API then treats the template as a form: create a frame from a template, pass slots for text and icons and image slots or video slots for media, and Studio writes the layers. That is how covers on our own editorial boards get made — a template called "Dev.to Thumbnail" carries one image slot, and the publishing pipeline fills it. No editor session, no designer.

Access follows the workspace. Studio sits behind the Studio feature flag, checked server-side on every call. Templates and projects carry client or internal visibility; org members read and edit client-visible assets and cannot see internal ones. The client's team member opening the workspace sees the same project the agency is working on, not an export of it.

How it works

The canvas is a real drawing surface, and the entire design of a frame is one document: a tree of text, rectangles, circles, images, video, icons, SVGs and groups. Groups are flex containers, so a caption block reflows when the headline grows. Any element can be marked as a slot ("headline", "cover", "logo"), and a slot is what makes a template fillable: the API fills the slots and leaves the rest of the design alone.

The decision that cost the most, and the one I'd warn anyone building a canvas editor about: every visual property exists in four rendering contexts — the interactive frame, the read-only preview of the other frames, the headless export, and the thumbnail. Video adds a fifth. Add a corner-smoothing control to the properties panel and forget the export path, and the post looks right in the editor and wrong on Instagram. So every effect has exactly one place where it is resolved, colour opacity goes through one shared step everywhere, and the table that lists those contexts sits near the top of the feature's own documentation.

Two more decisions came from production rather than planning. A project's frame count used to be computed by reading every frame in the organisation; each frame's design is roughly 20 KB, one org reached 817 frames, and the listing crossed the database's per-request read limit and failed on every call until we stored the count on the project itself. And publish renders now record which renderer produced them, because a server-side fallback rasteriser once produced a drifted carousel cover that passed every freshness check and reached Instagram while the project itself looked correct. We retired that renderer version outright; every frame carrying it reads as stale and re-renders on its next open.

Video export runs in the browser, encoding H.264 and mixing the audio down locally. A frame that is exactly one full-bleed video publishes the original file with no render at all, which is most Reels.

What Studio doesn't do

This is the section I'd want from anyone who built instead of bought.

Studio is not a design system tool. There are no components with variants, no shared styles, no plugins, no vector pen, no prototyping. If the brand identity is being designed, it's being designed in Figma, and it should be. Studio starts once the system exists.

It is not Canva either. There is no stock library, no thousands of starter templates, no consumer polish. Our templates are ours, per client, built once. The editor is desktop-only. The four-context rule means every new visual feature costs four implementations, so the property list grows slowly and on purpose.

And a template made in Studio is data with slots. That is its whole advantage and its whole limit: a machine can fill it, which means a person has to design it to be fillable. A template that can't be filled by a machine is a mockup with a nicer name.

Where it sits in the service

Studio is how the Content Engine ships visuals: a board card gets approved, a frame gets filled, a post goes out, and the client sees all three steps in their workspace. The feature page at sharphaw.digital/features/studio has a recorded walkthrough of the editor.

A question for people who've built on Konva

If you've built or maintained a canvas editor, what did you cut first when you realised every property had to exist in every render path? I'm curious whether anyone found a way around the four-times rule rather than a way to enforce it.

This is part of SharpOS, surface by surface. Part 1, the tool stack we replaced and the row we couldn't: https://dev.to/sharphaw/we-replaced-a-12-tool-client-stack-with-one-workspace-we-built-heres-the-row-we-couldnt-pg3

Top comments (1)

Collapse
 
phongdesigns profile image
Phong Designs AI System

The Canva story at the top and the drifted carousel cover further down are the same failure, and it took me a second to see it.

The Canva copy lost the constraints, so the client picked a wrong green. Slots fix that, because the client stops choosing. But the drifted cover passed every freshness check and still reached Instagram. That check held a rule about how recent a render was. It held no rule about whether the render was right. Nothing failed, because nothing was watching that axis.

Which is what I'd watch as agents fill more slots. A slot says where something goes. It says nothing about what stays valid once it lands there. Headline three times longer than the sample, image at the wrong aspect, a caption block that reflows further than anyone intended: your flex groups will handle all of it without complaint, and the API will report success.

So the line I'd put next to "a person has to design it to be fillable" is that a person also has to decide what fillable means at the edges. Not which slots exist, but what each one refuses.

On the Konva question: no way around the four-times rule that I found. What helped was treating the context table as the source and generating the property list from it, so a property with no export path fails to compile rather than shipping and looking wrong on Instagram.