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:
| Component | What to inspect |
|---|---|
| Product cards | Font family, size, weight, color for title, vendor, price. Card border, radius, shadow, padding. Image aspect ratio. Hover effects. |
| Filters sidebar | Header font, section title font, checkbox style, option font, separator color, mobile drawer behavior. |
| Pagination | Button style (bordered vs. text-only), active state, disabled state, font. |
| Sort dropdown | Border, radius, font, arrow icon style. |
| Sale badges | Position, color, font, radius. |
| CTA button | Text, font, colors, radius, hover effect, display mode (always/hover). |
| Page heading | Font family, size, weight, alignment, color. |
| Price display | Regular 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:
-
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.
-
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.
-
Map each native filter to a Marqo field — Common mappings:
Native Filter Marqo Field Filter Type Notes Product Type productTypecategorical Brand / Vendor productVendorcategorical Price priceMinrange Tags productTagscategorical arrayField: trueCollections productCollectionscategorical arrayField: trueColor productTagsor metafieldcategorical Often stored as tags with prefix (e.g., Color_Red)Size productTagsor metafieldcategorical Often stored as tags with prefix Designer productTagsor metafieldcategorical May be a named tag facet -
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 isproductTagswitharrayField: true. -
Update settings — Modify
filter_config.value.itemsin 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):
- Filter overrides
- Pagination overrides
- Sort dropdown overrides
- Product card overrides
- Collection page overrides
- CTA / Quick shop overrides
- Mobile / responsive overrides
- Miscellaneous (wishlist, reviews, etc.)
Key principles:
- Use
!importanton overrides since Layer 2 template CSS also uses!importantfor 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
@mediaqueries 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:
- Reads the integration doc for the design reference
- Screenshots the published theme (native styling target)
- Screenshots the Marqo theme (current state to override)
- Reads the Muji CSS (
tmp/muji_ca_settings_2026-05-29T1527.json) as a format reference for their component - Writes their CSS section
Agent scopes:
| Agent | Scope | Key Marqo classes |
|---|---|---|
| Product Cards + Grid | Cards, images, title, price, badges, CTA, carousel, grid | .marqo-product-card, .marqo-results-grid, .marqo-price*, .marqo-cta-button |
| Filter Sidebar | Filter container, headers, options, checkboxes, counts, toggle, mobile drawer | .marqo-filter-sidebar, .marqo-filter-title, .marqo-filter-option*, .marqo-filter-toggle |
| Pagination + Sort + Count | Pagination buttons, sort dropdown, results count, items-per-page | .marqo-pagination*, .marqo-sort-*, .marqo-results-count |
| Page Heading + Layout | Page 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:
- 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
- Inject into the settings backup:
python scripts/ecom/storefront_settings.py inject-css tmp/backup.json tmp/collated.css - 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:
- Hard-refresh the storefront (Cmd+Shift+R)
- Check search page (
/search?q=<term>) - Check collection page (
/collections/<handle>) - Check mobile viewport (resize to 375px width)
- Check filter interactions (open/close, select, clear)
- Check pagination (navigate pages)
- 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
- Shopify Styling Architecture -- Three-layer CSS system
- Shopify Template Customization Guide -- CSS variables reference
- Shopify Diagnostics -- GraphQL queries, metafield debugging
- Ecommerce Diagnostics -- Jobs, logs, search proxy
- Settings helper:
scripts/ecom/storefront_settings.py-- Backup, push, diff, extract/inject CSS - Muji CA/US backups in
tmp/muji_ca_settings_*.json-- Reference implementation with extensive custom CSS - MSQC integration doc:
docs/integrations/msqc.md-- First integration using this workflow