About this snippet

Type a prompt and press generate. A colored border spins around the input while it works, three skeleton cards hold the layout, then images fade in over them in a bento grid.

The prompt box grows with your text by resetting its height to auto and then setting it to scrollHeight on every input event. That reset matters, because scrollHeight can only report a smaller value once the element has been allowed to collapse first. The spinning border is a conic-gradient on a pseudo element that only gets its colors while the generating class is applied.

Skeletons occupy the exact grid cells the results will use, so the layout never jumps when the images arrive, and each image gets a staggered animation-delay from its index. The 3.5 second timeout stands in for a real request, so drop your API call in its place and leave the skeleton and reveal steps where they are.

Size
8.8 KB
Uses
HTML, CSS, JavaScript
Loads
placehold.co
Views
234
<div class="ai-vision-app">
  <div class="ambient-glow" id="ambient-glow"></div>
  
  <div class="creation-station">
    <div class="prompt-header">
      <div class="ai-badge">
        <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3 7 7 3-7 3-3 7-3-7-7-3 7-3z"></path></svg>
        <span>Vision AI v5.0</span>
      </div>
      <h2>Imagine Anything.</h2>
    </div>
    
    <div class="prompt-wrapper" id="prompt-wrapper">
      <div class="prompt-inner">
        <textarea id="ai-prompt" class="prompt-box" placeholder="Describe a bioluminescent forest floating above the clouds at twilight..."></textarea>
        
        <div class="prompt-footer">
          <div class="setting-pill">Aspect 16:9</div>
          <button class="generate-btn" id="generate-btn">
            <span id="btn-text">Generate Art</span>
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
          </button>
        </div>
      </div>
    </div>
  </div>
  
  <div class="gallery-showcase" id="gallery-showcase">
    <!-- Empty state -->
    <div class="empty-state">
      <svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#334155" stroke-width="1"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>
      <p>Your masterpiece will appear here</p>
    </div>
  </div>
</div>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  margin: 0;
  background: #040914;
  color: #f8fafc;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  min-height: 100vh;
  padding: 60px 20px;
  overflow-x: hidden;
}

.ai-vision-app {
  position: relative;
  width: 100%;
  max-width: 900px;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 2;
}

.ambient-glow {
  position: fixed;
  top: -10%;
  left: 50%;
  transform: translateX(-50%);
  width: 600px;
  height: 400px;
  background: radial-gradient(circle, rgba(99, 102, 241, 0.15) 0%, transparent 70%);
  z-index: 1;
  pointer-events: none;
  transition: all 1s ease;
}

.ambient-glow.pulse-glow {
  background: radial-gradient(circle, rgba(236, 72, 153, 0.2) 0%, rgba(99, 102, 241, 0.2) 40%, transparent 70%);
  transform: translateX(-50%) scale(1.2);
}

.creation-station {
  width: 100%;
  position: relative;
  z-index: 10;
  margin-bottom: 60px;
}

.prompt-header {
  text-align: center;
  margin-bottom: 24px;
}

.ai-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(99, 102, 241, 0.1);
  color: #818cf8;
  padding: 6px 16px;
  border-radius: 30px;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin-bottom: 16px;
  border: 1px solid rgba(99, 102, 241, 0.2);
}

.prompt-header h2 {
  font-size: 3rem;
  font-weight: 700;
  margin: 0;
  letter-spacing: -1px;
  background: linear-gradient(135deg, #ffffff, #94a3b8);
  color: transparent;
  background-clip: text;
  -webkit-background-clip: text;
}

/* The Magic Wrapper for Conic Border */
.prompt-wrapper {
  position: relative;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 28px;
  padding: 2px; /* This creates the border thickness */
  overflow: hidden;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.6);
  transition: transform 0.3s ease;
}

.prompt-wrapper::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200%;
  height: 200%;
  background: rgba(255, 255, 255, 0.1);
  transform: translate(-50%, -50%);
  z-index: 0;
  transition: opacity 0.3s;
}

.prompt-wrapper.is-generating {
  transform: translateY(-5px);
  box-shadow: 0 40px 80px rgba(99, 102, 241, 0.2);
}

.prompt-wrapper.is-generating::before {
  background: conic-gradient(from 0deg, transparent 0%, #6366f1 25%, #ec4899 50%, #6366f1 75%, transparent 100%);
  animation: border-spin 3s linear infinite;
}

@keyframes border-spin {
  100% { transform: translate(-50%, -50%) rotate(360deg); }
}

.prompt-inner {
  position: relative;
  background: #0f172a;
  border-radius: 26px;
  padding: 24px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.prompt-box {
  width: 100%;
  background: transparent;
  border: none;
  color: #f8fafc;
  font-family: inherit;
  font-size: 1.25rem;
  line-height: 1.6;
  resize: none;
  outline: none;
  min-height: 80px;
}

.prompt-box::placeholder {
  color: #475569;
}

.prompt-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.setting-pill {
  color: #64748b;
  font-size: 0.9rem;
  font-weight: 500;
  background: rgba(255, 255, 255, 0.03);
  padding: 8px 16px;
  border-radius: 12px;
}

.generate-btn {
  background: #f8fafc;
  color: #0f172a;
  border: none;
  padding: 14px 28px;
  border-radius: 16px;
  font-weight: 700;
  font-size: 1.05rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.generate-btn:hover {
  transform: scale(1.05);
  box-shadow: 0 10px 20px rgba(255, 255, 255, 0.2);
}

.prompt-wrapper.is-generating .generate-btn {
  background: #334155;
  color: #94a3b8;
  pointer-events: none;
  box-shadow: none;
}

.gallery-showcase {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 240px;
  gap: 24px;
  position: relative;
  z-index: 10;
}

.empty-state {
  grid-column: span 3;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #475569;
  height: 300px;
}

.empty-state p {
  margin-top: 20px;
  font-size: 1.25rem;
}

.result-card, .skeleton-card {
  border-radius: 24px;
  overflow: hidden;
  position: relative;
  box-shadow: 0 15px 30px rgba(0, 0, 0, 0.4);
}

.feat-main {
  grid-column: span 2;
  grid-row: span 2;
}

.skeleton-card {
  background: #0f172a;
  border: 1px solid rgba(255, 255, 255, 0.05);
}

.skeleton-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg, transparent 20%, rgba(99, 102, 241, 0.15) 50%, transparent 80%);
  transform: translateX(-100%);
  animation: shimmer 2s infinite ease-in-out;
}

@keyframes shimmer {
  100% { transform: translateX(100%); }
}

.result-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.05);
  animation: reveal 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}

@keyframes reveal {
  to { opacity: 1; transform: scale(1); }
}
const genBtn = document.getElementById("generate-btn");
const btnText = document.getElementById("btn-text");
const promptInput = document.getElementById("ai-prompt");
const wrapper = document.getElementById("prompt-wrapper");
const showcase = document.getElementById("gallery-showcase");
const ambient = document.getElementById("ambient-glow");

function autoResize() {
  this.style.height = "auto";
  this.style.height = this.scrollHeight + "px";
}
promptInput.addEventListener("input", autoResize);

function buildSkeletons() {
  showcase.innerHTML = "";
  
  for (let i = 0; i < 3; i++) {
    const card = document.createElement("div");
    card.className = "skeleton-card";
    if (i === 0) {
      card.classList.add("feat-main");
    }
    showcase.appendChild(card);
  }
}

function revealImages() {
  showcase.innerHTML = "";
  
  const tags = new Array();
  tags.push("futuristic");
  tags.push("neon");
  tags.push("abstract");
  
  for (let i = 0; i < 3; i++) {
    const card = document.createElement("div");
    card.className = "result-card";
    if (i === 0) {
      card.classList.add("feat-main");
    }
    
    const img = document.createElement("img");
    img.className = "result-img";
    
    /* Stagger the animations beautifully */
    img.style.animationDelay = (i * 0.15) + "s";
    
    const rand = Math.floor(Math.random() * 1000);
    const tag = tags.at(i);
    img.src = "https://placehold.co/800x800/0f172a/6366f1?text=" + tag + "+" + rand;
    
    card.appendChild(img);
    showcase.appendChild(card);
  }
}

genBtn.addEventListener("click", function() {
  if (promptInput.value.trim() === "") return;
  
  wrapper.classList.add("is-generating");
  ambient.classList.add("pulse-glow");
  btnText.innerText = "Synthesizing...";
  promptInput.disabled = true;
  
  buildSkeletons();
  
  /* Simulate deep AI processing */
  setTimeout(function() {
    wrapper.classList.remove("is-generating");
    ambient.classList.remove("pulse-glow");
    btnText.innerText = "Generate Art";
    promptInput.disabled = false;
    
    revealImages();
  }, 3500);
});
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save