feat: setup colors
This commit is contained in:
parent
6b1a8ff96f
commit
fe301756d7
|
|
@ -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
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue