What you are looking at
Start with a fixed generator before adding controls for every edge case.
The article feature image, the Open Graph companion, and any launch graphic belong to the same publishing job. The useful first step is one fixed generator that can output those surfaces from the same article state.
That means the first version only needs a few decisions: title wrapping, subtitle limits, logo position, accent treatment, and which formats export together. If those rules are still changing, more controls usually mean more chances for the exports to stop matching.
Starter rules
Four implementation decisions make the first version usable without turning it into a design tool.
These are the decisions that stop each article image from turning into a new manual layout pass.
One frame
Use one layout definition, not duplicated design files
Lock the shell, spacing, and text hierarchy into one layout. Otherwise every new article image becomes a slightly different manual interpretation of the same frame.
One copy contract
Keep title, support line, and logo in fixed roles
The title carries the article idea, the support line adds context, and the logo anchors the asset inside the brand. If those roles are not fixed, the editor ends up redistributing emphasis by hand on every export.
One accent logic
Use accent color as a rule, not a filler
The accent should mark the same structural element each time. If it moves to whatever area looks empty, the image is still being tuned visually instead of generated from rules.
One export path
Make the preview close enough to ship
If the editor has to rebuild the image after previewing it, the drift simply moves to the export step. The safer setup is the one where the preview output is already close enough to ship.
The version everyone starts with
Manual social image workflows look harmless until the publication starts moving quickly.
The first few social share images often get made in a design tool, duplicated, and adjusted by hand. That is a reasonable beginning. The problem is that duplication quietly turns layout, copy, export size, and visual emphasis into separate decisions every time a new article ships.
Once the pace picks up, those decisions drift apart. One export keeps the older title. Another uses slightly different spacing. The Open Graph image and the feature image still look related, but no longer look like they came from the same article.
Where the drift comes from
The same failures keep showing up.
A duplicated file preserves yesterday's copy and today's asset goes live with the wrong headline.
The square image and 1200 x 630 companion stop matching because they were exported from different edits.
Brand accents drift because every card gets one small eye-tuned adjustment before export.
Nobody can tell which file is authoritative, so the wrong export keeps circulating.
How the generator grows
Keep the first generator small enough that every new control has to solve a repeated problem.
HTML and SVG help because the frame stops living in duplicated files and starts living in versioned code. Once that happens, each requested control can be judged against actual use instead of guesswork.
Logo scale, background upload, a color swatch, alignment toggles, or alternate templates can all make sense. The useful test is whether the same issue keeps showing up across several exports, not whether one article looked awkward once.
Starter config
Define the smallest state shape that can generate a usable social image.
type ShareImageTemplate = { format: 'social' | 'square' | 'story'; horizontalAlign: 'left' | 'center' | 'right'; verticalAlign: 'top' | 'center' | 'bottom'; accent: string; logoScale?: number; backgroundMode?: 'none' | 'upload' | 'brand-plate';}; const starterTemplate: ShareImageTemplate = { format: 'social', horizontalAlign: 'left', verticalAlign: 'center', accent: '#e8620a',}; // Add later if the workflow really needs it:// logoScale: 0.92// backgroundMode: 'upload'Code block: format contract
Before styling, define the allowed copy budget for each format.
This is where the frame stops being a design file and starts acting like a layout contract. Write down width, height, heading limits, subtitle limits, and line counts before tuning anything else.
Once those numbers are explicit, the generator can make repeatable decisions about wrapping and truncation. It also becomes easier to automate because the limits are already part of the format definition.
Format contract
Define output dimensions and copy limits per format before tuning layout.
const FORMATS = [ { id: 'social', width: 1200, height: 630, headingChars: 23, headingLines: 3, subtitleChars: 66, subtitleLines: 2, }, { id: 'square', width: 1254, height: 1254, headingChars: 16, headingLines: 4, subtitleChars: 42, subtitleLines: 3, },]; // The format decides the safe copy budget before design tuning starts.Code block: copy guard
A bounded frame needs copy rules, not just typography.
The failure here is simple: someone pastes in too much copy and the frame starts breaking. Without a guard, the subtitle keeps growing until it hits the footer or pushes the whole block out of balance.
That is why the generator should do something concrete. Truncate the text, show the warning, and tell the editor what to do next: cut the copy down or use a larger format.
Copy guard
Truncate overflow copy before it collides with the footer or pushes the block out of bounds.
function truncateToLines(value, maxChars, maxLines) { const wrapped = wrapText(value, maxChars, maxLines); const sourceWords = value.trim().split(/\s+/); const shownWords = wrapped.join(' ').trim().split(/\s+/); if (shownWords.length >= sourceWords.length) return wrapped; const next = [...wrapped]; next[next.length - 1] = `${next.at(-1)?.replace(/[.,;:\s]+$/g, '')}...`; return next;} // If the copy is too long, truncate it and warn:// "Too much text for something like this. Condense the copy."Code block: safe region
The layout gets more reliable once the content block is measured against a safe region.
This is the step that replaces hand-placed coordinates with simple layout math. Give the content block a top boundary, a bottom boundary, and a measured height. Then the system can decide whether centering still fits or whether the block has to move upward.
It is still a light layout engine. But it is enough to stop the collisions that happen when headline height, subtitle length, logo placement, and footer position all start competing for the same area.
Layout engine
Measure the content block against a fixed safe region and pin it when vertical space runs out.
const contentTop = frameY + 28;const contentBottom = footerY - 82;const contentBlockHeight = logoHeight + eyebrowGap + headingGap + headingHeight + subtitleGap + subtitleHeight + dividerGap; const verticallyConstrained = contentBlockHeight > contentBottom - contentTop; // When constrained, pin the block to the safe region// and warn instead of letting the text collide.Common upgrades
The next step is usually one or two controls tied to repeated export problems.
Once the starter frame is working, people usually ask for a few specific controls. Add them when the same export problem keeps showing up, not when the interface just feels too small.
Logo scale
Add a slider only if logo size keeps needing adjustment
If the logo keeps needing the same small adjustment, a bounded slider can help. If it only fixes one awkward export, leave it out.
Color swatches
Add swatches when accent choices need to stay approved
A color picker is fine while you are still finding the look. Once the workflow settles, a short list of approved swatches is usually safer.
Template states
Save approved configurations instead of rebuilding them from memory
If the same settings keep getting reused, save them. Then people can pick an approved state instead of retuning the whole frame every time.
After the experimentation
Once the frame fits the publication, either remove choices or make approved settings easy to restore.
This is the part teams often skip. They prove the prototype works, then leave every extra option in place. The tool keeps behaving like an experiment even after the publishing workflow has stabilized.
A better handoff is either a stricter generator with fewer choices, or a generator with saved templates that restore approved settings quickly. Which approach is better depends on how much variation the publication really needs.
Template recall
Persist approved preset states or remove options that keep creating inconsistent exports.
const presets = { editorialDefault: { format: 'social', horizontalAlign: 'left', verticalAlign: 'center', accent: '#e8620a', }, featureSquare: { format: 'square', horizontalAlign: 'center', verticalAlign: 'center', accent: '#e8620a', },}; function applyPreset(name: keyof typeof presets) { return presets[name];} function lockDownForOps() { return { allowLogoUpload: false, allowBackgroundUpload: false, allowFormatSwitch: ['social', 'square'], };}Automation
One useful production path is letting the article generate its own feature and Open Graph image pair as part of release.
This page is one live example of that pattern. The article record already contains the title, support line, slug, and image requirements. Once the frame is in code, those fields can generate the square feature image and the Open Graph companion without a second manual export.
That is one practical use case once the system has settled. The article becomes the source state, and image generation moves into build or release instead of staying as a separate task at the end of publishing.
Automation path
Generate the feature image and Open Graph companion from the same article record in one build step.
const article = { slug: 'branded-social-images-without-canva-drift', title: 'Branded social images without drift', eyebrow: 'Technique teardown', subtitle: 'Start basic, then add the controls the workflow can justify.', accent: '#e8620a',}; await generateImagePair(article, [ { format: 'square', output: 'feature' }, { format: 'social', output: 'open-graph' },]); // Same copy contract, same frame logic, two release surfaces.Build order
If you are building one of these, the safest path is to make the workflow reliable in this order.
The easiest mistake is building the interface before the image contract is stable. A better sequence is to lock the output rules first, then add only the controls the workflow proves it needs.
Step 1
Define the article state that the generator is allowed to read
Start with the fields the image actually needs: title, eyebrow, support line, accent, and output format. If the generator reads half-finished editorial decisions or optional extras too early, the image contract becomes vague before the frame is even stable.
Step 2
Write the format contract before styling the frame
Decide width, height, heading limits, subtitle limits, and line counts per format before tuning spacing. That gives the layout engine something concrete to enforce instead of leaving every overflow case to visual judgment.
Step 3
Make preview and export use the same rendering path
Do not let the editor preview one thing and export another. The useful version is the one where the preview is already close enough to the release asset that editors are not rebuilding the design at the end.
Step 4
Only then add presets, uploads, or alignment controls
Once the frame survives real articles, you can judge which controls solve repeated problems. Until then, every extra option is more likely to hide an unstable contract than to improve the generator.
Release check
A generator like this is only finished when the release path is predictable.
The visual frame is only half the work. Before calling the tool done, check the parts that usually fail in production: the exported asset itself, the matching article metadata, and the places where editors can still create drift by accident.
A short release checklist is often more useful than another design control, because it keeps the generator tied to the publishing workflow instead of treating it like a standalone toy.
What to verify
Five checks catch most of the failures that matter.
The preview and exported SVG still match when you test a long real headline, not just the sample copy.
The square feature image and the 1200 x 630 social image are generated from the same article state in the same run.
Warnings appear before the frame breaks, and the editor can tell what to change next.
Default logo, accent, and allowed formats reflect the real publishing workflow rather than experimentation settings.
The published page points to the intended social asset in its metadata so the release surface and the article record still agree.
The rule of thumb
If the frame works only because someone remembers how to tune it, it is still a prototype.
The branded result matters. The stronger result is that the system can keep producing it without anyone making the same wrapping, spacing, and emphasis decisions again on every article.
If your branded social images still depend on duplicated files and last-minute exports, the next step is usually a starter generator, not a bigger design process.
Start by defining the copy contract, the format limits, and the build path that generates the feature image and Open Graph image from the same article record.
Bring the page, report, or workflow as it is now.
We reply with the clearest next step, or an honest no.
