Nothing, to be safe. When a page has a paywall: header, KahunaCart switches a visitor who can't read it to its own kahunacart-paywall template, and for that visitor the page's content is already the teaser. A theme that has never heard of the paywall still can't send the rest of the article to the wrong person, in the page, a listing, a feed or a search snippet.
What a theme adds is looks: the teaser in your article layout, a panel that matches your cards, and the receipt line if you override the receipt. You need KahunaCart 1.3.8 or later.
1. Override the paywall template
Copy user/plugins/kahunacart/templates/kahunacart-paywall.html.twig to your theme's templates/ folder and lay it out like your article template. A theme whose default.html.twig puts the title in a header and the body in a prose column would end up with something like this:
{# @version 1.3.8 #}
{% extends 'partials/base.html.twig' %}
{% block content %}
{% set pw = kahunacart_paywall(page) %}
<article class="post">
<header class="post-header">
<h1>{{ page.title }}</h1>
</header>
{% if pw.teaser %}
<div class="post-body">{{ pw.teaser|raw }}</div>
{% endif %}
<div class="kahunacart">
{% include 'partials/kahunacart-paywall-panel.html.twig' with {pw: pw} only %}
</div>
</article>
{% endblock %}
Three things matter in it:
- Never print
page.contenthere. Printpw.teaser. It is the part above the page's===line (or the first two blocks when there isn't one), already rendered as HTML, which is why it goes out with|raw. - Keep the
kahunacart_paywall(page)call and the panel include. The panel is where the Subscribe buttons and the sign-in link come from. - Keep the
@versionline. When a later KahunaCart changes the shipped template, KahunaCart → Operations → Health andbin/plugin kahunacart templates:drifttell you your copy is behind.
The kahunacart class around the panel gives its Subscribe buttons the store's button style, which KahunaCart's stylesheet only applies inside .kahunacart. Put it around the panel only, as above, so the plugin's styles never reach your article text. Leave it off if your theme styles .button itself.
2. Match the panel to your theme
KahunaCart adds its stylesheet to paywalled pages even outside the shop (unless builtin_css is off), so the panel already follows your theme's colours through the storefront's custom properties. To tune it, set those on .kahunacart:
.kahunacart {
--kahunacart-accent: #0e7490;
--kahunacart-accent-strong: #0e7490;
--kahunacart-card: #fff;
--kahunacart-border: #e5e7eb;
--kahunacart-radius-lg: 14px;
}
For more than colours, target the panel's own classes. The plugin styles each with a single class, so put your article's class in front (.post .kahunacart-paywall-panel) and your rule wins whichever stylesheet loads first:
| Class | What it is |
|---|---|
.kahunacart-paywall-panel |
The whole panel |
.kahunacart-paywall-heading, .kahunacart-paywall-copy |
"Subscribe to keep reading" and the line under it |
.kahunacart-paywall-offers, .kahunacart-paywall-offer |
The list of plans and each row in it |
.kahunacart-paywall-offer-label, .kahunacart-paywall-offer-price |
A plan's name and price |
.kahunacart-paywall-subscribe |
Each Subscribe button |
.kahunacart-paywall-signin |
"Already a subscriber? Sign in" |
.kahunacart-paywall-meter |
The free-article bar (see step 4) |
.kahunacart-continue-reading |
The receipt line (see step 5) |
To change the panel's markup rather than its look, copy templates/partials/kahunacart-paywall-panel.html.twig to the same place in your theme. Everything it prints comes from pw, and offer.price_html is already escaped, so it keeps |raw.
3. Check your article CSS for the reader's wrapper
A subscriber gets the page's normal template, so nothing changes for them, with one detail. Everything after the teaser comes inside <div class="kahunacart-paywalled">, which is what the page's structured data points search engines at. That extra level can break CSS written for direct children of your prose column. Look for rules like these in your theme:
.post-body > :last-child { margin-bottom: 0; }
.post-body > pre, .post-body > table { margin-inline: -2rem; }
and give them the wrapper too:
.post-body > .kahunacart-paywalled > :last-child { margin-bottom: 0; }
.post-body > .kahunacart-paywalled > :is(pre, table) { margin-inline: -2rem; }
4. The free-article bar
If the store has turned on free articles each month, a visitor on a free read gets the whole article with a slim bar above it: "2 free articles left this month. Subscribe". KahunaCart puts it at the top of the page's content for you, so a theme that prints page.content needs nothing. To change how it looks, style .kahunacart-paywall-meter or copy templates/partials/kahunacart-paywall-meter.html.twig to your theme.
5. The receipt line
After someone subscribes from an article, their receipt says Continue reading: the article's title once the order is paid. If your theme overrides kahunacart-order.html.twig, add this line where you want it, usually just above the order details:
{% include 'partials/kahunacart-continue-reading.html.twig' ignore missing with {paywall_continue: paywall_continue|default(null)} only %}
It draws nothing for everyone else, and ignore missing keeps the receipt working on a KahunaCart older than 1.3.8. Update your override's @version to 1.3.8 while you're there.
6. Listings and blog pages
A listing that prints each child's child.content or child.summary is already safe: for a visitor who can't read a paywalled child, its content is the teaser. If you want to mark those articles, ask kahunacart_paywall(child). It never counts a free read and never returns the body:
{% for child in page.collection %}
{% set pw = kahunacart_paywall(child) %}
<article class="post-card">
<h2><a href="{{ child.url }}">{{ child.title }}</a></h2>
{% if pw.paywalled %}<span class="badge">Subscribers</span>{% endif %}
{{ pw.paywalled ? pw.teaser|raw : child.summary|raw }}
</article>
{% endfor %}
pw.locked is true for a visitor who gets the teaser, pw.reader for one who can read the whole article, and pw.metered for one on a free read. The full list of keys is in Storefront → Twig functions.
Check it
- Open a paywalled article in a private window. You should see the title, the teaser and the panel. View the page source and search for a sentence from below the
===line: it shouldn't be there. - Sign in as a customer who isn't in the group and check the same thing, then as a subscriber, who should see the whole article.
- Check the article in light and dark and at phone width.
curl -skI https://yoursite.test/blog/your-articleshould showCache-Control: private, no-cacheon the paywalled article and not on an ordinary page.- If you use the feed plugin, update it to 1.10.4 or later so feeds print the teaser too. For YetiSearch Pro, 2.1.2 or later keeps a page saved in the admin out of search snippets.
More detail
- Articles with a teaser in the docs, for marking pages, the subscribe panel and free articles
- Theming for the storefront's custom properties