feat: setup colors

This commit is contained in:
Oleg Proskurin 2025-11-30 22:53:52 +07:00
parent 6b1a8ff96f
commit fe301756d7
9 changed files with 126 additions and 158 deletions

View File

@ -34,41 +34,36 @@ apps/landing/src/
1. **Work-focused, not marketing** - Small typography, efficient spacing
2. **Clean and functional** - Like Google AI Studio
3. **Neutral colors** - Zinc palette (true gray, no blue tint)
3. **Dual color system** - Zinc for layout, Slate for forms
4. **Consistent icons** - Lucide React only, no emojis
5. **Responsive** - Works on 768px to 1920px+ screens
---
## Color System (Zinc Palette)
## Color System
### Backgrounds
### Layout/Chrome → Zinc (Neutral Gray)
Used for sidebar, footer, and layout borders:
```css
bg-zinc-950 /* Main app background */
bg-zinc-900 /* Card backgrounds, elevated surfaces */
bg-zinc-900/80 /* Card with transparency + backdrop-blur-sm */
bg-zinc-800 /* Input backgrounds, secondary surfaces */
bg-zinc-800/50 /* Hover states, subtle overlays */
bg-zinc-950 /* Sidebar background */
bg-zinc-950/50 /* Footer background, layout wrappers */
border-zinc-800 /* Layout borders, dividers */
text-zinc-400 /* Sidebar text */
text-zinc-500 /* Footer text, muted */
```
### Borders
### Forms/Cards → Slate (Original Style)
Used for cards, inputs, and interactive UI elements:
```css
border-zinc-800 /* Standard borders */
border-zinc-700 /* Lighter borders, dividers */
border-white/10 /* Subtle borders (avoid in Lab) */
bg-slate-900/80 /* Card backgrounds */
bg-slate-900/50 /* Empty states, lighter cards */
bg-slate-800 /* Input backgrounds, secondary surfaces */
border-slate-700 /* Card borders, input borders */
text-gray-400 /* Labels, secondary text */
text-gray-500 /* Placeholders, hints */
```
### Text
```css
text-white /* Primary headings */
text-zinc-100 /* Body text, primary content */
text-zinc-300 /* Secondary text, descriptions */
text-zinc-400 /* Muted text, placeholders */
text-zinc-500 /* Disabled states */
text-zinc-600 /* Count badges, metadata */
```
### Accent Colors (Keep Purple/Cyan)
### Accent Colors (Purple/Cyan)
```css
/* Primary gradient */
bg-gradient-to-r from-purple-600 to-cyan-600
@ -78,8 +73,9 @@ focus:ring-2 focus:ring-purple-500
/* Single-color accents */
text-purple-400 /* Links, interactive text */
bg-purple-500/10 /* Info banners */
border-purple-500/20 /* Accent borders */
/* Info banners */
bg-purple-900/10 border-purple-700/50
```
### Status Colors
@ -87,7 +83,7 @@ border-purple-500/20 /* Accent borders */
/* Success */ bg-emerald-500/10 border-emerald-500/30 text-emerald-400
/* Warning */ bg-amber-500/10 border-amber-500/30 text-amber-400
/* Error */ bg-red-500/10 border-red-500/30 text-red-400
/* Info */ bg-purple-500/10 border-purple-500/30 text-purple-400
/* Info */ bg-purple-900/10 border-purple-700/50 text-purple-400
```
---
@ -109,16 +105,16 @@ text-sm font-medium text-white
### Body Text
```tsx
// Primary body
text-sm text-zinc-300
text-sm text-gray-300
// Secondary/descriptions
text-sm text-zinc-400
text-sm text-gray-400
// Small text (hints, metadata)
text-xs text-zinc-500
text-xs text-gray-500
// Labels (form fields)
text-xs font-medium text-zinc-400
text-xs font-medium text-gray-400
```
### Interactive
@ -130,7 +126,7 @@ text-sm font-semibold
text-sm text-purple-400 hover:text-purple-300
// Badge/count
text-xs text-zinc-600
text-xs text-gray-600
```
---
@ -158,10 +154,10 @@ px-3 py-2
### Section Spacing
```tsx
// Page padding
py-6 md:py-8
p-4 md:p-6
// Between sections
mb-4 md:mb-6
space-y-4
// Between cards in grid
gap-3 md:gap-4
@ -176,8 +172,7 @@ mb-1.5
### Border Radius
```tsx
rounded-lg /* Standard (inputs, small cards) */
rounded-xl /* Medium (cards, buttons) */
rounded-2xl /* Large - AVOID in Lab (too marketing) */
rounded-xl /* Medium (cards) */
```
---
@ -186,105 +181,76 @@ rounded-2xl /* Large - AVOID in Lab (too marketing) */
### Page Header
```tsx
<header className="mb-4 pb-3 border-b border-zinc-800">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Sparkles className="w-5 h-5 text-purple-400" />
<div>
<h1 className="text-lg font-semibold text-white">Generate</h1>
<p className="text-xs text-zinc-400">Create AI images from text prompts</p>
</div>
<header className="pb-3 border-b border-zinc-800">
<div className="flex items-center gap-2">
<Sparkles className="w-5 h-5 text-purple-400" />
<div>
<h1 className="text-lg font-semibold text-white">Generate</h1>
<p className="text-xs text-gray-400">Create AI images from text prompts</p>
</div>
<button className="px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-zinc-300 hover:text-white hover:bg-zinc-700 transition-colors">
<Settings className="w-4 h-4 mr-1.5 inline" />
Settings
</button>
</div>
</header>
```
### Standard Card
### Form Card (Slate)
```tsx
<div className="p-4 bg-zinc-900/80 backdrop-blur-sm border border-zinc-800 rounded-xl">
<section className="p-4 bg-slate-900/80 backdrop-blur-sm border border-slate-700 rounded-xl">
{/* content */}
</div>
</section>
```
### Compact Card (List Items)
```tsx
<div className="p-3 bg-zinc-900 border border-zinc-800 rounded-lg hover:border-zinc-700 transition-colors">
{/* content */}
</div>
```
### Form Input
### Form Input (Slate)
```tsx
<div className="space-y-1.5">
<label className="block text-xs font-medium text-zinc-400">Field Label</label>
<label className="block text-xs font-medium text-gray-400">Field Label</label>
<input
className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent"
className="w-full px-3 py-2 text-sm bg-slate-800 border border-slate-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent"
placeholder="Enter value..."
/>
</div>
```
### Textarea
### Textarea (Slate)
```tsx
<textarea
className="w-full px-3 py-2.5 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent resize-none"
className="w-full px-3 py-2.5 text-sm bg-slate-800 border border-slate-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent resize-none"
rows={4}
/>
```
### Primary Button
```tsx
<button className="px-4 py-2.5 text-sm rounded-lg bg-gradient-to-r from-purple-600 to-cyan-600 text-white font-semibold hover:from-purple-500 hover:to-cyan-500 transition-all shadow-lg shadow-purple-900/30 focus:ring-2 focus:ring-purple-500">
<button className="px-4 py-2 text-sm rounded-lg bg-gradient-to-r from-purple-600 to-cyan-600 text-white font-semibold hover:from-purple-500 hover:to-cyan-500 transition-all shadow-lg shadow-purple-900/30 focus:ring-2 focus:ring-purple-500 flex items-center gap-1.5">
<Sparkles className="w-4 h-4" />
Generate
</button>
```
### Secondary Button
### Secondary Button (Slate)
```tsx
<button className="px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-zinc-300 hover:text-white hover:bg-zinc-700 transition-colors focus:ring-2 focus:ring-purple-500">
<Settings className="w-4 h-4 mr-1.5 inline" />
<button className="w-full px-3 py-2 text-sm bg-slate-800 border border-slate-700 rounded-lg text-gray-400 hover:text-white hover:bg-slate-700 transition-colors flex items-center justify-center gap-1.5 focus:ring-2 focus:ring-purple-500">
<Settings className="w-4 h-4" />
Configure
</button>
```
### Ghost Button
### Empty State (Slate)
```tsx
<button className="px-3 py-2 text-sm text-zinc-400 hover:text-white hover:bg-zinc-800/50 rounded-lg transition-colors">
Clear
</button>
```
### Icon Button
```tsx
<button
className="p-2 text-zinc-400 hover:text-white hover:bg-zinc-800/50 rounded-lg transition-colors focus:ring-2 focus:ring-purple-500"
aria-label="Close"
>
<X className="w-4 h-4" />
</button>
```
### Empty State
```tsx
<div className="p-6 bg-zinc-900/50 border border-zinc-800 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-zinc-800 rounded-lg">
<ImageOff className="w-6 h-6 text-zinc-500" />
<div className="p-6 bg-slate-900/50 backdrop-blur-sm border border-slate-700 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-slate-800 rounded-lg">
<ImageOff className="w-6 h-6 text-gray-500" />
</div>
<h3 className="text-sm font-medium text-white mb-1">No results yet</h3>
<p className="text-xs text-zinc-400">Generated images will appear here</p>
<p className="text-xs text-gray-400">Generated images will appear here</p>
</div>
```
### Info Banner
```tsx
<div className="mb-4 p-3 bg-purple-500/5 border border-purple-500/20 rounded-lg">
<div className="p-3 bg-purple-900/10 border border-purple-700/50 rounded-lg">
<div className="flex items-start gap-2">
<Info className="w-4 h-4 text-purple-400 mt-0.5 shrink-0" />
<p className="text-xs text-zinc-300">
<p className="text-xs text-gray-300">
<span className="font-medium text-white">Lab Mode:</span> Experimental features enabled.
</p>
</div>
@ -354,7 +320,7 @@ hidden lg:block w-64
grid-cols-1 md:grid-cols-2 xl:grid-cols-3
// Form fields
grid-cols-1 md:grid-cols-2 lg:grid-cols-3
grid-cols-1 md:grid-cols-3
```
---
@ -362,8 +328,9 @@ grid-cols-1 md:grid-cols-2 lg:grid-cols-3
## Do's and Don'ts
### DO
- Use zinc palette for backgrounds/borders (not slate)
- Use text-sm/text-xs for most text (not text-lg/text-xl)
- Use **zinc** for layout (sidebar, footer, layout borders)
- Use **slate** for forms (cards, inputs, empty states)
- Use text-sm/text-xs for most text
- Use Lucide icons exclusively
- Keep spacing tight (p-3 to p-5)
- Add focus:ring-2 focus:ring-purple-500 to all interactive elements
@ -373,8 +340,7 @@ grid-cols-1 md:grid-cols-2 lg:grid-cols-3
- Use emojis anywhere in the UI
- Use marketing-size headings (text-3xl+)
- Use generous spacing (p-8+, py-12+)
- Use rounded-2xl (too marketing)
- Use slate colors (too blue-tinted)
- Mix zinc and slate inconsistently
- Forget aria-label on icon-only buttons
---

View File

@ -6,23 +6,23 @@ const ImagesPage = () => {
return (
<div className="p-4 md:p-6 space-y-4">
{/* Page Header */}
<header className="pb-3 border-b border-zinc-800">
<header className="pb-3 border-b border-slate-800">
<div className="flex items-center gap-2">
<Image className="w-5 h-5 text-purple-400" />
<div>
<h1 className="text-lg font-semibold text-white">Images</h1>
<p className="text-xs text-zinc-400">Browse and manage your generated images</p>
<p className="text-xs text-gray-400">Browse and manage your generated images</p>
</div>
</div>
</header>
{/* Empty State Placeholder */}
<div className="p-6 bg-zinc-900/50 border border-zinc-800 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-zinc-800 rounded-lg">
<ImageOff className="w-6 h-6 text-zinc-500" />
<div className="p-6 bg-slate-900/50 backdrop-blur-sm border border-slate-700 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-slate-800 rounded-lg">
<ImageOff className="w-6 h-6 text-gray-500" />
</div>
<h2 className="text-sm font-medium text-white mb-1">Image Browser</h2>
<p className="text-xs text-zinc-400">Component will be implemented here</p>
<p className="text-xs text-gray-400">Component will be implemented here</p>
</div>
</div>
);

View File

@ -6,23 +6,23 @@ const LivePage = () => {
return (
<div className="p-4 md:p-6 space-y-4">
{/* Page Header */}
<header className="pb-3 border-b border-zinc-800">
<header className="pb-3 border-b border-slate-800">
<div className="flex items-center gap-2">
<Zap className="w-5 h-5 text-purple-400" />
<div>
<h1 className="text-lg font-semibold text-white">Live</h1>
<p className="text-xs text-zinc-400">Real-time testing and experimentation</p>
<p className="text-xs text-gray-400">Real-time testing and experimentation</p>
</div>
</div>
</header>
{/* Live Testing Placeholder */}
<div className="p-6 bg-zinc-900/50 border border-zinc-800 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-zinc-800 rounded-lg">
<Radio className="w-6 h-6 text-zinc-500" />
<div className="p-6 bg-slate-900/50 backdrop-blur-sm border border-slate-700 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-slate-800 rounded-lg">
<Radio className="w-6 h-6 text-gray-500" />
</div>
<h2 className="text-sm font-medium text-white mb-1">Live Testing Interface</h2>
<p className="text-xs text-zinc-400">Component will be implemented here</p>
<p className="text-xs text-gray-400">Component will be implemented here</p>
</div>
</div>
);

View File

@ -6,23 +6,23 @@ const UploadPage = () => {
return (
<div className="p-4 md:p-6 space-y-4">
{/* Page Header */}
<header className="pb-3 border-b border-zinc-800">
<header className="pb-3 border-b border-slate-800">
<div className="flex items-center gap-2">
<Upload className="w-5 h-5 text-purple-400" />
<div>
<h1 className="text-lg font-semibold text-white">Upload</h1>
<p className="text-xs text-zinc-400">Upload and manage reference images</p>
<p className="text-xs text-gray-400">Upload and manage reference images</p>
</div>
</div>
</header>
{/* Upload Dropzone Placeholder */}
<div className="p-6 bg-zinc-900/50 border border-zinc-800 border-dashed rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-zinc-800 rounded-lg">
<UploadCloud className="w-6 h-6 text-zinc-500" />
<div className="p-6 bg-slate-900/50 backdrop-blur-sm border border-slate-700 border-dashed rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-slate-800 rounded-lg">
<UploadCloud className="w-6 h-6 text-gray-500" />
</div>
<h2 className="text-sm font-medium text-white mb-1">Upload Interface</h2>
<p className="text-xs text-zinc-400">Drag and drop files or click to browse</p>
<p className="text-xs text-gray-400">Drag and drop files or click to browse</p>
</div>
</div>
);

View File

@ -5,7 +5,7 @@
*
* Checkbox/radio button group for sidebar filters.
* Supports both single-select (radio) and multi-select (checkbox) modes.
* Uses zinc color palette.
* Uses slate palette for form controls (matches form styling).
*
* Features:
* - Radio buttons for single selection
@ -49,7 +49,7 @@ export const FilterPlaceholder = ({
const isSelected = (optionId: string) => selected.includes(optionId);
return (
<div className="mt-2 space-y-0.5" role="group" aria-label={`${groupId} filters`}>
<div className="mt-2 space-y-1" role="group" aria-label={`${groupId} filters`}>
{options.map((option) => {
const checked = isSelected(option.id);
const inputId = `${groupId}-${option.id}`;
@ -58,7 +58,7 @@ export const FilterPlaceholder = ({
<label
key={option.id}
htmlFor={inputId}
className="flex items-center gap-2 px-2 py-1.5 rounded-md text-sm cursor-pointer transition-colors hover:bg-zinc-800/50 group"
className="flex items-center gap-2 px-2 py-1.5 rounded-md text-sm cursor-pointer transition-colors hover:bg-white/5 group"
>
<input
type={multiSelect ? 'checkbox' : 'radio'}
@ -66,13 +66,13 @@ export const FilterPlaceholder = ({
name={groupId}
checked={checked}
onChange={() => handleSelect(option.id)}
className="w-4 h-4 bg-zinc-800 border-zinc-600 text-purple-600 focus:ring-2 focus:ring-purple-500 focus:ring-offset-0 rounded cursor-pointer"
className="w-4 h-4 bg-slate-800 border-slate-600 text-purple-600 focus:ring-2 focus:ring-purple-500 focus:ring-offset-0 rounded cursor-pointer"
/>
<span className={`flex-1 text-sm ${checked ? 'text-white font-medium' : 'text-zinc-400 group-hover:text-zinc-300'}`}>
<span className={`flex-1 ${checked ? 'text-white font-medium' : 'text-gray-400 group-hover:text-gray-300'}`}>
{option.label}
</span>
{option.count !== undefined && (
<span className="text-xs text-zinc-600">
<span className="text-xs text-gray-600">
{option.count}
</span>
)}

View File

@ -12,6 +12,8 @@
* - Options grid (aspect ratio, style, advanced)
* - Results area with empty state
* - Lucide icons throughout
*
* Color scheme: Forms use slate palette (original style)
*/
import { useState, useRef, KeyboardEvent } from 'react';
@ -50,18 +52,18 @@ export const GenerateFormPlaceholder = () => {
<div className="p-4 md:p-6 space-y-4">
{/* Compact Info Banner */}
{showBanner && (
<div className="p-3 bg-purple-500/5 border border-purple-500/20 rounded-lg">
<div className="p-3 bg-purple-900/10 border border-purple-700/50 rounded-lg">
<div className="flex items-start gap-2">
<Info className="w-4 h-4 text-purple-400 mt-0.5 shrink-0" />
<div className="flex-1">
<p className="text-xs text-zinc-300">
<p className="text-xs text-gray-300">
<span className="font-medium text-white">Lab Mode:</span>{' '}
Experimental features enabled. API calls may be rate-limited.
</p>
</div>
<button
onClick={() => setShowBanner(false)}
className="p-1 text-zinc-500 hover:text-zinc-300 transition-colors"
className="p-1 text-gray-500 hover:text-gray-300 transition-colors"
aria-label="Dismiss"
>
<X className="w-3 h-3" />
@ -72,14 +74,14 @@ export const GenerateFormPlaceholder = () => {
{/* Generation Form Card */}
<section
className="p-4 bg-zinc-900/80 backdrop-blur-sm border border-zinc-800 rounded-xl"
className="p-4 bg-slate-900/80 backdrop-blur-sm border border-slate-700 rounded-xl"
aria-label="Image Generation Form"
>
{/* Prompt Textarea */}
<div className="mb-3">
<label
htmlFor="lab-prompt-input"
className="block text-xs font-medium text-zinc-400 mb-1.5"
className="block text-xs font-medium text-gray-400 mb-1.5"
>
Prompt
</label>
@ -92,7 +94,7 @@ export const GenerateFormPlaceholder = () => {
placeholder="Describe the image you want to generate..."
disabled={generating}
rows={4}
className="w-full px-3 py-2.5 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed resize-none"
className="w-full px-3 py-2.5 text-sm bg-slate-800 border border-slate-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed resize-none"
aria-label="Image generation prompt"
/>
</div>
@ -103,7 +105,7 @@ export const GenerateFormPlaceholder = () => {
<div>
<label
htmlFor="lab-aspect-ratio"
className="block text-xs font-medium text-zinc-400 mb-1.5"
className="block text-xs font-medium text-gray-400 mb-1.5"
>
Aspect Ratio
</label>
@ -112,7 +114,7 @@ export const GenerateFormPlaceholder = () => {
value={aspectRatio}
onChange={(e) => setAspectRatio(e.target.value)}
disabled={generating}
className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full px-3 py-2 text-sm bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
>
<option value="1:1">Square (1:1)</option>
<option value="3:4">Portrait (3:4)</option>
@ -127,7 +129,7 @@ export const GenerateFormPlaceholder = () => {
<div>
<label
htmlFor="lab-template"
className="block text-xs font-medium text-zinc-400 mb-1.5"
className="block text-xs font-medium text-gray-400 mb-1.5"
>
Style Template
</label>
@ -136,7 +138,7 @@ export const GenerateFormPlaceholder = () => {
value={template}
onChange={(e) => setTemplate(e.target.value)}
disabled={generating}
className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full px-3 py-2 text-sm bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
>
<option value="photorealistic">Photorealistic</option>
<option value="illustration">Illustration</option>
@ -150,12 +152,12 @@ export const GenerateFormPlaceholder = () => {
{/* Advanced Options Button */}
<div>
<label className="block text-xs font-medium text-zinc-400 mb-1.5">
<label className="block text-xs font-medium text-gray-400 mb-1.5">
Advanced
</label>
<button
disabled={generating}
className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-lg text-zinc-400 hover:text-white hover:bg-zinc-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-1.5 focus:ring-2 focus:ring-purple-500"
className="w-full px-3 py-2 text-sm bg-slate-800 border border-slate-700 rounded-lg text-gray-400 hover:text-white hover:bg-slate-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-1.5 focus:ring-2 focus:ring-purple-500"
aria-label="Advanced options"
>
<Settings className="w-4 h-4" />
@ -165,8 +167,8 @@ export const GenerateFormPlaceholder = () => {
</div>
{/* Submit Button Row */}
<div className="flex items-center justify-between gap-3 pt-3 border-t border-zinc-800">
<div className="text-xs text-zinc-500">
<div className="flex items-center justify-between gap-3 pt-3 border-t border-slate-700/50">
<div className="text-xs text-gray-500">
{generating ? (
<span className="flex items-center gap-1.5 text-purple-400">
<Loader2 className="w-3.5 h-3.5 animate-spin" />
@ -191,7 +193,7 @@ export const GenerateFormPlaceholder = () => {
<section className="space-y-3" aria-label="Generated Results">
<div className="flex items-center justify-between">
<h2 className="text-sm font-semibold text-white">Results</h2>
<button className="flex items-center gap-1 text-xs text-zinc-500 hover:text-zinc-300 transition-colors">
<button className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-300 transition-colors">
<Trash2 className="w-3.5 h-3.5" />
Clear
</button>
@ -199,12 +201,12 @@ export const GenerateFormPlaceholder = () => {
{/* Empty State */}
{!generating && (
<div className="p-6 bg-zinc-900/50 border border-zinc-800 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-zinc-800 rounded-lg">
<ImageOff className="w-6 h-6 text-zinc-500" />
<div className="p-6 bg-slate-900/50 backdrop-blur-sm border border-slate-700 rounded-lg text-center">
<div className="w-12 h-12 mx-auto mb-3 flex items-center justify-center bg-slate-800 rounded-lg">
<ImageOff className="w-6 h-6 text-gray-500" />
</div>
<h3 className="text-sm font-medium text-white mb-1">No results yet</h3>
<p className="text-xs text-zinc-400">
<p className="text-xs text-gray-400">
Generated images will appear here
</p>
</div>
@ -212,11 +214,11 @@ export const GenerateFormPlaceholder = () => {
{/* Loading State */}
{generating && (
<div className="p-4 bg-zinc-900/80 backdrop-blur-sm border border-zinc-800 rounded-lg animate-pulse">
<div className="p-4 bg-slate-900/80 backdrop-blur-sm border border-slate-700 rounded-lg animate-pulse">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">
<div className="aspect-square bg-zinc-800 rounded-lg"></div>
<div className="aspect-square bg-zinc-800 rounded-lg"></div>
<div className="aspect-square bg-zinc-800 rounded-lg hidden xl:block"></div>
<div className="aspect-square bg-slate-800 rounded-lg"></div>
<div className="aspect-square bg-slate-800 rounded-lg"></div>
<div className="aspect-square bg-slate-800 rounded-lg hidden xl:block"></div>
</div>
</div>
)}

View File

@ -5,7 +5,7 @@
*
* Simple 1-line footer for lab section with contextual navigation links.
* Displays copyright on left and contextual docs/API links on right.
* Uses zinc color palette.
* Uses slate color palette (blue-tinted gray).
*/
import { usePathname } from 'next/navigation';
@ -34,12 +34,12 @@ export const LabFooter = () => {
return (
<footer
className="border-t border-zinc-800 bg-zinc-950/50 backdrop-blur-sm"
className="border-t border-slate-800 bg-slate-950/80 backdrop-blur-sm"
role="contentinfo"
>
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-3 md:gap-0 px-4 py-3 md:h-12">
{/* Left: Copyright */}
<p className="text-xs text-zinc-500 order-2 md:order-1">
<p className="text-xs text-slate-500 order-2 md:order-1">
© 2025 Banatie. Built for builders who create.
</p>
@ -47,14 +47,14 @@ export const LabFooter = () => {
<nav aria-label="Footer navigation" className="flex items-center gap-4 order-1 md:order-2">
<Link
href={links.docs}
className="text-xs text-zinc-500 hover:text-white transition-colors flex items-center gap-1.5"
className="text-xs text-slate-500 hover:text-white transition-colors flex items-center gap-1.5"
>
<BookOpen className="w-3.5 h-3.5" />
Documentation
</Link>
<Link
href={links.api}
className="text-xs text-zinc-500 hover:text-white transition-colors flex items-center gap-1.5"
className="text-xs text-slate-500 hover:text-white transition-colors flex items-center gap-1.5"
>
<Code className="w-3.5 h-3.5" />
API Reference

View File

@ -61,12 +61,12 @@ export const LabLayout = ({ children }: LabLayoutProps) => {
return (
<ThreeColumnLayout
left={
<div className={`border-r border-zinc-800 bg-zinc-950/50 backdrop-blur-sm ${containerHeight} overflow-y-auto transition-all duration-300`}>
<div className={`border-r border-slate-800 bg-slate-950 ${containerHeight} overflow-y-auto transition-all duration-300`}>
<LabSidebar />
</div>
}
center={
<div className={`flex flex-col ${containerHeight} transition-all duration-300`}>
<div className={`flex flex-col ${containerHeight} bg-[#0f121d] transition-all duration-300`}>
<div ref={contentRef} className="flex-1 overflow-y-auto min-h-0">{children}</div>
<LabFooter />
</div>

View File

@ -4,13 +4,13 @@
* Lab Sidebar - Filter Panel
*
* Narrow left sidebar for filtering lab content.
* Clean, work-focused design with Lucide icons and zinc palette.
* Clean, work-focused design with Lucide icons and slate palette.
*
* Features:
* - Multiple filter groups (Status, Date Range, Source Type)
* - Collapsible sections with Lucide icons
* - Checkbox/radio button controls
* - Zinc color palette (neutral gray)
* - Slate color palette (blue-tinted gray)
*/
import { useState, type ElementType } from 'react';
@ -75,11 +75,11 @@ export const LabSidebar = () => {
const isExpanded = (id: string) => expandedSections.includes(id);
return (
<aside className="h-full bg-zinc-950 border-r border-zinc-800" aria-label="Lab filters">
<aside className="h-full bg-slate-950 border-r border-slate-800" aria-label="Lab filters">
{/* Header */}
<div className="p-4 border-b border-zinc-800">
<div className="p-4 border-b border-slate-800">
<h2 className="text-sm font-semibold text-white">Filters</h2>
<p className="text-xs text-zinc-500 mt-0.5">Refine your results</p>
<p className="text-xs text-slate-500 mt-0.5">Refine your results</p>
</div>
{/* Filter Groups */}
@ -89,11 +89,11 @@ export const LabSidebar = () => {
const Icon = group.icon;
return (
<div key={group.id} className="border-b border-zinc-800 pb-3 last:border-b-0">
<div key={group.id} className="border-b border-slate-800 pb-3 last:border-b-0">
{/* Section Header */}
<button
onClick={() => toggleSection(group.id)}
className="w-full flex items-center justify-between px-2 py-1.5 rounded-lg text-sm transition-colors text-zinc-400 hover:text-white hover:bg-zinc-800/50"
className="w-full flex items-center justify-between px-2 py-1.5 rounded-lg text-sm transition-colors text-slate-400 hover:text-white hover:bg-slate-800/50"
aria-expanded={expanded}
>
<span className="flex items-center gap-2">
@ -119,9 +119,9 @@ export const LabSidebar = () => {
</div>
{/* Bottom Actions */}
<div className="mt-auto p-3 border-t border-zinc-800">
<div className="mt-auto p-3 border-t border-slate-800">
<button
className="w-full flex items-center justify-center gap-1.5 px-3 py-2 text-sm text-zinc-500 hover:text-zinc-300 transition-colors rounded-lg hover:bg-zinc-800/50"
className="w-full flex items-center justify-center gap-1.5 px-3 py-2 text-sm text-slate-500 hover:text-slate-300 transition-colors rounded-lg hover:bg-slate-800/50"
aria-label="Reset all filters"
>
<RotateCcw className="w-3.5 h-3.5" />