feat: redesign homepage with responsive hero, verdict card preview, and modular landing components
- Extract LandingNav, LiveTicker, StatsRow, VerdictCard, and HeroSearch into reusable landing components - Implement responsive two-layout strategy: mobile stacked (hero search + verdict card + CTA) vs desktop inline pill input with verdict card sidebar - Add serif/mono font tokens and live-dot pulse animation to CSS - Move verdict card above search input on mobile, to right sidebar on desktop - Replace hero "fill up now" mockup with dynamic VerdictCard showing top stations, pricing, and recommendation - Simplify navigation with uppercase tracking, add Fleet anchor, and gate CTA by auth state - Lazy-load LeafletMap with defineAsyncComponent to reduce initial bundle - Relocate SearchBar below hero on search attempt for persistent filter UI - Add meta description for SEO
This commit is contained in:
73
resources/js/components/landing/VerdictCard.vue
Normal file
73
resources/js/components/landing/VerdictCard.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{ 'max-w-md mx-auto': variant === 'full' }"
|
||||
class="bg-zinc-50 border border-zinc-300 rounded-3xl p-5 md:p-6 shadow-[0_20px_40px_-20px_rgba(74,63,59,0.25)]"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between gap-3 mb-5">
|
||||
<div class="min-w-0">
|
||||
<p class="font-mono text-[10px] uppercase tracking-widest text-zinc-500 mb-1 truncate">
|
||||
Today near {{ postcode }}
|
||||
</p>
|
||||
<h3
|
||||
:class="variant === 'compact' ? 'text-3xl' : 'text-4xl'"
|
||||
class="font-serif text-accent leading-none"
|
||||
>
|
||||
{{ verdict }}
|
||||
</h3>
|
||||
</div>
|
||||
<span class="inline-flex items-center gap-1.5 shrink-0 font-mono text-[10px] uppercase tracking-widest text-zinc-600 bg-white border border-zinc-300 rounded-full px-2.5 py-1">
|
||||
<span class="size-1.5 rounded-full bg-status-good live-dot"></span>
|
||||
Live
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Station rows -->
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(station, idx) in stations"
|
||||
:key="station.name + idx"
|
||||
class="flex items-center gap-3 bg-white border border-zinc-300 rounded-xl px-3 py-2.5"
|
||||
>
|
||||
<span class="font-mono text-xs text-zinc-500 tabular-nums">{{ String(idx + 1).padStart(2, '0') }}</span>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-semibold text-sm text-zinc-800 truncate">{{ station.name }}</p>
|
||||
<p class="font-mono text-[11px] text-zinc-500 mt-0.5">{{ station.distance }}</p>
|
||||
</div>
|
||||
<span
|
||||
v-if="station.tag"
|
||||
class="font-mono text-[9px] uppercase tracking-widest bg-accent text-white rounded-full px-2 py-0.5"
|
||||
>
|
||||
{{ station.tag }}
|
||||
</span>
|
||||
<span class="font-mono font-medium text-sm text-zinc-900 tabular-nums">{{ station.price }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Footer -->
|
||||
<p v-if="moreCount" class="font-mono text-[11px] text-zinc-500 mt-4 text-center">
|
||||
+ {{ moreCount }} more stations within {{ radiusMiles }} miles
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
postcode: { type: String, default: 'SW1A 1AA' },
|
||||
verdict: { type: String, default: 'Fill up today' },
|
||||
stations: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ name: 'Tesco Victoria', distance: '0.4 mi', price: '142.9p', tag: 'Cheapest' },
|
||||
{ name: "Sainsbury's Nine Elms", distance: '1.4 mi', price: '143.9p', tag: null },
|
||||
],
|
||||
},
|
||||
moreCount: { type: Number, default: 21 },
|
||||
radiusMiles: { type: Number, default: 5 },
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'full',
|
||||
validator: (v) => ['full', 'compact'].includes(v),
|
||||
},
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user