Finder-v2 Widget Documentation

πŸ” Overview

The Finder-v2 widget is a modern, Vue 3-based vehicle wheel/tire finder widget that provides a seamless integration experience for client websites. It features a powerful event system, customizable templates, and a flexible color system.

Key Features:
  • βœ… Vue 3 Composition API architecture
  • βœ… TailwindCSS v4 styling system
  • βœ… Cross-origin communication via postMessage
  • βœ… Customizable templates with Django-like syntax
  • βœ… Dynamic color theming system

🎯 Event System

The Finder-v2 widget implements a comprehensive event system that maintains full compatibility with the legacy WheelSizeWidgets JavaScript API while leveraging modern Vue 3 patterns.

Event Types

Event Name Description When Fired Payload
ready:document DOM is ready When widget DOM is fully loaded Context object
ready:window Window and data loaded After window load AND initial data load complete Context object
change:year Year selection changed When user selects a year { value: { slug, title } }
change:make Make selection changed When user selects a make { value: { slug, title } }
change:model Model selection changed When user selects a model { value: { slug, title } }
change:generation Generation selection changed When user selects a generation { value: { slug, title } }
change:modification Modification selection changed When user selects a modification { value: { slug, title } }
search:start Search initiated Before searchByVehicle() call Context with selections
search:complete Search completed After results loaded Includes counts and timings
search:error Search failed On search error { error_message: string }
results:display Results rendered After DOM renders results See detailed structure below

Event Payload Structure

All events are sent with a standardized envelope structure via window.parent.postMessage:

{
  "src": "<child iframe url>",
  "type": "<event type>",
  "data": {
    "context": {
      "config": {},
      "widgetUuid": "<uuid>",
      "widgetType": "finder-v2",
      "flowType": "primary|alternative|year_select",
      "selections": {
        "year": "<slug>",
        "make": "<slug>",
        "model": "<slug>",
        "generation": "<slug>",
        "modification": "<slug>"
      }
    }
  }
}

Special Event: results:display

The results:display event is fired after search results are rendered in the widget's DOM:

{
  "type": "results:display",
  "data": {
    "results_count": 3,
    "context": {
      "config": { /* widget configuration */ },
      "widgetUuid": "...",
      "widgetType": "finder-v2",
      "flowType": "primary|alternative|year_select",
      "selections": {
        "year": "<slug>",
        "make": "<slug>",
        "model": "<slug>",
        "generation": "<slug>",
        "modification": "<slug>"
      }
    }
  }
}
Note: The results:display event does NOT include full results data to keep messages lightweight. Results are rendered inside the widget. Parent pages can use results_count and context for analytics.

Parent Page Integration

Here's how to integrate the event system in your parent page:

<script src="//services.wheel-size.com/static/widget/code/local/ws-widget.js"></script>
<script>
  // Create widget instance
  var widget = WheelSizeWidgets.create('#ws-widget', { 
    uuid: 'your-uuid', 
    type: 'finder-v2' 
  });
  
  // Listen to events
  widget.on('ready:window', function(e) {
    console.log('Widget ready', e);
  });
  
  widget.on('change:make', function(e) {
    console.log('Make selected:', e.data.value);
    // e.data.value = { slug: 'toyota', title: 'Toyota' }
  });
  
  widget.on('search:start', function(e) {
    console.log('Search started with selections:', e.data.context.selections);
  });
  
  widget.on('search:complete', function(e) {
    console.log('Search completed');
  });
  
  widget.on('search:error', function(e) {
    console.error('Search failed:', e.data.error_message);
  });
  
  widget.on('results:display', function(e) {
    console.log('Results displayed. Count:', e.data.results_count);
    console.log('Vehicle:', e.data.context.selections);
  });
</script>

πŸ“ Template System

The Finder-v2 widget uses a Django-like template syntax that allows for powerful customization of the results display. Templates can access all data returned from the Finder API and use various control structures.

Template Syntax

Syntax Type Pattern Example Description
Variables {{ variable }} {{ make.name }} Output variable value
Nested Access {{ parent.child }} {{ engine.power.hp }} Access nested properties
Ternary {{ condition ? true : false }} {{ wheel.is_stock ? "OE" : "AM" }} Inline conditional output
If Statement {% if condition %}...{% endif %} {% if wheel.is_stock %}OE{% endif %} Conditional block
If-Else {% if condition %}...{% else %}...{% endif %} {% if wheel.rear.tire %}Dual{% else %}Single{% endif %} Conditional with fallback
If-Elif-Else {% if cond1 %}...{% elif cond2 %}...{% else %}...{% endif %} {% if hp > 300 %}High{% elif hp > 200 %}Med{% else %}Low{% endif %} Multiple conditions
Negation {% if not condition %}...{% endif %} {% if not wheel.showing_fp_only %}...{% endif %} Negated condition
Comparison {% if value == "text" %}...{% endif %} {% if wheel.front.rim_diameter > 18 %}Large{% endif %} Operators: ==, !=, >, <, >=, <=
For Loop {% for item in array %}...{% endfor %} {% for wheel in wheels %}...{% endfor %} Iterate over array

Loop Counter Variables

When inside a {% for %} loop, the following special variables are available:

Variable Type Description Example Values
{{ loop.index }} number Current iteration (1-based) 1, 2, 3...
{{ loop.index0 }} number Current iteration (0-based) 0, 1, 2...
{{ loop.first }} boolean True for first iteration true / false
{{ loop.last }} boolean True for last iteration true / false
{{ loop.length }} number Total number of items 3, 10, 25...
πŸ’‘ Pro Tip: Clean URL Parameters

Use loop counters to create clean URLs without trailing ampersands:

{% for wheel in wheels %}{% if not loop.first %}&{% endif %}param={{ wheel.rim }}{% endfor %}

This produces: param=value1&param=value2&param=value3

Important: Always use &amp; instead of bare & to avoid HTML entity conversion issues (like &para becoming ΒΆ).

Comparison Operators (NEW! ✨)

The template engine now supports full comparison operators for advanced conditional logic:

Operator Description String Example Numeric Example
== Equal to {% if engine.fuel == "Electric" %} {% if wheel.front.rim_diameter == 18 %}
!= Not equal to {% if engine.fuel != "Gasoline" %} {% if wheel.front.rim_diameter != 17 %}
> Greater than - {% if engine.power.hp > 300 %}
< Less than - {% if wheel.front.rim_diameter < 18 %}
>= Greater or equal - {% if engine.power.hp >= 200 %}
<= Less or equal - {% if wheel.front.rim_diameter <= 19 %}
⚠️ Important Limitations:
  • Nested ternary NOT supported: Don't use {{ a ? "x" : (b ? "y" : "z") }}
  • Use {% elif %} instead: For multiple conditions, always use {% if %}...{% elif %}...{% endif %}
  • No logical operators: and, or are not supported. Use nested {% if %} blocks.
  • No substring matching: The in operator is not available. Use exact string comparison.
  • Multiline variables OK: Variables can span multiple lines for readability.

Template Filters (NEW! ✨)

Transform variable values using filters with the pipe (|) syntax. Filters can be chained together.

Filter Description Syntax Example
dashify Replace spaces with dashes {{ variable | dashify }} "M14 x 1.5" β†’ "M14-x-1.5"
replace Replace characters {{ variable | replace:"old":"new" }} "hello world" β†’ "hello-world"
upper Convert to UPPERCASE {{ variable | upper }} "toyota" β†’ "TOYOTA"
lower Convert to lowercase {{ variable | lower }} "TOYOTA" β†’ "toyota"
trim Remove leading/trailing whitespace {{ variable | trim }} " text " β†’ "text"
default Use fallback if value is empty {{ variable | default:"N/A" }} "" β†’ "N/A"
urlencode URL encode for href attributes {{ variable | urlencode }} "hello world" β†’ "hello%20world"

Available Template Variables

The following variables are available in templates based on the Finder API v2 response:

Vehicle Information

Variable Path Type Description Example Value
make.name string Make/brand name "Toyota"
make.slug string Make identifier "toyota"
model.name string Model name "Camry"
model.slug string Model identifier "camry"
generation.name string Generation name "XV70"
generation.start number Generation start year 2017
generation.end number Generation end year 2024
generation.platform string Platform code "GA-K"
generation.bodies array Body types ["Sedan", "Wagon"]
vehicle.image string Vehicle image URL Paid Only "https://cdn.wheel-size.com/automobile/body/..."
start_year number Production start year 2018
end_year number Production end year 2024
name string Modification/trim name "GLE 53 4Matic+ (5AA-167161)"
trim string Short modification/trim name "GLE 53 4Matic+"
body string Body code (Japan) "5AA-167161"

Engine Information

Variable Path Type Description Example Value
engine.fuel string Fuel type "Gasoline"
engine.capacity string Engine displacement "2.5"
engine.type string Engine type "I4"
engine.code string Engine code "A25A-FKS"
engine.power.hp number Power in HP 203
engine.power.PS number Power in PS 206
engine.power.kW number Power in kW 151

Wheel/Tire Information (in wheels array)

Variable Path Type Description Example Value
wheel.is_stock boolean OE fitment flag true
wheel.is_recommended_for_winter boolean Winter recommendation false
wheel.is_runflat_tires boolean Run-flat tires false
wheel.is_extra_load_tires boolean Extra load rating false
wheel.is_pressed_steel_rims boolean Steel wheels false
wheel.showing_fp_only boolean Front pair only false
wheel.front.tire string Front tire size "215/55R17"
wheel.front.tire_full string Full tire spec "215/55R17 94V"
wheel.front.rim string Front rim size "7Jx17 ET45"
wheel.front.rim_diameter string Rim diameter "17"
wheel.front.rim_width string Rim width "7"
wheel.front.rim_offset string Rim offset "45"
wheel.front.tire_pressure.bar number Pressure in bar 2.3
wheel.front.tire_pressure.psi number Pressure in PSI 33
wheel.rear.* various Rear specs (same structure as front) -

Note: Rear wheel data is only available when different from front specs. When front and rear are identical, check wheel.showing_fp_only and use front values for both axles.

Technical Specifications

Variable Path Type Description Example Value
technical.bolt_pattern string Bolt pattern "5x114.3"
technical.stud_holes number Number of studs 5
technical.pcd string Pitch circle diameter "114.3"
technical.centre_bore string Center bore diameter "60.1"
technical.wheel_fasteners.type string Fastener type "Lug bolts"
technical.wheel_fasteners.thread_size string Thread specifications "M15 x 1.25"
technical.wheel_tightening_torque string/null Torque specification "88 - 108 Nm"
regions array Market regions ["USDM", "CDM"]

Template Examples

Wheel Size Filtering and Categorization

<!-- Show only large wheels (18" and up) -->
{% for wheel in wheels %}
  {% if wheel.front.rim_diameter >= 18 %}
    <div class="large-wheel-option">
      <span class="text-orange-500 font-bold">πŸ”₯ Large Wheels!</span>
      {{ wheel.front.rim_diameter }}" - {{ wheel.front.tire }}
    </div>
  {% endif %}
{% endfor %}

<!-- Categorize by wheel size -->
{% for wheel in wheels %}
  <div class="wheel-card">
    {% if wheel.front.rim_diameter >= 20 %}
      <span class="badge bg-red-500 text-white">Extra Large (20"+)</span>
    {% elif wheel.front.rim_diameter >= 18 %}
      <span class="badge bg-orange-500 text-white">Large (18-19")</span>
    {% elif wheel.front.rim_diameter >= 17 %}
      <span class="badge bg-blue-500 text-white">Standard (17")</span>
    {% else %}
      <span class="badge bg-gray-500 text-white">Compact (<17")</span>
    {% endif %}

    <div>{{ wheel.front.tire }} - {{ wheel.front.rim }}</div>
  </div>
{% endfor %}

Engine Power Classification

<div class="engine-info">
  <h4>{{ engine.type }} {{ engine.capacity }}L</h4>

  <!-- Power category badge -->
  {% if engine.power.hp > 300 %}
    <span class="badge bg-red-500 text-white">⚑ High Performance ({{ engine.power.hp }} hp)</span>
  {% elif engine.power.hp > 200 %}
    <span class="badge bg-orange-500 text-white">🏎️ Performance ({{ engine.power.hp }} hp)</span>
  {% elif engine.power.hp > 150 %}
    <span class="badge bg-blue-500 text-white">πŸš— Standard ({{ engine.power.hp }} hp)</span>
  {% else %}
    <span class="badge bg-gray-500 text-white">πŸ›΅ Economy ({{ engine.power.hp }} hp)</span>
  {% endif %}

  <!-- Fuel type with icons -->
  <div class="fuel-type">
    {% if engine.fuel == "Electric" %}
      ⚑ Electric Vehicle
    {% elif engine.fuel == "Hybrid" %}
      πŸ”‹ Hybrid Engine
    {% elif engine.fuel == "Diesel" %}
      πŸ›’οΈ Diesel Engine
    {% else %}
      β›½ Gasoline Engine
    {% endif %}
  </div>
</div>

Translation: Fastener Types (Localization Example)

<div class="technical-specs">
  <h4>Technical Specifications</h4>

  <!-- Translate fastener type to local language -->
  <div class="spec-row">
    <span class="spec-label">
      {% if technical.wheel_fasteners.type == "Lug bolts" %}
        SkrΕ«ves:  <!-- Latvian translation -->
      {% elif technical.wheel_fasteners.type == "Lug nuts" %}
        UzgrieΕΎΕ†i:  <!-- Latvian translation -->
      {% else %}
        {{ technical.wheel_fasteners.type }}:
      {% endif %}
    </span>
    <span class="spec-value">
      {{ technical.stud_holes }} Γ— {{ technical.wheel_fasteners.thread_size }}
    </span>
  </div>

  <div class="spec-row">
    <span class="spec-label">SkrΕ«vju solis:</span> <!-- Bolt Pattern in Latvian -->
    <span class="spec-value">{{ technical.bolt_pattern }}</span>
  </div>

  <div class="spec-row">
    <span class="spec-label">Centrālā atvere:</span> <!-- Center Bore in Latvian -->
    <span class="spec-value">{{ technical.centre_bore }} mm</span>
  </div>
</div>

Staggered vs Non-Staggered Setup Detection

{% for wheel in wheels %}
  <div class="fitment-card">
    <!-- Compare front and rear diameters -->
    {% if wheel.front.rim_diameter == wheel.rear.rim_diameter %}
      <span class="badge bg-blue-500 text-white">βœ“ Non-Staggered Setup</span>
      <div>Front & Rear: {{ wheel.front.tire }} - {{ wheel.front.rim }}</div>
    {% else %}
      <span class="badge bg-purple-500 text-white">⚑ Staggered Setup</span>
      <div>Front: {{ wheel.front.tire }} - {{ wheel.front.rim }}</div>
      {% if wheel.rear.tire %}
        <div>Rear: {{ wheel.rear.tire }} - {{ wheel.rear.rim }}</div>
      {% endif %}
    {% endif %}
  </div>
{% endfor %}

Conditional Styling Based on Values

{% for wheel in wheels %}
  <!-- Dynamic background color based on diameter -->
  <div class="
    {% if wheel.front.rim_diameter > 19 %}bg-red-100 border-red-500
    {% elif wheel.front.rim_diameter > 17 %}bg-orange-100 border-orange-500
    {% else %}bg-blue-100 border-blue-500
    {% endif %}
    border-2 p-4 rounded-lg">

    <!-- Dynamic text color based on stock status -->
    <h3 class="{% if wheel.is_stock %}text-primary-color{% else %}text-secondary-color{% endif %} font-bold">
      {{ wheel.is_stock ? "Original Equipment" : "Aftermarket Option" }}
    </h3>

    <div>{{ wheel.front.tire }} - {{ wheel.front.rim }}</div>

    <!-- Show premium badge for larger wheels -->
    {% if wheel.front.rim_diameter >= 19 %}
      <div class="mt-2 text-sm text-orange-600 font-semibold">
        🌟 Premium Size Upgrade
      </div>
    {% endif %}
  </div>
{% endfor %}

Basic Vehicle Information

<h3>{{ make.name }} {{ model.name }} ({{ start_year }}-{{ end_year }})</h3>
{% if generation.name %}
  <p>Generation: {{ generation.name }}</p>
{% endif %}

Engine Details with Conditionals

{% if engine.type %}
  <div class="engine-info">
    <span>{{ engine.type }} {{ engine.capacity }}L</span>
    {% if engine.power.hp %}
      <span>β€’ {{ engine.power.hp }} hp</span>
    {% endif %}
    {% if engine.fuel %}
      <span>β€’ {{ engine.fuel }}</span>
    {% endif %}
  </div>
{% endif %}

Wheel Fitments with Loops and Ternary

<div class="fitments">
  {% for wheel in wheels %}
    <div class="fitment-card">
      <span class="badge">{{ wheel.is_stock ? "OE" : "Aftermarket" }}</span>
      
      <div class="specs">
        <div>Front: {{ wheel.front.tire }} on {{ wheel.front.rim }}</div>
        
        {% if not wheel.showing_fp_only %}
          {% if wheel.rear.tire %}
            <div>Rear: {{ wheel.rear.tire }} on {{ wheel.rear.rim }}</div>
          {% endif %}
        {% endif %}
        
        {% if wheel.front.tire_pressure.bar %}
          <div>Pressure: {{ wheel.front.tire_pressure.bar }} bar / {{ wheel.front.tire_pressure.psi }} psi</div>
        {% endif %}
      </div>
      
      {% if wheel.is_recommended_for_winter %}
        <span class="winter-badge">❄️ Winter Recommended</span>
      {% endif %}
    </div>
  {% endfor %}
</div>

Using Loop Counters for Clean Output

<!-- Numbered list of wheels -->
{% for wheel in wheels %}
  <div>{{ loop.index }}. {{ wheel.front.rim }} - {{ wheel.front.tire }}</div>
{% endfor %}

<!-- Comma-separated list -->
Available sizes: {% for wheel in wheels %}{{ wheel.front.rim }}{% if not loop.last %}, {% endif %}{% endfor %}

<!-- Show progress indicator -->
{% for wheel in wheels %}
  <div class="wheel-option">
    <span>Option {{ loop.index }} of {{ loop.length }}</span>
    <span>{{ wheel.front.rim }}</span>
  </div>
{% endfor %}

<!-- Clean URL parameters without trailing ampersand -->
<a href="http://example.com/search?{% for wheel in wheels %}{% if not loop.first %}&amp;{% endif %}size={{ wheel.front.rim }}{% endfor %}">
  Search compatible wheels
</a>

Vehicle Image Display (Paid Widgets, Alternative Flow)

<div class="vehicle-result">
  <h3>{{ make.name }} {{ model.name }} ({{ start_year }}-{{ end_year }})</h3>
  
  {% if vehicle.image %}
    <div class="vehicle-image-container">
      <img src="{{ vehicle.image }}" 
           alt="{{ make.name }} {{ model.name }}"
           style="max-width: 100%; height: auto; border-radius: 8px;">
      <p class="image-caption">{{ generation.name }}</p>
    </div>
  {% else %}
    <div class="no-image-placeholder">
      <p>Vehicle image not available</p>
    </div>
  {% endif %}
  
  <!-- Wheel fitment data with numbering -->
  {% for wheel in wheels %}
    <div class="wheel-option">
      <strong>{{ loop.index }}. {{ wheel.is_stock ? 'OE' : 'Aftermarket' }}:</strong>
      {{ wheel.front.tire }} - {{ wheel.front.rim }}
      {% if loop.first %}<span class="badge">Primary</span>{% endif %}
    </div>
  {% endfor %}
</div>
Note: The vehicle.image placeholder is only available for:
  • Paid widgets (subscriptionPaid: true)
  • Alternative flow (not available in Primary or Year-first flows)
  • The image URL is extracted from the generation API response

Complete Template Example

<div class="ws-results">
  <header class="vehicle-header">
    <h2>{{ make.name }} {{ model.name }}</h2>
    <div class="vehicle-details">
      <span>{{ start_year }}-{{ end_year }}</span>
      {% if generation.name %}
        <span>β€’ {{ generation.name }}</span>
      {% endif %}
      {% if trim %}
        <span>β€’ {{ trim }}</span>
      {% endif %}
    </div>
    
    {% if engine.type %}
      <div class="engine-specs">
        {{ engine.capacity }}L {{ engine.type }} 
        {% if engine.power.hp %}
          ({{ engine.power.hp }} hp)
        {% endif %}
      </div>
    {% endif %}
  </header>
  
  <div class="technical-info">
    {% if technical.bolt_pattern %}
      <div>Bolt Pattern: {{ technical.bolt_pattern }}</div>
    {% endif %}
    {% if technical.centre_bore %}
      <div>Center Bore: {{ technical.centre_bore }} mm</div>
    {% endif %}
  </div>
  
  <div class="wheel-options">
    <h3>Wheel & Tire Options</h3>
    
    {% for wheel in wheels %}
      <div class="option-card {{ wheel.is_stock ? 'oe-option' : 'aftermarket-option' }}">
        <div class="option-header">
          <span class="option-type">
            {{ wheel.is_stock ? "Original Equipment" : "Aftermarket Option" }}
          </span>
          <span class="wheel-size">{{ wheel.front.rim_diameter }}" wheels</span>
        </div>
        
        <div class="fitment-details">
          <div class="front-fitment">
            <strong>Front:</strong>
            <div>Tire: {{ wheel.front.tire_full }}</div>
            <div>Rim: {{ wheel.front.rim }}</div>
            {% if wheel.front.tire_pressure.bar %}
              <div>Pressure: {{ wheel.front.tire_pressure.bar }} bar</div>
            {% endif %}
          </div>
          
          {% if not wheel.showing_fp_only %}
            {% if wheel.rear.tire %}
              <div class="rear-fitment">
                <strong>Rear:</strong>
                <div>Tire: {{ wheel.rear.tire_full }}</div>
                <div>Rim: {{ wheel.rear.rim }}</div>
                {% if wheel.rear.tire_pressure.bar %}
                  <div>Pressure: {{ wheel.rear.tire_pressure.bar }} bar</div>
                {% endif %}
              </div>
            {% endif %}
          {% endif %}
        </div>
        
        <div class="fitment-flags">
          {% if wheel.is_recommended_for_winter %}
            <span class="flag winter">❄️ Winter</span>
          {% endif %}
          {% if wheel.is_runflat_tires %}
            <span class="flag runflat">πŸ›‘οΈ Run-flat</span>
          {% endif %}
          {% if wheel.is_extra_load_tires %}
            <span class="flag xl">πŸ’ͺ XL</span>
          {% endif %}
          {% if wheel.is_pressed_steel_rims %}
            <span class="flag steel">πŸ”§ Steel</span>
          {% endif %}
        </div>
      </div>
    {% endfor %}
  </div>
</div>

🎨 Color System

The Finder-v2 widget implements a flexible color theming system that allows customization to match your brand. The system uses CSS custom properties that can be overridden while maintaining consistent naming conventions.

Primary Brand Color Classes

The primary brand color is the main accent color used throughout the widget for interactive elements, highlights, and brand-specific styling. These classes adapt to your configured primary color.

CSS Class Purpose Usage Example
text-primary-color Primary color text Links, important labels, OE badges
bg-primary-color Primary color background Buttons, selected states, badges
border-primary-color Primary color border Active inputs, highlighted cards
hover:bg-primary-color Primary hover background Button hover states
hover:text-primary-color Primary hover text Link hover states
hover:border-primary-color Primary hover border Card hover states

Opacity Variants

Use opacity modifiers for subtle backgrounds and overlays:

CSS Class Opacity Use Case
bg-primary-color/5 5% Very subtle background tint
bg-primary-color/10 10% Light background for OE options
bg-primary-color/20 20% Badge backgrounds, hover states
bg-primary-color/50 50% Overlays, disabled states

Additional Color Classes

CSS Class Purpose Typical Use
text-main-color Main text color Body text, headings
text-secondary-color Secondary text Labels, descriptions, muted text
bg-background-color Background color Card backgrounds, containers
border-secondary-color Secondary borders Dividers, neutral borders
text-accent-color Accent color text Alternative highlights
bg-accent-color Accent backgrounds Aftermarket badges

Usage Examples

OE vs Aftermarket Distinction

<!-- OE Option with primary color -->
<div class="border border-primary-color bg-primary-color/10 p-4 rounded">
  <span class="text-primary-color font-semibold">Original Equipment</span>
  <!-- content -->
</div>

<!-- Aftermarket Option with secondary color -->
<div class="border border-secondary-color hover:bg-secondary-color/10 p-4 rounded">
  <span class="text-secondary-color font-semibold">Aftermarket</span>
  <!-- content -->
</div>

Interactive Elements

<!-- Primary button -->
<button class="bg-primary-color text-white hover:bg-primary-color/90 px-4 py-2 rounded">
  Search
</button>

<!-- Secondary button -->
<button class="border border-primary-color text-primary-color hover:bg-primary-color/10 px-4 py-2 rounded">
  Clear
</button>

<!-- Link with primary color -->
<a href="#" class="text-primary-color hover:text-primary-color/80 underline">
  View Details
</a>

Badges and Labels

<!-- OE Badge -->
<span class="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-primary-color/20 text-primary-color">
  OE
</span>

<!-- Aftermarket Badge -->
<span class="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-secondary-color/20 text-secondary-color">
  AM
</span>

<!-- Feature Badge -->
<span class="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-accent-color/20 text-accent-color">
  Winter
</span>

Cards with Hover Effects

<div class="bg-background-color border border-secondary-color hover:border-primary-color hover:shadow-lg transition-all duration-300 p-4 rounded-lg">
  <h3 class="text-main-color font-semibold">17" Wheel Option</h3>
  <p class="text-secondary-color">215/55R17 - 7Jx17 ET45</p>
  <div class="mt-2">
    <span class="text-primary-color font-medium">View Details β†’</span>
  </div>
</div>

Complete Color System Example

<div class="widget-container">
  <!-- Header with primary accent -->
  <header class="border-b-2 border-primary-color pb-4 mb-6">
    <h1 class="text-2xl font-bold text-main-color">Wheel Finder Results</h1>
    <p class="text-secondary-color">Found 3 options for your vehicle</p>
  </header>
  
  <!-- Results grid -->
  <div class="grid gap-4">
    <!-- OE Option Card -->
    <div class="bg-primary-color/5 border-2 border-primary-color rounded-lg p-6">
      <div class="flex justify-between items-start mb-4">
        <h3 class="text-lg font-semibold text-main-color">Factory Standard</h3>
        <span class="bg-primary-color text-white px-3 py-1 rounded-full text-sm font-medium">
          OE
        </span>
      </div>
      
      <div class="space-y-2">
        <div class="flex justify-between">
          <span class="text-secondary-color">Tire:</span>
          <span class="text-main-color font-medium">215/55R17</span>
        </div>
        <div class="flex justify-between">
          <span class="text-secondary-color">Rim:</span>
          <span class="text-main-color font-medium">7Jx17 ET45</span>
        </div>
      </div>
      
      <button class="mt-4 w-full bg-primary-color hover:bg-primary-color/90 text-white py-2 rounded-lg transition-colors">
        Select This Option
      </button>
    </div>
    
    <!-- Aftermarket Option Card -->
    <div class="bg-background-color border border-secondary-color hover:border-primary-color rounded-lg p-6 transition-colors">
      <div class="flex justify-between items-start mb-4">
        <h3 class="text-lg font-semibold text-main-color">Performance Upgrade</h3>
        <span class="bg-accent-color/20 text-accent-color px-3 py-1 rounded-full text-sm font-medium">
          AM
        </span>
      </div>
      
      <div class="space-y-2">
        <div class="flex justify-between">
          <span class="text-secondary-color">Tire:</span>
          <span class="text-main-color font-medium">225/50R18</span>
        </div>
        <div class="flex justify-between">
          <span class="text-secondary-color">Rim:</span>
          <span class="text-main-color font-medium">8Jx18 ET40</span>
        </div>
      </div>
      
      <button class="mt-4 w-full border border-primary-color text-primary-color hover:bg-primary-color/10 py-2 rounded-lg transition-colors">
        View Details
      </button>
    </div>
  </div>
</div>
πŸ’‘ Pro Tip: The color system automatically adapts to your widget's configured theme. The primary color will match your brand settings, while secondary and accent colors provide consistent contrast and hierarchy.

πŸ“– API Reference

Widget Creation

// Basic widget creation
var widget = WheelSizeWidgets.create('#container-id', {
  uuid: 'your-widget-uuid',
  type: 'finder-v2',
  // Optional configuration
  config: {
    theme: 'light',
    language: 'en',
    // ... other config options
  }
});

// Alternative: Use existing iframe
var widget = WheelSizeWidgets.create('#existing-iframe-id');

Event Handling Methods

// Subscribe to an event
widget.on('event-name', function(eventData) {
  console.log('Event received:', eventData);
});

// Unsubscribe from an event
widget.off('event-name', callbackFunction);

// One-time event listener
widget.once('ready:window', function(eventData) {
  console.log('Widget is ready (fires once)');
});

Widget Methods

Method Parameters Description Returns
create selector, config Create widget instance Widget instance
on event, callback Add event listener void
off event, callback Remove event listener void
once event, callback One-time event listener void
destroy - Destroy widget instance void

Configuration Options

Option Type Default Description
uuid string required Widget UUID from admin panel
type string "finder-v2" Widget type identifier
language string "en" Widget language (en, de, fr, etc.)
theme string "light" Color theme (light, dark, custom)
flowType string "primary" Search flow (primary, alternative, year_select)

πŸ’‘ Complete Integration Examples

Basic Integration

<!DOCTYPE html>
<html>
<head>
  <title>Finder-v2 Widget Integration</title>
</head>
<body>
  <!-- Widget container -->
  <div id="wheel-finder"></div>
  
  <!-- Load widget API -->
  <script src="//services.wheel-size.com/static/widget/code/local/ws-widget.js"></script>
  
  <script>
    // Create widget when DOM is ready
    document.addEventListener('DOMContentLoaded', function() {
      var widget = WheelSizeWidgets.create('#wheel-finder', {
        uuid: 'your-widget-uuid',
        type: 'finder-v2'
      });
      
      // Listen for widget ready
      widget.on('ready:window', function() {
        console.log('Widget is ready for interaction');
      });
      
      // Track user selections
      widget.on('change:make', function(e) {
        console.log('User selected make:', e.data.value.title);
      });
      
      widget.on('change:model', function(e) {
        console.log('User selected model:', e.data.value.title);
      });
      
      // Handle search results
      widget.on('results:display', function(e) {
        console.log('Results displayed:', e.data.results_count);
        // Send to analytics, update page, etc.
      });
    });
  </script>
</body>
</html>

Advanced Integration with Analytics

<script>
document.addEventListener('DOMContentLoaded', function() {
  var widget = WheelSizeWidgets.create('#wheel-finder', {
    uuid: 'your-widget-uuid',
    type: 'finder-v2',
    language: 'en',
    theme: 'light'
  });
  
  // Analytics tracking
  function trackEvent(action, label, value) {
    if (typeof gtag !== 'undefined') {
      gtag('event', action, {
        'event_category': 'WheelFinder',
        'event_label': label,
        'value': value
      });
    }
  }
  
  // Track widget initialization
  widget.on('ready:window', function() {
    trackEvent('widget_loaded', 'finder-v2', 1);
  });
  
  // Track user journey
  var selectionPath = [];
  
  ['year', 'make', 'model', 'generation', 'modification'].forEach(function(field) {
    widget.on('change:' + field, function(e) {
      selectionPath.push({
        field: field,
        value: e.data.value.slug,
        title: e.data.value.title
      });
      
      trackEvent('selection_made', field, selectionPath.length);
    });
  });
  
  // Track search events
  widget.on('search:start', function(e) {
    trackEvent('search_initiated', 'vehicle_search', 1);
    console.log('Search path:', selectionPath);
  });
  
  widget.on('search:complete', function(e) {
    trackEvent('search_completed', 'success', 1);
  });
  
  widget.on('search:error', function(e) {
    trackEvent('search_failed', e.data.error_message, 0);
    console.error('Search error:', e.data.error_message);
  });
  
  // Track results interaction
  widget.on('results:display', function(e) {
    var vehicle = e.data.context.selections;
    var vehicleString = [
      vehicle.year,
      vehicle.make,
      vehicle.model,
      vehicle.generation,
      vehicle.modification
    ].filter(Boolean).join(' / ');
    
    trackEvent('results_shown', vehicleString, e.data.results_count);
    
    // Update page title or breadcrumb
    document.title = 'Wheels for ' + vehicleString;
    
    // Show additional content based on results
    if (e.data.results_count > 0) {
      document.getElementById('related-products').style.display = 'block';
    }
  });
});
</script>

Error Handling and Fallbacks

document.addEventListener('DOMContentLoaded', function() {
  var widgetContainer = document.getElementById('wheel-finder');
  var errorContainer = document.getElementById('widget-error');
  var loadingIndicator = document.getElementById('widget-loading');
  
  try {
    var widget = WheelSizeWidgets.create('#wheel-finder', {
      uuid: 'your-widget-uuid',
      type: 'finder-v2'
    });
    
    // Set loading timeout
    var loadTimeout = setTimeout(function() {
      loadingIndicator.style.display = 'none';
      errorContainer.innerHTML = 'Widget is taking longer than expected to load...';
      errorContainer.style.display = 'block';
    }, 10000); // 10 second timeout
    
    // Clear timeout on successful load
    widget.on('ready:window', function() {
      clearTimeout(loadTimeout);
      loadingIndicator.style.display = 'none';
      console.log('Widget loaded successfully');
    });
    
    // Handle search errors
    widget.on('search:error', function(e) {
      console.error('Search error:', e.data.error_message);
      
      // Display user-friendly error message
      var userMessage = 'Unable to load results. Please try again.';
      
      if (e.data.error_message.includes('network')) {
        userMessage = 'Connection error. Please check your internet connection.';
      } else if (e.data.error_message.includes('timeout')) {
        userMessage = 'Request timed out. Please try again.';
      }
      
      // Show error notification
      showNotification(userMessage, 'error');
    });
    
    // Handle no results scenario
    widget.on('results:display', function(e) {
      if (e.data.results_count === 0) {
        showNotification('No wheel options found for this vehicle.', 'info');
        // Optionally show alternative suggestions
        showAlternativeSuggestions(e.data.context.selections);
      }
    });
    
  } catch (error) {
    console.error('Failed to initialize widget:', error);
    loadingIndicator.style.display = 'none';
    errorContainer.innerHTML = 'Unable to load the wheel finder. Please refresh the page.';
    errorContainer.style.display = 'block';
  }
});

function showNotification(message, type) {
  // Implementation of notification display
  var notification = document.createElement('div');
  notification.className = 'notification notification-' + type;
  notification.textContent = message;
  document.body.appendChild(notification);
  
  setTimeout(function() {
    notification.remove();
  }, 5000);
}

function showAlternativeSuggestions(selections) {
  // Implementation to show alternative vehicle suggestions
  console.log('Show alternatives for:', selections);
}

Finder-v2 Widget Documentation β€’ Version 2.0 β€’