Interface: Design

Rule

Shadows

  • SHOULDLayer shadows (ambient + direct): shadow-[0_2px_4px_rgba(0,0,0,0.05),0_12px_24px_rgba(0,0,0,0.1)]
  • SHOULDPrefer box-shadow over border for subtle edges — shadows blend with backgrounds and avoid subpixel rendering issues:
/* Preferred: shadow blends with any background */
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);

/* Also works: inset variant */
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);

/* Fallback: explicit border when shadow isn't practical */
border: 1px solid rgb(0 0 0 / 0.05);

Borders

  • SHOULDHairline borders on retina:
:root {
  --border-hairline: 1px;
  @media (min-resolution: 2dppx) { --border-hairline: 0.5px; }
}

Radii

  • SHOULDNested radii: innerRadius = outerRadius - padding

Contrast

  • MUSTAPCA contrast compliance (apcacontrast.com)
  • MUSTIncrease contrast on :hover/:active/:focus
  • MUSTColor-blind friendly chart palettes

Gradients

  • SHOULDEased gradients to avoid banding (tool)
  • SHOULDmask-image over gradient for fades:
.fade-bottom { mask-image: linear-gradient(to bottom, black 80%, transparent); }
  • NEVERFade on scrollable content

Scrollbars

  • NEVERCustom page scrollbar
  • SHOULDCustom scrollbar only in contained elements (code blocks)

Focus

  • NEVERColored focus outlines (use grey/black/white only)

Color Restraint

  • SHOULDOne accent color per view
  • SHOULDUse existing tokens before adding new
  • NEVERPurple gradients, multicolor gradients (AI slop)
  • NEVERGlow effects as affordances

AI Slop Detection

Concrete patterns that signal AI-generated design. Grep-friendly — each is a specific code smell, not a vibe.

Visual Tells

PatternWhat to look forWhy it's slop
Gratuitous gradientsbg-gradient-to-* or linear-gradient used decoratively, not functionallyAI defaults to gradients for "visual interest" instead of actual design
Glow effectsshadow-[0_0_*], drop-shadow, box-shadow with blur >20px and colorGlow as decoration is a ChatGPT-era tell — real UIs use shadows for depth
transition: alltransition-all or transition: allLazy blanket transitions cause jank and unintended animations; specify properties
Visual monotonyEvery card/section uses identical padding, radius, shadowAI reuses the same container recipe everywhere — vary visual weight
Placeholder text shipped"Lorem ipsum", "Your text here", "Description goes here"AI leaves placeholder copy; real products have real content
Emoji as designEmoji used as section icons or feature illustrationsAI substitutes emoji for actual iconography or illustration

Structural Tells

PatternWhat to look forWhy it's slop
Hero → Features → Testimonials → CTACookie-cutter landing page structureEvery AI landing page uses this exact layout
Uniform border-radiusSame rounded-* on every elementAI applies one radius globally instead of varying by context
White cards on white bgCards with bg-white on a bg-white or bg-gray-50 parentCreates a flat, lifeless hierarchy with no real depth
Centered everythingEvery section center-aligned with text-center mx-autoAI defaults to center alignment; real layouts use asymmetry
System font stackNo custom fonts loaded; falls back to system-ui or sans-serifZero typographic personality

Code Tells

  • NEVERtransition-all — specify exact properties (transition-colors, transition-transform)
  • NEVERisolation: isolate used as a "just in case" stacking context — use only when you can explain why
  • NEVERblur-* > blur-xl (20px) on decorative elements — large blurs tank performance for no purpose
  • NEVERMultiple gradient overlays stacked — simplify to one or use a solid color with opacity

Decorative Elements

  • MUSTpointer-events: none on decorative overlays
  • SHOULDuser-select: none on code illustrations

Primitives

  • NEVERMix component libraries (Radix + Headless + Base UI)
  • MUSTUse project's existing primitives
  • MUSTUse accessible primitives (Radix, Base UI) for keyboard/focus behavior