Cabin by a mountain lake
4.92 (128)Hosted by Ingrid · 2 bedrooms · hot tub
12 to 17 Mar
€212€184 night· €920 total
Hosted by Ingrid · 2 bedrooms · hot tub
12 to 17 Mar
€212€184 night· €920 total
It is live: hover, focus and its own controls respond. Actions that would change data (approve, send, delete) are for your app to wire. The width is a real 390 px, 768 px or 1280 px box, shrunk to fit your screen.
<!-- Icons: Lucide (lucide.dev), ISC licence -->
<style>
.odl-stay-card { box-sizing: border-box; inline-size: 100%; max-inline-size: 24rem; margin-inline: auto; container-type: inline-size; color: var(--ds-color-text-primary); font: var(--ds-text-body-md); text-wrap: pretty; }
.odl-stay-card *, .odl-stay-card *::before, .odl-stay-card *::after { box-sizing: border-box; }
.odl-stay-card .odl-media { position: relative; border-radius: var(--ds-radius-container); overflow: hidden; background: var(--ds-color-surface-sunken); }
.odl-stay-card .odl-track { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; overscroll-behavior-x: contain; }
.odl-stay-card .odl-track::-webkit-scrollbar { display: none; }
.odl-stay-card .odl-slide { flex: 0 0 100%; aspect-ratio: 20 / 19; margin: 0; scroll-snap-align: start; }
.odl-stay-card .odl-slide img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
.odl-stay-card .odl-badge { position: absolute; inset-block-start: var(--ds-space-inset-md); inset-inline-start: var(--ds-space-inset-md); padding: var(--ds-space-inset-xs) var(--ds-space-inset-sm);
border-radius: var(--ds-radius-control-sm); background: var(--ds-color-white); color: var(--ds-color-neutral-light-12); font: var(--ds-text-label-md); box-shadow: var(--ds-elevation-raised); white-space: nowrap; }
.odl-stay-card .odl-over { position: absolute; inset: 0; pointer-events: none; }
.odl-stay-card .odl-over button { pointer-events: auto; }
.odl-stay-card button { display: grid; place-items: center; padding: 0; border: 0; cursor: pointer; font: inherit; }
.odl-stay-card .odl-fav { position: absolute; inset-block-start: var(--ds-space-inset-sm); inset-inline-end: var(--ds-space-inset-sm); inline-size: var(--ds-size-target-min); block-size: var(--ds-size-target-min);
border-radius: var(--ds-radius-full); background: transparent; color: var(--ds-color-white); }
.odl-stay-card .odl-fav svg { inline-size: var(--ds-size-icon-lg); block-size: var(--ds-size-icon-lg); overflow: visible; filter: drop-shadow(0 var(--ds-space-2) var(--ds-space-4) var(--ds-color-shadow-key)); }
.odl-stay-card .odl-fav path { fill: var(--ds-color-neutral-alpha-light-9); stroke: var(--ds-color-white); stroke-width: var(--ds-icon-stroke-lg);
transition: fill var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-stay-card .odl-fav[aria-pressed="true"] path { fill: var(--ds-color-danger-light-9); }
.odl-stay-card .odl-fav[aria-pressed="true"] svg { animation: odl-stay-pop var(--ds-motion-duration-medium) var(--ds-motion-easing-enter); }
@keyframes odl-stay-pop { 50% { transform: scale(1.2); } }
.odl-stay-card .odl-nav { position: absolute; inset-block-start: 50%; inline-size: var(--ds-size-target-min); block-size: var(--ds-size-target-min); transform: translateY(-50%);
border-radius: var(--ds-radius-full); background: transparent; opacity: 0; transition: opacity var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-stay-card .odl-nav span { display: grid; place-items: center; inline-size: var(--ds-size-control-sm); block-size: var(--ds-size-control-sm); border-radius: var(--ds-radius-full);
background: var(--ds-color-white); color: var(--ds-color-neutral-light-12); box-shadow: var(--ds-elevation-raised); }
.odl-stay-card .odl-nav svg { inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); stroke-width: var(--ds-icon-stroke-lg); }
.odl-stay-card .odl-prev { inset-inline-start: var(--ds-space-inset-xs); }
.odl-stay-card .odl-next { inset-inline-end: var(--ds-space-inset-xs); }
.odl-stay-card .odl-media:hover .odl-nav, .odl-stay-card .odl-nav:focus-visible { opacity: 1; }
.odl-stay-card .odl-nav[disabled] { visibility: hidden; }
@media (hover: none) { .odl-stay-card .odl-nav { display: none; } }
.odl-stay-card .odl-dots { position: absolute; inset-inline: 0; inset-block-end: var(--ds-space-inset-md); display: flex; justify-content: center; gap: var(--ds-space-6); pointer-events: none; }
.odl-stay-card .odl-dots i { inline-size: var(--ds-space-6); block-size: var(--ds-space-6); border-radius: var(--ds-radius-full); background: var(--ds-color-white); opacity: 0.55;
transition: opacity var(--ds-motion-duration-short) var(--ds-motion-easing-standard), transform var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-stay-card .odl-dots i.is-on { opacity: 1; transform: scale(1.25); }
.odl-stay-card .odl-body { display: grid; gap: var(--ds-space-stack-xs); padding-block-start: var(--ds-space-stack-md); }
.odl-stay-card .odl-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--ds-space-inline-sm); }
.odl-stay-card h3 { margin: 0; min-inline-size: 0; text-wrap: balance; font: var(--ds-text-title-md); }
.odl-stay-card .odl-rate { display: inline-flex; align-items: center; gap: var(--ds-space-inline-xs); flex: none; font: var(--ds-text-label-lg); font-variant-numeric: tabular-nums; }
.odl-stay-card .odl-rate svg { inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); color: var(--ds-color-text-primary); fill: currentColor; stroke-width: var(--ds-icon-stroke-sm); }
.odl-stay-card .odl-rate small { color: var(--ds-color-text-secondary); font: var(--ds-text-label-md); }
.odl-stay-card .odl-line { margin: 0; color: var(--ds-color-text-secondary); font: var(--ds-text-body-md); }
.odl-stay-card .odl-price { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: max(var(--ds-space-6), var(--ds-space-inline-xs)); margin: var(--ds-space-stack-xs) 0 0; font: var(--ds-text-body-md); }
.odl-stay-card .odl-price strong { font: var(--ds-text-label-lg); font-variant-numeric: tabular-nums; }
.odl-stay-card .odl-price s { color: var(--ds-color-text-tertiary); font-variant-numeric: tabular-nums; }
.odl-stay-card .odl-total { color: var(--ds-color-text-secondary); font: var(--ds-text-body-md); font-variant-numeric: tabular-nums; }
.odl-stay-card .odl-link { color: inherit; text-decoration: none; border-radius: var(--ds-radius-nested); }
.odl-stay-card .odl-link::after { content: ""; position: absolute; inset: 0; }
.odl-stay-card .odl-card { position: relative; display: grid; }
.odl-stay-card .odl-media { z-index: 1; }
.odl-stay-card button:focus-visible, .odl-stay-card .odl-link:focus-visible { outline: var(--ds-focus-ring-width) solid var(--ds-color-border-focus); outline-offset: var(--ds-focus-ring-offset); }
.odl-stay-card .odl-sr { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .odl-stay-card, .odl-stay-card *, .odl-stay-card *::before { transition: none !important; animation: none !important; scroll-behavior: auto !important; } }
</style>
<article class="odl-stay-card" aria-label="Cabin by a mountain lake">
<div class="odl-card">
<div class="odl-media">
<div class="odl-track" tabindex="-1" aria-roledescription="carousel" aria-label="Photos, 1 of 4">
<figure class="odl-slide"><img src="https://images.unsplash.com/photo-1559310589-2673bfe16970?w=768&h=730&fit=crop&auto=format&q=70" alt="Turquoise lake below snowy mountains, the view from the cabin" width="384" height="365" loading="lazy" decoding="async"></figure>
<figure class="odl-slide"><img src="https://images.unsplash.com/photo-1583847268964-b28dc8f51f92?w=768&h=730&fit=crop&auto=format&q=70" alt="Living room with a large window" width="384" height="365" loading="lazy" decoding="async"></figure>
<figure class="odl-slide"><img src="https://images.unsplash.com/photo-1611892440504-42a792e24d32?w=768&h=730&fit=crop&auto=format&q=70" alt="Bedroom with a wooden bed and a garden view" width="384" height="365" loading="lazy" decoding="async"></figure>
<figure class="odl-slide"><img src="https://images.unsplash.com/photo-1618773928121-c32242e63f39?w=768&h=730&fit=crop&auto=format&q=70" alt="Bed made with white linen and pillows" width="384" height="365" loading="lazy" decoding="async"></figure>
</div>
<span class="odl-badge">Guest favourite</span>
<div class="odl-over">
<button type="button" class="odl-fav" aria-label="Save to favourites" aria-pressed="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/></svg></button>
<button type="button" class="odl-nav odl-prev" aria-label="Previous photo" disabled><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg></span></button>
<button type="button" class="odl-nav odl-next" aria-label="Next photo"><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg></span></button>
</div>
<div class="odl-dots" aria-hidden="true"><i class="is-on"></i><i></i><i></i><i></i></div>
</div>
<div class="odl-body">
<div class="odl-row">
<h3><a class="odl-link" href="#stay">Cabin by a mountain lake</a></h3>
<span class="odl-rate"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"/></svg>4.92 <small>(128)</small></span>
</div>
<p class="odl-line">Hosted by Ingrid · 2 bedrooms · hot tub</p>
<p class="odl-line">12 to 17 Mar</p>
<p class="odl-price"><s aria-label="was €212">€212</s><span><strong>€184</strong> night</span><span class="odl-total">· €920 total</span></p>
</div>
</div>
</article>
<script>
document.querySelectorAll(".odl-stay-card").forEach((root) => {
if (root.dataset.odlReady) return; // wire once, even if the file is pasted twice
root.dataset.odlReady = "1";
const track = root.querySelector(".odl-track"), dots = root.querySelectorAll(".odl-dots i");
const prev = root.querySelector(".odl-prev"), next = root.querySelector(".odl-next"), n = dots.length;
const at = () => Math.round(track.scrollLeft / track.clientWidth);
const show = () => {
const i = Math.min(n - 1, Math.max(0, at()));
dots.forEach((d, k) => d.classList.toggle("is-on", k === i));
prev.disabled = i === 0; next.disabled = i === n - 1;
track.setAttribute("aria-label", `Photos, ${i + 1} of ${n}`);
};
const smooth = () => (matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth");
const go = (d) => track.scrollTo({ left: (at() + d) * track.clientWidth, behavior: smooth() });
prev.addEventListener("click", () => go(-1));
next.addEventListener("click", () => go(1));
track.addEventListener("scroll", show, { passive: true });
root.querySelector(".odl-fav").addEventListener("click", (e) => {
const b = e.currentTarget, on = b.getAttribute("aria-pressed") !== "true";
b.setAttribute("aria-pressed", String(on)); b.setAttribute("aria-label", on ? "Saved to favourites" : "Save to favourites");
});
show();
});
</script>
The top of a product page: a swipeable photo gallery with dots on a phone and thumbnails from tablet up, name, sale price, rating, colour swatches, size chips with one sold out, Add to bag and save.
Three plans as cards you select, with a monthly or yearly switch that changes the prices and one plan marked as the common choice.
A personal account: a card drawn in the brand colours with its number masked until you choose to show it, the balance, add money, send and a freeze switch, and recent transactions with a simple mark per merchant and a photo for people.