updated UI specifications and added an example UI layout
This commit is contained in:
parent
e4ea971eea
commit
2172a1c043
377
Docs/design/library-layouts.tsx
Normal file
377
Docs/design/library-layouts.tsx
Normal file
@ -0,0 +1,377 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Search, Grid, List, User, Plus, FolderOpen, Image, Upload } from 'lucide-react';
|
||||
|
||||
const InspirationEngine = () => {
|
||||
const [activeView, setActiveView] = useState('library');
|
||||
const [showDropzone, setShowDropzone] = useState(false);
|
||||
|
||||
// Warm Creative Palette
|
||||
const colors = {
|
||||
bg: '#1c1917',
|
||||
surface: '#292524',
|
||||
text: '#fafaf9',
|
||||
textMuted: '#a8a29e',
|
||||
border: '#44403c',
|
||||
primary: '#f97316',
|
||||
primaryHover: '#ea580c',
|
||||
accent: '#fbbf24'
|
||||
};
|
||||
|
||||
// Mock data for library items
|
||||
const mockItems = [
|
||||
{ id: 1, height: 200, width: 'normal' },
|
||||
{ id: 2, height: 280, width: 'wide' },
|
||||
{ id: 3, height: 160, width: 'normal' },
|
||||
{ id: 4, height: 240, width: 'normal' },
|
||||
{ id: 5, height: 200, width: 'normal' },
|
||||
{ id: 6, height: 300, width: 'wide' },
|
||||
{ id: 7, height: 180, width: 'normal' },
|
||||
{ id: 8, height: 220, width: 'normal' },
|
||||
{ id: 9, height: 260, width: 'wide' },
|
||||
{ id: 10, height: 190, width: 'normal' },
|
||||
{ id: 11, height: 240, width: 'normal' },
|
||||
{ id: 12, height: 200, width: 'wide' },
|
||||
{ id: 13, height: 220, width: 'normal' },
|
||||
{ id: 14, height: 180, width: 'normal' },
|
||||
{ id: 15, height: 260, width: 'normal' },
|
||||
{ id: 16, height: 200, width: 'wide' },
|
||||
{ id: 17, height: 240, width: 'normal' },
|
||||
{ id: 18, height: 180, width: 'normal' },
|
||||
];
|
||||
|
||||
// Mock data for collections
|
||||
const mockCollections = [
|
||||
{ id: 1, name: 'Brand Identity', itemCount: 47, preview: [colors.primary, colors.accent, colors.primaryHover, colors.primary] },
|
||||
{ id: 2, name: 'UI Inspiration', itemCount: 132, preview: [colors.accent, colors.primary, colors.accent, colors.primaryHover] },
|
||||
{ id: 3, name: 'Typography', itemCount: 28, preview: [colors.primaryHover, colors.primary, colors.accent, colors.primary] },
|
||||
{ id: 4, name: 'Color Palettes', itemCount: 64, preview: [colors.primary, colors.primaryHover, colors.accent, colors.primary] },
|
||||
{ id: 5, name: 'Illustration', itemCount: 89, preview: [colors.accent, colors.primary, colors.primaryHover, colors.accent] },
|
||||
{ id: 6, name: 'Photography', itemCount: 156, preview: [colors.primary, colors.accent, colors.primary, colors.primaryHover] },
|
||||
{ id: 7, name: 'Web Design', itemCount: 93, preview: [colors.primaryHover, colors.accent, colors.primary, colors.accent] },
|
||||
{ id: 8, name: 'Motion Graphics', itemCount: 41, preview: [colors.primary, colors.primaryHover, colors.accent, colors.primary] },
|
||||
];
|
||||
|
||||
const LibraryView = () => (
|
||||
<>
|
||||
{/* Compact Horizontal Filters Toolbar */}
|
||||
<div className="px-8 py-3 flex items-center gap-2 border-b" style={{ borderColor: colors.border }}>
|
||||
<button
|
||||
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.textMuted
|
||||
}}
|
||||
>
|
||||
<Grid size={14} />
|
||||
Filter
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-90 transition-all font-medium"
|
||||
style={{
|
||||
backgroundColor: colors.primary,
|
||||
color: '#fff'
|
||||
}}
|
||||
>
|
||||
All Items
|
||||
<span className="text-xs opacity-70">▼</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.textMuted
|
||||
}}
|
||||
>
|
||||
Source
|
||||
<span className="text-xs opacity-70">▼</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.textMuted
|
||||
}}
|
||||
>
|
||||
Media type
|
||||
<span className="text-xs opacity-70">▼</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.textMuted
|
||||
}}
|
||||
>
|
||||
Date added
|
||||
<span className="text-xs opacity-70">▼</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 flex items-center gap-2 text-sm hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.textMuted
|
||||
}}
|
||||
>
|
||||
Tags
|
||||
<span className="text-xs opacity-70">▼</span>
|
||||
</button>
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
<span className="text-sm font-medium" style={{ color: colors.textMuted }}>
|
||||
1,427 items
|
||||
</span>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.text
|
||||
}}
|
||||
>
|
||||
<Grid size={16} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="px-3 py-2 hover:opacity-80 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.textMuted
|
||||
}}
|
||||
>
|
||||
<List size={16} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Main Content - Tightly Packed Masonry Grid */}
|
||||
<main className="flex-1 overflow-auto">
|
||||
<div className="columns-5 gap-0.5">
|
||||
{mockItems.map((item, index) => {
|
||||
const demoColors = [colors.primary, colors.accent, colors.surface, colors.primaryHover];
|
||||
const itemColor = demoColors[index % demoColors.length];
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
className="mb-0.5 break-inside-avoid group cursor-pointer relative overflow-hidden transition-all hover:opacity-90"
|
||||
style={{
|
||||
height: `${item.height}px`,
|
||||
backgroundColor: itemColor
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-60 transition-all flex items-center justify-center opacity-0 group-hover:opacity-100">
|
||||
<button
|
||||
className="px-4 py-2 text-sm font-medium hover:scale-105 transition-transform"
|
||||
style={{ backgroundColor: colors.text, color: colors.bg }}
|
||||
>
|
||||
View Details
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
||||
const CollectionsView = () => (
|
||||
<>
|
||||
{/* Collections Toolbar */}
|
||||
<div className="px-8 py-4 flex items-center justify-between border-b" style={{ borderColor: colors.border }}>
|
||||
<div className="flex items-center gap-3">
|
||||
<h2 className="text-xl font-bold">Your Collections</h2>
|
||||
<span className="text-sm" style={{ color: colors.textMuted }}>
|
||||
{mockCollections.length} collections
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="px-4 py-2 flex items-center gap-2 text-sm font-medium hover:opacity-90 transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.primary,
|
||||
color: '#fff'
|
||||
}}
|
||||
>
|
||||
<Plus size={16} />
|
||||
New Collection
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Collections Grid */}
|
||||
<main className="flex-1 overflow-auto">
|
||||
<div className="grid grid-cols-4 gap-0.5">
|
||||
{mockCollections.map(collection => (
|
||||
<div
|
||||
key={collection.id}
|
||||
className="group cursor-pointer relative"
|
||||
>
|
||||
{/* Collection Preview Grid */}
|
||||
<div
|
||||
className="aspect-square overflow-hidden transition-all hover:opacity-90"
|
||||
style={{
|
||||
backgroundColor: colors.surface
|
||||
}}
|
||||
>
|
||||
<div className="grid grid-cols-2 grid-rows-2 h-full gap-0">
|
||||
{collection.preview.map((color, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
style={{
|
||||
backgroundColor: color
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Hover Overlay with Info */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-70 transition-all flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 p-4">
|
||||
<h3 className="font-bold text-base mb-2 text-center" style={{ color: colors.text }}>
|
||||
{collection.name}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 text-sm mb-4" style={{ color: colors.textMuted }}>
|
||||
<Image size={14} />
|
||||
<span>{collection.itemCount} items</span>
|
||||
</div>
|
||||
<button
|
||||
className="px-4 py-2 text-sm font-medium hover:scale-105 transition-transform"
|
||||
style={{ backgroundColor: colors.text, color: colors.bg }}
|
||||
>
|
||||
Open Collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Create New Collection Card */}
|
||||
<div
|
||||
className="aspect-square cursor-pointer transition-all hover:opacity-80 flex flex-col items-center justify-center gap-3"
|
||||
style={{
|
||||
backgroundColor: colors.surface
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-12 h-12 flex items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: colors.primary,
|
||||
color: '#fff'
|
||||
}}
|
||||
>
|
||||
<Plus size={24} />
|
||||
</div>
|
||||
<span className="font-medium text-sm" style={{ color: colors.textMuted }}>
|
||||
Create Collection
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full h-screen flex flex-col" style={{ backgroundColor: colors.bg, color: colors.text }}>
|
||||
{/* Header */}
|
||||
<header className="border-b" style={{ borderColor: colors.border }}>
|
||||
<div className="px-8 py-6 flex items-center justify-between">
|
||||
<div className="text-2xl font-bold tracking-tight">INSPIRATION</div>
|
||||
|
||||
{/* Prominent Central Search with Image Toggle */}
|
||||
<div className="flex-1 max-w-2xl mx-12 flex gap-3">
|
||||
<div className="relative flex-1">
|
||||
<Search
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2"
|
||||
style={{ color: colors.textMuted }}
|
||||
size={20}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search by idea, concept, or visual..."
|
||||
className="w-full py-4 pl-12 pr-4 text-base transition-all"
|
||||
style={{
|
||||
backgroundColor: colors.surface,
|
||||
color: colors.text,
|
||||
border: 'none',
|
||||
outline: 'none'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowDropzone(!showDropzone)}
|
||||
className="px-4 py-4 hover:opacity-90 transition-all"
|
||||
style={{
|
||||
backgroundColor: showDropzone ? colors.primary : colors.surface,
|
||||
color: showDropzone ? '#fff' : colors.textMuted
|
||||
}}
|
||||
title="Search by image"
|
||||
>
|
||||
<Image size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav className="flex items-center gap-6">
|
||||
<button
|
||||
onClick={() => setActiveView('library')}
|
||||
className="hover:opacity-70 transition-opacity font-medium"
|
||||
style={{ color: activeView === 'library' ? colors.text : colors.textMuted }}
|
||||
>
|
||||
Library
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveView('collections')}
|
||||
className="hover:opacity-70 transition-opacity font-medium"
|
||||
style={{ color: activeView === 'collections' ? colors.text : colors.textMuted }}
|
||||
>
|
||||
Collections
|
||||
</button>
|
||||
<button className="hover:opacity-70 transition-opacity">
|
||||
<User size={22} style={{ color: colors.textMuted }} />
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Expandable Image Search Dropzone */}
|
||||
{showDropzone && (
|
||||
<div className="px-8 pb-6">
|
||||
<div
|
||||
className="p-8 text-center cursor-pointer hover:opacity-80 transition-all"
|
||||
style={{ backgroundColor: colors.surface }}
|
||||
>
|
||||
<Upload size={32} style={{ color: colors.primary, margin: '0 auto 12px' }} />
|
||||
<p className="font-medium mb-2" style={{ color: colors.text }}>
|
||||
Drop your image to search
|
||||
</p>
|
||||
<p className="text-sm mb-4" style={{ color: colors.textMuted }}>
|
||||
Drag and drop, or click to browse
|
||||
</p>
|
||||
<div className="flex gap-3 justify-center">
|
||||
<button
|
||||
className="px-4 py-2 text-sm font-medium hover:opacity-90 transition-all"
|
||||
style={{ backgroundColor: colors.primary, color: '#fff' }}
|
||||
>
|
||||
Choose File
|
||||
</button>
|
||||
<button
|
||||
className="px-4 py-2 text-sm font-medium hover:opacity-80 transition-all"
|
||||
style={{ backgroundColor: colors.bg, color: colors.textMuted }}
|
||||
>
|
||||
Paste URL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* Active View */}
|
||||
{activeView === 'library' ? <LibraryView /> : <CollectionsView />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InspirationEngine;
|
||||
@ -94,12 +94,20 @@ graph TD
|
||||
* **Success Criteria:** The process feels creative and additive, not like a chore. The user can easily create, populate, and view the collection as a moodboard.
|
||||
* **Enhancements:** The system will support selecting multiple items at once via a "selection mode" and will eventually feature AI-powered suggestions for items to add to a collection, and even suggest automatically created collections based on thematic clusters in the user's library.
|
||||
|
||||
### **Flow 4: Search by Image**
|
||||
|
||||
* **User Goal:** To find similar items in their library by uploading or providing a reference image.
|
||||
* **Entry Points:** Inline image search toggle button adjacent to the main search bar in the Library view.
|
||||
* **Success Criteria:** The user can quickly toggle to image search mode, upload an image via drag-and-drop or file picker, and receive visually similar results from their library.
|
||||
* **Implementation:** An expandable dropzone slides down beneath the header when the image search icon is clicked, allowing users to upload images, paste URLs, or take photos without leaving the current view.
|
||||
|
||||
## **Wireframes & Mockups**
|
||||
|
||||
* **Design Tools:** Low-fidelity concepts and wireframes will be developed using **Excalidraw**. High-fidelity mockups and visual design will be created in **Adobe Photoshop**.
|
||||
* **Key Screen Layout (Desktop: "Library / Search Results"):** This layout is the blueprint for the application's primary view.
|
||||
* **`[ ZONE 1: HEADER ]`**: Left-aligned Logo; a large, prominent, centrally-placed Search Bar as the primary focus; right-aligned navigation links (Library, Collections) and a User/Settings Icon.
|
||||
* **`[ ZONE 2: MAIN CONTENT AREA ]`**: A responsive, masonry-style "Discovery Grid" that fills the majority of the screen space, populated with visual item tiles. Actions are revealed on hover to keep the UI clean.
|
||||
* **`[ ZONE 1: HEADER ]`**: Left-aligned Logo; a large, prominent, centrally-placed Search Bar as the primary focus with an adjacent Image Search toggle button; right-aligned navigation links (Library, Collections) and a User/Settings Icon.
|
||||
* **`[ ZONE 1B: FILTER TOOLBAR ]`**: A compact horizontal toolbar beneath the header containing dropdown filter buttons (Collections, Source, Media Type, Date Added, Tags), result count, and view toggle controls (Grid/List).
|
||||
* **`[ ZONE 2: MAIN CONTENT AREA ]`**: A responsive, masonry-style "Discovery Grid" that fills the majority of the screen space with minimal spacing, populated with visual item tiles. Actions are revealed on hover to keep the UI clean.
|
||||
* **Key Screen Layout (Mobile Adaptation):**
|
||||
* **`[ ZONE 1: HEADER / NAVIGATION ]`**: The header simplifies to a Logo and a Search Icon that expands on tap. Primary navigation moves to an ergonomic bottom tab bar.
|
||||
* **`[ ZONE 2: MAIN CONTENT AREA ]`**: The masonry grid reflows to a one- or two-column layout. Hover interactions are replaced with touch-based alternatives, such as a persistent "three-dot" menu icon on each tile.
|
||||
@ -117,12 +125,16 @@ graph TD
|
||||
|
||||
| Color Type | Hex Code | Usage |
|
||||
| :---------------------- | :-------- | :----------------------------------------------------- |
|
||||
| **Background** | `#070002` | The primary dark background for the "widescreen" feel. |
|
||||
| **Foreground (Text)** | `#fffdff` | Main text color for high contrast and readability. |
|
||||
| **Subtle Text/Borders** | `#e6e6e4` | Secondary text, subtle dividers, and borders. |
|
||||
| **Primary Action** | `#053897` | Buttons, active links, and key interactive elements. |
|
||||
| **Accent 1 (Destructive)**| `#e60042` | Destructive actions (e.g., Delete), notifications. |
|
||||
| **Accent 2 (Highlight)** | `#f7d827` | Secondary highlights, callouts, or decorative elements.|
|
||||
| **Background** | `#1c1917` | The primary dark background with warm, earthy tones. |
|
||||
| **Surface** | `#292524` | Secondary surfaces for cards, inputs, and elevated elements. |
|
||||
| **Foreground (Text)** | `#fafaf9` | Main text color for high contrast and readability. |
|
||||
| **Subtle Text/Borders** | `#a8a29e` | Secondary text, subtle dividers, and muted elements. |
|
||||
| **Border** | `#44403c` | Borders for inputs and structural elements when needed. |
|
||||
| **Primary Action** | `#f97316` | Buttons, active links, and key interactive elements. |
|
||||
| **Primary Hover** | `#ea580c` | Hover state for primary interactive elements. |
|
||||
| **Accent (Highlight)** | `#fbbf24` | Secondary highlights, callouts, or decorative elements.|
|
||||
|
||||
**Palette Rationale:** The warm, earthy color scheme creates an inviting, creative atmosphere while maintaining sophistication. Orange as the primary action color provides energy and visibility without the aggression of pure red or the coldness of blue. The warm browns reduce eye strain during extended use compared to pure black backgrounds.
|
||||
|
||||
#### **Typography**
|
||||
|
||||
@ -132,7 +144,9 @@ graph TD
|
||||
#### **Layout & Interaction**
|
||||
|
||||
* **Layout:** A widescreen, multi-column grid will be used to create split-screen sections and dynamic masonry layouts.
|
||||
* **Spacing Philosophy:** The interface embraces a **tight, immersive layout** with minimal spacing between elements. Grid items use 2px gaps to create a dense, content-forward experience similar to Pinterest or Google Images, allowing the visual content to be the primary focus. This reduces visual noise and creates a more editorial, magazine-like feel.
|
||||
* **Shapes:** All UI elements will use **bold, strong shapes with sharp, 0px radius corners**.
|
||||
* **Borders:** Borders are used sparingly and only where necessary for functional clarity (e.g., input fields, structural separation). Content cards have no borders to maximize visual impact.
|
||||
* **Scrolling:** The **Lenis** library will be used for smooth, sticky, and dynamic scrolling effects.
|
||||
|
||||
## **Accessibility Requirements**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user