2.0 Beta

Smootify 2.0 is in public beta. Your 1.x store keeps working. Move when you want.

What changed

"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.

v2.0 beta
Open .md
On this page
  1. The stylesheet
  2. State classes
  3. loaded
  4. Page flags
  5. Skeletons
  6. Popovers and backdrops
  7. CSS variables
  8. Upload classes
  9. Snippets
  10. Where people get stuck

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 in your own CSS.

The stylesheet

The integration adds the stylesheet next to the Smootify script, unless runtimeCssUrl is false: see Install Smootify in Astro. It holds:

  • the condition rules: every [condition="…"], [customer-condition="…"] or [cart-condition="…"] element is hidden with display: none !important while its state does not match. Without the stylesheet, these elements always show;
  • the skeleton placeholders for elements with a skeleton attribute (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-availability dialog get a backdrop.

Your own CSS goes in the component’s scoped <style>, or in global.css. Smootify clones templates, and the clone keeps the component’s scope attribute, so scoped rules still match the state classes. Use :global() for HTML that Smootify writes from data, which has no scope attribute, and for classes it sets on a host:

<style>
  smootify-product.is-on-sale .product-price {
    color: #b42318;
  }
  :global(smootify-product.loaded) .product-info {
    opacity: 1;
  }
</style>

See Write components with Smootify elements.

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 levels many-in-stock, low-stock, low-not-last-stock, last-in-stock.
  • On the values of a picker: is-active on the selected value, is-disabled on 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: the hidden attribute, on the card (.sm-dyn-item) or on smootify-product. Smootify marks the hidden it sets and removes only that one.
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] and body.is-user-logged-in while a customer is logged in;
  • html[customer-status="loaded"] and body.customer-status-loaded once the session is known, logged in or not;
  • body.has-customer-names, has-addresses / no-addresses, has-orders / no-orders, has-subscriptions / no-subscriptions for the account pages;
  • body.has-wishlist, body.has-last-viewed, body.has-cart-upsells while those lists have products;
  • has-related, has-complementary, has-best-seller and the other has-<source> classes on html and body, while a smootify-product list with that data-id has products;
  • body.cookie-banner-visible while the smootify-consent banner 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 to 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 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:

--skeleton-loading-start

The first color of the pulse

Default
hsl(200, 20%, 80%)
--skeleton-loading-end

The second color of the pulse

Default
hsl(200, 20%, 95%)
--skeleton-radius

The corner radius of the placeholder

Default
4px
--smootify-backdrop-color

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
--smootify-closed-display

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
flex on desktop: a filter drawer becomes a column
--backdrop-color

The backdrop of the market suggestion and of the store-availability dialog

Default
rgba(0, 0, 0, 0.1)
--metafield-rating

A rating's share of its scale

Written on
A rating or list.rating metafield element.
Value
A percentage, such as 90%.
--metafield-color

The color of a color metafield

Written on
A color metafield element.
Value
The color.
--upload-progress

How much of a file has uploaded

Written on
Each upload item and its progress element.
Value
From 0 to 1 while the file uploads.
--scrollbar-width

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-dragover while a file is dragged over, is-uploading, has-files, is-full when no more files fit, is-invalid when 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 common needs. Put them in global.css 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 checkbox or radio button. To style the whole label, text included, when its input is checked:

label:has(input:checked) { /* your styles */ }

The page while a menu is open. A menu is a [popover] or a <dialog>. To change the page behind it, for example to stop it from scrolling:

body:has(:popover-open, dialog[open]) { overflow: hidden; }

Where people get stuck

Check these first

  • Elements with a condition always show. The stylesheet is not loaded: do not set runtimeCssUrl: false unless the site has those rules itself.
  • Nothing pulses while the page loads. In 2.0 only elements with a skeleton attribute are placeholders. Add it where you want one.

    ↑ ↓ to moveEnter to openEsc to close