Warm Editorial — sections
Three package cards plus a pill toggle for single session versus course pricing. The recommended card is distinguished with an inverted surface and a small marker, not with a saturated accent band.
Paste this into Claude, Cursor or Copilot — or point the agent at
https://ui-deploy.com/r/warm-editorial/pricing-cards/prompt.md
# Pricing Cards — Warm Editorial
Paste this whole file into Claude, Cursor or Copilot.
---
## What to build
Three package cards under an eyebrow and a section heading, with a **two-state pill
switch** above them that swaps every figure between single-visit and course pricing.
The switch is a pill containing two pills. The active one lifts onto `--bg2` with
the small shadow; the inactive one is transparent.
---
## Tokens
```css
:root{
--bg2:#F6F3ED; --bg3:#E4DED2; --border:#DAD3C6;
--text:#22201D; --text2:#5C574F; --text3:#8C857A;
--accent:#A38A6A; --accent-text:#7D6644;
--accent-s:rgba(163,138,106,.12); --accent-b:rgba(163,138,106,.30);
--invert-bg:#151210; --invert-text:#EDE7DD;
--invert-text2:#A79E92; --invert-text3:#8A8177; --invert-border2:#40382F;
--radius:28px; --radius-pill:999px;
--shadow-sm:0 1px 2px rgba(34,32,29,.05);
}
[data-theme="dark"]{
--bg2:#1E1A17; --bg3:#262119; --border:#2E2822;
--text:#EDE7DD; --text2:#A79E92; --text3:#8A8177;
--accent:#B39B7C; --accent-text:#C0A888;
--accent-s:rgba(179,155,124,.13); --accent-b:rgba(179,155,124,.28);
/* In dark mode the featured card LIFTS, it does not flip to cream. */
--invert-bg:#1E1A17; --invert-text:#EDE7DD;
--invert-text2:#A79E92; --invert-text3:#8A8177; --invert-border2:#4E4438;
--shadow-sm:0 1px 2px rgba(0,0,0,.4);
}
```
---
## Rules — these are not preferences
1. **The featured card is marked by weight, not by colour.** It takes the inverted
surface plus a small bronze marker pill. A saturated accent band across the top
of a card is the wrong language — this palette never shouts.
2. **In dark mode the inverted card does not become cream.** It lifts one step to
`#1E1A17`. A cream card in the middle of a dark page destroys reading. This is
the same rule that governs `.chapter-invert` for sections.
3. **Prices are display type**: `Inter Tight`, weight 600, tracking `-.03em`,
`clamp(34px, 4vw, 48px)`.
4. **Swap figures by data attribute**, not by rebuilding the card. Each figure
carries `data-single` and `data-course`; the toggle rewrites `textContent`.
5. **Give the switch `role="radiogroup"` and each option `role="radio"` with
`aria-checked`.** Two visually-styled `<div>`s are not a control.
6. **The bullet marker is a 6px bronze dot**, positioned at `top:.62em` so it
optically centres on the first line. No checkmark icons, no emoji.
7. Buttons are pills, 48px, `margin-top:auto` so uneven cards align their footers.
8. Corners 28px. Cards drop to one column below ~900px via `auto-fit`.
---
## Adapting it
- **Monthly / yearly instead of single / course** — rename the `data-*` keys; the
script reads `opt.dataset.mode` and looks up `el.dataset[mode]`, so nothing else
changes.
- **A third state** — the switch is a flex row of buttons; add one. Beyond three,
use a select instead; the pill stops reading as a switch.
- **Currency formatting** — do it server-side and write the finished string into
the data attributes. Do not format in the click handler.
---
## Reference implementation
See `code.html` — 14 lines of dependency-free JS.
Paste this whole file into Claude, Cursor or Copilot.
Three package cards under an eyebrow and a section heading, with a two-state pill switch above them that swaps every figure between single-visit and course pricing.
The switch is a pill containing two pills. The active one lifts onto --bg2 with the small shadow; the inactive one is transparent.
:root{
--bg2:#F6F3ED; --bg3:#E4DED2; --border:#DAD3C6;
--text:#22201D; --text2:#5C574F; --text3:#8C857A;
--accent:#A38A6A; --accent-text:#7D6644;
--accent-s:rgba(163,138,106,.12); --accent-b:rgba(163,138,106,.30);
--invert-bg:#151210; --invert-text:#EDE7DD;
--invert-text2:#A79E92; --invert-text3:#8A8177; --invert-border2:#40382F;
--radius:28px; --radius-pill:999px;
--shadow-sm:0 1px 2px rgba(34,32,29,.05);
}
[data-theme="dark"]{
--bg2:#1E1A17; --bg3:#262119; --border:#2E2822;
--text:#EDE7DD; --text2:#A79E92; --text3:#8A8177;
--accent:#B39B7C; --accent-text:#C0A888;
--accent-s:rgba(179,155,124,.13); --accent-b:rgba(179,155,124,.28);
/* In dark mode the featured card LIFTS, it does not flip to cream. */
--invert-bg:#1E1A17; --invert-text:#EDE7DD;
--invert-text2:#A79E92; --invert-text3:#8A8177; --invert-border2:#4E4438;
--shadow-sm:0 1px 2px rgba(0,0,0,.4);
}
#1E1A17. A cream card in the middle of a dark page destroys reading. This is the same rule that governs .chapter-invert for sections.Inter Tight, weight 600, tracking -.03em, clamp(34px, 4vw, 48px).data-single and data-course; the toggle rewrites textContent.role="radiogroup" and each option role="radio" with aria-checked.** Two visually-styled <div>s are not a control.top:.62em so it optically centres on the first line. No checkmark icons, no emoji.margin-top:auto so uneven cards align their footers.auto-fit.data-* keys; the script reads opt.dataset.mode and looks up el.dataset[mode], so nothing else changes.
use a select instead; the pill stops reading as a switch.
the data attributes. Do not format in the click handler.
See code.html — 14 lines of dependency-free JS.
<!-- Warm Editorial — Pricing cards with a two-state pill switch
Requires tokens.css. -->
<section class="we-price">
<div class="we-price-head">
<p class="we-price-eyebrow"><span aria-hidden="true"></span>Rates</p>
<h2 class="we-price-title">Pay per visit, or commit to a course.</h2>
<div class="we-price-switch" role="radiogroup" aria-label="Pricing mode">
<button class="we-price-opt is-active" type="button" role="radio" aria-checked="true" data-mode="single">Single</button>
<button class="we-price-opt" type="button" role="radio" aria-checked="false" data-mode="course">Course of six</button>
</div>
</div>
<div class="we-price-grid">
<article class="we-price-card">
<h3>Assessment</h3>
<p class="we-price-desc">Ninety minutes. Measurement, baseline, a written plan.</p>
<p class="we-price-fig"><span data-single="£90" data-course="£450">£90</span></p>
<p class="we-price-note" data-single="per visit" data-course="six visits">per visit</p>
<ul class="we-price-list">
<li>Full intake and baseline</li>
<li>Written protocol to take away</li>
<li>No package required</li>
</ul>
<a class="we-price-btn" href="#book">Book</a>
</article>
<article class="we-price-card is-featured">
<p class="we-price-mark">Most taken</p>
<h3>Recovery</h3>
<p class="we-price-desc">Contrast therapy, breath work and a guided downshift.</p>
<p class="we-price-fig"><span data-single="£140" data-course="£700">£140</span></p>
<p class="we-price-note" data-single="per visit" data-course="six visits">per visit</p>
<ul class="we-price-list">
<li>Everything in Assessment</li>
<li>Contrast and breath protocol</li>
<li>Progress measured each visit</li>
</ul>
<a class="we-price-btn we-price-btn-invert" href="#book">Book</a>
</article>
<article class="we-price-card">
<h3>Sustained</h3>
<p class="we-price-desc">For people training hard or recovering from load.</p>
<p class="we-price-fig"><span data-single="£190" data-course="£950">£190</span></p>
<p class="we-price-note" data-single="per visit" data-course="six visits">per visit</p>
<ul class="we-price-list">
<li>Everything in Recovery</li>
<li>Extended session, two hours</li>
<li>Between-visit check-ins</li>
</ul>
<a class="we-price-btn" href="#book">Book</a>
</article>
</div>
</section>
<style>
.we-price{max-width:76rem;margin:0 auto;padding:0 20px}
.we-price-head{margin-bottom:40px}
.we-price-eyebrow{
display:flex;align-items:center;gap:7px;margin:0 0 16px;
font-family:var(--font-display);font-size:11.5px;font-weight:600;
letter-spacing:.16em;text-transform:uppercase;color:var(--accent-text);
}
.we-price-eyebrow span{width:5px;height:5px;border-radius:50%;background:var(--accent);flex:0 0 5px}
.we-price-title{
font-family:var(--font-display);font-weight:600;
font-size:clamp(28px,4.4vw,56px);line-height:1.03;letter-spacing:-.03em;
color:var(--text);margin:0 0 28px;max-width:20ch;
}
/* Two-state switch, itself a pill holding two pills. */
.we-price-switch{
display:inline-flex;gap:4px;padding:4px;
background:var(--bg3);border:1px solid var(--border);
border-radius:var(--radius-pill);
}
.we-price-opt{
padding:10px 20px;border:0;border-radius:var(--radius-pill);
background:transparent;color:var(--text2);
font-family:var(--font-body);font-size:13.5px;font-weight:500;cursor:pointer;
transition:background .2s var(--ease), color .2s var(--ease);
}
.we-price-opt.is-active{background:var(--bg2);color:var(--text);box-shadow:var(--shadow-sm)}
.we-price-opt:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.we-price-grid{
display:grid;gap:20px;
grid-template-columns:repeat(auto-fit,minmax(min(100%,270px),1fr));
}
.we-price-card{
display:flex;flex-direction:column;
background:var(--bg2);border:1px solid var(--border);
border-radius:var(--radius);padding:32px 28px;
}
/* The featured card is marked by weight, not by a saturated band. */
.we-price-card.is-featured{
background:var(--invert-bg);border-color:var(--invert-border2);
color:var(--invert-text);
}
.we-price-card.is-featured h3,
.we-price-card.is-featured .we-price-fig{color:var(--invert-text)}
.we-price-card.is-featured .we-price-desc,
.we-price-card.is-featured .we-price-list li{color:var(--invert-text2)}
.we-price-card.is-featured .we-price-note{color:var(--invert-text3)}
.we-price-card.is-featured .we-price-list li::before{background:var(--accent)}
.we-price-mark{
display:inline-flex;align-self:flex-start;margin:0 0 14px;padding:5px 12px;
border-radius:var(--radius-pill);background:var(--accent-s);
border:1px solid var(--accent-b);
font-family:var(--font-display);font-size:10.5px;font-weight:600;
letter-spacing:.16em;text-transform:uppercase;color:var(--accent-text);
}
.we-price-card h3{
font-family:var(--font-display);font-size:22px;font-weight:600;
letter-spacing:-.02em;color:var(--text);margin:0 0 8px;
}
.we-price-desc{margin:0 0 22px;font-size:14.5px;line-height:1.65;color:var(--text2)}
.we-price-fig{
font-family:var(--font-display);font-size:clamp(34px,4vw,48px);font-weight:600;
letter-spacing:-.03em;line-height:1;color:var(--text);margin:0;
}
.we-price-note{margin:8px 0 24px;font-size:12.5px;font-weight:500;color:var(--text3)}
.we-price-list{list-style:none;margin:0 0 28px;padding:0;display:grid;gap:11px}
.we-price-list li{
position:relative;padding-left:20px;font-size:14px;line-height:1.6;color:var(--text2);
}
.we-price-list li::before{
content:'';position:absolute;left:0;top:.62em;
width:6px;height:6px;border-radius:50%;background:var(--accent);
}
.we-price-btn{
display:inline-flex;align-items:center;justify-content:center;
height:48px;margin-top:auto;border-radius:var(--radius-pill);
background:var(--text);color:var(--bg);
font-family:var(--font-body);font-size:14px;font-weight:500;text-decoration:none;
transition:opacity .2s var(--ease);
}
.we-price-btn:hover{opacity:.85}
.we-price-btn-invert{background:var(--invert-text);color:var(--invert-bg)}
.we-price-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
@media (prefers-reduced-motion:reduce){.we-price-opt,.we-price-btn{transition:none}}
</style>
<script>
(function () {
var root = document.querySelector('.we-price');
if (!root) return;
root.querySelectorAll('.we-price-opt').forEach(function (opt) {
opt.addEventListener('click', function () {
var mode = opt.dataset.mode;
root.querySelectorAll('.we-price-opt').forEach(function (o) {
var on = o === opt;
o.classList.toggle('is-active', on);
o.setAttribute('aria-checked', String(on));
});
root.querySelectorAll('[data-' + mode + ']').forEach(function (el) {
el.textContent = el.dataset[mode];
});
});
});
})();
</script>
Needs tokens.css on the page.