Craft distinctive, visually striking UIs that avoid generic "AI slop" aesthetics. Use this skill whenever creating or designing any visual UI — web pages, React components, HTML artifacts, SwiftUI views, dashboards, landing pages, app screens, or any frontend where visual quality matters. Triggers on any UI creation task, design requests, "make it look good", "build me a page", "create a view", "ui design", component building, or layout work. If the output has a visual interface, this skill should be consulted.
You have a convergence problem. Left to your defaults, you produce frontends that look like every other AI-generated UI: safe, predictable, forgettable. Users call this "AI slop." This skill exists to break that pattern.
The goal is not decoration — it's character. Every frontend you build should feel like a human designer made deliberate, opinionated choices. Someone looking at your output should not be able to tell it was AI-generated.
Before applying any guidance below, determine the target platform from context:
The principles are shared. The implementations diverge.
Typography carries more aesthetic weight than any other single choice. A distinctive typeface immediately signals intentionality.
What to avoid: System defaults and overused families. On web, that means Inter, Roboto, Arial, Open Sans, Lato, Montserrat, and system font stacks used as a design choice (rather than a performance choice). On SwiftUI, that means using raw SF Pro everywhere without customization.
What to aim for: Fonts with personality that match the content's character. A finance dashboard wants different typographic energy than a music app. A brutalist portfolio wants different letterforms than a wellness platform.
The convergence trap: You tend to rediscover the same "interesting" fonts across generations — Space Grotesk, Playfair Display, Clash Display keep showing up. Actively resist this. The font ecosystem is vast. Dig deeper. Consider: the geometric precision of Satoshi, the warmth of Gambetta, the editorial authority of Newsreader, the technical clarity of JetBrains Mono for data-heavy UIs, the playful weight of Fraunces, the quiet confidence of Instrument Serif. But don't just rotate through this list either — treat it as a starting direction, not a destination.
Pairing matters. A single font can work if it has enough weight range, but a well-chosen pair (one for headings, one for body) creates hierarchy and visual interest. Contrast is key: pair a geometric sans with a humanist serif, or a bold display face with a clean workhorse.
Timid palettes are the hallmark of AI-generated design. Five muted pastels distributed evenly across a layout produce visual oatmeal.
Commit to a dominant color. Let it own 60-70% of the chromatic weight. Then choose one or two accent colors that create real contrast — not just lighter/darker versions of the same hue. Think about color temperature: warm dominants with cool accents (or vice versa) create energy. Monochromatic palettes work when they're bold about it (dark charcoal + electric amber, not gray + slightly different gray).
Draw from unexpected sources. IDE themes (Dracula, Solarized, Nord, Tokyo Night) are designed for long-duration viewing and have distinctive identities. Cultural aesthetics — Japanese woodblock palettes, Bauhaus primaries, film noir contrast, solarpunk greens — give you a coherent story to tell. Architecture, nature photography, vintage advertising, album covers — any of these can seed a palette that feels intentional rather than generated.
The purple gradient problem. Purple-to-blue gradients on white backgrounds have become the universal signifier of "AI made this." Avoid them entirely unless the context specifically demands purple. When you reach for a gradient, ask: would a human designer choose this, or am I defaulting?
Watch for muddy warm tones. Warm accent colors (gold, amber, orange) are great — they create distinctive, non-generic palettes. But occasionally they can drift a bit too far toward brown, especially on very dark backgrounds. If a warm accent starts looking more "burnt" than "glowing," nudge it slightly toward a cleaner, more saturated version of itself. This is a subtle adjustment, not a reason to abandon warm palettes. Warm accents with character are vastly preferable to retreating to safe teal/cyan, which is its own form of AI default.
The teal/cyan trap. Just as purple-blue gradients scream "AI" on light backgrounds, teal and cyan on dark backgrounds are becoming the new AI-safe-color. If you find yourself reaching for teal as your primary accent, pause and consider whether a warmer, bolder, or more unexpected choice would serve the design better.
Glass and frosted effects are a powerful tool in your toolkit. When used with a light touch — subtle frosting, thin borders at low opacity white, just enough to define boundaries — they add premium depth. But they're one option, not the only option. Match the technique to the design's character.
Atmospheric backgrounds are great, but make sure users can actually read your content. If text or cards start blending into the background, nudge contrast up slightly. Secondary text on dark surfaces occasionally needs a small opacity boost. Data-dense areas (lists, categories) need enough visual rhythm that rows feel distinct. Trust your eye — if something looks hard to read, it is.
Animation should feel like punctuation, not noise. One perfectly timed entrance sequence creates more impact than twenty hover effects.
Prioritize page load. The first impression matters most. Stagger element reveals using animation delays so content cascades into place rather than appearing all at once. This single technique — elements arriving in a choreographed sequence — transforms the feel of any page.
Be purposeful with micro-interactions. Button hovers, card lifts, toggle switches — these should feel physical and responsive, but they don't all need to be fancy. Pick a few key interaction moments and make them great rather than adding animation to everything.
Timing is character. Fast, snappy easing feels modern and confident. Slow, elastic easing feels playful and organic. Match the motion personality to the overall aesthetic. A brutalist site wants abrupt, mechanical motion. A wellness app wants gentle, flowing motion.
A solid white or solid dark background is a missed opportunity. The background is the largest surface in your design — use it.
Layer depth. Subtle gradients, grain textures, geometric patterns, or contextual effects (a topographic map for a hiking app, circuit traces for a tech dashboard) all create atmosphere without competing with content. The key word is subtle — backgrounds support, they don't shout.
Consider noise and texture. A faint grain overlay (CSS filter or SVG noise) on a gradient background adds tactile quality that flat colors lack. Mesh gradients with soft blurs create depth. Even a very slight radial gradient from center to edges prevents the "flat panel" look.
Use Google Fonts, Adobe Fonts, or Fontsource for distinctive typefaces. Always specify font-display: swap for performance. For artifacts and quick prototypes, Google Fonts via <link> in the <head> is fine.
<!-- Example: an unexpected pairing -->
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
Use CSS animations and transitions for simple effects. For React projects where the Motion library (formerly Framer Motion) is available, prefer it for orchestrated sequences:
// Staggered reveal pattern — high impact, low complexity
{items.map((item, i) => (
<motion.div
key={item.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.08, duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
>
{item.content}
</motion.div>
))}
For CSS-only contexts, use @keyframes with animation-delay for the same stagger effect.
/* Mesh gradient with grain */
.atmospheric-bg {
background:
radial-gradient(ellipse at 20% 50%, rgba(120, 80, 200, 0.15), transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(255, 140, 50, 0.1), transparent 50%),
#0a0a0f;
position: relative;
}
.atmospheric-bg::after {
content: '';
position: absolute;
inset: 0;
background: url("data:image/svg+xml,...") repeat; /* SVG noise texture */
opacity: 0.03;
pointer-events: none;
}
When using Tailwind, extend the config with custom colors and fonts rather than relying solely on the default palette. Tailwind's default blue-500, gray-100, etc. are recognizable and contribute to the "template" look. Define semantic color tokens that match your chosen palette.
SF Pro is the safe default — and that's the problem. For apps where you control the design, register and use custom fonts to create distinction:
// Register in your App init or Info.plist
Text("Dashboard")
.font(.custom("Gambetta-Medium", size: 32))
Text("Account balance")
.font(.custom("DMSans-Regular", size: 14))
When custom fonts aren't practical (quick prototypes, system-integrated UIs), at least exploit SF Pro's range: use SF Pro Rounded for warmth, SF Mono for data-heavy displays, vary weights dramatically between hierarchy levels (ultralight titles with bold labels, or heavy titles with light body).
// Dramatic weight contrast within system fonts
Text("$42,800")
.font(.system(size: 48, weight: .ultraLight, design: .monospaced))
Text("Total Revenue")
.font(.system(size: 13, weight: .semibold, design: .rounded))
.textCase(.uppercase)
.tracking(1.5)
Define custom Color extensions rather than using SwiftUI's built-in .blue, .green, etc. These system colors are recognizable and make apps feel like sample code.
extension Color {
static let ink = Color(red: 0.06, green: 0.06, blue: 0.09)
static let ember = Color(red: 0.94, green: 0.35, blue: 0.18)
static let drift = Color(red: 0.55, green: 0.62, blue: 0.72)
}
Default .easeInOut is the SwiftUI equivalent of "generic." Use springs with intention:
// Energetic, confident
withAnimation(.spring(duration: 0.4, bounce: 0.3)) { ... }
// Gentle, organic
withAnimation(.spring(duration: 0.7, bounce: 0.1)) { ... }
// Staggered reveals in SwiftUI
ForEach(Array(items.enumerated()), id: \.element.id) { index, item in
ItemCard(item: item)
.opacity(appeared ? 1 : 0)
.offset(y: appeared ? 0 : 20)
.animation(
.spring(duration: 0.5, bounce: 0.2)
.delay(Double(index) * 0.08),
value: appeared
)
}
For iOS 17+, PhaseAnimator and KeyframeAnimator enable complex sequenced animations without manual state management:
// A pulsing attention indicator
PhaseAnimator([false, true]) { content, phase in
content
.scaleEffect(phase ? 1.05 : 1.0)
.opacity(phase ? 1.0 : 0.7)
} animation: { _ in
.easeInOut(duration: 1.2)
}
Use .contentTransition(.numericText()) for number changes — it's a small detail that signals craft.
SwiftUI has tools for atmospheric depth that don't exist on web. Use them:
// Mesh gradient (iOS 18+) — organic, distinctive backgrounds
MeshGradient(
width: 3, height: 3,
points: [
[0, 0], [0.5, 0], [1, 0],
[0, 0.5], [0.5, 0.5], [1, 0.5],
[0, 1], [0.5, 1], [1, 1]
],
colors: [
.ink, .ink, Color(red: 0.12, green: 0.1, blue: 0.18),
.ink, Color(red: 0.15, green: 0.08, blue: 0.12), .ink,
Color(red: 0.08, green: 0.1, blue: 0.14), .ink, .ink
]
)
.ignoresSafeArea()
// Material layers for glass depth
.background(.ultraThinMaterial)
.background(Color.ember.opacity(0.1))
// Visual effect modifier for parallax-like depth
.visualEffect { content, proxy in
content.offset(y: proxy.frame(in: .scrollView).minY * 0.3)
}
When using materials on dark backgrounds, use a light touch. .ultraThinMaterial with a hint of accent color creates depth without washing out your design. Heavier materials (.thinMaterial, .regularMaterial) can make content hard to read through the frosting — use them sparingly and only when the glass effect itself is the design intent.
These are the telltale signs of AI-generated SwiftUI. Avoid them:
AccentColor in your asset catalog or use .tint()List and NavigationStack without custom styling — always customize row backgrounds, separators, and navigation bar appearanceRoundedRectangle(cornerRadius: 12) cards with no shadow, border, or material treatment.hierarchical, .palette), weights, and sizesAcross different projects and prompts, you must produce visually distinct results. If you find yourself reaching for the same font, the same dark-with-accent-color scheme, the same card layout — stop and choose differently.
Vary these across generations:
The context should drive these choices. A children's educational app and a financial analytics dashboard should look nothing alike, and neither should look like "default AI output."
Before finalizing any frontend, quickly verify: