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

View File

@ -6,23 +6,23 @@ const ImagesPage = () => {
return ( return (
<div className="p-4 md:p-6 space-y-4"> <div className="p-4 md:p-6 space-y-4">
{/* Page Header */} {/* 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"> <div className="flex items-center gap-2">
<Image className="w-5 h-5 text-purple-400" /> <Image className="w-5 h-5 text-purple-400" />
<div> <div>
<h1 className="text-lg font-semibold text-white">Images</h1> <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>
</div> </div>
</header> </header>
{/* Empty State Placeholder */} {/* Empty State Placeholder */}
<div className="p-6 bg-zinc-900/50 border border-zinc-800 rounded-lg text-center"> <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-zinc-800 rounded-lg"> <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-zinc-500" /> <ImageOff className="w-6 h-6 text-gray-500" />
</div> </div>
<h2 className="text-sm font-medium text-white mb-1">Image Browser</h2> <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>
</div> </div>
); );

View File

@ -6,23 +6,23 @@ const LivePage = () => {
return ( return (
<div className="p-4 md:p-6 space-y-4"> <div className="p-4 md:p-6 space-y-4">
{/* Page Header */} {/* 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"> <div className="flex items-center gap-2">
<Zap className="w-5 h-5 text-purple-400" /> <Zap className="w-5 h-5 text-purple-400" />
<div> <div>
<h1 className="text-lg font-semibold text-white">Live</h1> <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>
</div> </div>
</header> </header>
{/* Live Testing Placeholder */} {/* Live Testing Placeholder */}
<div className="p-6 bg-zinc-900/50 border border-zinc-800 rounded-lg text-center"> <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-zinc-800 rounded-lg"> <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-zinc-500" /> <Radio className="w-6 h-6 text-gray-500" />
</div> </div>
<h2 className="text-sm font-medium text-white mb-1">Live Testing Interface</h2> <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>
</div> </div>
); );

View File

@ -6,23 +6,23 @@ const UploadPage = () => {
return ( return (
<div className="p-4 md:p-6 space-y-4"> <div className="p-4 md:p-6 space-y-4">
{/* Page Header */} {/* 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"> <div className="flex items-center gap-2">
<Upload className="w-5 h-5 text-purple-400" /> <Upload className="w-5 h-5 text-purple-400" />
<div> <div>
<h1 className="text-lg font-semibold text-white">Upload</h1> <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>
</div> </div>
</header> </header>
{/* Upload Dropzone Placeholder */} {/* Upload Dropzone Placeholder */}
<div className="p-6 bg-zinc-900/50 border border-zinc-800 border-dashed rounded-lg text-center"> <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-zinc-800 rounded-lg"> <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-zinc-500" /> <UploadCloud className="w-6 h-6 text-gray-500" />
</div> </div>
<h2 className="text-sm font-medium text-white mb-1">Upload Interface</h2> <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>
</div> </div>
); );

View File

@ -5,7 +5,7 @@
* *
* Checkbox/radio button group for sidebar filters. * Checkbox/radio button group for sidebar filters.
* Supports both single-select (radio) and multi-select (checkbox) modes. * Supports both single-select (radio) and multi-select (checkbox) modes.
* Uses zinc color palette. * Uses slate palette for form controls (matches form styling).
* *
* Features: * Features:
* - Radio buttons for single selection * - Radio buttons for single selection
@ -49,7 +49,7 @@ export const FilterPlaceholder = ({
const isSelected = (optionId: string) => selected.includes(optionId); const isSelected = (optionId: string) => selected.includes(optionId);
return ( 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) => { {options.map((option) => {
const checked = isSelected(option.id); const checked = isSelected(option.id);
const inputId = `${groupId}-${option.id}`; const inputId = `${groupId}-${option.id}`;
@ -58,7 +58,7 @@ export const FilterPlaceholder = ({
<label <label
key={option.id} key={option.id}
htmlFor={inputId} 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 <input
type={multiSelect ? 'checkbox' : 'radio'} type={multiSelect ? 'checkbox' : 'radio'}
@ -66,13 +66,13 @@ export const FilterPlaceholder = ({
name={groupId} name={groupId}
checked={checked} checked={checked}
onChange={() => handleSelect(option.id)} 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} {option.label}
</span> </span>
{option.count !== undefined && ( {option.count !== undefined && (
<span className="text-xs text-zinc-600"> <span className="text-xs text-gray-600">
{option.count} {option.count}
</span> </span>
)} )}

View File

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

View File

@ -5,7 +5,7 @@
* *
* Simple 1-line footer for lab section with contextual navigation links. * Simple 1-line footer for lab section with contextual navigation links.
* Displays copyright on left and contextual docs/API links on right. * 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'; import { usePathname } from 'next/navigation';
@ -34,12 +34,12 @@ export const LabFooter = () => {
return ( return (
<footer <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" 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"> <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 */} {/* 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. © 2025 Banatie. Built for builders who create.
</p> </p>
@ -47,14 +47,14 @@ export const LabFooter = () => {
<nav aria-label="Footer navigation" className="flex items-center gap-4 order-1 md:order-2"> <nav aria-label="Footer navigation" className="flex items-center gap-4 order-1 md:order-2">
<Link <Link
href={links.docs} 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" /> <BookOpen className="w-3.5 h-3.5" />
Documentation Documentation
</Link> </Link>
<Link <Link
href={links.api} 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" /> <Code className="w-3.5 h-3.5" />
API Reference API Reference

View File

@ -61,12 +61,12 @@ export const LabLayout = ({ children }: LabLayoutProps) => {
return ( return (
<ThreeColumnLayout <ThreeColumnLayout
left={ 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 /> <LabSidebar />
</div> </div>
} }
center={ 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> <div ref={contentRef} className="flex-1 overflow-y-auto min-h-0">{children}</div>
<LabFooter /> <LabFooter />
</div> </div>

View File

@ -4,13 +4,13 @@
* Lab Sidebar - Filter Panel * Lab Sidebar - Filter Panel
* *
* Narrow left sidebar for filtering lab content. * 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: * Features:
* - Multiple filter groups (Status, Date Range, Source Type) * - Multiple filter groups (Status, Date Range, Source Type)
* - Collapsible sections with Lucide icons * - Collapsible sections with Lucide icons
* - Checkbox/radio button controls * - Checkbox/radio button controls
* - Zinc color palette (neutral gray) * - Slate color palette (blue-tinted gray)
*/ */
import { useState, type ElementType } from 'react'; import { useState, type ElementType } from 'react';
@ -75,11 +75,11 @@ export const LabSidebar = () => {
const isExpanded = (id: string) => expandedSections.includes(id); const isExpanded = (id: string) => expandedSections.includes(id);
return ( 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 */} {/* 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> <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> </div>
{/* Filter Groups */} {/* Filter Groups */}
@ -89,11 +89,11 @@ export const LabSidebar = () => {
const Icon = group.icon; const Icon = group.icon;
return ( 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 */} {/* Section Header */}
<button <button
onClick={() => toggleSection(group.id)} 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} aria-expanded={expanded}
> >
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
@ -119,9 +119,9 @@ export const LabSidebar = () => {
</div> </div>
{/* Bottom Actions */} {/* 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 <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" aria-label="Reset all filters"
> >
<RotateCcw className="w-3.5 h-3.5" /> <RotateCcw className="w-3.5 h-3.5" />