Components
Activity Card
BetaActivityCard presents an excursion, tour, or experience with difficulty level, duration, price, and a booking CTA — used in destination activity grids and holiday extras.
Maasai Mara Sunrise Safari
Board a 4x4 at first light for the most rewarding game-viewing window of the day. Your expert guide will navigate the Mara triangle in search of the Big Five.
From
£195
per person
Purpose
ActivityCard surfaces excursions, tours, and experiences that a traveller can add to their holiday. It appears in the "Things to do" section of destination pages, in the "Add extras" step of the booking funnel, and in curated experience guides ("Best of Kenya", "Maldives activities").
The component answers four questions that determine whether a traveller will book an activity:
- What is it? — title and description
- Where? — location with a map pin
- How long and how hard? — duration and difficulty
- How much? — price with a clear "Book now" CTA
The difficulty badge is the most distinctive element of ActivityCard compared to other card types. It uses semantic colour (green / amber / red) to convey physical demand at a glance — critical for safari walks, trekking excursions, or ski touring where the physical demand is a genuine filter criterion.
Usage
Reach for ActivityCard when presenting a bookable excursion, tour, or experience — as opposed to AccommodationCard (a property) or a TripCard (a full package). The distinguishing signal is the difficulty pill, so use ActivityCard specifically when physical demand is a meaningful filter criterion for the traveller.
It typically appears in a grid within the "Things to do" section of a destination page, in the "Add extras" step of a booking funnel, or within curated experience guides.
Anatomy
Coral Reef Snorkel Tour
Explore the vibrant house reef at North Malé Atoll with a marine biologist guide who reveals the hidden life between the corals.
From
£85
per person
- 1Image — 3:2 aspect ratio, object-cover. Optional badge pill in teal sits absolute top-left (e.g. "Popular", "Bestseller"). Difficulty pill sits absolute top-right in green/amber/red depending on value.
- 2Title — 16px bold. The activity name. Not a link — the "Book now" CTA and the href carry the interaction.
- 3Description — 14px grey, clamped to 2 lines. A short sentence or two about what the activity involves. Full detail is on the activity page.
- 4Meta row — Location (MapPin icon) and duration (Clock icon) in 13px grey, separated by a middle dot.
- 5Price block — "From £X" in 18px bold. "per person" in 12px grey below.
- 6CTA — "Book now" teal button linking to href. Full width on mobile, right-aligned on desktop.
Variants
Easy — green difficulty badge
difficulty="Easy" renders a green pill. Use for activities accessible to all fitness levels and ages: boat trips, guided tours, beach walks, snorkelling on calm reefs.
Glass-Bottom Boat Reef Tour
Float over the coral gardens of the South Malé Atoll in a traditional dhoni fitted with a glass viewing panel. No swimming required.
From
£65
per person
Moderate — amber difficulty badge
difficulty="Moderate" renders an amber pill. Use for activities that require some fitness or capability but are achievable for most adults: safari game walks, half-day bike rides, snorkelling in current, introductory diving.
Serengeti Walking Safari
Leave the vehicle behind and track wildlife on foot with an armed ranger. A transformative perspective on the bush — you will notice details invisible from a 4x4.
From
£220
per person
Challenging — red difficulty badge
difficulty="Challenging" renders a red pill. Use for activities with genuine physical demands or technical requirements: gorilla trekking through steep forest, ski touring, summit hikes, advanced diving. The red pill is a clear pre-filter — travellers self-select before clicking through.
Mountain Gorilla Trek
Trek through the dense rainforest of the Volcanoes National Park to spend one unforgettable hour with a wild gorilla family. Altitude 2,500–4,500m. Fitness required.
From
£1,500
per person
With teal badge
The optional badge prop renders a teal pill on the top-left of the image. Use for editorial signals like "Popular", "Bestseller", or "Limited spaces". The teal badge coexists with the difficulty pill on the opposite corner.
Private Sunset Dhow Cruise
Sail the sheltered waters of Stone Town harbour aboard a traditional wooden dhow as the sun sets over the Indian Ocean. Canapés and sundowners included.
From
£145
per person
Without difficulty
When difficulty is omitted the difficulty pill is hidden. Use this for activities where difficulty is not a relevant concept — city tours, cultural experiences, spa treatments, cookery classes.
Galle Spice and Cooking Class
Learn to prepare a three-course Sri Lankan feast from a local chef in a heritage kitchen in Galle Fort. All spices are sourced from the herb garden outside.
From
£95
per person
Ski activity
ActivityCard works equally well for ski and alpine experiences. Difficulty maps naturally to ski grading conventions.
Ski Touring — Haute Route
Follow in the footsteps of the legendary Chamonix to Zermatt high route with a IFMGA-certified mountain guide. Seven days of glacier travel and untracked powder.
From
£3,200
per person
States
Default
Card rests with a light shadow and #E5E5E5 border. The "Book now" button is teal/filled. The description is clamped to 2 lines.
Hover
Shadow deepens. Image zooms slightly (scale(1.02)) within its clipped container. The card does not scale. "Book now" button background darkens by 10% to indicate readiness.
Focus
A visible teal focus ring appears on the "Book now" button when focused via keyboard. The card itself is not a link — only the "Book now" button is interactive, so focus lands precisely on the action.
Sold out / unavailable (future)
When availability data indicates the activity is sold out, the "Book now" button renders as disabled with aria-disabled="true" and a "Sold out" label. The card otherwise displays normally — the image and information remain visible.
Best practices
Write descriptions that are specific and sensory — what will the traveller actually experience? What should they know before booking?
Two lines of description give enough context for the traveller to decide whether to click through.
Write placeholder or SEO-generic descriptions that do not help the traveller decide whether the activity suits them.
A generic description ('A great excursion for the whole family') gives no decision-making information.
Grade difficulty accurately. If gorilla trekking takes 6 hours at altitude, it is Challenging even if the experience itself is slow-paced.
The difficulty badge is a pre-filter — honest grading means fewer unsuitable bookings.
Default to Easy for all activities to make them appear accessible to the broadest audience.
Under-grading creates disappointed customers and costly support requests.
Write duration as a plain human string that matches how travellers think about time: '2 hours', 'Half day', 'Full day', '3 days'.
'Full day' and '3 hours' are clear, human-readable durations that answer the planning question.
Pass duration as an ISO 8601 duration string or a raw minute count without a label.
'PT3H30M' or '210 minutes' requires calculation — the traveller cannot quickly visualise it.
Use the teal badge prop for editorial labels ('Popular', 'Bestseller', 'Limited spaces') and leave difficulty for the physical grading.
Teal badge for editorial signal, difficulty badge for physical demand — each serves a distinct purpose.
Use the badge prop to convey difficulty information that is already shown in the difficulty pill.
Using the teal badge to repeat the difficulty ('Hard activity') makes both badges redundant.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | string | — | Activity name. Rendered as the primary heading in 16px bold. |
| image | { src: string; alt: string } | — | Card hero image at 3:2 aspect ratio. The alt text should describe the scene depicted, not just repeat the title. |
| description | string | — | Short description clamped to 2 lines. Gives the traveller enough context to decide whether to view the full activity page. |
| location | string | — | Activity location shown with a MapPin icon. e.g. 'Maasai Mara, Kenya', 'Stone Town, Zanzibar'. |
| duration | string | — | Human-readable duration shown with a Clock icon. e.g. '2 hours', 'Half day', 'Full day', '3 days'. |
| difficulty | 'Easy' | 'Moderate' | 'Challenging' | — | Physical difficulty level. 'Easy' renders a green pill, 'Moderate' an amber pill, 'Challenging' a red pill. When omitted, no difficulty pill is shown. |
| priceFrom | number | — | Lead price numeral. Displayed as 'From [currency][priceFrom]'. |
| currency | string | '£' | Currency symbol prepended to the price numeral. |
| priceBasis | string | 'per person' | Suffix below the price. e.g. 'per person', 'per group', 'per couple'. |
| href | string | — | URL for the activity detail page. The "Book now" button links here. |
| badge | string | — | Editorial label rendered in a teal pill on the top-left of the image. e.g. 'Popular', 'Bestseller', 'Limited spaces'. |
| className | string | — | Additional Tailwind classes on the root element. |
Accessibility
Interactive structure
Unlike AccommodationCard (where the whole card is a link), ActivityCard uses a button-based CTA model. The card root is a <div> — not an <a>. Only the "Book now" <button> (or <a>) is interactive. This is appropriate because activity cards are frequently displayed inside a booking flow where the card itself may also have a "select" checkbox or toggle state.
If the card is used in a pure listing context with no secondary state, you may wrap the card in an <a> and make the "Book now" button aria-hidden with tabIndex={-1} to avoid duplicate tab stops — the same pattern as AccommodationCard.
Difficulty badge colour
The difficulty pill uses three colours: green, amber, red. Colour alone is not sufficient — the pill always renders its text label ("Easy", "Moderate", "Challenging"). This means travellers who cannot distinguish the colours can still read the difficulty level.
The pill also has an aria-label of "Difficulty: [value]" for screen readers, so the context is not lost when the label is read in isolation from the visual pill shape.
Description clamp
The description is visually clamped to 2 lines with line-clamp-2. The full text is available in the DOM and is readable by screen readers. The truncation is purely visual — no content is hidden from assistive technology.
Image alt text
Describe the activity in progress, not just the location. "Land Rover crossing the Mara at sunrise" is better than "Kenya safari".
{/* ✅ Scene-descriptive */}
image={{ alt: 'Trekkers climbing through bamboo forest toward gorilla habitat, Rwanda' }}
{/* ❌ Title repetition — screen reader would hear title twice */}
image={{ alt: 'Mountain Gorilla Trek Rwanda' }}
"Book now" button label
The button label "Book now" is not descriptive in isolation — screen reader users scanning a page by interactive elements would hear "Book now, Book now, Book now" for every activity card. The button should have aria-label="Book [title]" so each button is uniquely identified:
{/* The component renders this internally */}
<a href={href} aria-label={`Book ${title}`} className="...">
Book now
</a>
Focus order
Within the card, focus order is: image (skipped — not focusable) → title (heading, not focusable) → description (not focusable) → "Book now" button. A single Tab stop per card keeps navigation efficient in a grid of activities.
Related components
- AccommodationCard — the equivalent card pattern for hotel properties rather than excursions.
- Card — the generic container this component specialises for activity content.