Search Interaction

A small study of the moment between asking and finding.

About the interaction

A small study on what a search field should feel like while it works. The empty field comes alive with a soft, liquid orb and a quiet "Creating..." label, so the wait feels calm instead of frozen. Built with a few layers of gradients, blur and slow, looping motion.

Prompt

Paste this into your AI tool of choice to recreate the interaction.

Build a single, self-contained HTML file (HTML, CSS and a few lines of JavaScript, no libraries) that shows a search field "waking up" while it works.

Layout
- Centre everything on a light grey page (#e4e4e4).
- The search field is a wide white pill: about 560px wide, 84px tall, fully rounded. Give it a very subtle top-to-bottom white gradient, a thin bright inner highlight along the top edge, and a soft, diffused drop shadow so it feels slightly raised.
- Inside the field, on the left, there is a round orb (about 56px). To its right, a label that reads "Creating..." in a light cool grey (#a7adba), around 28px, regular weight.

The orb
- It should look like a small glossy sphere of liquid colour: a deep blue base (#2326c4) with soft swirls of magenta (#d2368c), blush pink (#f4a9c6) and a darker navy (#1a1470).
- Build the swirls from three blurred colour blobs inside a circular mask, each rotating continuously at a different speed and direction (roughly 7s, 9s and 12s), so the colours slowly churn and never repeat in an obvious way.
- Add an inner shadow at the bottom right and a small blurred white highlight at the top left so it reads as a 3D sphere.
- Surround it with a soft pink glow ring that slightly spills past its edge.

Motion
- Start in an idle state: an empty field, no orb and no label.
- After about 1.2 seconds, switch to a working state: the orb scales up from 40% to 100% with a gentle overshoot (spring-like easing) while fading in, and the label fades in and slides a few pixels from the left with a short delay after the orb.
- Loop the whole sequence every 9 seconds so it can be watched again.
- Respect prefers-reduced-motion by stopping the swirling.

Keep the code clean and commented, with colours defined as CSS variables at the top.

Code

The same interaction as a single HTML file. Save it and open it in a browser.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Search Interaction</title>
<style>
  :root {
    --bg: #e4e4e4;
    --field: #fbfbfb;
    --label: #a7adba;
    --blue: #2326c4;
    --navy: #1a1470;
    --pink: #d2368c;
    --blush: #f4a9c6;
  }

  body {
    margin: 0;
    min-height: 100vh;
    display: grid;
    place-items: center;
    background: var(--bg);
    font-family: "Inter", system-ui, sans-serif;
  }

  /* the search field: soft white pill with a light top edge */
  .field {
    width: min(560px, 90vw);
    height: 84px;
    padding: 0 18px;
    display: flex;
    align-items: center;
    gap: 18px;
    border-radius: 999px;
    background: linear-gradient(180deg, #ffffff 0%, var(--field) 100%);
    box-shadow:
      inset 0 2px 0 rgba(255, 255, 255, 0.9),
      0 18px 40px rgba(0, 0, 0, 0.08),
      0 2px 6px rgba(0, 0, 0, 0.05);
  }

  /* the orb: a glowing ring around a slowly churning liquid sphere */
  .orb {
    position: relative;
    flex: none;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    opacity: 0;
    transform: scale(0.4);
    transition:
      opacity 0.5s ease,
      transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  .orb::before {
    content: "";
    position: absolute;
    inset: -9px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(244, 169, 198, 0.6) 58%, rgba(244, 169, 198, 0) 74%);
    filter: blur(2px);
  }

  .liquid {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    overflow: hidden;
    background: var(--blue);
    box-shadow: inset -6px -8px 14px rgba(20, 10, 80, 0.45), inset 5px 6px 10px rgba(255, 255, 255, 0.25);
  }

  /* three colour blobs drifting at different speeds create the "liquid" */
  .liquid span {
    position: absolute;
    inset: -35%;
    border-radius: 42%;
    filter: blur(4px);
    animation: churn 7s linear infinite;
  }

  .liquid span:nth-child(1) {
    background: radial-gradient(ellipse 30% 22% at 32% 38%, var(--pink) 0 60%, transparent 100%);
  }

  .liquid span:nth-child(2) {
    background: radial-gradient(ellipse 18% 26% at 66% 60%, var(--blush) 0 50%, transparent 100%);
    animation-duration: 9s;
    animation-direction: reverse;
  }

  .liquid span:nth-child(3) {
    background: radial-gradient(ellipse 34% 20% at 55% 74%, var(--navy) 0 55%, transparent 100%);
    animation-duration: 12s;
  }

  /* a small glossy highlight keeps it reading as a sphere */
  .liquid::after {
    content: "";
    position: absolute;
    top: 12%;
    left: 18%;
    width: 34%;
    height: 22%;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.35);
    filter: blur(3px);
  }

  .label {
    font-size: 28px;
    letter-spacing: -0.01em;
    color: var(--label);
    opacity: 0;
    transform: translateX(-8px);
    transition:
      opacity 0.6s ease 0.25s,
      transform 0.6s ease 0.25s;
  }

  .field.is-working .orb {
    opacity: 1;
    transform: scale(1);
  }

  .field.is-working .label {
    opacity: 1;
    transform: none;
  }

  @keyframes churn {
    to {
      transform: rotate(360deg);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .liquid span {
      animation: none;
    }
  }
</style>
</head>
<body>
  <div class="field" role="status" aria-live="polite">
    <div class="orb" aria-hidden="true">
      <div class="liquid"><span></span><span></span><span></span></div>
    </div>
    <span class="label">Creating...</span>
  </div>

  <script>
    // idle -> working -> idle, on a loop: the field rests, then wakes up
    const field = document.querySelector(".field");
    const cycle = () => {
      field.classList.remove("is-working");
      setTimeout(() => field.classList.add("is-working"), 1200);
    };
    cycle();
    setInterval(cycle, 9000);
  </script>
</body>
</html>