Book a callGitHub10
Turquoise lake below snowy mountains, the view from the cabin
Living room with a large window
Bedroom with a wooden bed and a garden view
Bed made with white linen and pillows
Guest favourite

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.

Source
<!-- 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>

More like this

B2BFriendly · SaaS

Plan picker

Three plans as cards you select, with a monthly or yearly switch that changes the prices and one plan marked as the common choice.

ConsumerFriendly · Personal finance

Card wallet

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.

Your kit

Nothing here yet. Add parts with Add to kit, and a look if you want the whole system.

    Parts
    0
    Files to fetch
    0 KB
    Licence
    MIT
    Total
    Free, now and later

    How do you want it?

    Your prompt

    No sign-up. Nothing leaves your browser until you paste it.

    Free