design scarecrow · §11 pricing plugin
How marketing sites render live offers from tangible.one — and how much a marketer can customise at each step. Data below is real: SearchSync's two offers, pulled the way the plugin will pull them (cron → cached options → server render). Prices re-validate at checkout, so everything on this page is display-only trust.
Four tiers. Each tier is a superset of the previous, and Julia never edits PHP.
The answer to "spitting JSON or a hidden DOM element feels wrong": correct — the
plugin exposes offers as a server-side data source (shortcode presets and
Loops & Logic tags). The only DOM contract is one
data-tgbl-price attribute for PPP hydration.
Tier 0 · drop-in
[tangible_pricing product="searchsync"]
Default layout, all public offers, done. A product page is one shortcode.
Tier 1 · presets
[tangible_pricing product="searchsync" layout="cards|rows|table|strip|featured|slider" theme="inherit|tui|dark|tangible-one" offers="starter,creator" highlight="creator"]
Pick layout, theme, which offers (and their order), which one gets the halo.
Tier 2 · copy overrides
[tangible_pricing product="searchsync" layout="cards"]
[offer id="starter" label="Solo"
blurb="For one production site" cta="Start searching"]
[offer id="creator" badge="Best value"]
[/tangible_pricing]
Text, order, badges — everything editorial — without touching structure. Prices stay live.
Tier 3 · full custom (L&L)
[tangible_pricing product="searchsync" template="searchsync-pricing-v2"]
Points at a template post (post type the plugin registers) written in Loops & Logic. Julia owns the entire DOM; the plugin owns the data. See the tag vocabulary below.
The blocks ship structural CSS only — every colour, radius and font is a
--tui-* custom property with a stock-TUI fallback.
A theme is nothing but a token override block: the same CSS
Julia's theming playground
emits from its "Copy CSS" button. So the default look is stock TUI, tangible.one is
one preset among several, and theme="inherit"
emits no tokens at all — on a TUI-built site the page's own theme cascades straight in
(TUI tokens sit at :where() zero specificity),
and on a classic WP site a ~10-line bridge maps vars the theme already exposes.
The two "site" entries below use real values sampled from the live sites.
What a theme IS
/* playground "Copy CSS" output = a shippable theme */
.tgbl-pricing {
--tui-theme-primary-base: oklch(0.52 0.19 268);
--tui-color-bg-surface: oklch(0.977 0.01 260);
--tui-radius-md: 8px;
…
}
Julia tunes knobs in the playground, copies the block, and that is the theme. No plugin release needed for a new look.
What inherit does on a classic WP site
/* the whole bridge, e.g. launchpad's theme.json vars */
.tgbl-pricing {
--tui-theme-primary-base: var(--wp--preset--color--primary-500);
--tui-color-fg: var(--wp--preset--color--gray-900);
--tgbl-font-body: var(--wp--preset--font-family--nunito-sans);
…
}
On a TUI-built site even this is unnecessary — the page's tokens are already in the cascade. Fonts inherit by default either way.
layout="cards"The default layout. The switcher above is Tier-1's theme= attribute — same DOM, different tokens.
For one production site.
$79 /year
For freelancers & agencies.
$149 /year
Also included in Creators' Club — every Tangible plugin, one licence.
/cdn-cgi/trace) →
JS rewrites only [data-tgbl-price] nodes; no-JS visitors see base prices; checkout re-validates.
layout="table"For feature-heavy products; rows come from offer metadata, not hand-written HTML.
| Starter | Creator Popular | |
|---|---|---|
| Price | $79/yr | $149/yr |
| Site activations | 1 | 5 |
| Algolia + Meilisearch | ✓ | ✓ |
| Updates | 1 year | 1 year |
| Support | Standard | Priority |
| Get Starter | Get Creator |
layout="strip"Compact, for docs pages and blog posts — the "by the way, this is buyable" module.
layout="featured" offers="creator"One offer, hero treatment — landing pages that sell exactly one thing.
layout="rows"Scales vertically, so it's the workhorse for 3–6 offers. SearchSync really has two — Freelance and Agency below are illustrative padding to show the shape.
layout="slider"Our tiers differ by seats, not features — so any number of tiers collapses into one question. This is also the honest cure for "too many offers" when the count comes from a seat ladder. (Agency/Studio illustrative.)
Not a carousel on desktop — ever. Purchase options hidden behind swipes
don't get compared and don't get bought. The plugin's policy: cards wrap on
desktop (fine to ~6); past 4 the editor nudges toward rows/table
or curating with offers=; and a seat ladder should be a
slider instead. On mobile, the same DOM becomes a scroll-snap row —
all offers reachable, zero JS — which is the only "carousel" we ship.
Desktop: 6 offers wrap
Mobile: same DOM, scroll-snap row (framed at phone width)
* illustrative tiers — SearchSync ships two offers; the padding exists to stress the layouts.
The plugin registers a loop type over its cached offer data. A pricing template is a normal L&L template post — Julia changes text, order, and content with the tools she already knows, and the render is server-side (full-page-cache safe, nothing hidden in the DOM).
<Loop type=tangible_offer product=searchsync>
<div class="my-price-card">
<h3><Field name /></h3>
<p class="price">
<span data-tgbl-price data-offer="{Field slug}" data-base="{Field amount}">
<Field price_formatted />
</span> / <Field interval />
</p>
<p><Field activation_limit /> site<If check="{Field activation_limit}" not_value=1>s</If></p>
<a href="{Field checkout_url}">Buy <Field name /></a>
</div>
</Loop>
| Field | Example (SearchSync Creator) | Notes |
|---|---|---|
| name | Searchsync Creator | display name from the offer |
| slug | searchsync-creator | stable handle for offers= / overrides |
| amount / price_formatted | 14900 / $149 | cents + pre-formatted |
| interval | year | billing interval |
| activation_limit | 5 | from the offer's policy |
| checkout_url | https://tangible.one/checkout?offerId=… | UTM/affiliate params forwarded automatically |
| visibility | public | hidden offers only render when asked for explicitly |
| included_in (loop) | creators-club | bundles whose grants cover this product — the upsell slot |
/api/sync/pricing into an option (ETag'd); pages render from the cache — never a live call in a request.price-updates:<product> (registry already live — this site is row #1); a contentless ping triggers a debounced re-pull + cache purge. Missed pings self-heal on the hourly cron..tgbl-pricing, every visual decision a var(--tui-*, stock-TUI-fallback). Zero brand paint of its own.--tui-typography-font-family: inherit — TUI ships no typeface and no display family (headings differentiate by weight/size). The blocks chain through that token, so stock rendering uses the host page's font. --tgbl-font-display is a plugin-level extension for brand themes only. (The TUI presets on this page pin system-ui to simulate a neutral host — this scarecrow page itself typesets in brand fonts.)tui (default), dark, tangible-one. New themes need no plugin release.theme="inherit" emits no tokens: TUI-built sites theme automatically (zero-specificity tokens cascade in); classic WP sites bridge with ~10 lines mapping vars the theme already exposes (--wp--preset--* on block themes; tangible-views' --primary/--body-font)./cdn-cgi/trace; JS rewrites [data-tgbl-price] only. No-JS = base prices. Checkout re-validates, so display trust is zero-stakes.rows/table or offers= curation; seat ladders become a slider. On mobile the same DOM is a CSS scroll-snap row (all offers reachable, no JS) — the only "carousel" shipped.data-tgbl-price/data-base attributes are the single, minimal client contract.