Good UI is not decoration. It is comprehension.
A strong interface helps judges understand the product without effort. In a hackathon, you have about 10 seconds to convince someone your project is worth looking at. The UI is what buys you those seconds.
flowchart TD
A[One clear action] --> B[Simple layout]
B --> C[Strong hierarchy]
C --> D[Useful empty states]
D --> E[Loading and success states]
These are ready-to-use components. Copy them, change the colors and text, and you’re done.
<button class="px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 active:bg-blue-800 transition-colors duration-150 shadow-md hover:shadow-lg">
Get Started
</button>
<div class="bg-white rounded-xl shadow-md p-6 hover:shadow-lg transition-shadow duration-200 border border-gray-100">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Card Title</h3>
<p class="text-gray-600 text-sm leading-relaxed">Card description goes here. Keep it short and clear.</p>
</div>
<div class="space-y-1">
<label class="block text-sm font-medium text-gray-700">Email</label>
<input
type="email"
placeholder="you@example.com"
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all duration-150 text-sm"
/>
</div>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div class="bg-white rounded-2xl shadow-2xl p-8 max-w-md w-full mx-4 transform transition-all">
<h2 class="text-xl font-bold text-gray-900 mb-4">Confirm Action</h2>
<p class="text-gray-600 mb-6">Are you sure you want to proceed? This action cannot be undone.</p>
<div class="flex gap-3 justify-end">
<button class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors">Cancel</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Confirm</button>
</div>
</div>
</div>
<nav class="bg-white border-b border-gray-200 px-6 py-4">
<div class="max-w-7xl mx-auto flex items-center justify-between">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-blue-600 rounded-lg"></div>
<span class="font-bold text-gray-900 text-lg">AppName</span>
</div>
<div class="hidden md:flex items-center gap-6">
<a href="#" class="text-gray-600 hover:text-gray-900 text-sm font-medium">Features</a>
<a href="#" class="text-gray-600 hover:text-gray-900 text-sm font-medium">Pricing</a>
<a href="#" class="text-gray-600 hover:text-gray-900 text-sm font-medium">About</a>
<button class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700">Sign Up</button>
</div>
</div>
</nav>
<section class="bg-gradient-to-br from-blue-50 to-indigo-100 py-20 px-6">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 mb-6 leading-tight">
Build faster, ship sooner
</h1>
<p class="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
The toolkit that helps hackathon teams go from idea to demo in hours, not days.
</p>
<div class="flex gap-4 justify-center">
<button class="px-8 py-3.5 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors shadow-lg">Start Building</button>
<button class="px-8 py-3.5 border border-gray-300 text-gray-700 font-semibold rounded-lg hover:bg-gray-50 transition-colors">Learn More</button>
</div>
</div>
</section>
<footer class="bg-gray-900 text-gray-400 py-12 px-6">
<div class="max-w-7xl mx-auto grid grid-cols-2 md:grid-cols-4 gap-8">
<div>
<h4 class="text-white font-semibold mb-4">Product</h4>
<ul class="space-y-2 text-sm">
<li><a href="#" class="hover:text-white transition-colors">Features</a></li>
<li><a href="#" class="hover:text-white transition-colors">Pricing</a></li>
<li><a href="#" class="hover:text-white transition-colors">Docs</a></li>
</ul>
</div>
<div>
<h4 class="text-white font-semibold mb-4">Company</h4>
<ul class="space-y-2 text-sm">
<li><a href="#" class="hover:text-white transition-colors">About</a></li>
<li><a href="#" class="hover:text-white transition-colors">Blog</a></li>
<li><a href="#" class="hover:text-white transition-colors">Careers</a></li>
</ul>
</div>
</div>
<div class="max-w-7xl mx-auto mt-8 pt-8 border-t border-gray-800 text-center text-sm">
<p>© 2026 AppName. Built at a hackathon.</p>
</div>
</footer>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Active
</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
Pending
</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
Failed
</span>
<div class="flex items-center gap-3">
<img
src="https://i.pravatar.cc/40?img=1"
alt="User avatar"
class="w-10 h-10 rounded-full ring-2 ring-white shadow-sm"
/>
<div>
<p class="text-sm font-medium text-gray-900">Jane Smith</p>
<p class="text-xs text-gray-500">jane@example.com</p>
</div>
</div>
<div class="overflow-x-auto rounded-lg border border-gray-200">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="text-left px-4 py-3 font-medium text-gray-700">Name</th>
<th class="text-left px-4 py-3 font-medium text-gray-700">Status</th>
<th class="text-left px-4 py-3 font-medium text-gray-700">Date</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 text-gray-900">Project Alpha</td>
<td class="px-4 py-3"><span class="px-2 py-0.5 text-xs font-medium bg-green-100 text-green-700 rounded-full">Active</span></td>
<td class="px-4 py-3 text-gray-500">Sep 1, 2026</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 text-gray-900">Project Beta</td>
<td class="px-4 py-3"><span class="px-2 py-0.5 text-xs font-medium bg-yellow-100 text-yellow-700 rounded-full">Pending</span></td>
<td class="px-4 py-3 text-gray-500">Sep 2, 2026</td>
</tr>
</tbody>
</table>
</div>
<aside class="w-64 bg-gray-50 border-r border-gray-200 h-screen p-4">
<div class="space-y-1">
<a href="#" class="flex items-center gap-3 px-3 py-2 text-sm font-medium text-gray-900 bg-white rounded-lg shadow-sm">
<span class="w-5 h-5 bg-blue-600 rounded"></span>
Dashboard
</a>
<a href="#" class="flex items-center gap-3 px-3 py-2 text-sm font-medium text-gray-600 hover:bg-white hover:text-gray-900 rounded-lg transition-colors">
<span class="w-5 h-5 bg-gray-400 rounded"></span>
Projects
</a>
<a href="#" class="flex items-center gap-3 px-3 py-2 text-sm font-medium text-gray-600 hover:bg-white hover:text-gray-900 rounded-lg transition-colors">
<span class="w-5 h-5 bg-gray-400 rounded"></span>
Settings
</a>
</div>
</aside>
<div class="fixed bottom-4 right-4 bg-green-600 text-white px-4 py-3 rounded-lg shadow-lg flex items-center gap-3 animate-in slide-in-from-bottom-5">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<span class="text-sm font-medium">Changes saved successfully</span>
</div>
<div class="border-b border-gray-200">
<nav class="flex gap-6 px-6">
<button class="py-3 text-sm font-medium text-blue-600 border-b-2 border-blue-600">Overview</button>
<button class="py-3 text-sm font-medium text-gray-500 hover:text-gray-700 border-b-2 border-transparent">Analytics</button>
<button class="py-3 text-sm font-medium text-gray-500 hover:text-gray-700 border-b-2 border-transparent">Settings</button>
</nav>
</div>
<div class="border border-gray-200 rounded-lg divide-y divide-gray-200">
<div class="px-4 py-3">
<button class="w-full flex items-center justify-between text-sm font-medium text-gray-900">
What is this project?
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
</div>
<div class="px-4 py-3">
<button class="w-full flex items-center justify-between text-sm font-medium text-gray-900">
How does it work?
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
</div>
</div>
<div class="animate-pulse space-y-4">
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
<div class="h-32 bg-gray-200 rounded"></div>
<div class="flex gap-4">
<div class="h-8 bg-gray-200 rounded w-24"></div>
<div class="h-8 bg-gray-200 rounded w-24"></div>
</div>
</div>
Pick one palette and stick with it. Consistency beats creativity here.
| Role | Hex | Use |
|—|—|—|
| Primary | #2563EB | Buttons, links, active states |
| Secondary | #0EA5E9 | Accents, hover states |
| Background | #F8FAFC | Page background |
| Surface | #FFFFFF | Cards, modals |
| Text | #1E293B | Headings, body |
| Muted | #94A3B8 | Secondary text |
| Success | #22C55E | Positive feedback |
| Error | #EF4444 | Errors, destructive actions |
| Role | Hex | Use |
|—|—|—|
| Primary | #8B5CF6 | Buttons, links, active states |
| Secondary | #A78BFA | Accents, hover states |
| Background | #0F172A | Page background |
| Surface | #1E293B | Cards, modals |
| Text | #F1F5F9 | Headings, body |
| Muted | #64748B | Secondary text |
| Success | #34D399 | Positive feedback |
| Error | #F87171 | Errors, destructive actions |
| Role | Hex | Use |
|—|—|—|
| Primary | #F97316 | Buttons, links, active states |
| Secondary | #FB923C | Accents, hover states |
| Background | #FFFBEB | Page background |
| Surface | #FFFFFF | Cards, modals |
| Text | #292524 | Headings, body |
| Muted | #A8A29E | Secondary text |
| Success | #22C55E | Positive feedback |
| Error | #EF4444 | Errors, destructive actions |
| Role | Hex | Use |
|—|—|—|
| Primary | #059669 | Buttons, links, active states |
| Secondary | #10B981 | Accents, hover states |
| Background | #ECFDF5 | Page background |
| Surface | #FFFFFF | Cards, modals |
| Text | #064E3B | Headings, body |
| Muted | #6B7280 | Secondary text |
| Success | #22C55E | Positive feedback |
| Error | #EF4444 | Errors, destructive actions |
| Role | Hex | Use |
|—|—|—|
| Primary | #06B6D4 | Buttons, links, active states |
| Secondary | #22D3EE | Accents, hover states |
| Background | #0A0A0A | Page background |
| Surface | #1A1A2E | Cards, modals |
| Text | #E2E8F0 | Headings, body |
| Muted | #64748B | Secondary text |
| Success | #34D399 | Positive feedback |
| Error | #F87171 | Errors, destructive actions |
Mobile isn’t optional. Judges will try your app on their phones. Run through this checklist:
These take almost no time but make your app usable for more people. Judges notice this.
<!-- Bad -->
<img src="chart.png">
<!-- Good -->
<img src="chart.png" alt="Bar chart showing monthly revenue growth from $1,200 to $3,400">
<!-- Bad -->
<div class="nav">
<div class="nav-item">Home</div>
</div>
<!-- Good -->
<nav>
<a href="/">Home</a>
</nav>
Use WebAIM’s contrast checker. Your text needs a contrast ratio of at least 4.5:1 against its background.
button:focus-visible {
outline: 2px solid #2563EB;
outline-offset: 2px;
}
<button aria-label="Close dialog">
<svg><!-- X icon --></svg>
</button>
Show your app to someone who’s never seen it. Give them 3 seconds. Then ask:
If they can’t answer both, your UI fails the 3-second rule.
How to fix it:
The test in practice:
The size of an element tells the user how important it is. Use this hierarchy:
| Element | Size Range | Weight | Color |
|---|---|---|---|
| Page title | 32-48px | Bold (700-800) | Darkest color |
| Section heading | 24-32px | Semibold (600) | Dark color |
| Subheading | 18-20px | Medium (500) | Dark color |
| Body text | 14-16px | Regular (400) | Medium color |
| Caption/label | 12-14px | Regular (400) | Muted color |
| Primary button | 16px text | Semibold (600) | Primary color, filled |
| Secondary button | 14-16px text | Medium (500) | Border or ghost |
| Link | 14-16px | Medium (500) | Primary color, underline on hover |
The rule: If everything is big, nothing is big. If everything is bold, nothing is bold. Use size and weight to create contrast between important and less important elements.
Take any ugly hackathon UI and transform it in 20 minutes.
Add a CSS reset. This alone fixes 30% of ugly UIs.
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; }
Choose one of the palettes above. Replace all hardcoded colors with palette variables.
Replace random margins and paddings with a spacing system:
4px — tiny gap8px — small gap16px — default gap24px — section gap32px — large gap48px — page section gap64px — major section gapReplace sharp corners with consistent radius:
8px12px8px16px50%Replace harsh borders with soft shadows:
.card { box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
Smooth interactions feel premium:
button { transition: all 0.15s ease; }
Pick one thing to make special — a gradient header, a colored sidebar, or an illustrated hero. One accent is enough. More than one looks messy.
Take screenshots of:
Store them in:
assets/screenshots/assets/gifs/Screenshot tips:
The app should look understandable in three seconds.
UI should make the product feel obvious, not complicated. Every pixel should answer a question for the user. If it doesn’t serve the demo, remove it. If it does, make it beautiful.