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

300 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `.dcard`s. (`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).