8pt

Hover Service Index

Hover Service Index•Text
Overview

A three-column services index: a monospace label on the left, a column of large bold service names in the middle (light gray until hovered or focused, then black), and a tall image on the right that crossfades and scales slightly to match. With no interaction it rotates through the list.

Taken from the page template Houtu Creative Studio Home. Use it on its own, or combine it with the other components from the same template to rebuild the full page.

Plain HTML, CSS and a little vanilla JS, with no framework dependency. Copy the prompt into Claude Code, Cursor, Trae or Codex and the model adds Hover Service Index to your project in your colors.

Where to use it

Animated headlines, testimonials, emphasis copy and large-type layouts.

Install

With Builder, install it with the 8pt CLI or call it directly from an editor connected to 8pt MCP.

$ npm i -g @8pt-dev/cli && 8pt add houtu-service-list
mcp › Use 8pt MCP to call get_component("houtu-service-list")

Code

Source for Hover Service Index. Motion uses only transform and opacity and respects the Reduce motion setting.

<div class="x-hsl"><p class="x-hsl__lbl">服务</p><div><ul class="x-hsl__list"><li><button type="button" class="is-on">品牌</button></li><li><button type="button">数字</button></li><li><button type="button">内容</button></li><li><button type="button">动态</button></li><li><button type="button">印刷</button></li></ul><a class="x-hsl__more" href="#">查看更多<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 12h15"/><path d="m13.5 6 6 6-6 6"/></svg></a></div><div class="x-hsl__frame"><img class="is-on" src="https://8pt.dev/api/media/0323c8430eefc3479f288435.webp" alt="彩色粗针织物"><img src="https://8pt.dev/api/media/753a33bd99dc9499d42f843e.webp" alt="版式海报与色卡"><img src="https://8pt.dev/api/media/ee76c50d97626dae74b76707.webp" alt="工作间里整理礼服的匠人"><img src="https://8pt.dev/api/media/5ecdf074ff94f69a69c7b033.webp" alt="漂浮的镜面缎带"><img src="https://8pt.dev/api/media/464d4d88e7c42a8bfe0d0409.webp" alt="黄色包装的能量棒"></div></div>
@import url("https://fonts.googleapis.com/css2?family=Fragment+Mono&family=Inter+Tight:wght@500;600;700&family=Noto+Sans+SC:wght@400;500;700;900&family=Noto+Serif+SC:wght@400;600&family=ZCOOL+QingKe+HuangYou&display=swap");.x-hsl{display:grid;grid-template-columns:22% 1fr 420px;width:100%;max-width:1160px;box-sizing:border-box;padding:40px 28px;background:#FFFFFF;color:#000;font-family:"Inter Tight","Noto Sans SC",system-ui,sans-serif}.x-hsl__lbl{margin:0;font-family:"Fragment Mono","Noto Sans SC",ui-monospace,monospace;font-size:12px;letter-spacing:.06em}.x-hsl__list{list-style:none;margin:0;padding:0}.x-hsl__list button{display:block;padding:0;border:0;background:none;font-family:inherit;font-size:66px;font-weight:700;line-height:1.04;letter-spacing:-.02em;color:#D6D5D3;cursor:pointer;transition:color .3s}.x-hsl__list button.is-on{color:#2B2A27}.x-hsl__more{display:inline-flex;align-items:center;gap:6px;margin-top:48px;height:28px;padding:0 14px;border-radius:6px;background:#F2F2EF;color:#000;font-size:13px;font-weight:500;text-decoration:none}.x-hsl__more svg{width:13px;height:13px}.x-hsl__frame{position:relative;width:420px;height:540px;border-radius:6px;overflow:hidden;background:#F2F2EF}.x-hsl__frame img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transform:scale(1.06);transition:opacity .6s,transform 1.2s cubic-bezier(.2,.7,.2,1)}.x-hsl__frame img.is-on{opacity:1;transform:none}@media (prefers-reduced-motion:reduce){.x-hsl__frame img,.x-hsl__list button{transition:none}}
(root) => {
  const btns = [...root.querySelectorAll(".x-hsl__list button")];
  const imgs = [...root.querySelectorAll(".x-hsl__frame img")];
  let cur = 0, timer = null;
  const pick = (i) => { cur = i; btns.forEach((b, k) => b.classList.toggle("is-on", k === i)); imgs.forEach((m, k) => m.classList.toggle("is-on", k === i)); };
  const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  const start = () => { if (reduce || timer) return; timer = setInterval(() => pick((cur + 1) % btns.length), 2600); };
  const stop = () => { clearInterval(timer); timer = null; };
  const hs = btns.map((b, i) => { const h = () => { stop(); pick(i); }; b.addEventListener("mouseenter", h); b.addEventListener("focus", h); b.addEventListener("click", h); return h; });
  root.addEventListener("mouseleave", start);
  start();
  return () => { stop(); root.removeEventListener("mouseleave", start); btns.forEach((b, i) => { b.removeEventListener("mouseenter", hs[i]); b.removeEventListener("focus", hs[i]); b.removeEventListener("click", hs[i]); }); };
…
Subscribe to Builder to see the full source.See pricing

Other components from this template

View all

Orbit Sphere Marquee

Chrome Outline Wordmark

Split Enquiry Form

Tagged Journal Cards

Hover Reveal Work Grid

Pill Link Footer

More text components

View all

Tabbed Logo Testimonial

Serif Statement Hero

Three Claim Cards

Hover Preview

Text Scramble

Typewriter Heading

More components

View all

Dot Matrix Glow Hero

Dotted Globe Features

Chaos Versus Ship Split

Segmented Pricing Tiers

Live Deploy Bento

Floating Tile CTA Banner

Aurora Prompt Hero

Integration Tile Grid

Keep exploring