Components
The cartisto-* custom elements — platform-rendered UI you place and style.
When the platform renders the UI itself, it ships a custom element. You place
the tag; the platform renders and binds it. Every platform element has one
canonical cartisto-* name.
Components render light DOM with familiar class names and layer their base CSS
in @layer cartisto, so your unlayered theme CSS always wins — you restyle
without !important.
Catalog
| Element | What it does |
|---|---|
<cartisto-add-to-cart product-id="…" [has-variants] [out-of-stock]> |
Wraps your own <button> via display:contents — you keep the look, it owns add-to-cart. |
<cartisto-wishlist product-id="…"> |
Same pattern for wishlist toggling; sets aria-pressed. |
<cartisto-bundles> |
Renders all offer types (Mix & Match, BOGO, free gift, …) from platform-seeded data. |
<cartisto-auth-modal> |
Auto-mounted login/register modal; opens on gated actions or ?auth=. |
<cartisto-cart-drawer> |
Auto-mounted slide-in cart; opens via [data-cartisto="cart-trigger"]. |
<cartisto-quantity min max> |
Quantity stepper; emits cartisto:quantity:change, exposes .value. |
<cartisto-toast> |
Toast host; cartisto.showNotification(...) routes through it. |
<cartisto-modal> |
Accessible modal primitive; cartisto.modal.confirm(...). |
Enhancing your own button
<cartisto-add-to-cart> and <cartisto-wishlist> don’t render a button — they
enhance yours, so your styling is untouched:
<cartisto-add-to-cart product-id="{{ product.id }}"
product-url="{{ '/products/' | append: product.slug | lang_path }}"
{% if product.hasVariants %}has-variants{% endif %}
{% if product.quantity <= 0 %}out-of-stock{% endif %}>
<button class="my-btn my-btn--primary">{{ 'product.addToCart' | t }}</button>
</cartisto-add-to-cart>
Add out-of-stock and it won’t add to cart; add has-variants (with a
product-url) and it sends the shopper to the product page to pick a variant
instead of adding directly.
Placing a platform-rendered widget
{# offers on the product page — data is seeded by the platform #}
<cartisto-bundles></cartisto-bundles>
{# a cart trigger anywhere in your header #}
<button data-cartisto="cart-trigger" aria-label="Cart">🛒</button>
The one theme-owned element
<product-card> is not prefixed and is theme-owned — the card is your
look, so you render its markup. It’s the deliberate exception to the “shared
component” rule.
Adding a component is a platform job The component catalog is first-party. A shared interactive widget belongs in the platform component library — never copied per theme. That’s what keeps a feature O(1) instead of O(themes).