refactor: use shadcn Card component in pastel app

This commit is contained in:
2026-02-25 13:35:29 +01:00
parent 57ba63aa32
commit 4ccf316184
9 changed files with 608 additions and 494 deletions

View File

@@ -10,6 +10,7 @@ import {
SelectValue, SelectValue,
} from '@/components/ui/select'; } from '@/components/ui/select';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PaletteGrid } from '@/components/pastel/PaletteGrid'; import { PaletteGrid } from '@/components/pastel/PaletteGrid';
import { ExportMenu } from '@/components/pastel/ExportMenu'; import { ExportMenu } from '@/components/pastel/ExportMenu';
import { useLighten, useDarken, useSaturate, useDesaturate, useRotate } from '@/lib/pastel/api/queries'; import { useLighten, useDarken, useSaturate, useDesaturate, useRotate } from '@/lib/pastel/api/queries';
@@ -102,8 +103,11 @@ export default function BatchPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Input */} {/* Input */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Input Colors</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Input Colors</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4"> <p className="text-sm text-muted-foreground mb-4">
Enter colors (one per line or comma-separated). Supports hex format Enter colors (one per line or comma-separated). Supports hex format
</p> </p>
@@ -118,11 +122,14 @@ export default function BatchPage() {
<p className="text-xs text-muted-foreground mt-2"> <p className="text-xs text-muted-foreground mt-2">
{parseColors(inputColors).length} valid colors found {parseColors(inputColors).length} valid colors found
</p> </p>
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Operation</h2> <CardHeader>
<div className="space-y-4"> <CardTitle className="text-sm font-medium">Operation</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Select <Select
value={operation} value={operation}
onValueChange={(value) => setOperation(value as Operation)} onValueChange={(value) => setOperation(value as Operation)}
@@ -170,30 +177,38 @@ export default function BatchPage() {
</> </>
)} )}
</Button> </Button>
</div> </CardContent>
</div> </Card>
</div> </div>
{/* Output */} {/* Output */}
<div className="space-y-6"> <div className="space-y-6">
{outputColors.length > 0 ? ( {outputColors.length > 0 ? (
<> <>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4"> <CardHeader>
<CardTitle className="text-sm font-medium">
Output Colors ({outputColors.length}) Output Colors ({outputColors.length})
</h2> </CardTitle>
</CardHeader>
<CardContent>
<PaletteGrid colors={outputColors} /> <PaletteGrid colors={outputColors} />
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<CardContent className="pt-6">
<ExportMenu colors={outputColors} /> <ExportMenu colors={outputColors} />
</div> </CardContent>
</Card>
</> </>
) : ( ) : (
<div className="p-12 border rounded-lg bg-card text-center text-muted-foreground"> <Card>
<CardContent className="p-12 text-center text-muted-foreground">
<Download className="h-12 w-12 mx-auto mb-4 opacity-50" /> <Download className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>Enter colors and click Process to see results</p> <p>Enter colors and click Process to see results</p>
</div> </CardContent>
</Card>
)} )}
</div> </div>
</div> </div>

View File

@@ -11,6 +11,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from '@/components/ui/select'; } from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useSimulateColorBlindness } from '@/lib/pastel/api/queries'; import { useSimulateColorBlindness } from '@/lib/pastel/api/queries';
import { Loader2, Eye, Plus, X } from 'lucide-react'; import { Loader2, Eye, Plus, X } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -77,9 +78,9 @@ export default function ColorBlindPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Controls */} {/* Controls */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<div className="flex items-center justify-between mb-4"> <CardHeader className="flex flex-row items-center justify-between space-y-0">
<h2 className="text-sm font-medium">Colors to Test</h2> <CardTitle className="text-sm font-medium">Colors to Test</CardTitle>
<Button <Button
onClick={addColor} onClick={addColor}
variant="outline" variant="outline"
@@ -89,9 +90,9 @@ export default function ColorBlindPage() {
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Add Color Add Color
</Button> </Button>
</div> </CardHeader>
<div className="space-y-4"> <CardContent className="space-y-4">
{colors.map((color, index) => ( {colors.map((color, index) => (
<div key={index} className="flex items-start gap-3"> <div key={index} className="flex items-start gap-3">
<div className="flex-1"> <div className="flex-1">
@@ -112,12 +113,14 @@ export default function ColorBlindPage() {
)} )}
</div> </div>
))} ))}
</div> </CardContent>
</div> </Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Blindness Type</h2> <CardHeader>
<div className="space-y-4"> <CardTitle className="text-sm font-medium">Blindness Type</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Select <Select
value={blindnessType} value={blindnessType}
onValueChange={(value) => setBlindnessType(value as ColorBlindnessType)} onValueChange={(value) => setBlindnessType(value as ColorBlindnessType)}
@@ -153,17 +156,20 @@ export default function ColorBlindPage() {
</> </>
)} )}
</Button> </Button>
</div> </CardContent>
</div> </Card>
</div> </div>
{/* Results */} {/* Results */}
<div className="space-y-6"> <div className="space-y-6">
{simulations.length > 0 ? ( {simulations.length > 0 ? (
<> <>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Simulation Results</h2> <CardHeader>
<p className="text-sm text-muted-foreground mb-6"> <CardTitle className="text-sm font-medium">Simulation Results</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground mb-2">
Compare original colors (left) with how they appear to people with{' '} Compare original colors (left) with how they appear to people with{' '}
{blindnessType} (right) {blindnessType} (right)
</p> </p>
@@ -196,9 +202,11 @@ export default function ColorBlindPage() {
</div> </div>
))} ))}
</div> </div>
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card bg-blue-50 dark:bg-blue-950/20"> <Card className="bg-blue-50 dark:bg-blue-950/20 border-blue-100 dark:border-blue-900/30 shadow-none">
<CardContent className="pt-6">
<h3 className="font-semibold mb-2 flex items-center gap-2"> <h3 className="font-semibold mb-2 flex items-center gap-2">
<Eye className="h-5 w-5" /> <Eye className="h-5 w-5" />
Accessibility Tip Accessibility Tip
@@ -207,14 +215,17 @@ export default function ColorBlindPage() {
Ensure important information isn&apos;t conveyed by color alone. Use text Ensure important information isn&apos;t conveyed by color alone. Use text
labels, patterns, or icons to make your design accessible to everyone labels, patterns, or icons to make your design accessible to everyone
</p> </p>
</div> </CardContent>
</Card>
</> </>
) : ( ) : (
<div className="p-12 border rounded-lg bg-card text-center text-muted-foreground"> <Card>
<CardContent className="p-12 text-center text-muted-foreground">
<Eye className="h-12 w-12 mx-auto mb-4 opacity-50" /> <Eye className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>Add colors and click Simulate to see how they appear</p> <p>Add colors and click Simulate to see how they appear</p>
<p className="text-sm mt-2">with different types of color blindness</p> <p className="text-sm mt-2">with different types of color blindness</p>
</div> </CardContent>
</Card>
)} )}
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
import { ColorPicker } from '@/components/pastel/ColorPicker'; import { ColorPicker } from '@/components/pastel/ColorPicker';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { getContrastRatio, hexToRgb, checkWCAGCompliance } from '@/lib/pastel/utils/color'; import { getContrastRatio, hexToRgb, checkWCAGCompliance } from '@/lib/pastel/utils/color';
import { ArrowLeftRight, Check, X } from 'lucide-react'; import { ArrowLeftRight, Check, X } from 'lucide-react';
@@ -68,12 +69,14 @@ export default function ContrastPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Color Pickers */} {/* Color Pickers */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<div className="flex items-center justify-between mb-4"> <CardHeader>
<h2 className="text-sm font-medium">Foreground Color</h2> <CardTitle className="text-sm font-medium">Foreground Color</CardTitle>
</div> </CardHeader>
<CardContent>
<ColorPicker color={foreground} onChange={setForeground} /> <ColorPicker color={foreground} onChange={setForeground} />
</div> </CardContent>
</Card>
<div className="flex justify-center"> <div className="flex justify-center">
<Button <Button
@@ -86,17 +89,24 @@ export default function ContrastPage() {
</Button> </Button>
</div> </div>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Background Color</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Background Color</CardTitle>
</CardHeader>
<CardContent>
<ColorPicker color={background} onChange={setBackground} /> <ColorPicker color={background} onChange={setBackground} />
</div> </CardContent>
</Card>
</div> </div>
{/* Results */} {/* Results */}
<div className="space-y-6"> <div className="space-y-6">
{/* Preview */} {/* Preview */}
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Preview</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Preview</CardTitle>
</CardHeader>
<CardContent>
<div <div
className="rounded-lg p-8 text-center" className="rounded-lg p-8 text-center"
style={{ backgroundColor: background, color: foreground }} style={{ backgroundColor: background, color: foreground }}
@@ -104,12 +114,16 @@ export default function ContrastPage() {
<p className="text-xl font-bold mb-2">Normal Text (16px)</p> <p className="text-xl font-bold mb-2">Normal Text (16px)</p>
<p className="text-3xl font-bold">Large Text (24px)</p> <p className="text-3xl font-bold">Large Text (24px)</p>
</div> </div>
</div> </CardContent>
</Card>
{/* Contrast Ratio */} {/* Contrast Ratio */}
{ratio !== null && ( {ratio !== null && (
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Contrast Ratio</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Contrast Ratio</CardTitle>
</CardHeader>
<CardContent>
<div className="text-center mb-6"> <div className="text-center mb-6">
<div className="text-5xl font-bold">{ratio.toFixed(2)}:1</div> <div className="text-5xl font-bold">{ratio.toFixed(2)}:1</div>
<p className="text-sm text-muted-foreground mt-2"> <p className="text-sm text-muted-foreground mt-2">
@@ -122,14 +136,17 @@ export default function ContrastPage() {
: 'Poor contrast'} : 'Poor contrast'}
</p> </p>
</div> </div>
</div> </CardContent>
</Card>
)} )}
{/* WCAG Compliance */} {/* WCAG Compliance */}
{compliance && ( {compliance && (
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">WCAG 2.1 Compliance</h2> <CardHeader>
<div className="space-y-4"> <CardTitle className="text-sm font-medium">WCAG 2.1 Compliance</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div> <div>
<h3 className="text-sm font-semibold mb-2">Level AA</h3> <h3 className="text-sm font-semibold mb-2">Level AA</h3>
<div className="space-y-2"> <div className="space-y-2">
@@ -161,8 +178,8 @@ export default function ContrastPage() {
/> />
</div> </div>
</div> </div>
</div> </CardContent>
</div> </Card>
)} )}
</div> </div>
</div> </div>

View File

@@ -12,6 +12,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from '@/components/ui/select'; } from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useGenerateDistinct } from '@/lib/pastel/api/queries'; import { useGenerateDistinct } from '@/lib/pastel/api/queries';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -49,11 +50,12 @@ export default function DistinctPage() {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Controls */} {/* Controls */}
<div className="lg:col-span-1"> <div className="lg:col-span-1">
<div className="p-6 border rounded-lg bg-card space-y-6"> <Card>
<div> <CardHeader>
<h2 className="text-sm font-medium mb-4">Settings</h2> <CardTitle className="text-sm font-medium">Settings</CardTitle>
</div> </CardHeader>
<CardContent className="space-y-6">
<div> <div>
<label htmlFor="count" className="text-sm font-medium mb-2 block"> <label htmlFor="count" className="text-sm font-medium mb-2 block">
Number of Colors Number of Colors
@@ -109,22 +111,29 @@ export default function DistinctPage() {
This may take a few moments.. This may take a few moments..
</div> </div>
)} )}
</div> </CardContent>
</Card>
</div> </div>
{/* Results */} {/* Results */}
<div className="lg:col-span-2 space-y-6"> <div className="lg:col-span-2 space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4"> <CardHeader>
<CardTitle className="text-sm font-medium">
Generated Colors {colors.length > 0 && `(${colors.length})`} Generated Colors {colors.length > 0 && `(${colors.length})`}
</h2> </CardTitle>
</CardHeader>
<CardContent>
<PaletteGrid colors={colors} /> <PaletteGrid colors={colors} />
</div> </CardContent>
</Card>
{colors.length > 0 && ( {colors.length > 0 && (
<div className="p-6 border rounded-lg bg-card"> <Card>
<CardContent className="pt-6">
<ExportMenu colors={colors} /> <ExportMenu colors={colors} />
</div> </CardContent>
</Card>
)} )}
</div> </div>
</div> </div>

View File

@@ -6,6 +6,7 @@ import { PaletteGrid } from '@/components/pastel/PaletteGrid';
import { ExportMenu } from '@/components/pastel/ExportMenu'; import { ExportMenu } from '@/components/pastel/ExportMenu';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useGenerateGradient } from '@/lib/pastel/api/queries'; import { useGenerateGradient } from '@/lib/pastel/api/queries';
import { Loader2, Plus, X } from 'lucide-react'; import { Loader2, Plus, X } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -59,8 +60,11 @@ export default function GradientPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Controls */} {/* Controls */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Color Stops</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Color Stops</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-4"> <div className="space-y-4">
{stops.map((stop, index) => ( {stops.map((stop, index) => (
<div key={index} className="flex items-start gap-3"> <div key={index} className="flex items-start gap-3">
@@ -87,11 +91,14 @@ export default function GradientPage() {
Add Stop Add Stop
</Button> </Button>
</div> </div>
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Settings</h2> <CardHeader>
<div className="space-y-4"> <CardTitle className="text-sm font-medium">Settings</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div> <div>
<label htmlFor="count" className="text-sm font-medium mb-2 block"> <label htmlFor="count" className="text-sm font-medium mb-2 block">
Number of Colors Number of Colors
@@ -120,34 +127,44 @@ export default function GradientPage() {
'Generate Gradient' 'Generate Gradient'
)} )}
</Button> </Button>
</div> </CardContent>
</div> </Card>
</div> </div>
{/* Preview */} {/* Preview */}
<div className="space-y-6"> <div className="space-y-6">
{gradient && gradient.length > 0 && ( {gradient && gradient.length > 0 && (
<> <>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Gradient Preview</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Gradient Preview</CardTitle>
</CardHeader>
<CardContent>
<div <div
className="h-32 rounded-lg" className="h-32 rounded-lg"
style={{ style={{
background: `linear-gradient(to right, ${gradient.join(', ')})`, background: `linear-gradient(to right, ${gradient.join(', ')})`,
}} }}
/> />
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4"> <CardHeader>
<CardTitle className="text-sm font-medium">
Colors ({gradient.length}) Colors ({gradient.length})
</h2> </CardTitle>
</CardHeader>
<CardContent>
<PaletteGrid colors={gradient} /> <PaletteGrid colors={gradient} />
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<CardContent className="pt-6">
<ExportMenu colors={gradient} /> <ExportMenu colors={gradient} />
</div> </CardContent>
</Card>
</> </>
)} )}
</div> </div>

View File

@@ -12,6 +12,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from '@/components/ui/select'; } from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useGeneratePalette } from '@/lib/pastel/api/queries'; import { useGeneratePalette } from '@/lib/pastel/api/queries';
import { Loader2, Palette } from 'lucide-react'; import { Loader2, Palette } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -70,14 +71,20 @@ export default function HarmonyPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Controls */} {/* Controls */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Base Color</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Base Color</CardTitle>
</CardHeader>
<CardContent>
<ColorPicker color={baseColor} onChange={setBaseColor} /> <ColorPicker color={baseColor} onChange={setBaseColor} />
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Harmony Type</h2> <CardHeader>
<div className="space-y-4"> <CardTitle className="text-sm font-medium">Harmony Type</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Select <Select
value={harmonyType} value={harmonyType}
onValueChange={(value) => setHarmonyType(value as HarmonyType)} onValueChange={(value) => setHarmonyType(value as HarmonyType)}
@@ -116,32 +123,40 @@ export default function HarmonyPage() {
</> </>
)} )}
</Button> </Button>
</div> </CardContent>
</div> </Card>
</div> </div>
{/* Results */} {/* Results */}
<div className="space-y-6"> <div className="space-y-6">
{palette.length > 0 && ( {palette.length > 0 && (
<> <>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4"> <CardHeader>
<CardTitle className="text-sm font-medium">
Generated Palette ({palette.length} colors) Generated Palette ({palette.length} colors)
</h2> </CardTitle>
</CardHeader>
<CardContent>
<PaletteGrid colors={palette} /> <PaletteGrid colors={palette} />
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<CardContent className="pt-6">
<ExportMenu colors={palette} /> <ExportMenu colors={palette} />
</div> </CardContent>
</Card>
</> </>
)} )}
{palette.length === 0 && ( {palette.length === 0 && (
<div className="p-12 border rounded-lg bg-card text-center text-muted-foreground"> <Card>
<CardContent className="p-12 text-center text-muted-foreground">
<Palette className="h-12 w-12 mx-auto mb-4 opacity-50" /> <Palette className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>Select a harmony type and click Generate to create your palette</p> <p>Select a harmony type and click Generate to create your palette</p>
</div> </CardContent>
</Card>
)} )}
</div> </div>
</div> </div>

View File

@@ -10,6 +10,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from '@/components/ui/select'; } from '@/components/ui/select';
import { Card, CardContent } from '@/components/ui/card';
import { useNamedColors } from '@/lib/pastel/api/queries'; import { useNamedColors } from '@/lib/pastel/api/queries';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { parse_color } from '@valknarthing/pastel-wasm'; import { parse_color } from '@valknarthing/pastel-wasm';
@@ -76,7 +77,8 @@ export default function NamedColorsPage() {
</div> </div>
{/* Colors Grid */} {/* Colors Grid */}
<div className="p-6 border rounded-lg bg-card"> <Card>
<CardContent className="pt-6">
{isLoading && ( {isLoading && (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
@@ -115,7 +117,8 @@ export default function NamedColorsPage() {
No colors match your search No colors match your search
</div> </div>
)} )}
</div> </CardContent>
</Card>
</div> </div>
</div> </div>
); );

View File

@@ -6,6 +6,7 @@ import { ColorPicker } from '@/components/pastel/ColorPicker';
import { ColorDisplay } from '@/components/pastel/ColorDisplay'; import { ColorDisplay } from '@/components/pastel/ColorDisplay';
import { ColorInfo } from '@/components/pastel/ColorInfo'; import { ColorInfo } from '@/components/pastel/ColorInfo';
import { ManipulationPanel } from '@/components/pastel/ManipulationPanel'; import { ManipulationPanel } from '@/components/pastel/ManipulationPanel';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useColorInfo } from '@/lib/pastel/api/queries'; import { useColorInfo } from '@/lib/pastel/api/queries';
import { useColorHistory } from '@/lib/pastel/stores/historyStore'; import { useColorHistory } from '@/lib/pastel/stores/historyStore';
import { Loader2, Share2, History, X } from 'lucide-react'; import { Loader2, Share2, History, X } from 'lucide-react';
@@ -84,30 +85,36 @@ function PlaygroundContent() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Left Column: Color Picker and Display */} {/* Left Column: Color Picker and Display */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Color Picker</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Color Picker</CardTitle>
</CardHeader>
<CardContent>
<ColorPicker color={color} onChange={setColor} /> <ColorPicker color={color} onChange={setColor} />
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<div className="flex items-center justify-between mb-4"> <CardHeader className="flex flex-row items-center justify-between space-y-0">
<h2 className="text-sm font-medium">Preview</h2> <CardTitle className="text-sm font-medium">Preview</CardTitle>
<Button onClick={handleShare} variant="outline" size="sm"> <Button onClick={handleShare} variant="outline" size="sm">
<Share2 className="h-4 w-4 mr-2" /> <Share2 className="h-4 w-4 mr-2" />
Share Share
</Button> </Button>
</div> </CardHeader>
<CardContent>
<div className="flex justify-center"> <div className="flex justify-center">
<ColorDisplay color={color} size="xl" /> <ColorDisplay color={color} size="xl" />
</div> </div>
</div> </CardContent>
</Card>
{recentColors.length > 0 && ( {recentColors.length > 0 && (
<div className="p-6 border rounded-lg bg-card"> <Card>
<div className="flex items-center justify-between mb-4"> <CardHeader className="flex flex-row items-center justify-between space-y-0">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<History className="h-5 w-5" /> <History className="h-5 w-5" />
<h2 className="text-sm font-medium">Recent Colors</h2> <CardTitle className="text-sm font-medium">Recent Colors</CardTitle>
</div> </div>
<Button <Button
onClick={clearHistory} onClick={clearHistory}
@@ -117,7 +124,8 @@ function PlaygroundContent() {
> >
Clear Clear
</Button> </Button>
</div> </CardHeader>
<CardContent>
<div className="grid grid-cols-5 gap-2"> <div className="grid grid-cols-5 gap-2">
{recentColors.map((entry) => ( {recentColors.map((entry) => (
<div <div
@@ -150,15 +158,18 @@ function PlaygroundContent() {
</div> </div>
))} ))}
</div> </div>
</div> </CardContent>
</Card>
)} )}
</div> </div>
{/* Right Column: Color Information */} {/* Right Column: Color Information */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Color Information</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Color Information</CardTitle>
</CardHeader>
<CardContent>
{isLoading && ( {isLoading && (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
@@ -173,12 +184,17 @@ function PlaygroundContent() {
)} )}
{colorInfo && <ColorInfo info={colorInfo} />} {colorInfo && <ColorInfo info={colorInfo} />}
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Color Manipulation</h2> <CardHeader>
<CardTitle className="text-sm font-medium">Color Manipulation</CardTitle>
</CardHeader>
<CardContent>
<ManipulationPanel color={color} onColorChange={setColor} /> <ManipulationPanel color={color} onColorChange={setColor} />
</div> </CardContent>
</Card>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { useState } from 'react';
import { ColorPicker } from '@/components/pastel/ColorPicker'; import { ColorPicker } from '@/components/pastel/ColorPicker';
import { ColorDisplay } from '@/components/pastel/ColorDisplay'; import { ColorDisplay } from '@/components/pastel/ColorDisplay';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useTextColor } from '@/lib/pastel/api/queries'; import { useTextColor } from '@/lib/pastel/api/queries';
import { Loader2, Palette, Plus, X, CheckCircle2, XCircle } from 'lucide-react'; import { Loader2, Palette, Plus, X, CheckCircle2, XCircle } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -66,9 +67,9 @@ export default function TextColorPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Input */} {/* Input */}
<div className="space-y-6"> <div className="space-y-6">
<div className="p-6 border rounded-lg bg-card"> <Card>
<div className="flex items-center justify-between mb-4"> <CardHeader className="flex flex-row items-center justify-between space-y-0">
<h2 className="text-sm font-medium">Background Colors</h2> <CardTitle className="text-sm font-medium">Background Colors</CardTitle>
<Button <Button
onClick={addBackground} onClick={addBackground}
disabled={backgrounds.length >= 10} disabled={backgrounds.length >= 10}
@@ -78,8 +79,9 @@ export default function TextColorPage() {
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Add Add
</Button> </Button>
</div> </CardHeader>
<CardContent className="space-y-4">
<div className="space-y-4"> <div className="space-y-4">
{backgrounds.map((color, index) => ( {backgrounds.map((color, index) => (
<div key={index} className="flex items-center gap-3"> <div key={index} className="flex items-center gap-3">
@@ -119,31 +121,37 @@ export default function TextColorPage() {
</> </>
)} )}
</Button> </Button>
</div> </CardContent>
</Card>
<div className="p-6 border rounded-lg bg-card bg-blue-50 dark:bg-blue-950/20"> <Card className="bg-blue-50 dark:bg-blue-950/20 border-blue-100 dark:border-blue-900/30 shadow-none">
<CardContent className="pt-6">
<h3 className="font-semibold mb-2">How it works</h3> <h3 className="font-semibold mb-2">How it works</h3>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
This tool analyzes each background color and automatically selects either black This tool analyzes each background color and automatically selects either black
or white text to ensure maximum readability. The algorithm guarantees WCAG AA or white text to ensure maximum readability. The algorithm guarantees WCAG AA
compliance (4.5:1 contrast ratio) for normal text compliance (4.5:1 contrast ratio) for normal text
</p> </p>
</div> </CardContent>
</Card>
</div> </div>
{/* Results */} {/* Results */}
<div className="space-y-6"> <div className="space-y-6">
{results.length > 0 ? ( {results.length > 0 ? (
<> <>
<div className="p-6 border rounded-lg bg-card"> <Card>
<h2 className="text-sm font-medium mb-4">Optimized Results</h2> <CardHeader>
<div className="space-y-4"> <CardTitle className="text-sm font-medium">Optimized Results</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{results.map((result, index) => ( {results.map((result, index) => (
<div <Card
key={index} key={index}
className="p-4 border rounded-lg" className="overflow-hidden shadow-none"
style={{ backgroundColor: result.background }} style={{ backgroundColor: result.background }}
> >
<CardContent className="p-4">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ColorDisplay color={result.background} size="sm" /> <ColorDisplay color={result.background} size="sm" />
@@ -202,16 +210,19 @@ export default function TextColorPage() {
</span> </span>
</div> </div>
</div> </div>
</div> </CardContent>
</Card>
))} ))}
</div> </CardContent>
</div> </Card>
</> </>
) : ( ) : (
<div className="p-12 border rounded-lg bg-card text-center text-muted-foreground"> <Card>
<CardContent className="p-12 text-center text-muted-foreground">
<Palette className="h-12 w-12 mx-auto mb-4 opacity-50" /> <Palette className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>Add background colors and click Optimize to see results</p> <p>Add background colors and click Optimize to see results</p>
</div> </CardContent>
</Card>
)} )}
</div> </div>
</div> </div>