The Iron Man Principle

Imagine Tony Stark without the suit. Brilliant? Sure. But limited. The suit doesn’t make him smarter—it amplifies his capabilities, handles logistics, and lets him focus on what he does best: solving complex problems.

That’s been my experience working with AI in real development. It’s not about replacing human expertise. It’s about amplifying it.

This is the story of how I built an interactive tooltip/popup component for my website, and how collaborating with AI as an “exo-suit” transformed a frustrating debugging session into a masterclass in problem-solving.

The Problem: Building a Better Tooltip

I wanted to add interactive popups to my shop page that would explain Spanish slang terms (like “Onda”) to visitors. Simple enough, right?

Here’s what I needed:

  • A reusable Astro component
  • Hover-to-show behavior
  • Proper positioning relative to the trigger text
  • Support for multiple instances on the same page
  • No external dependencies

Sounds straightforward. But this journey had a twist ending that would teach me a lot about how modern web security actually works.

Layer 1: The Obvious Issues (The Warm-Up)

We started building the component. I had a vision, and the AI had suggestions. Back and forth:

Me: “Can we make the popup show on hover?”

AI: “Sure, here’s a component with event listeners.”

Me: “It’s showing but in a weird position.”

AI: “We need to fix the HTML structure—the popup is nested inside an inline span.”

We iterated. We refactored. The component structure improved:

---
interface Example {
  phrase: string;
  meaning: string;
}

const id = crypto.randomUUID();

const {
  term = "Onda",
  title = "Onda — Spanish Slang Meaning",
  description = "\"Onda\" means vibe, energy, or mood.",
  examples = [
    { phrase: "Buena onda", meaning: "good vibe, cool person" },
    { phrase: "Mala onda", meaning: "bad vibe, bad attitude" }
  ] as Example[],
  sourceUrl = "https://www.spanishdict.com/translate/onda",
  sourceLabel = "SpanishDict"
} = Astro.props;
---

<span class="slang-trigger" data-popup-trigger>
  {term}
  <span class="info-icon">i</span>
</span>

<div class="slang-popup" data-popup>
  <strong>{title}</strong>
  <p>{description}</p>
  {/* Content here */}
</div>

Good. We fixed positioning. We fixed the HTML structure. Everything looked right on the surface.

Layer 2: The CSS Gotcha (The Closer Look)

Then we hit something subtle. The popup was using CSS transform for positioning:

.slang-popup {
  position: absolute;
  top: 2.5rem;
  left: 50%;
  transform: translateX(-50%);  /* Position horizontally */
  opacity: 0;
  transform: translateY(-6px);  /* WAIT—this overwrites the previous transform! */
  transition: opacity 0.25s ease, transform 0.25s ease;
}

.slang-popup.visible {
  opacity: 1;
  transform: translateY(0);     /* This still overwrites translateX! */
}

The Problem: CSS transform doesn’t combine multiple values—the second one overwrites the first. So when we applied .visible, we lost the horizontal centering.

The Solution: Switch to position: fixed and handle positioning with JavaScript instead.

.slang-popup {
  position: fixed;           /* Let JS handle top/left */
  width: 260px;
  background: white;
  border: 1px solid #ddd;
  opacity: 0;               /* Only animate opacity */
  pointer-events: none;
  transition: opacity 0.25s ease;
}

.slang-popup.visible {
  opacity: 1;
  pointer-events: auto;
}

Better. But it still wasn’t working. That’s when things got really interesting.

Layer 3: The Silent Killer (The Plot Twist)

I refreshed the page. Hovered over “Onda.” Nothing.

No JavaScript errors. No console warnings. Just… silence.

The AI and I went back and forth:

  • “Is it a z-index issue?”
  • “Maybe the positioning calculation is wrong?”
  • “Let’s add debug styles…”

And then—I checked the security tab in Chromes dev tools.

There it was:

Content Security Policy (CSP) prevents the evaluation of arbitrary strings 
as JavaScript to make it more difficult for an attacker to inject unauthorized code.

Blocked: script-src directive

The Script Was Never Running.

The JavaScript was blocked by my site’s Content Security Policy (CSP). This is a security feature that prevents inline script injection attacks. But it also meant my carefully crafted JavaScript wasn’t executing at all.

We were debugging a JavaScript problem by looking at CSS and HTML. No wonder we couldn’t find it! 🤦‍♂️

The Breakthrough: CSP-Compliant JavaScript

Here’s the original script (which CSP was blocking):

<script define:vars={{ id }}>
  // This uses define:vars, which gets evaluated at build time
  // CSP sees this as potentially unsafe
  const trigger = document.getElementById(`${id}-trigger`);
  const popup = document.getElementById(`${id}-popup`);
  // ... event handlers
</script>

The fix? Don’t use define:vars with string interpolation. Instead, use relative DOM queries that don’t require variable injection:

<script>
  // Find our instance relative to the script tag itself
  // No string interpolation needed
  const wrapper = document.currentScript?.parentElement;
  const trigger = wrapper?.querySelector('[data-popup-trigger]');
  const popup = wrapper?.querySelector('[data-popup]');

  if (!trigger || !popup) return;

  // Position the popup relative to the trigger
  function positionPopup() {
    const triggerRect = trigger.getBoundingClientRect();
    popup.style.top = (triggerRect.bottom + window.scrollY + 10) + 'px';
    popup.style.left = (triggerRect.left + triggerRect.width / 2) + 'px';
    popup.style.transform = 'translateX(-50%)';
  }

  // Position initially and on scroll/resize
  positionPopup();
  window.addEventListener('scroll', positionPopup);
  window.addEventListener('resize', positionPopup);

  // Hover behavior
  trigger.addEventListener('mouseenter', () => {
    popup.classList.add('visible');
  });

  trigger.addEventListener('mouseleave', () => {
    popup.classList.remove('visible');
  });

  // Click fallback for mobile
  trigger.addEventListener('click', () => {
    popup.classList.toggle('visible');
  });

  // Click outside to close
  document.addEventListener('click', (e) => {
    if (!trigger.contains(e.target) && !popup.contains(e.target)) {
      popup.classList.remove('visible');
    }
  });
</script>

Key changes:

  1. Use document.currentScript to find the script tag itself
  2. Query relative to that script to find trigger and popup
  3. Use data attributes instead of IDs with string interpolation
  4. No define:vars = no CSP violations

Boom. 💥 It worked.

What Made This Debugging Journey Work

Here’s what I realized: I could have debugged this alone. But it would have taken much longer, and I might have missed the CSP angle entirely. Here’s why the collaboration helped:

What the AI Brought:

  • Speed: Rapid iteration and refactoring suggestions
  • Breadth: Suggestions across HTML, CSS, and JavaScript
  • Pattern Knowledge: Knowing how to avoid CSP violations with document.currentScript

What I Brought:

  • Domain Knowledge: Understanding of the business need
  • Direction: “Let’s test this specific hypothesis first”
  • Intuition: Knowing when something felt wrong (even if I couldn’t articulate why)
  • The Breakthrough: Actually spotting the CSP error in dev tools

Together:

  • We eliminated red herrings quickly (not z-index, not the position calculation)
  • We got to the root cause faster (I was able to resolve the issue in c. 30 min)
  • We arrived at a solution that’s not just “working”—it’s right (CSP-compliant, secure, scalable)

The Lesson: Collaboration, Not Replacement

Here’s what blew my mind about this process:

The AI didn’t solve the problem. I didn’t solve the problem alone either.

We solved it together. Each iteration made us smarter about the problem space. Each failure taught us something. The AI suggested paths to explore; I validated whether they mattered. I spotted the breakthrough; the AI knew the pattern to fix it.

This is what “AI as an exo-suit” (AIaaES if you will, 😉) means:

  • “Iron Man” isn’t less intelligent with the suit — he’s more capable.
  • The suit doesn’t think for him; it amplifies his thinking.
  • It handles logistics (refactoring), lets him focus on strategy (debugging approach).
  • It suggests possibilities; he evaluates them.

That’s the dynamic that actually works in professional development.

The Real Takeaway

If you’re working with AI in development, remember:

  1. You’re Still the Expert - Your domain knowledge, intuition, and judgment are irreplaceable
  2. AI is Logistics - It handles refactoring, research, code generation, and rapid iteration
  3. Collaboration Beats Both - A human + AI team is faster and smarter than either alone
  4. Trust Your Tools - When something doesn’t work, check everything — including security policies you might have forgotten about
  5. Document the Journey - The story of how you solved it is often more valuable than the solution itself

Want to Use This Component?

The full Popup.astro component is now live on my site. Here’s how to use it:

---
import Popup from '../components/Popup.astro';
---

<p>
  The concept of <Popup 
    term="Onda" 
    title="Onda — Spanish Slang" 
    description="Means vibe, energy, or mood."
    examples={[
      { phrase: "Buena onda", meaning: "good vibe" },
      { phrase: "Mala onda", meaning: "bad vibe" }
    ]}
  /> is deeply embedded in Latin culture.
</p>

It’s reusable, multiple instances work independently, and—most importantly—it respects your site’s security policies.

What’s Next?

I’m planning more content about the human-AI dynamic in modern development:

  • Working effectively with AI code generation
  • When to trust AI suggestions (and when to question them)
  • Building systems that play to each tool’s strengths

For now, I’m just grateful for the partnership that made this debugging session productive, insightful, and—dare I say—fun.

Because that’s what a good exo-suit does. It makes you better at what you already do well.


Have you worked with AI as a collaborative tool? I’d love to hear your story. Hit me up on Twitter or send me a message.