Skip to main content

Rentiva v4.37.2 — Vendor profile UX polish

· 8 min read
MaxHandMade
Maintainer

Seven UX findings on the vendor profile page deferred from v4.37.0/v4.37.1 close in v4.37.2: a deterministic SVG initials avatar that replaces the Gravatar mystery-man silhouette without breaking third-party avatar plugins, a vehicle thumbnail cascade that falls back through featured image → gallery → compact card, an SEO defaults class that yields to Yoast / Rank Math / AIOSEO when present, a hero CTA stack with filterable URLs, scoped CSS class names, and a mobile breakpoint that finally collapses small screens to a single column.

Why a polish release

v4.37.0 shipped the vendor profile page as a Pro feature. v4.37.1 hot-patched three functional bugs that smoke testing surfaced (badge wiring, rating-count semantic mismatch, review-stars meta-key fallback). What was left were the design rough edges — small things that don't break the page but make it look unfinished:

  • A grey Gravatar silhouette where every vendor without a registered Gravatar email looked identical.
  • Vehicle cards with no photo and a single glyph next to a number, while the hero showed ★★★★½.
  • A <title>Rentiva Dev</title> browser tab on the live profile, with no <meta name="description"> either.
  • A KONUM section duplicating the city already in the hero.
  • A .label class with no .mhm- prefix sitting next to theme defaults.
  • A 3-vehicle vendor showing two cards on row one and a solo orphan on row two.
  • No call-to-action — the page was passive information with no obvious next click.

Each of these is a small fix on its own. The constraint is that the plugin is global — it has to land on every theme out there, with every avatar plugin, with every SEO plugin. So the polish isn't really about CSS. It's about choosing the right ecosystem hook for each fix so the plugin doesn't fight the rest of the site.

B4 — Deterministic SVG initials avatar

The Gravatar mystery-man fallback is recognisable but it's the same silhouette for every vendor without a registered email. With three or four vendors on a directory page (coming in v4.38.0), the visual sameness reads as "these vendors aren't real."

VendorAvatarFallback substitutes a coloured SVG circle bearing the vendor's two-letter initials. The colour comes from a deterministic hash of the display name (Slack / Gmail / Material pattern):

$hash = crc32($display_name);
$hue = $hash % 360;
return hsl_to_hex($hue, 55, 50);

Same name → same colour every render. Different vendors get visually distinct avatars without us picking a brand palette (we don't know the host site's brand).

The implementation hooks get_avatar_data at priority 99 — the default WordPress avatar pipeline runs at priority 10, and third-party avatar plugins (Simple Local Avatars, Avatar Privacy, WP User Avatar) typically register there too. By running last, we only act when nothing earlier resolved a real avatar. We further check that the URL we'd be replacing is actually a Gravatar mystery-man placeholder (?d=mm / ?d=mp / ?d=blank) — a real Gravatar URL or a custom 3rd-party-resolved URL passes through unchanged.

The SVG is inline (data:image/svg+xml;utf8,...) — no external HTTP request to render the avatar.

There's a small but instructive bonus bug we caught here: WordPress's esc_url() strips the data: scheme by default — it's not on the protocol whitelist. The first browser smoke after wiring the fallback showed empty src="" attributes (browsers fell back to the page URL as a relative reference). The hero partial now branches on the scheme:

$is_inline_svg = strpos($url, 'data:image/svg+xml') === 0;
echo $is_inline_svg ? esc_attr($url) : esc_url($url);

esc_attr() is safe here because the SVG payload is built by our own class — no remote content, no user input.

B5 — Vehicle thumbnail cascade

Provider::collect_vehicles() now resolves the card thumbnail through three steps:

  1. WP featured image (get_the_post_thumbnail_url).
  2. First attachment ID in the Rentiva gallery meta — supports the canonical _mhm_rentiva_gallery_images plus the legacy _mhm_gallery_images and _mhm_rentiva_gallery aliases that VehicleDetails::get_gallery() already supports.
  3. Empty string — the template renders a --compact modifier card with no image element instead of showing a generic "no image" placeholder rectangle.

The compact card is a deliberate choice. A site with five photographed vehicles and one without should not show four photographed cards plus one card with a grey "no image" rectangle — that one card sticks out. A subtly different compact card with just title + rating reads as intentional, not broken.

Vehicle card rating now also uses the same stars_html() helper as the hero — full half-star bar instead of a single glyph. Consistent rating display across the page.

B6 — show_location default flipped to no

This one's a near-deletion that we're keeping forward-compatible. The location section duplicated the city already in the hero (📍 Antalya · 2024'den beri üye), so it added zero information.

The plan reserves the section for the v4.40.0+ Transfer Map enrichment — a real map pin makes the section worth its space. Until then, layouts that want it render explicitly with show_location="yes". The default just doesn't show it. Block.json default and Elementor widget control default both flipped to match the shortcode.

B7 — .label.mhm-vendor-section-label

A generic .label class is a collision waiting to happen — Bootstrap, theme starter kits, and several form plugins all ship a .label style. Renamed across all four section partials and the matching CSS rule. This is the kind of fix where you can't tell anything changed, which is exactly the point.

B8 — SEO defaults that yield to SEO plugins

VendorProfileSeo::register() runs on every vendor profile request, but the first thing it does is check for active SEO plugins:

return class_exists('WPSEO_Frontend')                   // Yoast
|| defined('WPSEO_VERSION') // Yoast (alt)
|| class_exists('RankMath') // Rank Math
|| defined('RANK_MATH_VERSION') // Rank Math (alt)
|| defined('AIOSEO_VERSION') // All-in-One SEO
|| class_exists('AIOSEO\\Plugin\\AIOSEO') // AIOSEO Pro
|| class_exists('Smartcrawl_Init') // SmartCrawl
|| defined('SEOPRESS_VERSION') // SEOPress
|| defined('THE_SEO_FRAMEWORK_VERSION'); // The SEO Framework

If anything matches, we register no filters and emit no head tags. Those plugins have richer settings, dedicated UI for vendor-style content, and their users have already configured them — the last thing they need is our extra <meta name="description"> competing with theirs.

When no real SEO plugin is active, we register two things:

  • A document_title_parts filter that replaces the theme default title with the vendor's display name. The site name part is left for themes that show it.
  • A wp_head action at priority 1 that emits <meta name="description"> from the vendor's bio, trimmed to 155 characters at a word boundary so the description doesn't cut mid-word.

Site owners can opt out entirely:

add_filter('mhm_rentiva_vendor_profile_seo_disable', '__return_true');

B9 — Hero CTA stack

The vendor profile was passive — read about a vendor, browse their cars, leave. The CTA stack adds a clear next click without bloating the hero:

<div class="mhm-vendor-hero-cta">
<a class="mhm-vendor-hero-cta-primary" href="#mhm-vendor-vehicles">View vehicles</a>
<!-- optional secondary -->
<a class="mhm-vendor-hero-cta-secondary" href="...">Send message</a>
</div>

The primary CTA is always shown — a same-page anchor scroll to the vehicle grid. The secondary CTA only renders when mhm_rentiva_vendor_profile_message_url filter returns a non-empty URL, so the button doesn't appear on sites that haven't wired a vendor messaging surface.

Both label and URL are filterable. A site with an external booking system can replace the primary CTA with a deep link; a directory site without messaging can hide the secondary entirely without overriding the partial.

B10 — Mobile breakpoint 480px → 600px

The previous breakpoint at 480px meant phone-landscape (568px) and small tablets (480-768px range) showed two cards per row. With three vehicles the third card was a solo orphan on row two.

The breakpoint moved to 600px so anything below that — phone-landscape, small Android tablets, narrow custom layouts — collapses to a single column. With the cascade making more cards possible (gallery fallback when featured isn't set), the wider single-column zone reads better.

Tests

Suitev4.37.1v4.37.2
PHPUnit total9871007
Assertions31703208
New regression filesVendorAvatarFallbackTest, VendorProfileSeoTest, VendorVehicleThumbnailCascadeTest, VendorProfileShowLocationDefaultTest
PHPCS errors00

Browser smoke validated all three test vendors at desktop (1920px) and mobile (390px). The existing VendorProfileShortcodeTest::test_max_vehicles_attribute_caps_vehicle_count regression also got an assertion update because v4.37.2 introduced a mhm-vendor-vehicle-card--compact modifier on the same class attribute, so the previous exact class="mhm-vendor-vehicle-card" substring no longer matched. The assertion now uses a regex that matches the start of the card class on <a> openings.