"CSS: states, skeletons and variables"
What Smootify's stylesheet does, the state classes and page flags you can style, the skeleton placeholders and their variables, popover backdrops, upload classes and the CSS variables Smootify writes.
Smootify’s stylesheet is small and leaves your design alone. Most of what you style comes from Smootify in two forms: classes it adds to elements as their state changes, and CSS variables it writes or reads. This page lists what you can use, for combo classes in the Designer or for your own CSS.
The stylesheet
The loader adds the stylesheet when its script tag has data-css="true": see Install Smootify 2.0. It holds:
- the
conditionrules: every[condition="…"],[customer-condition="…"]or[cart-condition="…"]element is hidden withdisplay: none !importantwhile its state does not match. Without the stylesheet, these elements always show; - the skeleton placeholders for elements with a
skeletonattribute (below); - a few defaults: closed popovers are hidden, popovers and the cart’s dialog lose the browser’s padding and border (your classes still set theirs), and the cart’s dialog, the market suggestion and the
store-availabilitydialog get a backdrop.
Your own CSS goes in a <style> tag in Site settings, Custom code, or in a Code Embed. In the Designer, the same classes work as combo classes.
State classes
Smootify adds and removes classes as things change. The main ones:
- On
smootify-product:loaded,is-available/is-not-available,is-on-sale,is-free,is-new,is-variable,price-varies,has-subscription,not-existing-variant,is-currently-out-of-stock, and the stock levelsmany-in-stock,low-stock,low-not-last-stock,last-in-stock. - On the values of a picker:
is-activeon the selected value,is-disabledon a value that cannot be bought (it stays clickable). - On
smootify-cart:loaded,is-rendered,is-empty-cart,has-taxes,has-empty-quote. - On a product that Shopify does not return: Webflow’s
w-dyn-bind-empty, on the Collection item or onsmootify-product.
smootify-product.is-on-sale .product-price {
color: #b42318;
}
variant-swatches button.is-active {
outline: 2px solid currentColor;
}
variant-swatches button.is-disabled {
opacity: 0.4;
}Every element’s classes are in the Reference: state classes.
loaded
loaded goes on an element once Smootify has written it: products, prices, the cart, the account lists, sort and filter widgets, images and more. Use it for an entrance transition:
smootify-product .product-info {
opacity: 0;
transition: opacity 0.3s;
}
smootify-product.loaded .product-info {
opacity: 1;
}Page flags
Some states apply to the whole page, on html or body:
html[logged-in]andbody.is-user-logged-inwhile a customer is logged in;html[customer-status="loaded"]andbody.customer-status-loadedonce the session is known, logged in or not;body.has-customer-names,has-addresses/no-addresses,has-orders/no-orders,has-subscriptions/no-subscriptionsfor the account pages;body.has-wishlist,body.has-last-viewed,body.has-cart-upsellswhile those lists have products;has-related,has-complementary,has-best-sellerand the otherhas-<source>classes onhtmlandbody, while asmootify-productlist with thatdata-idhas products;body.cookie-banner-visiblewhile thesmootify-consentbanner shows.
html:not([logged-in]) .account-menu {
display: none;
}Skeletons
An element with a skeleton attribute is a placeholder until Smootify writes it. Smootify then removes the attribute, also when the value is missing and the element is hidden. Only elements with the attribute are placeholders.
skeleton="text","button"or"block": a pulsing background, transparent text and border, children invisible, not clickable;skeleton="image", on an Image: a pulsing blur;skeleton="hide": hidden (visibility: hidden), so it keeps its space.
Their colours and corner radius come from three CSS variables.
Customer values ([customer], [customer-condition], [company] and the customer metafields) pulse the same way until html[customer-status="loaded"], without a skeleton attribute. How designers add skeletons is in loading.
Popovers and backdrops
The mini cart’s dialog, the market suggestion and the store-availability dialog get a backdrop, coloured by two CSS variables. The market suggestion and the store-availability dialog also blur the page behind, through the class with-blur, unless the element has disable-blur. Other popovers, Webflow’s Popover element included, have no backdrop: set --smootify-backdrop-color on a panel’s class to give it one. The popovers inside that panel do not get it.
A closed popover is hidden with display: var(--smootify-closed-display, none) !important, so style its open state with :popover-open. To show a closed popover in the page at some breakpoints, set --smootify-closed-display on its class there. More in Popovers and Market suggestion.
CSS variables
Five variables change the look of skeletons and backdrops. Smootify writes the other four, for your CSS to read:
The first color of the pulse
- Default
hsl(200, 20%, 80%)
The second color of the pulse
- Default
hsl(200, 20%, 95%)
The corner radius of the placeholder
- Default
4px
The backdrop of a popover, set on the panel's class, and of the mini cart's dialog
- Default
- none on popovers;
rgba(0, 0, 0, 0.8)on the mini cart's dialog - Not passed
- to popovers inside the panel
A closed popover shown in the page, on the panel's class at the breakpoints you choose
- Default
none: a closed popover is hidden- Example
flexon desktop: a filter drawer becomes a column
The backdrop of the market suggestion and of the store-availability dialog
- Default
rgba(0, 0, 0, 0.1)
A rating's share of its scale
- Written on
- A
ratingorlist.ratingmetafield element. - Value
- A percentage, such as
90%.
The color of a color metafield
- Written on
- A color metafield element.
- Value
- The color.
How much of a file has uploaded
- Written on
- Each upload item and its progress element.
- Value
- From
0to1while the file uploads.
The width of the page's scrollbar
- Written on
html.- Value
- The width, measured at load.
A property="--my-name" attribute on a metafield element writes the rating or the colour into that variable instead. See Metafields on the page.
A fill inside a rating element with width: var(--metafield-rating) draws stars, and an upload’s progress element can scale with --upload-progress:
:root {
--skeleton-loading-start: #e4e4e7;
--skeleton-loading-end: #f4f4f5;
--skeleton-radius: 8px;
}
.filter-panel {
--smootify-backdrop-color: rgba(15, 23, 42, 0.6);
}
.stars-fill {
width: var(--metafield-rating);
}
[upload="progress"] {
transform: scaleX(var(--upload-progress, 0));
transform-origin: left;
}Upload classes
Upload fields (file-uploader, file-input, configurator-file-input, server plan) use your markup and add classes you style:
- on the element:
is-dragoverwhile a file is dragged over,is-uploading,has-files,is-fullwhen no more files fit,is-invalidwhen a file was refused; - on each item:
is-uploading,is-done,is-error,is-image.
The parts are in upload and File uploads.
Snippets
Short rules for things the Designer cannot do alone. Paste them in Site settings › Custom code › Head code, inside a <style> tag, and change the styles to fit your design.
A tooltip on swatch buttons. Buttons that show only a colour or an image can show the value’s name on hover: every variant-swatches button carries its value in data-value.
variant-swatches button { position: relative; }
variant-swatches button:hover::before {
content: attr(data-value);
position: absolute;
bottom: calc(100% + 0.4rem);
left: 50%;
transform: translateX(-50%);
z-index: 10;
padding: 0.2rem 0.5rem;
border-radius: 0.3rem;
background: black;
color: white;
font-size: 0.8rem;
white-space: nowrap;
}No shimmer on a cart line that changes. While its quantity changes or it is removed, a line gets the class loading and a pulsing layer over it. To keep the line still (it still ignores clicks until the change is done):
cart-item.loading::before,
cart-item.loading bundle-item > *::before { display: none !important; }A checked Radio button or Checkbox. Webflow styles the checked state of the input only. To style the whole Radio button or Checkbox, label included, when it is checked (.w--redirected-checked is the class Webflow gives a custom-styled input):
.w-radio:has(.w--redirected-checked), .w-radio:has(input:checked) { /* your styles */ }
.w-checkbox:has(.w--redirected-checked), .w-checkbox:has(input:checked) { /* your styles */ }The page while the Navbar menu is open. Webflow styles the open state of the menu only. To change the page, for example to stop it from scrolling:
body:has([data-nav-menu-open]) { overflow: hidden; }Where people get stuck
Check these first
- Elements with a condition always show. The stylesheet is not loaded: add
data-css="true"to the loader, or link the stylesheet. - Nothing pulses while the page loads. In 2.0 only elements with a
skeletonattribute are placeholders. Add it where you want one. - Upload styles stopped working. 1.x used FilePond and its
.filepond--*classes. 2.0 uses your markup: style the upload parts and classes above. - Styles for the cookie banner no longer apply. They target
is-shopify-cookie-banner-visible, the 1.x class. In 2.0 the body class iscookie-banner-visible.
Coming from 1.x
- No automatic skeleton. 1.x animated every text, image, button and link of an element that had not loaded yet. 2.0 animates only elements with a
skeletonattribute. Adding theloadedclass to switch the skeleton off is no longer needed. - Search, search and discovery, account lists and store availability no longer pulse on their own either: add
skeletonto the placeholders you want. - The state classes and the
conditionrules are unchanged.smootify-variantnow getsis-on-saleonly when the compare-at price is above the price, assmootify-productdoes. is-shopify-cookie-banner-visibleis nowcookie-banner-visible..filepond--*is gone with FilePond.