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:
- Use
document.currentScriptto find the script tag itself - Query relative to that script to find trigger and popup
- Use data attributes instead of IDs with string interpolation
- 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:
- You’re Still the Expert - Your domain knowledge, intuition, and judgment are irreplaceable
- AI is Logistics - It handles refactoring, research, code generation, and rapid iteration
- Collaboration Beats Both - A human + AI team is faster and smarter than either alone
- Trust Your Tools - When something doesn’t work, check everything — including security policies you might have forgotten about
- 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.
Comments
Loading comments...
Leave a Comment