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
14 KiB
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-appscope (already used byshortcode_listingandsingle-viator_tour.php). Homepage section shortcodes get wrapped in#vc-apptoo. - 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/bodyare dropped or converted to scoped equivalents (#vc-app,#vc-app *).scroll-behavior,overflow-x:hiddenonhtml/bodyare 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) viavc_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.svgand 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 withvc_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 —
.railhorizontal 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
#ringPathScrollin the sprite; animation is CSS). - Hero booking/availability form
preventDefaultdemo 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--archivewith eyebrow pill,h1, and the horizontal filter barform.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 like0-50,50-150,150-1000; already parsed byget_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
- Search input →
- Results head →
.resultswith#resultCount("Showing X–Y of Z tours", server values) and a "Clear filters" link when filters active. - Grid →
ul.tgridofli.titem > article.dcard.dcard--widecards (see 5.2). Active-filter chips retained, restyled. - Pager → custom server-rendered pager matching
.pager/.pager__numdesign (replacespaginate_linksdefault 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-tourwithhero-tour__photo(primary image),hero-tour__kicker("Welcome to"),hero-tour__word(destination/title), decorativehero-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.factsofli.fact(Duration, Max Travelers, Free Cancellation, Category) from_vc_duration_minutes,_vc_max_travelers,_vc_free_cancellation,viator_tag. - Overview →
section.tour__blockwiththe_content(). - Gallery →
.gallery(hero + thumbs) from_vc_gallery_image_urls. - Includes / Excludes →
.listsof.ilistwith.tick--yes/--nofrom_vc_inclusions/_vc_exclusions. - Good to know →
tour__blockfrom_vc_additional_info. - Cancellation policy →
.policyof.statcard--yes/--norefund 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--tealFree Cancellation +statcard--yesReserve-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-navprev/next) over aul.rail.rail--destof.dcards. (card-destination.php)viator_top_tours→ "Most Liked Tours" rail (same rail pattern,card-tour.phpindcardvariant).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.bookingwith Location/Date/Seats). Location select is populated fromviator_destinationterms; submit routes to the listing archive with the chosen filters (real behavior, not a console stub).viator_faq→section.faqaccordion ([data-accordion]). Content supplied via shortcode attributes / inner content or a simple options list (final content source TBD in plan).viator_testimonials→section.testimonialsmarquee ([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-appscoping; 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
#ringPathScrollpath 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).