Data Attributes
Most of what a marketing site wants from Open Shop is small: hide the Login link when someone is signed in, print their first name, open the cart from a nav button. The data-os-* attributes do those without a line of JavaScript.
Add the attribute to your own HTML. The bundle reads it and acts, using the same JavaScript API underneath. Your site builder does nothing with these attributes beyond publishing them into the markup.
Example
Section titled “Example”<a href="/customer" data-os-show="guest">Login</a><a href="/customer" data-os-show="customer">Hi, <span data-os-text="customer.firstName"></span></a>
<span data-os-show="referral">Shopping with <span data-os-text="referral.firstName"></span></span><div data-os-hide="cart">Free shipping on your first order</div>
<a href="/assessment/" data-os-handoff>Take the Assessment</a><button data-os-cart-open>Cart (<span data-os-text="cart.itemCount">0</span>)</button><button data-os-add-to-cart="vip-perks">Subscribe</button>The attributes
Section titled “The attributes”| Attribute | Behaviour |
|---|---|
data-os-show="guest | customer | referral | cart" | Sets hidden on the element unless the state matches. cart means the cart holds at least one item. Comma-separated values are OR’d: data-os-show="customer,referral" shows for either. |
data-os-hide="guest | customer | referral | cart" | The mirror of data-os-show: sets hidden when a listed state does match. Same four values, also OR’d. |
data-os-text="<key>" | Writes the value into the element’s textContent. Keys come from the whitelist below; anything else leaves the element untouched. |
data-os-handoff | On click, mints a handoff token and sends the shopper to the anchor’s href with ?token= appended. |
data-os-cart-open | On click, opens the cart slideout. |
data-os-cart-close | On click, closes the cart slideout. |
data-os-add-to-cart="<slug>" | On click, adds that product to the cart. Errors surface through the bundle’s own toasts. |
data-os-quantity="<number>" | Optional companion to data-os-add-to-cart. How many to add. |
data-os-auth="pending | guest | customer" | Set by the bundle on <html>, never by you. The CSS-only version of data-os-show. |
data-os-show
Section titled “data-os-show”The pattern is a pair of elements, not one element that flips. Build both states in your design, mark each with the state it belongs to, and let Open Shop hide the wrong one.
<a href="/customer" data-os-show="guest">Login</a><a href="/account" data-os-show="customer">My Account</a>| Value | Shown when |
|---|---|
guest | Login state resolved and nobody is signed in. |
customer | Login state resolved and someone is signed in. |
referral | An enroller is resolved for this visit. |
cart | The cart holds at least one item. |
Neither login variant shows while login state is still resolving, so the nav does not flash the wrong link. See data-os-auth for styling that in-between moment yourself.
One moment the bundle cannot cover is the one before it runs: the browser can paint your markup as written before a deferred script executes, so on a slow first visit both links would appear for an instant. One line of site CSS closes that gap by hiding every data-os-show element until the bundle’s first paint, which is also when <html> first gets its data-os-auth attribute:
html:not([data-os-auth]) [data-os-show] { display: none;}A value the bundle does not recognise leaves the element exactly as you authored it, so a typo like data-os-show="custmer" shows the element rather than hiding it. Mistakes fail visible, never blank.
data-os-hide
Section titled “data-os-hide”The mirror of data-os-show. It takes the same four values, comma-separated and OR’d, and hides the element when any listed state matches. When none match, the element shows.
<div data-os-hide="cart">Free shipping on your first order</div>| Value | Hidden when |
|---|---|
guest | Login state resolved and nobody is signed in. |
customer | Login state resolved and someone is signed in. |
referral | An enroller is resolved for this visit. |
cart | The cart holds at least one item. |
A value the bundle does not recognise leaves the element untouched, exactly as with data-os-show.
While login state is resolving
Section titled “While login state is resolving”guest and customer in a hide list match nothing until the customer resolves, so data-os-hide hides nothing on their account during that window. This is deliberately the opposite of data-os-show, which hides both variants while pending.
The practical consequence: a data-os-hide="customer" element starts visible and disappears once a signed-in shopper resolves. That is fine when a brief appearance is harmless, and wrong when it is not.
Which one to use
Section titled “Which one to use”Use data-os-show pairs for the login link. A hide-based nav would show the wrong link during the pending window — a signed-in shopper would see Login until their session resolved, which is the flash the pair pattern exists to prevent.
Use data-os-hide for content that should be visible by default and step aside once a state applies:
| Markup | What it does |
|---|---|
data-os-hide="cart" | Hides a promo banner once the cart has something in it. |
data-os-hide="referral" | Hides an enroller-finder block once an enroller is attached. |
data-os-hide="customer" | Hides a “create an account” nudge from signed-in shoppers. |
That last one is safe precisely because the pending window is harmless: a signed-in shopper may see the signup nudge for a moment before their login state resolves, which costs nothing. Apply the same test to your own case — if a brief appearance would be wrong, use a show pair instead.
Using both on one element
Section titled “Using both on one element”An element carrying both attributes is hidden if its show list fails or its hide list matches. Both tests must pass for it to show.
<div data-os-show="customer" data-os-hide="cart">Ready to reorder?</div>How hiding works
Section titled “How hiding works”Both data-os-show and data-os-hide hide an element the same way: the bundle sets the hidden attribute on it, and ships this rule inside its os-components cascade layer:
[data-os-show][hidden],[data-os-hide][hidden] { display: none !important;}That rule exists because hidden alone is fragile: any display value on the element overrides it, and site builders set one on almost every nav link. Webflow gives links display: flex or inline-block as a matter of course, which would otherwise un-hide them.
An !important declaration inside a cascade layer outranks everything in unlayered CSS — your normal rules and your !important ones, whatever their specificity. So the element stays hidden no matter what display your classes set. You do not need to do anything to make hiding work.
data-os-text
Section titled “data-os-text”Writes a single value into the element. The whole reachable surface is this whitelist — it is not a path evaluator, so customer.id or cart.items[0].name do nothing.
| Key | Value |
|---|---|
customer.firstName | Signed-in shopper’s given name. |
customer.lastName | Signed-in shopper’s family name. |
customer.accountTypeName | Their account type. |
customer.priceTypeName | Their price type. |
referral.firstName | Resolved enroller’s given name. |
referral.lastName | Resolved enroller’s family name. |
referral.handle | Resolved enroller’s referral handle. |
cart.itemCount | Number of items in the cart. |
cart.subtotal | Cart subtotal. |
store.name | The store’s name. |
Put a sensible fallback inside the element and it shows until the real value arrives:
<button data-os-cart-open>Cart (<span data-os-text="cart.itemCount">0</span>)</button>data-os-handoff
Section titled “data-os-handoff”Put it on an anchor that points at another app your store hands shoppers off to. On click, Open Shop mints a handoff token and navigates to the href with ?token= appended, so the shopper does not sign in again.
<a href="/assessment/" data-os-handoff>Take the Assessment</a>The attribute needs no value — its presence is the switch.
It falls through to the plain href — the ordinary link, no token — whenever a token is not the right answer:
- The shopper is a guest.
- Minting the token fails.
- The click is a modified click (⌘, Ctrl, Shift, Alt) or a middle click, so “open in a new tab” keeps working.
target and the two click paths
Section titled “target and the two click paths”A shopper the bundle already knows is a guest is not intercepted at all — the browser follows the link natively, so target="_blank", rel, and every other link behaviour work exactly as authored.
For a signed-in shopper, and for anyone whose login state has not resolved yet, the bundle intercepts the click, mints the token, and then navigates in the same tab. target="_blank" is not honoured on that path. Do not rely on a handoff link opening a new tab: a guest would get one and a signed-in shopper would not.
Cart attributes
Section titled “Cart attributes”<button data-os-cart-open>Open cart</button><button data-os-cart-close>Keep shopping</button><button data-os-add-to-cart="vip-perks" data-os-quantity="2">Subscribe</button>data-os-cart-open and data-os-cart-close need os-cart-slideout on the page to have something to open.
data-os-add-to-cart takes the product slug. A successful add opens the cart slideout, the same as the components’ own add-to-cart buttons. Failures — an ineligible product, a duplicate enrollment — are reported by the bundle’s own toast, again the same way the product components report them. If you need to handle the failure yourself, call OpenShop.cart.add() instead and catch the error.
data-os-auth
Section titled “data-os-auth”The bundle sets data-os-auth on the <html> element and keeps it current. You never write it; you style against it.
| Value | Meaning |
|---|---|
pending | Login state has not resolved yet. |
guest | Resolved: nobody is signed in. |
customer | Resolved: a shopper is signed in. |
html[data-os-auth='customer'] .nav-login { display: none;}This is the CSS-only twin of data-os-show, and it is the tool for the in-between moment. Write the rules as “hide until the state that shows this is known” rather than matching pending: before the bundle has run at all, <html> carries no attribute, and :not() covers that window too.
html:not([data-os-auth='guest']) .nav-login,html:not([data-os-auth='customer']) .nav-account { visibility: hidden;}In practice pending is brief, and on repeat page views it is usually skipped entirely — the last known state is restored from the session cache before the request comes back.
How bindings resolve
Section titled “How bindings resolve”- Clicks are delegated. One listener on
documenthandles every click attribute, so elements your own scripts insert later work with no extra setup. - Bindings are scanned at
ready, then again on everycustomer:change,cart:change, andreferral:change, and once more when the store overview resolves — that last pass is what fillsstore.name. Site-builder pages are static markup, so that covers them. If your code injectsdata-os-show,data-os-hide, ordata-os-textmarkup after those events, nothing rescans it — call the JavaScript API directly for that content instead. - Values are written with
textContent. NeverinnerHTML. - Keys come from a whitelist. Unknown keys are ignored and leave the element exactly as you authored it.
Adding an attribute in Webflow
Section titled “Adding an attribute in Webflow”Webflow calls these custom attributes. They live on the element, not in an embed, so you can add them to links and buttons you already designed.
1 In the Webflow Designer, select the element you want to control — a nav link, a button, a text span.
2 Open the Element settings panel on the right (the gear icon above the Style panel).
3 Scroll to Custom attributes and click the +.
4 Type the attribute in Name and its value in Value, then press the checkmark to save:
| Goal | Name | Value |
|---|---|---|
| Show this link only to guests | data-os-show | guest |
| Show this link only to signed-in shoppers | data-os-show | customer |
| Hide this banner once the cart has items | data-os-hide | cart |
| Print the shopper’s first name | data-os-text | customer.firstName |
| Open the cart from this button | data-os-cart-open | leave empty |
| Hand off a signed-in shopper | data-os-handoff | leave empty |
5 Repeat for each element, then Publish. Custom attributes only reach the page on a published site — the Designer canvas will not run them.
For a full walkthrough that puts this into a real nav, see Step 6 of the Webflow tutorial.