Customization
Widget Styling
Customise the look and feel of the SearchX widget to match your brand identity.
Overview
SearchX is styled through a single mechanism: a CSS file loaded by the widget after its own default styles. You override the default look by:
- Setting theme tokens (CSS custom properties) to change colours, typography, radii, and shadows site-wide in one place.
- Targeting component class names to fine-tune individual surfaces (product cards, facet panels, the popup, etc.).
There's no theme picker, no JavaScript hook for visual customisation, no separate theme store — one CSS file is the entire surface area, and everything you can change is documented on this page.
What this means in practice
You don't need to learn a new template syntax, fork the widget, or write JavaScript to restyle it. Override a couple of CSS variables and you're 80% of the way to a custom theme that still gets every future widget update.
What you can customise
- Brand colour palette — primary scale, neutral scale, status colour. Cascades everywhere automatically.
- Typography — font family and base size.
- Shape language — border radii (small, medium, pill), border widths, shadows.
- Animation timing — global transition duration.
- Per-component appearance — every visible surface has a stable class name you can target with normal CSS rules. The selectors are listed in Component CSS classes below.
- Element visibility — hide any control with
display: none. - Grid layout — switch the products grid to a different column count or change the gap.
What you can't customise
- The HTML markup of the widget. You can't reshape the DOM, add new buttons inside cards, or move the price next to the title. If your design needs that level of restructuring, talk to us first — it usually means a setting needs to exist.
- Behavioural logic. CSS controls appearance. Behaviour (which facets are shown, what the popup contains, how pagination works, etc.) is configured through the SDK settings, not CSS.
- Translations. Text strings are configured through Localization, not CSS — don't try to swap labels with
content:overrides, they won't survive a re-render. !important-free overrides every time. Most overrides win on natural specificity, but a small number of layout rules ship with!importantto protect against aggressive host themes; you may need!importantto override those.
Loading a custom theme
Create a CSS file and pass its URL to the widget via the customCSSUrl option:
SearchXSDK.init({
// ... other config
customCSSUrl: '/path/to/your/client-theme.css',
});
The custom CSS file loads after the default styles, so your overrides win at the same specificity without !important in most cases.
Theme tokens
A SearchX theme is organised into two layers — colour tokens and non-colour tokens. You only need to override the ones you want to change; everything else falls back to the defaults.
1. Colour tokens
The foundational colours used across the widget. Each colour has a full scale from 50 (lightest) to 950 (darkest), so a single primary scale override re-skins every active state in the widget.
:root {
/* Neutral */
--searchx-white: #ffffff;
--searchx-black: #111111;
/* Primary scale — your brand colour */
--searchx-primary-50: #fff5f5;
--searchx-primary-100: #ffeaea;
--searchx-primary-200: #ffd6d6;
--searchx-primary-300: #ffb4b4;
--searchx-primary-400: #f28c8c;
--searchx-primary-500: #cc5b5b;
--searchx-primary-600: #a20808; /* Base accent — buttons, active states */
--searchx-primary-700: #7f0606;
--searchx-primary-800: #5f0404;
--searchx-primary-900: #3e0202;
--searchx-primary-950: #240101;
/* Gray scale */
--searchx-gray-50: #fafafa;
--searchx-gray-100: #f5f5f5;
--searchx-gray-200: #e5e5e5;
--searchx-gray-300: #d4d4d4;
--searchx-gray-400: #a3a3a3;
--searchx-gray-500: #737373;
--searchx-gray-600: #525252;
--searchx-gray-700: #404040;
--searchx-gray-800: #262626;
--searchx-gray-900: #171717;
--searchx-gray-950: #0a0a0a;
/* Status */
--searchx-danger-color: #dc2626;
}
2. Non-colour tokens
Typography, border radius, shadows, and transitions in one place.
:root {
/* Typography */
--sx-font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial;
--sx-font-size-base: 16px;
/* Border radius */
--sx-radius-sm: 6px;
--sx-radius-md: 12px;
--sx-radius-pill: 999px;
/* Borders */
--sx-border-width: 1px;
--sx-border-color: var(--searchx-gray-300);
/* Shadows */
--sx-shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.06);
--sx-shadow-md: 0 8px 24px rgba(0, 0, 0, 0.1);
/* Transitions */
--sx-transition: 200ms ease;
}
Component CSS classes
All SearchX components use BEM-style class names scoped under .searchx__container.searchx__theme (search bar + popup) or .searchx__page.searchx__theme (results page). Scope your overrides with one of these two prefixes so you don't accidentally restyle other parts of your site.
Search bar
| Selector | Description |
|---|---|
.searchx__form | Search bar wrapper (border, background, shadow) |
.searchx__input | Text input container |
.searchx__input input | The actual input element |
.searchx__input input::placeholder | Placeholder text |
.searchx__mobile-trigger | Mobile search toggle button |
.sx-settings__trigger | Settings gear icon (when shown) |
.searchx__form-close-icon | Clear / close button |
Autocomplete popup
| Selector | Description |
|---|---|
.searchx__popup | Popup container |
.searchx__popup-results-counter | Results count badge |
.searchx__popup-item | Individual product suggestion row |
.searchx__popup-item-image | Product thumbnail in the popup |
.searchx__popup-item-title | Product name in the popup |
.searchx__popup-item-price | Price in the popup |
.searchx__popup-item-icon | The quick Add to Cart icon next to each suggestion |
.searchx__popup-no-results-icon | Empty-state icon |
.searchx__popup-no-results-title | Empty-state message |
.searchx__showcase | Featured products grid |
.searchx__suggested-links | The "See also" promotional chips container |
.searchx__suggested-link | An individual suggested-link chip |
Results page
| Selector | Description |
|---|---|
.searchx__page-heading-title | Search query heading |
.searchx__page-heading-subtitle | Results count subtitle |
.searchx__page-sorting | Sort dropdown |
.searchx__page-results | Product grid container |
.searchx__page-items | Alternative product grid container |
Product cards
| Selector | Description |
|---|---|
.searchx__page-item | Card wrapper |
.searchx__page-item-image-wrapper | Image area container |
.searchx__page-item-image-container | Image container (for aspect ratio) |
.searchx__page-item-image | Product image |
.searchx__page-item-image--hover | Second image (hover reveal) |
.searchx__page-item-details | Link wrapper for image + info |
.searchx__page-item-info | Text info area below image |
.searchx__page-item-brand | Brand name |
.searchx__page-item-title | Product title |
.searchx__page-item-price | Current price |
.searchx__page-item-price--original | Original price (struck through on sale) |
.searchx__page-item-sizes | Available sizes |
.searchx__page-item-badge | Discount / sale badge |
.searchx__page-item-button | Add to Cart button |
.searchx__page-item-button.wishlist | Wishlist heart button |
.searchx__page-item-actions | Button actions container |
Filter sidebar (facets)
| Selector | Description |
|---|---|
.searchx__facets | The whole filter sidebar |
.searchx__facet-block | Filter groups container |
.searchx__facet-slot | A single facet group (one per panel — Brand, Price, On Sale, etc.) |
.searchx__facet-title | Filter group heading |
.searchx__facet-list | The list of items inside a facet |
.searchx__facet-item | Individual filter option |
.searchx__facet-item--active | Active / selected filter |
.searchx__facet-search-input | Search input inside a long facet |
.searchx__facet-showmore | The "Show more / Show less" button |
.searchx__on-sale-row | The single labelled checkbox inside the On Sale facet |
.searchx__on-sale-count | The "(N)" count next to the On Sale label |
.price-range-filter | The whole price range panel (slider + inputs + Apply button) |
.price-range-filter__inputs | The min/max inputs row |
.price-range-filter__input | The individual min / max number input |
.price-range-filter__apply | The circular Apply button next to the inputs |
.multi-range-slider__range | Price range slider track |
.multi-range-slider__thumb | Price range slider handle |
.searchx__clear-filters | The Clear filters button at the top of the sidebar |
Pagination
| Selector | Description |
|---|---|
.sx-pagination | Pagination container |
.sx-page-arrow | Previous / Next arrows |
.sx-page-pill | Page number button |
.sx-page-pill.is-active | Current page |
.sx-page-ellipsis | Ellipsis between pages |
Mobile filter sheet
| Selector | Description |
|---|---|
.facet-toggle-button | "Filters" toggle button |
.facet-sheet__controls | Filter sheet header |
.facet-sheet__label | Filter labels in the sheet |
.facet-sheet__select | Dropdown selects in the sheet |
.facet-sheet__apply | "Apply" button in filter sheet |
Example — minimal monochrome theme
A flat, square-corner aesthetic with no shadows — ideal for fashion or luxury brands.
:root {
--searchx-primary-600: #222222;
--searchx-primary-700: #111111;
--searchx-primary-800: #0a0a0a;
--sx-font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
--sx-font-size-base: 14px;
--sx-radius-sm: 0px;
--sx-radius-md: 0px;
--sx-radius-pill: 0px;
--sx-shadow-sm: none;
--sx-shadow-md: none;
}
/* Square search bar with black border */
.searchx__container.searchx__theme .searchx__form {
border: 1px solid #000;
border-radius: 0;
box-shadow: none;
}
/* Uppercase placeholder */
.searchx__container.searchx__theme .searchx__input input::placeholder {
text-transform: uppercase;
font-size: 12px;
letter-spacing: 0.08em;
}
/* Flat product cards with no border or shadow */
.searchx__page.searchx__theme .searchx__page-item {
border: none !important;
border-radius: 0 !important;
box-shadow: none !important;
}
.searchx__page.searchx__theme .searchx__page-item:hover {
transform: none !important;
}
/* Image zoom on hover */
.searchx__page.searchx__theme .searchx__page-item-image {
clip-path: inset(0);
transition: transform 500ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.searchx__page.searchx__theme .searchx__page-item:hover .searchx__page-item-image {
transform: scale(1.05);
}
/* Uppercase facet titles */
.searchx__page.searchx__theme .searchx__facet-title {
text-transform: uppercase;
font-size: 11px;
letter-spacing: 0.1em;
}
Example — ocean teal theme
Override only the primary scale to switch the entire accent colour:
:root {
--searchx-primary-50: #f0fdfa;
--searchx-primary-100: #ccfbf1;
--searchx-primary-200: #99f6e4;
--searchx-primary-300: #5eead4;
--searchx-primary-400: #2dd4bf;
--searchx-primary-500: #14b8a6;
--searchx-primary-600: #0d9488;
--searchx-primary-700: #0f766e;
--searchx-primary-800: #115e59;
--searchx-primary-900: #134e4a;
--searchx-primary-950: #042f2e;
}
Since every interactive surface references --searchx-primary-*, a single scale change updates buttons, active filters, hover states, pagination, and sort controls.
Controlling the product grid
Override the grid layout with CSS grid:
/* 4 columns on desktop, 3 on tablet, 2 on mobile */
.searchx__page.searchx__theme .searchx__page-results,
.searchx__page.searchx__theme .searchx__page-items {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px 16px;
}
@media (max-width: 1024px) {
.searchx__page.searchx__theme .searchx__page-results,
.searchx__page.searchx__theme .searchx__page-items {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 768px) {
.searchx__page.searchx__theme .searchx__page-results,
.searchx__page.searchx__theme .searchx__page-items {
grid-template-columns: repeat(2, 1fr);
}
}
Hover image swap
Show a second product image on hover using CSS:
/* Hide second image by default */
.searchx__page.searchx__theme .searchx__page-item-image--hover {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 320ms ease;
z-index: 2;
}
/* Reveal on hover */
.searchx__page.searchx__theme .searchx__page-item:hover .searchx__page-item-image--hover {
opacity: 1;
}
Hiding elements
You can hide any control with CSS:
/* Hide the Add to Cart button on cards */
.searchx__page.searchx__theme .searchx__page-item-button:not(.wishlist) {
display: none !important;
}
/* Hide the wishlist button */
.searchx__page.searchx__theme .searchx__page-item-button.wishlist {
display: none !important;
}
/* Hide the powered-by badge */
.searchx__powered-by {
display: none !important;
}
Prefer the SDK toggle when one exists
For most elements there's a setting that hides them cleanly without CSS (e.g. showAddToCartButton: false). CSS-based hiding is the right call when you want to hide something that doesn't have a dedicated toggle, or when the element should disappear only for a specific theme.
Greek typography (uppercase + accent stripping)
In Greek, words written in uppercase traditionally drop their accents (e.g. Φλεβών → ΦΛΕΒΩΝ, not ΦΛΕΒΏΝ). Browsers do this automatically when CSS applies text-transform: uppercase — but only when the element carries lang="el".
The SearchX widget propagates lang from your active locale to its root container automatically, so headings, buttons, and labels driven by your locale file work correctly out of the box.
If your catalogue text is in Greek but your interface language is English (e.g. an admin previewing a Greek shop), the widget also detects Greek characters in facet item labels and stamps lang="el" on those items individually. This means an English UI can still uppercase Greek brand names correctly:
/* Uppercase brand names on cards — Greek tones strip correctly */
.searchx__page.searchx__theme .searchx__page-item-brand {
text-transform: uppercase;
}
No additional configuration needed — this works as long as your CSS uses text-transform: uppercase (not a custom JavaScript transform).
Best practices
- Start with tokens. Override
--searchx-primary-*and--sx-*variables first. This handles 80% of theming with minimal CSS. - Scope your selectors. Always prefix with
.searchx__container.searchx__themeor.searchx__page.searchx__themeto avoid affecting the rest of your page. - Use the SDK toggle when one exists. If a setting hides the element, use the setting — easier to manage across themes.
- Test on mobile. The mobile filter sheet (
.facet-sheet__*) and compact pagination (.sx-page-pill) have their own selectors. - Respect specificity. Custom CSS loads after the defaults so equal-specificity rules win. Reach for
!importantonly when overriding a layout safeguard (the inputs row in the price range filter is one of these).
Related
- SDK Reference — All configuration options including
customCSSUrl. - Localization — Customise widget text and translations.
- Event Handling — Handle cart and wishlist interactions.