howl/src/image.tsx
Chris Wanstrath 0cad100197 ________ ______ _______ ______ ________
/        |/      \ /       \  /      \ /        |
$$$$$$$$//$$$$$$  |$$$$$$$  |/$$$$$$  |$$$$$$$$/
$$ |__   $$ |  $$ |$$ |__$$ |$$ | _$$/ $$ |__
$$    |  $$ |  $$ |$$    $$< $$ |/    |$$    |
$$$$$/   $$ |  $$ |$$$$$$$  |$$ |$$$$ |$$$$$/
$$ |     $$ \__$$ |$$ |  $$ |$$ \__$$ |$$ |_____
$$ |     $$    $$/ $$ |  $$ |$$    $$/ $$       |
$$/       $$$$$$/  $$/   $$/  $$$$$$/  $$$$$$$$/
2026-01-16 08:33:38 -08:00

197 lines
6.3 KiB
TypeScript

import { define } from 'forge'
import { Section } from './section'
import { H2, H3, H4, H5, Text } from './text'
import { VStack, HStack } from './stack'
import { Grid } from './grid'
export const Image = define('Image', {
base: 'img',
variants: {
objectFit: {
cover: { objectFit: 'cover' },
contain: { objectFit: 'contain' },
fill: { objectFit: 'fill' },
none: { objectFit: 'none' },
'scale-down': { objectFit: 'scale-down' },
},
},
})
export type ImageProps = Parameters<typeof Image>[0]
export const Test = () => {
const sampleImages = [
'https://picsum.photos/seed/1/400/600', // Portrait
'https://picsum.photos/seed/2/600/400', // Landscape
'https://picsum.photos/seed/3/300/300', // Square
'https://picsum.photos/seed/4/200/100', // Small image
]
return (
<Section>
<H2>Image Examples</H2>
{/* Size variations */}
<VStack gap={4}>
<H3>Size Variations</H3>
<HStack gap={4} wrap>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]} objectFit="cover" style={{ width: 64, height: 64 }} alt="64x64" />
<Text>64x64</Text>
</VStack>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]} objectFit="cover" style={{ width: 96, height: 96 }} alt="96x96" />
<Text>96x96</Text>
</VStack>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]} objectFit="cover" style={{ width: 128, height: 128 }} alt="128x128" />
<Text>128x128</Text>
</VStack>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]} objectFit="cover" style={{ width: 192, height: 128 }} alt="192x128" />
<Text>192x128</Text>
</VStack>
</HStack>
</VStack>
{/* Object fit variations */}
<VStack gap={4}>
<H3>Object Fit Variations</H3>
<Text>Same image with different object-fit values</Text>
<HStack gap={6} wrap>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]}
objectFit="cover"
style={{ width: 128, height: 128, border: '1px solid black' }}
alt="Object cover"
/>
<Text>object-fit: cover</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]}
objectFit="contain"
style={{ width: 128, height: 128, border: '1px solid black', backgroundColor: '#f3f4f6' }}
alt="Object contain"
/>
<Text>object-fit: contain</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]}
objectFit="fill"
style={{ width: 128, height: 128, border: '1px solid black' }}
alt="Object fill"
/>
<Text>object-fit: fill</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]}
objectFit="scale-down"
style={{ width: 128, height: 128, border: '1px solid black', backgroundColor: '#f3f4f6' }}
alt="Object scale-down"
/>
<Text>object-fit: scale-down</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]}
objectFit="none"
style={{ width: 128, height: 128, border: '1px solid black', backgroundColor: '#f3f4f6' }}
alt="Object none"
/>
<Text>object-fit: none</Text>
</VStack>
</HStack>
</VStack>
{/* Styling examples */}
<VStack gap={4}>
<H3>Styling Examples</H3>
<HStack gap={6} wrap>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]}
objectFit="cover"
style={{ width: 128, height: 128, borderRadius: '8px', border: '4px solid #3b82f6' }}
alt="Rounded with border"
/>
<Text>Rounded + Border</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[1]}
objectFit="cover"
style={{ width: 128, height: 128, boxShadow: '0 10px 15px rgba(0, 0, 0, 0.3)' }}
alt="With shadow"
/>
<Text>With Shadow</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[2]}
objectFit="cover"
style={{
width: 128,
height: 128,
borderRadius: '9999px',
border: '4px solid #22c55e',
boxShadow: '0 10px 15px rgba(0, 0, 0, 0.3)',
}}
alt="Circular with effects"
/>
<Text>Circular + Effects</Text>
</VStack>
</HStack>
</VStack>
{/* Common use cases */}
<VStack gap={6}>
<H3>Common Use Cases</H3>
{/* Avatar */}
<VStack gap={2}>
<H4>Avatar</H4>
<Image
src={sampleImages[0]}
objectFit="cover"
style={{ width: 48, height: 48, borderRadius: '9999px' }}
alt="Avatar"
/>
</VStack>
{/* Card image */}
<VStack gap={2} style={{ maxWidth: '384px' }}>
<H4>Card Image</H4>
<VStack gap={0} style={{ border: '1px solid #d1d5db', borderRadius: '8px', overflow: 'hidden' }}>
<Image src={sampleImages[1]} objectFit="cover" style={{ width: 384, height: 192 }} alt="Card image" />
<VStack gap={1} style={{ padding: '16px' }}>
<H5>Card Title</H5>
<Text>Card description goes here</Text>
</VStack>
</VStack>
</VStack>
{/* Gallery grid */}
<VStack gap={2}>
<H4>Gallery Grid</H4>
<Grid cols={3} gap={2}>
{sampleImages.map((src, i) => (
<Image
key={i}
src={src}
objectFit="cover"
style={{ width: 120, height: 120, borderRadius: '4px' }}
alt={`Gallery ${i}`}
/>
))}
</Grid>
</VStack>
</VStack>
</Section>
)
}