Design scarecrow · campaign engine · v2
v1 was a port of Julia's Figma. This one is the round trip. She pulled v1 back into the file, Cristian built Campaign Editor v02 from it, and the pieces that came back — theme presets, the accent pairs, the spinwheel as a component, “Accent 1 carries the CTA” — are v1's. So v2 stops guessing about the things that are now settled, and spends its budget on the things that still are not.
Settled in the Figma
Slot class per format, printed on every format card. Background colour is its own field. Eyebrow style is exactly Chip | Spinwheel | Text. So v2 renders all seven formats, where v1 could only justify three.
Proposed here
A Behaviour group, which v02's own help text promises but no frame draws. And section 03: an arbitration simulator, because “one bar, one overlay, one per inline slot” is finally something you can watch instead of read.
Still open
Three overlay formats compete for one overlay slot. A spinwheel eyebrow cannot carry the word “Black Friday”. And Sidebar card is slot-class inline with exactly one place to sit. Section 09.
01
Cristian's v02 shape: a narrow form column and a preview that owns the rest of the window. He built it because v01 “wasn't great to preview the bigger banners” — and he is right, a 1440-wide topbar has nowhere to go in a side panel. The form below is derived from the format: switch format and fields appear and disappear, and one muted line says why. Hidden fields keep their values, so you can fill everything in once and move between formats.
Draft campaigns are never delivered.
Groups campaigns in the list. A campaign can sit in more than one.
Inline formats sit in a named slot. That is their position.
Product tiles beside the copy. Tinted from Accent 2.
The surface. Separate from Accent 1 — this is what v02 fixed.
Where the surface shows up on the screen.
A placement choice, not a content one.
Sizes subtract whole blocks. They never restyle.
Optionally set all four colours at once, then tweak them as needed.
Accent 1 carries the CTA.
Accent 2 tints the art and fills the badge.
Restyles the rendered campaign. Renders nothing on its own, and is scoped to this campaign.
Replaces the whole content block with your own markup. The container, the dismiss control and everything under Behaviour stay under system control.
Counts a fixed duration down from each viewer's first impression, rather than to a date.
Leave empty for no countdown.
v02's Custom HTML help says “everything under Behaviour stays under system control”, so the group is already load-bearing in the copy. Nothing draws it. Here is the smallest version that carries what the Notes frames argue for.
A mat blocks the page, so a cap matters more here than anywhere else.
Rotation always ships its pause control. Auto-advancing text without one fails reduced-motion users.
Breaks the tie when two campaigns want the same slot. See section 03.
The caption is Julia's convention, and it is load-bearing: the surface renders at actual size whenever it fits, and only scales when it cannot. There is no zoom control — the canvas decides and the caption reports.
02
v1 had to leave this open — it could not tell whether a Banner was inline or something of its own, so it shipped three formats and said so. The format cards in v02 print the answer on their face. That single decision is what lets v2 draw the whole set, and it is what makes the arbitration in section 03 expressible at all.
03 · proposed in v2
The delivery rule reads: at most one bar, one overlay, and one campaign per inline slot; priority breaks the tie. Written down it sounds obvious. Turned on, it is the most surprising part of the system — because three of the seven formats are overlays, so a welcome mat and a slide-in cannot both run, however unrelated their campaigns are. Toggle these and watch.
| On | Campaign | Slot | Prio |
|---|
The app, with whatever won
04
The enum closed at three. Badge folded into Chip, on Julia's word in comment #32. The spinwheel is now a component at sm 64 / md 80 / lg 96, filled from Accent 2 with a per-campaign override — which is v1's invention coming back as a spec. Hover one.
Found while building it · the two-token problem
Every spinwheel in the Figma reads 40% OFF. That is not decoration, it is a constraint: a disc 64–96px across holds about two short tokens. But the field feeding it is eyebrow, and the value in the same file's Content group is “Black Friday” — which is also two tokens, and still wrong, because it is a name rather than an offer. Below is the same eyebrow value in all three styles.
So a spinwheel eyebrow needs its own short copy, and the editor should say so rather than let a marketer discover it at render time. v2 swaps the field's help text and placeholder when the style is Spinwheel — the cheapest fix that does not add a field. If it needs to be a real second field, that is a schema decision, not a form one.
05
Same campaign, three sizes. Nothing gets smaller and nothing gets denser — the countdown, the second CTA, the disclaimer and the inline art come off in that order, and the heading steps the type ramp xxl → xl → lg. Modal does the same ladder at 720 / 560 / 400.
06
Layout is a placement choice: split holds the CTA in one place whatever the copy does, centred reads as an announcement, left aligns to the content margin. The fourth is the awkward one — rotation means several campaigns share the single bar, which the “one bar” rule cannot express as a single winner. It ships with a pause control, always.
07
v02 draws a file uploader, which is the right long-term control and the reason v1's note said the form “waits on” one. It is still worth keeping these: a preset is authored in one click, costs no asset pipeline, weighs nothing, and re-tints itself when the campaign's Accent 2 changes. An uploaded PNG does none of that. Both should exist.
08
Left is the row, with every field the editor holds — including the ones currently hidden, because switching format must not destroy typed work. Right is what the delivery query hands the renderer for this viewer: the hidden fields are gone, the slot class is resolved, and the countdown has become an absolute instant so the client cannot drift.
Authored · campaigns row
Delivered · getEligibleCampaigns
09
Banner is inline.
v1's open contract question. The format card settles it, and with it the whole arbitration model — three inline formats, each in its own named slot, so they never compete with each other, only with themselves.
Accent 1 is the CTA, and the surface got its own field.
v1 flagged that the anatomy said “Surface fill → Accent 1” while the drawing paired a pale surface with a saturated Accent 1. v02 adds Background colour to Artwork and prints “Accent 1 carries the CTA” under the swatch. Contradiction gone.
One overlay slot, three overlay formats.
Welcome mat, slide-in and modal all resolve to overlay. Under “at most one overlay” a slide-in nudging people at checkout silently suppresses the seasonal mat, or the reverse — and neither marketer can see why. Either overlay splits into interstitial and corner, or priority needs to be visible in the list the way section 03 shows it.
A spinwheel eyebrow cannot say “Black Friday”.
The disc holds two short tokens and every drawing of it reads 40% OFF. The field feeding it holds a campaign name. Section 04 shows the collision. Cheapest fix is a style-aware placeholder and help line; the honest fix is a second field.
Slot class and slot choice are different axes.
Sidebar card is slot-class inline, but it has exactly one place it can sit — the foot of the rail. So “inline” tells you how it is arbitrated, not that the marketer picks a slot. Banner and Inline card get a named slot; Sidebar card must not show that field, for the same reason Topbar does not show a position.
Behaviour is referenced but not drawn.
v02's Custom HTML help promises that “everything under Behaviour stays under system control”. There is no Behaviour group in any frame. Dismissible, trigger, frequency cap, rotation and priority are all specified in the Notes and all homeless. v2 proposes the group in section 01.
Two small slips in the mock, for whoever redraws it.
v02's Slot class field reads bar while its help text describes a slide-in — a slide-in is overlay. And the theme presets are named by colour (Blue, Fall) where v1 named them by occasion (Tangible, Black Friday). A marketer opening this in November is looking for Black Friday, not for Fall. Same swatches either way — this is only the label.
Comments #33 and #34 are still unanswered, and #34 is a design question worth answering properly: yes, v02 is the right shape, and the reason is exactly the one he gave — a bar and a mat are 1440 wide, so the preview has to own the window rather than sit in a panel. This page is built on his layout. #35 already got “nice!”.
One coordination note: the spinwheel he could not find is in /marketing-campaigns, mid-page, and the index's nearest-looking entry is /spinners, which is loading spinners. Not his fault. The catalogue index should name it.
Still not built
This is a scarecrow. There is no Convex schema, no getEligibleCampaigns, no CampaignRenderer and no CampaignSlot. The one hard dependency is the spinwheel: it needs to land in tangible-ui taking a campaign-set fill before any of this is implementable. The forty lines of repeating-conic-gradient on this page prove it is cheap, not that it is done.