Logo
Back to Blog
Things That Should Be Illegal in Modern Web Development
frontenduxbest practices

Things That Should Be Illegal in Modern Web Development

A rant-style guide to the most frustrating patterns on the modern web—and how to fix them with better UX and code.

The modern web is powerful, but a lot of it feels like a UX crime scene.

You open a page and get hit with a fullscreen popup.
You click a button and the whole layout jumps.
A spinner loads forever with no explanation.

This post is a small list of things that should be illegal in modern web development—and how to avoid them in your own projects.


1. Pop‑Ups Before You’ve Read Anything

Landing on a page and instantly seeing a “Subscribe!” modal is a guaranteed way to make users close the tab.

Why it’s bad

  • Interrupts reading before trust is built.
  • Punishes curiosity instead of rewarding it.
  • Trains users to ignore all future prompts.

Do this instead

  • Delay prompts until the user has scrolled a bit or spent some time on the page.
  • Make them dismissable and non‑blocking.
// Show newsletter only after engagement
const [showNewsletter, setShowNewsletter] = useState(false);

useEffect(() => {
  const timer = setTimeout(() => setShowNewsletter(true), 45000); // 45s
  return () => clearTimeout(timer);
}, []);

2. Spinners With No Timeout, Error, or Retry

A spinner that never stops is not a loading state, it’s a dead end.

Why it’s bad

  • Users don’t know if it’s slow, broken, or stuck.
  • Wastes time and kills trust.

Do this instead

  • Always have a timeout.
  • Show an error and a retry path.
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 10000);

  async function load() {
    try {
      const res = await fetch("/api/data", { signal: controller.signal });
      if (!res.ok) throw new Error("Failed to load");
      // handle data...
    } catch {
      setError("Something went wrong. Please try again.");
    } finally {
      clearTimeout(timeout);
      setLoading(false);
    }
  }

  load();
  return () => controller.abort();
}, []);

3. Layout Shift That Dodges Your Click

Nothing feels more scammy than trying to press “Cancel” and hitting “Delete” because the layout jumped.

Why it’s bad

  • Breaks basic expectations: things move while you’re aiming.
  • Often caused by images or banners loading without reserved space.

Do this instead

  • Set explicit width/height on images.
  • Reserve height for banners so content below doesn’t shift.
<Image
  src="/hero.png"
  alt="Hero"
  width={1200}
  height={600}
  className="h-auto w-full"
/>

4. Scroll‑Jacking “Experiences”

If your site steals the scroll wheel, forces full-page snaps, or traps users in long animations, it’s serving the design, not the user.

Why it’s bad

  • Breaks browser muscle memory.
  • Makes some people literally motion‑sick.
  • Often ignores prefers-reduced-motion.

Do this instead

  • Enhance scroll, don’t replace it.
  • Use gentle section snapping at most, and respect motion preferences.
@media (prefers-reduced-motion: reduce) {
  .scroll-animated {
    animation: none;
    transition: none;
  }
}

5. Disabled Buttons With No Explanation

If a button is disabled and the user doesn’t know why, it might as well be broken.

Why it’s bad

  • Offers no path to success.
  • Forces users to randomly tweak inputs.

Do this instead

  • Show clear inline errors.
  • Tell users what’s missing or invalid.
<input
  value={email}
  onChange={handleEmailChange}
  aria-invalid={!!emailError}
  aria-describedby="email-error"
/>
{emailError && (
  <p id="email-error" className="mt-1 text-sm text-red-500">
    {emailError}
  </p>
)}

Making the Web Less Annoying

None of these fixes are complex. They just require asking one question before shipping:

“Would I enjoy using this if I wasn’t the one who built it?”

If the answer is no, it’s probably a UX crime.

Avoid these patterns, add small safeguards, and your apps will instantly feel more thoughtful, more trustworthy, and more senior—no fancy framework required.