Skip to main content

Storefront CSS Customization Guide

How to create custom CSS for a new Shopify storefront integration. This guide is for agents and developers onboarding a new merchant onto the Marqo search widget.

Prerequisites

  • The merchant's Shopify store URL and admin access token (for GraphQL theme inspection)
  • The storefront settings API key (Authorization: Bearer <key>)
  • The storefront admin API base URL (e.g., https://admin.ecom.marqo.ai/api/v1/storefront)
  • A working Marqo index with product data for the store

Architecture Overview

The storefront widget uses a three-layer CSS architecture (see shopify-styling-architecture.md for full details):

Layer 1: CSS Variables (auto-generated from admin settings, sets --marqo-* tokens)
Layer 2: Template CSS (structural layout, stored in uiComponents[key].css)
Layer 3: Custom CSS (merchant overrides, stored in custom_css.value.css)

All per-merchant styling goes in Layer 3 (custom_css.value.css). Never modify Layer 2 templates for a single merchant — those are shared defaults.

Process

1. Backup Current Settings

Before any changes, fetch and save the current settings with a timestamp:

curl -s -H "Authorization: Bearer <API_KEY>" \
"https://admin.ecom.marqo.ai/api/v1/storefront/shops/<shop>.myshopify.com/settings" \
| python3 -m json.tool > tmp/<shop>_settings_<YYYY-MM-DD>T<HHMM>.json

Keep backups in the repo's tmp/ directory with the naming convention: <shop>_settings_<ISO-timestamp>.json

Always back up before AND after making changes. This gives you a rollback point.

2. Inspect the Native Theme

Open the merchant's live store in a browser. If the Marqo theme is already active (via preview or as the published theme), you need to view the non-Marqo theme to see native styling. Use preview_theme_id to switch:

# View the published (non-Marqo) theme:
https://<store>.com/?_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>

# View the Marqo (unpublished) theme:
https://<store>.com/?preview_theme_id=<marqo_theme_id>

Important: Shopify preview sessions are sticky via server-side HttpOnly cookies. Clearing JS-accessible cookies or localStorage does NOT reset the preview. You must explicitly navigate with a preview_theme_id parameter to switch. Even closing and reopening the browser may not reset it -- always use the explicit parameter.

To find theme IDs, check window.Shopify.theme in the browser console, or query the Shopify Admin API.

Identify the native styling for each component the Marqo widget replaces:

ComponentWhat to inspect
Product cardsFont family, size, weight, color for title, vendor, price. Card border, radius, shadow, padding. Image aspect ratio. Hover effects.
Filters sidebarHeader font, section title font, checkbox style, option font, separator color, mobile drawer behavior.
PaginationButton style (bordered vs. text-only), active state, disabled state, font.
Sort dropdownBorder, radius, font, arrow icon style.
Sale badgesPosition, color, font, radius.
CTA buttonText, font, colors, radius, hover effect, display mode (always/hover).
Page headingFont family, size, weight, alignment, color.
Price displayRegular vs. sale price colors, compare-at style, font size, order (price above or below title).

Tools for inspection:

  • Browser DevTools (Elements panel) on the native theme
  • Playwright MCP for automated inspection via browser_evaluate
  • Shopify GraphQL Admin API to query theme assets for CSS files

Shopify GraphQL theme query (useful for finding CSS variables and font declarations):

{
themes(first: 10) {
edges {
node {
id
name
role
files(first: 50, filenames: ["config/settings_data.json", "assets/theme.css"]) {
edges {
node {
filename
body
}
}
}
}
}
}
}

3. Map Native Filters to Marqo

The native theme's filters (e.g., Brand, Color, Size, Designer) need to be replicated in the Marqo widget's filter_config.value.items. This requires:

  1. Inspect native filter options — Visit the published theme's collection page and note every filter facet (name, type, values). These are often powered by Algolia, Shopify native, or another search provider.

  2. Inspect the Marqo index schema — Query the index to see available document fields:

    curl -X POST \
    -H 'Content-Type: application/json' \
    -H 'x-marqo-index-id: <index-id>' \
    -d '{"q":"test","limit":1}' \
    'https://ecom.marqo-ep.ai/api/v1/indexes/<shop>/search'

    Examine the returned document to see which fields are available for filtering.

  3. Map each native filter to a Marqo field — Common mappings:

    Native FilterMarqo FieldFilter TypeNotes
    Product TypeproductTypecategorical
    Brand / VendorproductVendorcategorical
    PricepriceMinrange
    TagsproductTagscategoricalarrayField: true
    CollectionsproductCollectionscategoricalarrayField: true
    ColorproductTags or metafieldcategoricalOften stored as tags with prefix (e.g., Color_Red)
    SizeproductTags or metafieldcategoricalOften stored as tags with prefix
    DesignerproductTags or metafieldcategoricalMay be a named tag facet
  4. Construct filter_config.value.items — Each filter becomes an item:

    {
    "id": "brand",
    "label": "Brand",
    "field": "productVendor",
    "type": "categorical",
    "enabled": true
    }

    For tag-based filters that use prefixed tags (e.g., Color_Red, Size_Large), the field is productTags with arrayField: true.

  5. Update settings — Modify filter_config.value.items in the settings JSON and push.

Reference docs:

  • Marqo Filter DSL: marqodocs/docs/other-resources/guides/filter-dsl.md
  • Facet management: marqodocs/docs/merchandising/navigation-facets/facet-management.md
  • Filter manager source: components/shopify/storefront_search/src/filter-manager.ts

4. Write Custom CSS

Structure the CSS in clearly labeled sections. Follow this format:

/* =============================================
<SHOP NAME> -- <COMPONENT> OVERRIDES
Native reference: <theme name> theme
============================================= */

/* <element description>
Native: <what the native theme uses> */
.marqo-<element> {
/* override properties */
}

Section order (for consistency across integrations):

  1. Filter overrides
  2. Pagination overrides
  3. Sort dropdown overrides
  4. Product card overrides
  5. Collection page overrides
  6. CTA / Quick shop overrides
  7. Mobile / responsive overrides
  8. Miscellaneous (wishlist, reviews, etc.)

Key principles:

  • Use !important on overrides since Layer 2 template CSS also uses !important for structural properties
  • Reference theme CSS variables where possible (e.g., var(--font-size-body)) so the styling adapts if the merchant changes their theme settings
  • Include a comment explaining what the native styling is for each block — this makes future maintenance possible
  • Hide elements the native theme doesn't show (e.g., .marqo-vendor { display: none !important; })
  • Test on mobile as well as desktop — use @media queries matching the theme's breakpoints

4. Push Settings

Update the custom CSS via the storefront API:

curl -X PUT \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
"https://admin.ecom.marqo.ai/api/v1/storefront/shops/<shop>.myshopify.com/settings" \
-d @updated_settings.json

Important: The API expects the full settings object via POST (not PUT). Do not send a partial update — fetch the current settings, modify uiComponents.custom_css.value.css, and POST the full object back.

Settings helper script (scripts/ecom/storefront_settings.py):

# Backup
python scripts/ecom/storefront_settings.py backup <shop>.myshopify.com --api-key "<key>"

# Inject CSS into a settings backup to create a candidate
python scripts/ecom/storefront_settings.py inject-css tmp/backup.json tmp/custom.css

# Push candidate
python scripts/ecom/storefront_settings.py push <shop>.myshopify.com tmp/backup_candidate.json --api-key "<key>"

# Diff before/after
python scripts/ecom/storefront_settings.py diff tmp/before.json tmp/after.json

# Extract just the CSS from a settings file
python scripts/ecom/storefront_settings.py extract-css tmp/settings.json > tmp/custom.css

5. Fan Out CSS Work to Parallel Agents

For efficiency, split the CSS work into 4 parallel agents. Each agent independently:

  1. Reads the integration doc for the design reference
  2. Screenshots the published theme (native styling target)
  3. Screenshots the Marqo theme (current state to override)
  4. Reads the Muji CSS (tmp/muji_ca_settings_2026-05-29T1527.json) as a format reference for their component
  5. Writes their CSS section

Agent scopes:

AgentScopeKey Marqo classes
Product Cards + GridCards, images, title, price, badges, CTA, carousel, grid.marqo-product-card, .marqo-results-grid, .marqo-price*, .marqo-cta-button
Filter SidebarFilter container, headers, options, checkboxes, counts, toggle, mobile drawer.marqo-filter-sidebar, .marqo-filter-title, .marqo-filter-option*, .marqo-filter-toggle
Pagination + Sort + CountPagination buttons, sort dropdown, results count, items-per-page.marqo-pagination*, .marqo-sort-*, .marqo-results-count
Page Heading + LayoutPage title, collection header, search layout, loading/error/no-results states.marqo-page-heading, .marqo-search-layout, .marqo-collection-*

Critical instruction for each agent: Do not blindly trust the design reference values. Take your own screenshots of both themes and visually inspect for styling details that may have been missed — border-radius, horizontal rules, hover states, focus states, transitions, shadows, opacity, background colors, spacing, etc.

See Agent Prompt Templates below for ready-to-use prompts.

6. Collate and Push

After all agents return their CSS:

  1. Collate all CSS blocks into a single file in this section order: Page Heading, Collection Page, Layout, Product Cards, Filter Sidebar, Pagination, Sort, Results Count, States
  2. Inject into the settings backup: python scripts/ecom/storefront_settings.py inject-css tmp/backup.json tmp/collated.css
  3. Push: python scripts/ecom/storefront_settings.py push <shop>.myshopify.com tmp/backup_candidate.json --api-key "<key>"

7. Verify in Browser

After pushing, verify the changes via Playwright or manual browser testing:

  1. Hard-refresh the storefront (Cmd+Shift+R)
  2. Check search page (/search?q=<term>)
  3. Check collection page (/collections/<handle>)
  4. Check mobile viewport (resize to 375px width)
  5. Check filter interactions (open/close, select, clear)
  6. Check pagination (navigate pages)
  7. Check sort (change sort order)

Back up the final settings after verification:

python scripts/ecom/storefront_settings.py backup <shop>.myshopify.com --api-key "<key>"

8. Document the Integration

Create a per-merchant integration doc in docs/integrations/<shop>.md following the template below.

Integration Doc Template

# <Merchant Name> Integration

## Store Details

| Field | Value |
|---|---|
| Store URL | `https://www.<domain>.com` |
| Shopify domain | `<shop>.myshopify.com` |
| Theme | <theme name> (ID: <theme_id>) |
| Marqo index | `<shop>.myshopify.com` |
| Settings backup | `tmp/<shop>_settings_<timestamp>.json` |

## Status

- [ ] Index created and populated
- [ ] App embed enabled
- [ ] App blocks inserted (search template, collection template)
- [ ] Product card CSS matched to theme
- [ ] Filter sidebar CSS matched to theme
- [ ] Pagination CSS matched to theme
- [ ] Sort dropdown CSS matched to theme
- [ ] Page heading / typography matched
- [ ] Sale badges styled
- [ ] CTA button configured
- [ ] Mobile responsive verified
- [ ] Collection page verified
- [ ] Search page verified
- [ ] Settings backed up (post-customization)

## Theme Analysis

<Native theme fonts, colors, spacing, and key CSS classes>

## Custom CSS Sections

<List of CSS sections applied and what they override>

## Notes

<Any merchant-specific quirks, known issues, or special requirements>

Common Patterns

Hiding elements the native theme doesn't show

.marqo-vendor,
.marqo-variant-title,
.marqo-collections {
display: none !important;
}

Reordering price above title

.marqo-product-price {
order: -1 !important;
}

Matching a theme's sticky navbar offset for mobile filter drawer

@media (max-width: 768px) {
#marqo-filter-sidebar.marqo-filter-open {
top: <navbar-height>px !important;
height: calc(100vh - <navbar-height>px) !important;
}
}

Overriding filter accordion icons

.marqo-filter-title::after {
content: "" !important;
background-image: url("data:image/svg+xml,...") !important;
/* ... sizing and positioning */
}

Agent Prompt Templates

Use these templates when spawning parallel CSS agents. Replace <placeholders> with the actual values from the integration doc.

Template: Product Cards + Grid Agent

You are writing custom CSS for <MERCHANT> Shopify storefront to make the Marqo search widget match their native theme. You are responsible for **Product Cards + Grid** styling only.

The CSS goes into `custom_css.value.css` (Layer 3 overrides). Read docs/integrations/<shop>.md for the design reference.

IMPORTANT: Before writing CSS, you MUST:
1. Navigate to the PUBLISHED theme: https://<store>?_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>
- Take screenshots of collection and search pages
- Visually inspect card borders, image styling, title font/weight, price colors, badges, hover effects, spacing, border-radius
2. Navigate to the MARQO theme: https://<store>?preview_theme_id=<marqo_theme_id>
- Take screenshots and note differences from native
3. Read Muji CSS for format reference: extract custom_css.value.css from tmp/muji_ca_settings_2026-05-29T1527.json (PRODUCT CARD section only)

Scope: .marqo-product-card, .marqo-results-grid, .marqo-product-card-image, .marqo-product-image, .marqo-product-card-content, h3, .marqo-price, .marqo-price-sale, .marqo-price-compare, .marqo-vendor, .marqo-sale-badge, .marqo-variant-title, .marqo-collections, .marqo-cta-button, .marqo-carousel-prev/next, .marqo-carousel-track

Format CSS with section headers like:
/* =============================================
<MERCHANT> — PRODUCT CARD OVERRIDES
Native reference: <theme name>
============================================= */

Return ONLY the CSS block.

Template: Filter Sidebar Agent

You are writing custom CSS for <MERCHANT> Shopify storefront to make the Marqo search widget match their native theme. You are responsible for **Filter Sidebar** styling only.

The CSS goes into `custom_css.value.css` (Layer 3 overrides). Read docs/integrations/<shop>.md for the design reference.

IMPORTANT: Before writing CSS, you MUST:
1. Navigate to the PUBLISHED theme: https://<store>?_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>
- Take screenshots focusing on the filter sidebar
- Inspect: section header font/color/weight, separator lines, checkbox styling, option labels, counts, toggle button, background colors, border-radius, accordion behavior
2. Navigate to the MARQO theme: https://<store>?preview_theme_id=<marqo_theme_id>
- Take screenshots and note filter differences from native
3. Read Muji CSS for format reference: extract FILTER section from tmp/muji_ca_settings_2026-05-29T1527.json

Scope: .marqo-filter-sidebar, .marqo-filter-header, .marqo-filter-title, .marqo-filter-section, .marqo-filter-option, .marqo-filter-option-checkbox, .marqo-filter-label, .marqo-filter-count, .marqo-filter-clear, .marqo-filter-toggle, .marqo-active-filter, .marqo-filter-close, .marqo-range-*, mobile @media rules

Return ONLY the CSS block.

Template: Pagination + Sort + Results Count Agent

You are writing custom CSS for <MERCHANT> Shopify storefront to make the Marqo search widget match their native theme. You are responsible for **Pagination, Sort Dropdown, and Results Count** styling only.

The CSS goes into `custom_css.value.css` (Layer 3 overrides). Read docs/integrations/<shop>.md for the design reference.

IMPORTANT: Before writing CSS, you MUST:
1. Navigate to the PUBLISHED theme: https://<store>?_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>
- Take FULL PAGE screenshot to see pagination at the bottom
- Take viewport screenshot of results header (sort, count)
- Inspect: pagination button shapes/colors/borders, active page style, disabled state, sort dropdown border/font, results count format/color
2. Navigate to the MARQO theme: https://<store>?preview_theme_id=<marqo_theme_id>
- Take screenshots and note pagination/sort differences
3. Read Muji CSS for format reference: extract PAGINATION and SORT sections from tmp/muji_ca_settings_2026-05-29T1527.json

Scope: .marqo-pagination, .marqo-pagination-button, .marqo-pagination-ellipsis, .marqo-sort-dropdown, .marqo-sort-select, .marqo-select, .marqo-results-count, .marqo-items-per-page-dropdown

Return ONLY the CSS block.

Template: Page Heading + Layout + Misc Agent

You are writing custom CSS for <MERCHANT> Shopify storefront to make the Marqo search widget match their native theme. You are responsible for **Page Headings, Collection Page, and Miscellaneous Layout** styling only.

The CSS goes into `custom_css.value.css` (Layer 3 overrides). Read docs/integrations/<shop>.md for the design reference.

IMPORTANT: Before writing CSS, you MUST:
1. Navigate to the PUBLISHED theme:
- Collection page: https://<store>/collections/<handle>?_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>
- Search page: https://<store>/search?q=<term>&_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>
- No-results page: https://<store>/search?q=xyzzzz123&_ab=0&_fd=0&_sc=1&preview_theme_id=<published_theme_id>
- Take screenshots of ALL three. Inspect: heading font/size/weight/alignment, results header layout, spacing, breadcrumbs, collection description
2. Navigate to the MARQO theme for the same pages
- Take screenshots and note heading/layout differences
3. Read Muji CSS for format reference: extract COLLECTION PAGE section from tmp/muji_ca_settings_2026-05-29T1527.json

Scope: .marqo-page-heading, .marqo-search-layout, .marqo-results-header, .marqo-header-left, .marqo-collection-header, .marqo-collection-description*, .marqo-loading, .marqo-error, .marqo-no-results, .marqo-skeleton-pulse

Return ONLY the CSS block.

Reference