Files
hoponhopofftickets/viator-collector/docs/superpowers/specs/2026-09-11-new-front-reskin-design.md
amir 7ada8860f8 Add viator-collector plugin (Hop On Hop Off front-end reskin)
Consolidate the plugin into the TourWebsite repository (its separate
nested git repo has been removed). Includes the full new-design reskin:
scoped design stylesheet, SVG icon sprite, rebuilt listing/single/section
templates, and homepage hero/FAQ/testimonials shortcodes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J4hNL1PSHxXbu94SCenpKj
2026-09-12 21:11:20 +03:30

14 KiB
Raw Blame History

Viator Collector — "Hop On Hop Off" Front-End Re-skin

Date: 2026-09-11 Status: Design — pending approval Author: Amir Bagheri (with Claude)

1. Goal

Replace the entire visual front-end of the viator-collector WordPress plugin with the new "Hop On Hop Off" design that currently exists as a static mockup in ../new front/, without changing any of the plugin's data model, query logic, or WordPress plumbing. Every dynamic value keeps coming from the viator_tour post type, its meta, and its taxonomies exactly as today; only markup, CSS, icons, JS interactions, and decorative assets change.

2. Decisions (locked)

Question Decision
Surfaces to re-skin All three: listing/archive, single tour, homepage sections
Header / nav / footer Content only — the WordPress theme (e.g. Astra) keeps providing site header/nav/footer. The plugin re-skins only its content area.
Listing filter behavior Keep server-side (WP_Query + GET params + server pagination). Only the look changes. No switch to main.js client-side filtering.
Homepage static chrome Include it — plugin also ships hero/booking, FAQ, and testimonials as shortcodes so the full index.html look is achievable.
Old CSS/JS Replace — retire vc-listing.css, vc-single.css, viator-collector.css, vc-sections.css, and the Lucide dependency.

3. Source of truth

The three canonical mockup pages are:

  • new front/index.html — homepage (hero+booking, destinations rail, feature/bus section, most-liked tours rail, about, testimonials marquee, articles, FAQ)
  • new front/tours.html — listing/archive (hero with horizontal filter bar, tour grid, results count, pager)
  • new front/tour.html — single tour (hero-tour banner, facts, overview, gallery, includes/excludes, good-to-know, cancellation policy, reviews, booking aside)

Driven by new front/css/style.css (~3096 lines, token-based) and new front/js/main.js (~419 lines).

Ignored: new front/tourdetailsinfo.html and new front/tourdetailscontent.html are raw Figma/React export scraps (absolute-positioned, Tailwind classNames). They are not part of the design system and will not be used except as visual reference for the booking aside (already fully expressed in tour.html).

4. Architecture

4.1 Scoping strategy (critical)

Because the plugin renders inside a WordPress theme, the new design's generic class names (.container, .section, .hero, .btn, .rating, .meta, .dcard, .footer, .site-header…) and its global resets (*, html, body, ul, a, button) must not leak into or fight the theme.

Approach:

  • Every plugin surface is wrapped in the existing #vc-app scope (already used by shortcode_listing and single-viator_tour.php). Homepage section shortcodes get wrapped in #vc-app too.
  • The ported stylesheet is transformed so every rule is prefixed with #vc-app (e.g. .dcard → #vc-app .dcard). The :root { --… } token block stays global (harmless custom properties).
  • Global resets on */html/body are dropped or converted to scoped equivalents (#vc-app, #vc-app *). scroll-behavior, overflow-x:hidden on html/body are dropped (theme's concern).
  • Header/nav/footer CSS (.site-header, .nav, .burger, .footer, .skip-link) from the mockup is not ported — the theme owns those. Exception: the .footer "back to top" ring-badge is a reusable widget we may keep for section use; TBD, default = drop.

4.2 Asset organization

New plugin asset layout:

viator-collector/assets/
  css/
    vc-design.css        # ported + scoped style.css (single stylesheet)
  js/
    vc-frontend.js       # existing server-side filter behaviors (kept, trimmed)
    vc-design.js         # ported interactions: rails, accordion, marquee, ring-badge
  img/
    (decorative design assets copied from new front/assets: bus-iso.png,
     bus-open-top.png, tower.png, ellipse-17.png, ellipse-17-2.png,
     hero.png, hero2.png, hero3.jpg)
  icons/
    sprite.svg           # the <symbol> icon set + #ringPathScroll path def
  • Decorative images (bus, tower, ellipses, hero backdrops) ship with the plugin — they are chrome, not tour data.
  • Demo tour images (city-*.png, liked-*.png, tour-*.png) are NOT shipped; those slots are filled by real tour data (_vc_primary_image_url, gallery URLs) via vc_tour_image_html().

4.3 Icons

The mockup uses an inline SVG sprite: a hidden <svg class="sprite"> holding <symbol id="i-…"> defs, referenced by <svg class="icon"><use href="#i-…"></svg>. It also defines #ringPathScroll (the circular text path for the "BOOK NOW" badge).

  • Ship the sprite as assets/icons/sprite.svg and print it once per plugin page via a small helper (vc_print_icon_sprite()), guarded so it prints at most once.
  • Add a helper vc_icon( $name ) returning <svg class="icon"><use href="#i-$name"></svg>.
  • Replace all current <i data-lucide="…"> usages with vc_icon(). Lucide enqueue is removed.

Icon name mapping (Lucide → sprite):

Lucide Sprite
map-pin i-location
clock i-duration
users / user i-person / i-user
shield-check i-shield-check
calendar-check i-calendar
rotate-ccw i-duration (or reuse)
clipboard-list i-grid
images i-grid
star (rating) i-star

(Exact per-call mapping finalized during implementation; any missing glyph is added to the sprite.)

4.4 JavaScript

vc-design.js (new) ports from main.js only what our content uses:

  • Card rails — .rail horizontal scroll with [data-rail-prev/next] buttons (used by homepage destination/tour rails).
  • FAQ accordion — [data-accordion] / .acc__q.
  • Testimonials marquee — [data-marquee] duplicate-for-loop.
  • Ring-badge — the rotating "BOOK NOW" SVG textPath (needs #ringPathScroll in the sprite; animation is CSS).
  • Hero booking/availability form preventDefault demo handlers become real submits to the tour's booking URL / listing action (no console stubs).

Explicitly NOT ported: main.js client-side listing filter/sort/ pager (server-side stays), and header/nav/burger/sticky-header (theme's).

vc-frontend.js is kept for the server-side listing behaviors it already provides (auto-submit selects/checks, filter drawer on mobile). The noUiSlider price slider is removed — the new design uses a Price Range <select>, which the existing get_filter_state() already parses.

4.5 Enqueue changes (class-vc-display.php::enqueue_assets)

  • Remove: Lucide, noUiSlider (css+js), vc-listing.css, vc-single.css, viator-collector.css.
  • Add: Google Fonts "Plus Jakarta Sans"; vc-design.css; vc-design.js.
  • Keep: vc-frontend.js (trimmed).
  • Gating logic (single vs archive vs shortcode page) stays as-is.

5. Surface-by-surface mapping

5.1 Listing / archive — templates/listing.php + templates/card-tour.php

Target markup: new front/tours.html.

  • Hero → section.hero.hero--archive with eyebrow pill, h1, and the horizontal filter bar form.filters (submits GET to the same action as today). Replaces the old sidebar + noUiSlider layout.
    • Search input → vc_q
    • Destination <select> → dest[] (single value)
    • Category <select> → cat[]
    • Price Range <select> → price (values like 0-50, 50-150, 150-1000; already parsed by get_filter_state())
    • Duration <select> → dur[] buckets (half/full/multi, labeled Half Day / Full Day / Multi-Day — adapted from the mockup's day options to the plugin's real buckets)
    • "Apply Filters" submit button
    • Checkbox row (filters__bar): Free Cancellation → freecancel, Best Deal → bestdeal, Likely to Sell Out → sellout
    • A sort control (styled to the design) → sort
  • Results head → .results with #resultCount ("Showing X–Y of Z tours", server values) and a "Clear filters" link when filters active.
  • Grid → ul.tgrid of li.titem > article.dcard.dcard--wide cards (see 5.2). Active-filter chips retained, restyled.
  • Pager → custom server-rendered pager matching .pager / .pager__num design (replaces paginate_links default markup and the JS pager). Preserves current query string.
  • Empty state → styled "no tours" message.

5.2 Tour card — templates/card-tour.php

Target: the .dcard--wide card in tours.html (and the rail .dcard in index.html for homepage rails — one template, optional modifier).

Fields → design slots:

Design slot Data source
.dcard__img vc_tour_image_html() (real image)
.rating pill (<b>4.0</b>/5 + star) _vc_rating
.dcard__title get_the_title()
meta "Start at" $xx _vc_price_from + _vc_currency via vc_money()
meta "Duration" _vc_duration_text / vc_duration_human()
ring "BOOK NOW" badge link permalink
.titem__desc (listing only) excerpt
card badge (Best Deal / Sell-out) vc_card_badge()
destination line (if kept) viator_destination term

The card must render correctly both as a listing li.titem and as a homepage rail li.dcard; a $variant arg selects wrapper/markup.

5.3 Single tour — templates/single-viator_tour.php

Target: new front/tour.html.

  • Hero-tour banner → section.hero-tour with hero-tour__photo (primary image), hero-tour__kicker ("Welcome to"), hero-tour__word (destination/title), decorative hero-tour__figure (tower.png, decorative asset). Breadcrumb retained.
  • Layout → .container.tour__layout = .tour__main + aside.tour__aside.
  • Head → .tour-head__title + .tmeta (rating, location, supplier).
  • Facts → ul.facts of li.fact (Duration, Max Travelers, Free Cancellation, Category) from _vc_duration_minutes, _vc_max_travelers, _vc_free_cancellation, viator_tag.
  • Overview → section.tour__block with the_content().
  • Gallery → .gallery (hero + thumbs) from _vc_gallery_image_urls.
  • Includes / Excludes → .lists of .ilist with .tick--yes/--no from _vc_inclusions / _vc_exclusions.
  • Good to know → tour__block from _vc_additional_info.
  • Cancellation policy → .policy of .statcard--yes/--no refund tiers from _vc_cancellation_policy.
  • Reviews → restyled summary from _vc_rating / _vc_review_count.
  • Booking aside → .bookcard: From/price (_vc_price_from), "Check Availability" → _vc_booking_url, statcard--teal Free Cancellation + statcard--yes Reserve-now cards.
  • Mobile book bar retained, restyled.

5.4 Homepage sections — class-vc-sections.php / class-vc-display.php shortcodes

Existing data shortcodes are re-skinned; new static-chrome shortcodes are added.

Re-skinned (data-driven):

  • viator_top_destinations → "Where you wanna go?" section: sec-head (title + lead + See-all + rail-nav prev/next) over a ul.rail.rail--dest of .dcards. (card-destination.php)
  • viator_top_tours → "Most Liked Tours" rail (same rail pattern, card-tour.php in dcard variant).
  • viator_top_attractions → the feature / "bus-tours" section styling.
  • viator_browse_activities → activity section styling.

New (static chrome, per decision — full index.html look):

  • viator_hero → the homepage hero + booking form (section.hero + form.booking with Location/Date/Seats). Location select is populated from viator_destination terms; submit routes to the listing archive with the chosen filters (real behavior, not a console stub).
  • viator_faq → section.faq accordion ([data-accordion]). Content supplied via shortcode attributes / inner content or a simple options list (final content source TBD in plan).
  • viator_testimonials → section.testimonials marquee ([data-marquee]). Content via shortcode attributes/inner content.

The About and Articles sections from index.html are ordinary editorial content; they are not plugin shortcodes (the user builds them with the theme/builder). Noted here to bound scope.

6. Out of scope

  • Site header, primary nav, burger menu, sticky header, site footer (theme-owned).
  • Any change to importing, cron, API, CPT/taxonomy registration, meta keys, or query/filter logic.
  • Client-side filtering/sorting/pagination (server-side retained).
  • About / Articles homepage sections as plugin features.

7. Risks & mitigations

  • Style leakage / theme conflicts → strict #vc-app scoping; drop global resets; no header/footer CSS. Verify against Astra.
  • Generic class collisions (.container, .btn, .hero) → all scoped under #vc-app; test with theme active.
  • Duration option mismatch (mockup "1/3/7 days" vs plugin half/full/multi buckets) → use the plugin's real buckets with mockup styling.
  • Icon coverage → any glyph used by a template but missing from the sprite is added to sprite.svg.
  • Ring-badge dependency → ensure #ringPathScroll path def is in the sprite so the rotating badge renders.

8. Acceptance criteria

  • Listing, single, and homepage sections visually match the mockup within the plugin's content area, with the theme's header/footer around them.
  • All dynamic values render from real tour data; no demo images/text ship as content.
  • Server-side filtering, sorting, and pagination work via GET params as before; shareable URLs preserved.
  • No new console errors; Lucide and noUiSlider no longer loaded.
  • No style bleed into the theme outside #vc-app (verified with Astra).