This commit is contained in:
Chris Wanstrath 2025-11-29 22:38:15 -08:00
parent 67fae70c83
commit 6c3b3e80b0
16 changed files with 436 additions and 170 deletions

View File

@ -1,6 +1,46 @@
# 🐺 howl # 🐺 howl
Howl is a fork of `werewolf-ui`, without any Tailwind. Howl is a fork of `werewolf-ui`, without any Tailwind. A minimal, zero-dependency React component library.
## Installation
```bash
bun install howl
```
## Usage
```tsx
import { Button, VStack, Text } from "howl"
function App() {
return (
<VStack>
<Text>Hello, world!</Text>
<Button>Click me</Button>
</VStack>
)
}
```
## Components
- **Avatar** - Profile image component
- **Box** - Container components (Box, RedBox, GreenBox, BlueBox, GrayBox)
- **Button** - Button component
- **Divider** - Horizontal divider
- **Grid** - Grid layout
- **Icon** - Icon display using lucide-static
- **IconLink** - Icon with link functionality
- **Image** - Image component
- **Input** - Text input field
- **Placeholder** - Placeholder component
- **Section** - Section container
- **Select** - Dropdown select input
- **Stack** - Layout components (VStack, HStack)
- **Text** - Text components (H1, H2, H3, H4, H5, Text, SmallText)
## Development
```bash ```bash
bun install bun install

View File

@ -1,3 +1,5 @@
import { Section } from "./section"
import { H2, Text } from "./text"
import "hono/jsx" import "hono/jsx"
import type { FC, JSX } from "hono/jsx" import type { FC, JSX } from "hono/jsx"
import { VStack, HStack } from "./stack" import { VStack, HStack } from "./stack"
@ -32,17 +34,17 @@ export const Test = () => {
] ]
return ( return (
<VStack gap={8} style={{ padding: "24px" }}> <Section>
{/* Size variations */} {/* Size variations */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Size Variations</h2> <H2>Size Variations</H2>
<HStack gap={4}> <HStack gap={4}>
{[24, 32, 48, 64, 96].map((size) => ( {[24, 32, 48, 64, 96].map((size) => (
<VStack key={size} h="center" gap={2}> <VStack key={size} h="center" gap={2}>
<Avatar src={sampleImages[0]!} size={size} alt="Sample" /> <Avatar src={sampleImages[0]!} size={size} alt="Sample" />
<p style={{ fontSize: "14px" }}> <Text>
{size}x{size} {size}x{size}
</p> </Text>
</VStack> </VStack>
))} ))}
</HStack> </HStack>
@ -50,27 +52,27 @@ export const Test = () => {
{/* Rounded vs Square */} {/* Rounded vs Square */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Rounded vs Square</h2> <H2>Rounded vs Square</H2>
<HStack gap={6}> <HStack gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Avatar src={sampleImages[0]!} size={64} alt="Sample" /> <Avatar src={sampleImages[0]!} size={64} alt="Sample" />
<p style={{ fontSize: "14px" }}>Square</p> <Text>Square</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Avatar src={sampleImages[0]!} size={64} rounded alt="Sample" /> <Avatar src={sampleImages[0]!} size={64} rounded alt="Sample" />
<p style={{ fontSize: "14px" }}>Rounded</p> <Text>Rounded</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>
{/* Different images */} {/* Different images */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Different Images</h2> <H2>Different Images</H2>
<HStack gap={4}> <HStack gap={4}>
{sampleImages.map((src, index) => ( {sampleImages.map((src, index) => (
<VStack key={src} h="center" gap={2}> <VStack key={src} h="center" gap={2}>
<Avatar src={src} size={64} rounded alt={`Sample ${index + 1}`} /> <Avatar src={src} size={64} rounded alt={`Sample ${index + 1}`} />
<p style={{ fontSize: "14px" }}>Image {index + 1}</p> <Text>Image {index + 1}</Text>
</VStack> </VStack>
))} ))}
</HStack> </HStack>
@ -78,7 +80,7 @@ export const Test = () => {
{/* Custom styles */} {/* Custom styles */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>With Custom Styles</h2> <H2>With Custom Styles</H2>
<HStack gap={6}> <HStack gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Avatar <Avatar
@ -88,7 +90,7 @@ export const Test = () => {
style={{ border: "4px solid #3b82f6" }} style={{ border: "4px solid #3b82f6" }}
alt="With border" alt="With border"
/> />
<p style={{ fontSize: "14px" }}>With Border</p> <Text>With Border</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Avatar <Avatar
@ -98,7 +100,7 @@ export const Test = () => {
style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }} style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }}
alt="With shadow" alt="With shadow"
/> />
<p style={{ fontSize: "14px" }}>With Shadow</p> <Text>With Shadow</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Avatar <Avatar
@ -108,10 +110,10 @@ export const Test = () => {
style={{ border: "4px solid #22c55e", boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }} style={{ border: "4px solid #22c55e", boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }}
alt="Border + shadow" alt="Border + shadow"
/> />
<p style={{ fontSize: "14px" }}>Border + Shadow</p> <Text>Border + Shadow</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>
</VStack> </Section>
) )
} }

84
src/box.tsx Normal file
View File

@ -0,0 +1,84 @@
import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx"
type BoxProps = PropsWithChildren & {
bg?: string
color?: string
p?: number
style?: JSX.CSSProperties
}
export const Box: FC<BoxProps> = ({ children, bg, color, p, style }) => {
const boxStyle: JSX.CSSProperties = {
backgroundColor: bg,
color: color,
padding: p ? `${p}px` : undefined,
...style,
}
return <div style={boxStyle}>{children}</div>
}
// Common demo box colors
export const RedBox: FC<PropsWithChildren> = ({ children }) => (
<Box bg="#ef4444" p={4} style={{ textAlign: "center" }}>
{children}
</Box>
)
export const GreenBox: FC<PropsWithChildren> = ({ children }) => (
<Box bg="#22c55e" p={4} style={{ textAlign: "center" }}>
{children}
</Box>
)
export const BlueBox: FC<PropsWithChildren> = ({ children }) => (
<Box bg="#3b82f6" p={4} style={{ textAlign: "center" }}>
{children}
</Box>
)
export const GrayBox: FC<PropsWithChildren & { style?: JSX.CSSProperties }> = ({ children, style }) => (
<Box bg="#f3f4f6" p={16} style={style}>
{children}
</Box>
)
export const Test = () => {
return (
<div style={{ display: "flex", flexDirection: "column", gap: "32px", padding: "24px" }}>
<div>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Box Component</h2>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<Box bg="#3b82f6" color="#ffffff" p={16}>
Basic Box with custom background and text color
</Box>
<Box p={8} style={{ border: "2px solid #d1d5db", borderRadius: "8px" }}>
Box with padding and border
</Box>
</div>
</div>
<div>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Color Variants</h2>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<RedBox>Red Box</RedBox>
<GreenBox>Green Box</GreenBox>
<BlueBox>Blue Box</BlueBox>
<GrayBox>Gray Box</GrayBox>
</div>
</div>
<div>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Nested Boxes</h2>
<Box bg="#f3f4f6" p={16}>
<Box bg="#e5e7eb" p={12}>
<Box bg="#d1d5db" p={8}>
Nested boxes demonstration
</Box>
</Box>
</Box>
</div>
</div>
)
}

View File

@ -1,6 +1,8 @@
import "hono/jsx" import "hono/jsx"
import type { JSX, FC } from "hono/jsx" import type { JSX, FC } from "hono/jsx"
import { VStack, HStack } from "./stack" import { VStack, HStack } from "./stack"
import { Section } from "./section"
import { H2 } from "./text"
export type ButtonProps = JSX.IntrinsicElements["button"] & { export type ButtonProps = JSX.IntrinsicElements["button"] & {
variant?: "primary" | "secondary" | "outline" | "ghost" | "destructive" variant?: "primary" | "secondary" | "outline" | "ghost" | "destructive"
@ -68,7 +70,7 @@ export const Button: FC<ButtonProps> = (props) => {
...baseStyles, ...baseStyles,
...variantStyles[variant], ...variantStyles[variant],
...sizeStyles[size], ...sizeStyles[size],
...(style || {}), ...(style as JSX.CSSProperties),
} }
return <button {...buttonProps} style={combinedStyles} /> return <button {...buttonProps} style={combinedStyles} />
@ -76,10 +78,10 @@ export const Button: FC<ButtonProps> = (props) => {
export const Test = () => { export const Test = () => {
return ( return (
<VStack gap={8} style={{ padding: "24px" }}> <Section>
{/* Variants */} {/* Variants */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Button Variants</h2> <H2>Button Variants</H2>
<HStack gap={4}> <HStack gap={4}>
<Button variant="primary">Primary</Button> <Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button> <Button variant="secondary">Secondary</Button>
@ -91,7 +93,7 @@ export const Test = () => {
{/* Sizes */} {/* Sizes */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Button Sizes</h2> <H2>Button Sizes</H2>
<HStack gap={4} v="end"> <HStack gap={4} v="end">
<Button size="sm">Small</Button> <Button size="sm">Small</Button>
<Button size="md">Medium</Button> <Button size="md">Medium</Button>
@ -101,7 +103,7 @@ export const Test = () => {
{/* With custom content */} {/* With custom content */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Custom Content</h2> <H2>Custom Content</H2>
<HStack gap={4}> <HStack gap={4}>
<Button variant="primary"> <Button variant="primary">
<span>🚀</span> <span>🚀</span>
@ -116,7 +118,7 @@ export const Test = () => {
{/* Native attributes work */} {/* Native attributes work */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Native Attributes</h2> <H2>Native Attributes</H2>
<HStack gap={4}> <HStack gap={4}>
<Button onClick={() => alert("Clicked!")} variant="primary"> <Button onClick={() => alert("Clicked!")} variant="primary">
Click Me Click Me
@ -129,6 +131,6 @@ export const Test = () => {
</Button> </Button>
</HStack> </HStack>
</VStack> </VStack>
</VStack> </Section>
) )
} }

View File

@ -1,3 +1,5 @@
import { Section } from "./section"
import { H2 } from "./text"
import "hono/jsx" import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx" import type { FC, PropsWithChildren, JSX } from "hono/jsx"
import { VStack } from "./stack" import { VStack } from "./stack"
@ -41,8 +43,8 @@ export const Divider: FC<DividerProps> = ({ children, style }) => {
export const Test = () => { export const Test = () => {
return ( return (
<VStack gap={4} style={{ padding: "16px", maxWidth: "448px" }}> <Section gap={4} maxWidth="448px" style={{ padding: "16px" }}>
<h2 style={{ fontSize: "18px", fontWeight: "bold" }}>Divider Examples</h2> <H2>Divider Examples</H2>
<VStack gap={0}> <VStack gap={0}>
<p>Would you like to continue</p> <p>Would you like to continue</p>
@ -56,6 +58,6 @@ export const Test = () => {
<Divider /> <Divider />
<p>So cool, so straight!</p> <p>So cool, so straight!</p>
</VStack> </VStack>
</VStack> </Section>
) )
} }

View File

@ -3,6 +3,8 @@ import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx" import type { FC, PropsWithChildren, JSX } from "hono/jsx"
import { VStack } from "./stack" import { VStack } from "./stack"
import { Button } from "./button" import { Button } from "./button"
import { Section } from "./section"
import { H2, H3 } from "./text"
type GridProps = PropsWithChildren & { type GridProps = PropsWithChildren & {
cols?: GridCols cols?: GridCols
@ -69,13 +71,13 @@ const justifyItemsMap = {
export const Test = () => { export const Test = () => {
return ( return (
<VStack gap={4} style={{ padding: "16px" }}> <Section gap={4} style={{ padding: "16px" }}>
<VStack gap={6}> <VStack gap={6}>
<h2 style={{ fontSize: "18px", fontWeight: "bold" }}>Grid Examples</h2> <H2>Grid Examples</H2>
{/* Simple 3-column grid */} {/* Simple 3-column grid */}
<VStack gap={2}> <VStack gap={2}>
<h3 style={{ fontSize: "16px", fontWeight: "600" }}>Simple 3 columns: cols=3</h3> <H3>Simple 3 columns: cols=3</H3>
<Grid cols={3} gap={4}> <Grid cols={3} gap={4}>
<div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Item 1</div> <div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Item 1</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Item 2</div> <div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Item 2</div>
@ -88,9 +90,7 @@ export const Test = () => {
{/* Responsive grid */} {/* Responsive grid */}
<VStack gap={2}> <VStack gap={2}>
<h3 style={{ fontSize: "16px", fontWeight: "600" }}> <H3>Responsive: cols=&#123;sm: 1, md: 2, lg: 3&#125;</H3>
Responsive: cols=&#123;sm: 1, md: 2, lg: 3&#125;
</h3>
<Grid cols={{ sm: 1, md: 2, lg: 3 }} gap={4}> <Grid cols={{ sm: 1, md: 2, lg: 3 }} gap={4}>
<div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Card 1</div> <div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Card 1</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Card 2</div> <div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Card 2</div>
@ -101,9 +101,7 @@ export const Test = () => {
{/* More responsive examples */} {/* More responsive examples */}
<VStack gap={2}> <VStack gap={2}>
<h3 style={{ fontSize: "16px", fontWeight: "600" }}> <H3>More responsive: cols=&#123;sm: 2, lg: 4, xl: 6&#125;</H3>
More responsive: cols=&#123;sm: 2, lg: 4, xl: 6&#125;
</h3>
<Grid cols={{ sm: 2, lg: 4, xl: 6 }} gap={4}> <Grid cols={{ sm: 2, lg: 4, xl: 6 }} gap={4}>
<div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Item A</div> <div style={{ backgroundColor: "#fecaca", padding: "16px", textAlign: "center" }}>Item A</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Item B</div> <div style={{ backgroundColor: "#bbf7d0", padding: "16px", textAlign: "center" }}>Item B</div>
@ -116,7 +114,7 @@ export const Test = () => {
{/* Payment method example */} {/* Payment method example */}
<VStack gap={2}> <VStack gap={2}>
<h3 style={{ fontSize: "16px", fontWeight: "600" }}>Payment buttons example</h3> <H3>Payment buttons example</H3>
<Grid cols={3} gap={4}> <Grid cols={3} gap={4}>
<Button variant="outline" style={{ height: "80px", flexDirection: "column" }}> <Button variant="outline" style={{ height: "80px", flexDirection: "column" }}>
<div style={{ fontSize: "24px" }}>💳</div> <div style={{ fontSize: "24px" }}>💳</div>
@ -135,9 +133,7 @@ export const Test = () => {
{/* Alignment examples */} {/* Alignment examples */}
<VStack gap={2}> <VStack gap={2}>
<h3 style={{ fontSize: "16px", fontWeight: "600" }}> <H3>Alignment: v="center" h="center"</H3>
Alignment: v="center" h="center"
</h3>
<Grid cols={3} gap={4} v="center" h="center" style={{ height: "128px", backgroundColor: "#f3f4f6" }}> <Grid cols={3} gap={4} v="center" h="center" style={{ height: "128px", backgroundColor: "#f3f4f6" }}>
<div style={{ backgroundColor: "#fecaca", padding: "8px" }}>Item 1</div> <div style={{ backgroundColor: "#fecaca", padding: "8px" }}>Item 1</div>
<div style={{ backgroundColor: "#bbf7d0", padding: "8px" }}>Item 2</div> <div style={{ backgroundColor: "#bbf7d0", padding: "8px" }}>Item 2</div>
@ -146,13 +142,13 @@ export const Test = () => {
</VStack> </VStack>
<VStack gap={2}> <VStack gap={2}>
<h3 style={{ fontSize: "16px", fontWeight: "600" }}>Alignment: v="start" h="end"</h3> <H3>Alignment: v="start" h="end"</H3>
<Grid cols={2} gap={4} v="start" h="end" style={{ height: "96px", backgroundColor: "#f3f4f6" }}> <Grid cols={2} gap={4} v="start" h="end" style={{ height: "96px", backgroundColor: "#f3f4f6" }}>
<div style={{ backgroundColor: "#e9d5ff", padding: "8px" }}>Left</div> <div style={{ backgroundColor: "#e9d5ff", padding: "8px" }}>Left</div>
<div style={{ backgroundColor: "#fed7aa", padding: "8px" }}>Right</div> <div style={{ backgroundColor: "#fed7aa", padding: "8px" }}>Right</div>
</Grid> </Grid>
</VStack> </VStack>
</VStack> </VStack>
</VStack> </Section>
) )
} }

View File

@ -2,7 +2,9 @@ import "hono/jsx"
import type { FC, JSX } from "hono/jsx" import type { FC, JSX } from "hono/jsx"
import * as icons from "lucide-static" import * as icons from "lucide-static"
import { Grid } from "./grid" import { Grid } from "./grid"
import { VStack, HStack } from "./stack" import { VStack } from "./stack"
import { Section } from "./section"
import { H2, Text } from "./text"
export type IconName = keyof typeof icons export type IconName = keyof typeof icons
@ -69,100 +71,100 @@ export const IconLink: FC<IconLinkProps> = (props) => {
export const Test = () => { export const Test = () => {
return ( return (
<div style={{ padding: "24px" }}> <Section>
{/* === ICON TESTS === */} {/* === ICON TESTS === */}
{/* Size variations */} {/* Size variations */}
<div style={{ marginBottom: "32px" }}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Icon Size Variations</h2> <H2>Icon Size Variations</H2>
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}> <div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
{([3, 4, 5, 6, 8, 10, 12, 16] as const).map((size) => ( {([3, 4, 5, 6, 8, 10, 12, 16] as const).map((size) => (
<div key={size} style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }}> <VStack h="center" gap={2} key={size}>
<Icon name="Heart" size={size} /> <Icon name="Heart" size={size} />
<p style={{ fontSize: "14px" }}>{size}</p> <Text>{size}</Text>
</div> </VStack>
))} ))}
</div> </div>
</div> </VStack>
{/* Styling with CSS classes */} {/* Styling with CSS classes */}
<div style={{ marginBottom: "32px" }}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Styling with CSS Classes</h2> <H2>Styling with CSS Classes</H2>
<Grid cols={5} gap={6}> <Grid cols={5} gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Star" size={12} /> <Icon name="Star" size={12} />
<p style={{ fontSize: "14px" }}>Default</p> <Text>Default</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Star" size={12} style={{ color: "#3b82f6" }} /> <Icon name="Star" size={12} style={{ color: "#3b82f6" }} />
<p style={{ fontSize: "14px" }}>Blue Color</p> <Text>Blue Color</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Star" size={12} class="stroke-1" /> <Icon name="Star" size={12} class="stroke-1" />
<p style={{ fontSize: "14px" }}>Thin Stroke</p> <Text>Thin Stroke</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Star" size={12} style={{ color: "#fbbf24", fill: "currentColor", stroke: "none" }} /> <Icon name="Star" size={12} style={{ color: "#fbbf24", fill: "currentColor", stroke: "none" }} />
<p style={{ fontSize: "14px" }}>Filled</p> <Text>Filled</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Star" size={12} style={{ color: "#a855f7", transition: "color 0.2s" }} /> <Icon name="Star" size={12} style={{ color: "#a855f7", transition: "color 0.2s" }} />
<p style={{ fontSize: "14px" }}>Hover Effect</p> <Text>Hover Effect</Text>
</VStack> </VStack>
</Grid> </Grid>
</div> </VStack>
{/* Advanced styling */} {/* Advanced styling */}
<div style={{ marginBottom: "32px" }}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Advanced Styling</h2> <H2>Advanced Styling</H2>
<Grid cols={4} gap={6}> <Grid cols={4} gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Heart" size={12} style={{ color: "#ef4444", fill: "currentColor", stroke: "none" }} /> <Icon name="Heart" size={12} style={{ color: "#ef4444", fill: "currentColor", stroke: "none" }} />
<p style={{ fontSize: "14px" }}>Filled Heart</p> <Text>Filled Heart</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Shield" size={12} style={{ color: "#16a34a", strokeWidth: "2" }} /> <Icon name="Shield" size={12} style={{ color: "#16a34a", strokeWidth: "2" }} />
<p style={{ fontSize: "14px" }}>Thick Stroke</p> <Text>Thick Stroke</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Sun" size={12} style={{ color: "#eab308" }} /> <Icon name="Sun" size={12} style={{ color: "#eab308" }} />
<p style={{ fontSize: "14px" }}>Sun Icon</p> <Text>Sun Icon</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Icon name="Zap" size={12} style={{ color: "#60a5fa", filter: "drop-shadow(0 4px 6px rgba(0,0,0,0.3))" }} /> <Icon name="Zap" size={12} style={{ color: "#60a5fa", filter: "drop-shadow(0 4px 6px rgba(0,0,0,0.3))" }} />
<p style={{ fontSize: "14px" }}>Drop Shadow</p> <Text>Drop Shadow</Text>
</VStack> </VStack>
</Grid> </Grid>
</div> </VStack>
{/* === ICON LINK TESTS === */} {/* === ICON LINK TESTS === */}
{/* Basic icon links */} {/* Basic icon links */}
<div style={{ marginBottom: "32px" }}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Icon Links</h2> <H2>Icon Links</H2>
<div style={{ display: "flex", gap: "24px" }}> <div style={{ display: "flex", gap: "24px" }}>
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }}> <VStack h="center" gap={2}>
<IconLink name="Home" size={8} href="/" /> <IconLink name="Home" size={8} href="/" />
<p style={{ fontSize: "14px" }}>Home Link</p> <Text>Home Link</Text>
</div> </VStack>
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }}> <VStack h="center" gap={2}>
<IconLink name="ExternalLink" size={8} href="https://example.com" target="_blank" /> <IconLink name="ExternalLink" size={8} href="https://example.com" target="_blank" />
<p style={{ fontSize: "14px" }}>External Link</p> <Text>External Link</Text>
</div> </VStack>
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }}> <VStack h="center" gap={2}>
<IconLink name="Mail" size={8} href="mailto:hello@example.com" /> <IconLink name="Mail" size={8} href="mailto:hello@example.com" />
<p style={{ fontSize: "14px" }}>Email Link</p> <Text>Email Link</Text>
</div> </VStack>
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }}> <VStack h="center" gap={2}>
<IconLink name="Phone" size={8} href="tel:+1234567890" /> <IconLink name="Phone" size={8} href="tel:+1234567890" />
<p style={{ fontSize: "14px" }}>Phone Link</p> <Text>Phone Link</Text>
</div> </VStack>
</div> </div>
</div> </VStack>
{/* Styled icon links */} {/* Styled icon links */}
<div> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold", marginBottom: "16px" }}>Styled Icon Links</h2> <H2>Styled Icon Links</H2>
<Grid cols={4} gap={6}> <Grid cols={4} gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<IconLink <IconLink
@ -176,7 +178,7 @@ export const Test = () => {
borderRadius: "8px", borderRadius: "8px",
}} }}
/> />
<p style={{ fontSize: "14px" }}>Button Style</p> <Text>Button Style</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<IconLink <IconLink
@ -189,19 +191,19 @@ export const Test = () => {
borderRadius: "9999px", borderRadius: "9999px",
}} }}
/> />
<p style={{ fontSize: "14px" }}>Circle Border</p> <Text>Circle Border</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<IconLink name="Heart" size={8} href="#" style={{ color: "#ef4444" }} /> <IconLink name="Heart" size={8} href="#" style={{ color: "#ef4444" }} />
<p style={{ fontSize: "14px" }}>Red Heart</p> <Text>Red Heart</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<IconLink name="Star" size={8} href="#" style={{ color: "#fbbf24", fill: "currentColor" }} /> <IconLink name="Star" size={8} href="#" style={{ color: "#fbbf24", fill: "currentColor" }} />
<p style={{ fontSize: "14px" }}>Filled Star</p> <Text>Filled Star</Text>
</VStack> </VStack>
</Grid> </Grid>
</div> </VStack>
</div> </Section>
) )
} }

View File

@ -1,3 +1,5 @@
import { Section } from "./section"
import { H2, H3, H4, H5, Text, SmallText } from "./text"
import "hono/jsx" import "hono/jsx"
import type { FC, JSX } from "hono/jsx" import type { FC, JSX } from "hono/jsx"
import { VStack, HStack } from "./stack" import { VStack, HStack } from "./stack"
@ -32,38 +34,38 @@ export const Test = () => {
] ]
return ( return (
<VStack gap={8} style={{ padding: "24px" }}> <Section>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Image Examples</h2> <H2>Image Examples</H2>
{/* Size variations */} {/* Size variations */}
<VStack gap={4}> <VStack gap={4}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}>Size Variations</h3> <H3>Size Variations</H3>
<HStack gap={4} wrap> <HStack gap={4} wrap>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={64} height={64} objectFit="cover" alt="64x64" /> <Image src={sampleImages[0]!} width={64} height={64} objectFit="cover" alt="64x64" />
<p style={{ fontSize: "14px" }}>64x64</p> <Text>64x64</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={96} height={96} objectFit="cover" alt="96x96" /> <Image src={sampleImages[0]!} width={96} height={96} objectFit="cover" alt="96x96" />
<p style={{ fontSize: "14px" }}>96x96</p> <Text>96x96</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={128} height={128} objectFit="cover" alt="128x128" /> <Image src={sampleImages[0]!} width={128} height={128} objectFit="cover" alt="128x128" />
<p style={{ fontSize: "14px" }}>128x128</p> <Text>128x128</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={192} height={128} objectFit="cover" alt="192x128" /> <Image src={sampleImages[0]!} width={192} height={128} objectFit="cover" alt="192x128" />
<p style={{ fontSize: "14px" }}>192x128</p> <Text>192x128</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>
{/* Object fit variations */} {/* Object fit variations */}
<VStack gap={4}> <VStack gap={4}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}>Object Fit Variations</h3> <H3>Object Fit Variations</H3>
<p style={{ fontSize: "14px", color: "#6b7280" }}> <Text>
Same image with different object-fit values Same image with different object-fit values
</p> </Text>
<HStack gap={6} wrap> <HStack gap={6} wrap>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -74,7 +76,7 @@ export const Test = () => {
style={{ border: "1px solid black" }} style={{ border: "1px solid black" }}
alt="Object cover" alt="Object cover"
/> />
<p style={{ fontSize: "14px" }}>object-fit: cover</p> <Text>object-fit: cover</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -85,7 +87,7 @@ export const Test = () => {
style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }} style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }}
alt="Object contain" alt="Object contain"
/> />
<p style={{ fontSize: "14px" }}>object-fit: contain</p> <Text>object-fit: contain</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -96,7 +98,7 @@ export const Test = () => {
style={{ border: "1px solid black" }} style={{ border: "1px solid black" }}
alt="Object fill" alt="Object fill"
/> />
<p style={{ fontSize: "14px" }}>object-fit: fill</p> <Text>object-fit: fill</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -107,7 +109,7 @@ export const Test = () => {
style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }} style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }}
alt="Object scale-down" alt="Object scale-down"
/> />
<p style={{ fontSize: "14px" }}>object-fit: scale-down</p> <Text>object-fit: scale-down</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -118,14 +120,14 @@ export const Test = () => {
style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }} style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }}
alt="Object none" alt="Object none"
/> />
<p style={{ fontSize: "14px" }}>object-fit: none</p> <Text>object-fit: none</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>
{/* Styling examples */} {/* Styling examples */}
<VStack gap={4}> <VStack gap={4}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}>Styling Examples</h3> <H3>Styling Examples</H3>
<HStack gap={6} wrap> <HStack gap={6} wrap>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -136,7 +138,7 @@ export const Test = () => {
style={{ borderRadius: "8px", border: "4px solid #3b82f6" }} style={{ borderRadius: "8px", border: "4px solid #3b82f6" }}
alt="Rounded with border" alt="Rounded with border"
/> />
<p style={{ fontSize: "14px" }}>Rounded + Border</p> <Text>Rounded + Border</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -147,7 +149,7 @@ export const Test = () => {
style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }} style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }}
alt="With shadow" alt="With shadow"
/> />
<p style={{ fontSize: "14px" }}>With Shadow</p> <Text>With Shadow</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Image <Image
@ -162,18 +164,18 @@ export const Test = () => {
}} }}
alt="Circular with effects" alt="Circular with effects"
/> />
<p style={{ fontSize: "14px" }}>Circular + Effects</p> <Text>Circular + Effects</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>
{/* Common use cases */} {/* Common use cases */}
<VStack gap={6}> <VStack gap={6}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}>Common Use Cases</h3> <H3>Common Use Cases</H3>
{/* Avatar */} {/* Avatar */}
<VStack gap={2}> <VStack gap={2}>
<h4 style={{ fontWeight: "500" }}>Avatar</h4> <H4>Avatar</H4>
<Image <Image
src={sampleImages[0]!} src={sampleImages[0]!}
width={48} width={48}
@ -186,19 +188,19 @@ export const Test = () => {
{/* Card image */} {/* Card image */}
<VStack gap={2} style={{ maxWidth: "384px" }}> <VStack gap={2} style={{ maxWidth: "384px" }}>
<h4 style={{ fontWeight: "500" }}>Card Image</h4> <H4>Card Image</H4>
<VStack gap={0} style={{ border: "1px solid #d1d5db", borderRadius: "8px", overflow: "hidden" }}> <VStack gap={0} style={{ border: "1px solid #d1d5db", borderRadius: "8px", overflow: "hidden" }}>
<Image src={sampleImages[1]!} width={384} height={192} objectFit="cover" alt="Card image" /> <Image src={sampleImages[1]!} width={384} height={192} objectFit="cover" alt="Card image" />
<VStack gap={1} style={{ padding: "16px" }}> <VStack gap={1} style={{ padding: "16px" }}>
<h5 style={{ fontWeight: "500" }}>Card Title</h5> <H5>Card Title</H5>
<p style={{ fontSize: "14px", color: "#6b7280" }}>Card description goes here</p> <Text>Card description goes here</Text>
</VStack> </VStack>
</VStack> </VStack>
</VStack> </VStack>
{/* Gallery grid */} {/* Gallery grid */}
<VStack gap={2}> <VStack gap={2}>
<h4 style={{ fontWeight: "500" }}>Gallery Grid</h4> <H4>Gallery Grid</H4>
<Grid cols={3} gap={2}> <Grid cols={3} gap={2}>
{sampleImages.map((src, i) => ( {sampleImages.map((src, i) => (
<Image <Image
@ -214,6 +216,6 @@ export const Test = () => {
</Grid> </Grid>
</VStack> </VStack>
</VStack> </VStack>
</VStack> </Section>
) )
} }

View File

@ -25,4 +25,10 @@ export type { SelectProps, SelectOption } from "./select"
export { Placeholder } from "./placeholder" export { Placeholder } from "./placeholder"
export { default as PlaceholderDefault } from "./placeholder" export { default as PlaceholderDefault } from "./placeholder"
export { H1, H2, H3, H4, H5, Text, SmallText } from "./text"
export { Box, RedBox, GreenBox, BlueBox, GrayBox } from "./box"
export { Section } from "./section"
export type { TailwindSize } from "./types" export type { TailwindSize } from "./types"

View File

@ -1,3 +1,5 @@
import { Section } from "./section"
import { H2, H3, H4, H5, Text, SmallText } from "./text"
import "hono/jsx" import "hono/jsx"
import type { JSX, FC } from "hono/jsx" import type { JSX, FC } from "hono/jsx"
import { VStack, HStack } from "./stack" import { VStack, HStack } from "./stack"
@ -18,7 +20,7 @@ export const Input: FC<InputProps> = (props) => {
backgroundColor: "white", backgroundColor: "white",
fontSize: "14px", fontSize: "14px",
outline: "none", outline: "none",
...style, ...(style as JSX.CSSProperties),
} }
if (!children) { if (!children) {
@ -69,10 +71,10 @@ export const Input: FC<InputProps> = (props) => {
export const Test = () => { export const Test = () => {
return ( return (
<VStack gap={8} style={{ padding: "24px", maxWidth: "448px" }}> <Section maxWidth="448px">
{/* Basic inputs */} {/* Basic inputs */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Basic Inputs</h2> <H2>Basic Inputs</H2>
<VStack gap={4}> <VStack gap={4}>
<Input placeholder="Enter your name" /> <Input placeholder="Enter your name" />
<Input type="email" placeholder="Enter your email" /> <Input type="email" placeholder="Enter your email" />
@ -82,7 +84,7 @@ export const Test = () => {
{/* Custom styling */} {/* Custom styling */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Custom Styling</h2> <H2>Custom Styling</H2>
<VStack gap={4}> <VStack gap={4}>
<Input style={{ height: "32px", fontSize: "12px" }} placeholder="Small input" /> <Input style={{ height: "32px", fontSize: "12px" }} placeholder="Small input" />
<Input placeholder="Default input" /> <Input placeholder="Default input" />
@ -92,7 +94,7 @@ export const Test = () => {
{/* With values */} {/* With values */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>With Values</h2> <H2>With Values</H2>
<VStack gap={4}> <VStack gap={4}>
<Input value="John Doe" placeholder="Name" /> <Input value="John Doe" placeholder="Name" />
<Input type="email" value="john@example.com" placeholder="Email" /> <Input type="email" value="john@example.com" placeholder="Email" />
@ -101,7 +103,7 @@ export const Test = () => {
{/* Disabled state */} {/* Disabled state */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Disabled State</h2> <H2>Disabled State</H2>
<VStack gap={4}> <VStack gap={4}>
<Input disabled placeholder="Disabled input" style={{ opacity: 0.5, cursor: "not-allowed" }} /> <Input disabled placeholder="Disabled input" style={{ opacity: 0.5, cursor: "not-allowed" }} />
<Input disabled value="Disabled with value" style={{ opacity: 0.5, cursor: "not-allowed" }} /> <Input disabled value="Disabled with value" style={{ opacity: 0.5, cursor: "not-allowed" }} />
@ -110,7 +112,7 @@ export const Test = () => {
{/* Label above */} {/* Label above */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Label Above</h2> <H2>Label Above</H2>
<VStack gap={4}> <VStack gap={4}>
<Input placeholder="Enter your name">Name</Input> <Input placeholder="Enter your name">Name</Input>
<Input type="email" placeholder="Enter your email"> <Input type="email" placeholder="Enter your email">
@ -124,7 +126,7 @@ export const Test = () => {
{/* Label to the left */} {/* Label to the left */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Label Left</h2> <H2>Label Left</H2>
<VStack gap={4}> <VStack gap={4}>
<Input labelPosition="left" placeholder="Enter your name"> <Input labelPosition="left" placeholder="Enter your name">
Name Name
@ -140,7 +142,7 @@ export const Test = () => {
{/* Label to the right */} {/* Label to the right */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Label Right</h2> <H2>Label Right</H2>
<VStack gap={4}> <VStack gap={4}>
<Input labelPosition="right" placeholder="Enter your name"> <Input labelPosition="right" placeholder="Enter your name">
Name Name
@ -156,7 +158,7 @@ export const Test = () => {
{/* Horizontal layout */} {/* Horizontal layout */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Horizontal Layout</h2> <H2>Horizontal Layout</H2>
<HStack gap={4}> <HStack gap={4}>
<Input placeholder="First name">First</Input> <Input placeholder="First name">First</Input>
<Input placeholder="Last name">Last</Input> <Input placeholder="Last name">Last</Input>
@ -166,7 +168,7 @@ export const Test = () => {
{/* Custom styling */} {/* Custom styling */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Custom Input Styling</h2> <H2>Custom Input Styling</H2>
<VStack gap={4}> <VStack gap={4}>
<Input style={{ borderColor: "#93c5fd" }} placeholder="Custom styled input"> <Input style={{ borderColor: "#93c5fd" }} placeholder="Custom styled input">
<span style={{ color: "#2563eb", fontWeight: "bold" }}>Custom Label</span> <span style={{ color: "#2563eb", fontWeight: "bold" }}>Custom Label</span>

View File

@ -1,3 +1,5 @@
import { Section } from "./section"
import { H2, H3, H4, H5, Text, SmallText } from "./text"
import "hono/jsx" import "hono/jsx"
import { Avatar } from "./avatar" import { Avatar } from "./avatar"
import type { AvatarProps } from "./avatar" import type { AvatarProps } from "./avatar"
@ -34,19 +36,19 @@ export const Placeholder = {
export const Test = () => { export const Test = () => {
return ( return (
<VStack gap={8} style={{ padding: "24px" }}> <Section>
{/* === AVATAR TESTS === */} {/* === AVATAR TESTS === */}
{/* Show all available avatar styles */} {/* Show all available avatar styles */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}> <H2>
All Avatar Styles ({allStyles.length} total) All Avatar Styles ({allStyles.length} total)
</h2> </H2>
<Grid cols={10} gap={3}> <Grid cols={10} gap={3}>
{allStyles.map((style) => ( {allStyles.map((style) => (
<VStack h="center" gap={1} key={style}> <VStack h="center" gap={1} key={style}>
<Placeholder.Avatar type={style} size={48} /> <Placeholder.Avatar type={style} size={48} />
<p style={{ fontSize: "12px", fontWeight: "500" }}>{style}</p> <SmallText style={{ fontWeight: "500" }}>{style}</Text>
</VStack> </VStack>
))} ))}
</Grid> </Grid>
@ -54,12 +56,12 @@ export const Test = () => {
{/* Avatar size variations */} {/* Avatar size variations */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Avatar Size Variations</h2> <H2>Avatar Size Variations</H2>
<HStack gap={4}> <HStack gap={4}>
{[24, 32, 48, 64].map((size) => ( {[24, 32, 48, 64].map((size) => (
<VStack h="center" gap={2} key={size}> <VStack h="center" gap={2} key={size}>
<Placeholder.Avatar size={size} /> <Placeholder.Avatar size={size} />
<p style={{ fontSize: "14px" }}>{size}px</p> <Text>{size}px</Text>
</VStack> </VStack>
))} ))}
</HStack> </HStack>
@ -67,41 +69,41 @@ export const Test = () => {
{/* Avatar styling combinations */} {/* Avatar styling combinations */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Avatar Styling Options</h2> <H2>Avatar Styling Options</H2>
<HStack gap={6}> <HStack gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Placeholder.Avatar rounded size={64} /> <Placeholder.Avatar rounded size={64} />
<p style={{ fontSize: "14px" }}>Rounded + Background</p> <Text>Rounded + Background</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<div style={{ backgroundColor: "#e5e7eb", padding: "8px" }}> <div style={{ backgroundColor: "#e5e7eb", padding: "8px" }}>
<Placeholder.Avatar rounded transparent size={64} /> <Placeholder.Avatar rounded transparent size={64} />
</div> </div>
<p style={{ fontSize: "14px" }}>Rounded + Transparent</p> <Text>Rounded + Transparent</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Placeholder.Avatar size={64} /> <Placeholder.Avatar size={64} />
<p style={{ fontSize: "14px" }}>Square + Background</p> <Text>Square + Background</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<div style={{ backgroundColor: "#e5e7eb", padding: "8px" }}> <div style={{ backgroundColor: "#e5e7eb", padding: "8px" }}>
<Placeholder.Avatar transparent size={64} /> <Placeholder.Avatar transparent size={64} />
</div> </div>
<p style={{ fontSize: "14px" }}>Square + Transparent</p> <Text>Square + Transparent</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>
{/* Avatar seed variations */} {/* Avatar seed variations */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}> <H2>
Avatar Seeds (Same Style, Different People) Avatar Seeds (Same Style, Different People)
</h2> </H2>
<HStack gap={4}> <HStack gap={4}>
{["alice", "bob", "charlie", "diana"].map((seed) => ( {["alice", "bob", "charlie", "diana"].map((seed) => (
<VStack h="center" gap={2} key={seed}> <VStack h="center" gap={2} key={seed}>
<Placeholder.Avatar seed={seed} size={64} /> <Placeholder.Avatar seed={seed} size={64} />
<p style={{ fontSize: "14px" }}>"{seed}"</p> <Text>"{seed}"</Text>
</VStack> </VStack>
))} ))}
</HStack> </HStack>
@ -109,11 +111,11 @@ export const Test = () => {
{/* === IMAGE TESTS === */} {/* === IMAGE TESTS === */}
<VStack gap={6}> <VStack gap={6}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Placeholder Images</h2> <H2>Placeholder Images</H2>
{/* Size variations */} {/* Size variations */}
<VStack gap={3}> <VStack gap={3}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}>Size Variations</h3> <H3>Size Variations</H3>
<HStack gap={4}> <HStack gap={4}>
{[ {[
{ width: 100, height: 100 }, { width: 100, height: 100 },
@ -123,9 +125,9 @@ export const Test = () => {
].map(({ width, height }) => ( ].map(({ width, height }) => (
<VStack h="center" gap={2} key={`${width}x${height}`}> <VStack h="center" gap={2} key={`${width}x${height}`}>
<Placeholder.Image width={width} height={height} seed={1} /> <Placeholder.Image width={width} height={height} seed={1} />
<p style={{ fontSize: "14px" }}> <Text>
{width}×{height} {width}×{height}
</p> </Text>
</VStack> </VStack>
))} ))}
</HStack> </HStack>
@ -133,14 +135,14 @@ export const Test = () => {
{/* Different seeds - show variety */} {/* Different seeds - show variety */}
<VStack gap={3}> <VStack gap={3}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}> <H3>
Different Images (Different Seeds) Different Images (Different Seeds)
</h3> </H3>
<HStack gap={4}> <HStack gap={4}>
{[1, 2, 3, 4, 5].map((seed) => ( {[1, 2, 3, 4, 5].map((seed) => (
<VStack h="center" gap={2} key={seed}> <VStack h="center" gap={2} key={seed}>
<Placeholder.Image width={150} height={150} seed={seed} /> <Placeholder.Image width={150} height={150} seed={seed} />
<p style={{ fontSize: "14px" }}>Seed {seed}</p> <Text>Seed {seed}</Text>
</VStack> </VStack>
))} ))}
</HStack> </HStack>
@ -148,7 +150,7 @@ export const Test = () => {
{/* With custom styles */} {/* With custom styles */}
<VStack gap={3}> <VStack gap={3}>
<h3 style={{ fontSize: "18px", fontWeight: "600" }}>With Custom Styles</h3> <H3>With Custom Styles</H3>
<HStack gap={6}> <HStack gap={6}>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Placeholder.Image <Placeholder.Image
@ -158,7 +160,7 @@ export const Test = () => {
objectFit="cover" objectFit="cover"
style={{ borderRadius: "8px", border: "4px solid #3b82f6" }} style={{ borderRadius: "8px", border: "4px solid #3b82f6" }}
/> />
<p style={{ fontSize: "14px" }}>Rounded + Border</p> <Text>Rounded + Border</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Placeholder.Image <Placeholder.Image
@ -168,7 +170,7 @@ export const Test = () => {
objectFit="cover" objectFit="cover"
style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }} style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }}
/> />
<p style={{ fontSize: "14px" }}>With Shadow</p> <Text>With Shadow</Text>
</VStack> </VStack>
<VStack h="center" gap={2}> <VStack h="center" gap={2}>
<Placeholder.Image <Placeholder.Image
@ -182,7 +184,7 @@ export const Test = () => {
boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)", boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)",
}} }}
/> />
<p style={{ fontSize: "14px" }}>Circular + Effects</p> <Text>Circular + Effects</Text>
</VStack> </VStack>
</HStack> </HStack>
</VStack> </VStack>

48
src/section.tsx Normal file
View File

@ -0,0 +1,48 @@
import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx"
import { VStack } from "./stack"
import type { TailwindSize } from "./types"
type SectionProps = PropsWithChildren & {
gap?: TailwindSize
maxWidth?: string
style?: JSX.CSSProperties
}
export const Section: FC<SectionProps> = ({ children, gap = 8, maxWidth, style }) => {
return (
<VStack gap={gap} style={{ padding: "24px", maxWidth, ...style }}>
{children}
</VStack>
)
}
export const Test = () => {
return (
<div>
<Section>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Default Section</h2>
<p>This is a section with default gap (8)</p>
<p>It has padding and vertical spacing between children</p>
</Section>
<Section gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Compact Section</h2>
<p>This section has a smaller gap (4)</p>
<p>Items are closer together</p>
</Section>
<Section gap={12}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Spacious Section</h2>
<p>This section has a larger gap (12)</p>
<p>Items have more breathing room</p>
</Section>
<Section maxWidth="600px" style={{ backgroundColor: "#f3f4f6" }}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Constrained Width Section</h2>
<p>This section has a max width of 600px and a gray background</p>
<p>Good for centering content on wide screens</p>
</Section>
</div>
)
}

View File

@ -1,3 +1,5 @@
import { Section } from "./section"
import { H2, H3, H4, H5, Text, SmallText } from "./text"
import "hono/jsx" import "hono/jsx"
import type { JSX, FC } from "hono/jsx" import type { JSX, FC } from "hono/jsx"
import { VStack, HStack } from "./stack" import { VStack, HStack } from "./stack"
@ -158,10 +160,10 @@ export const Test = () => {
] ]
return ( return (
<VStack gap={8} style={{ padding: "24px", maxWidth: "448px" }}> <Section maxWidth="448px">
{/* Basic selects */} {/* Basic selects */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Basic Selects</h2> <H2>Basic Selects</H2>
<VStack gap={4}> <VStack gap={4}>
<Select options={months} placeholder="Select month" /> <Select options={months} placeholder="Select month" />
<Select options={years} placeholder="Select year" /> <Select options={years} placeholder="Select year" />
@ -171,7 +173,7 @@ export const Test = () => {
{/* With values */} {/* With values */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>With Values</h2> <H2>With Values</H2>
<VStack gap={4}> <VStack gap={4}>
<Select options={months} value="03" /> <Select options={months} value="03" />
<Select options={years} value="2025" /> <Select options={years} value="2025" />
@ -180,7 +182,7 @@ export const Test = () => {
{/* Disabled state */} {/* Disabled state */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Disabled State</h2> <H2>Disabled State</H2>
<VStack gap={4}> <VStack gap={4}>
<Select <Select
options={months} options={months}
@ -194,7 +196,7 @@ export const Test = () => {
{/* Custom styling */} {/* Custom styling */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Custom Styling</h2> <H2>Custom Styling</H2>
<VStack gap={4}> <VStack gap={4}>
<Select options={countries} style={{ borderColor: "#93c5fd" }} placeholder="Custom styled select" /> <Select options={countries} style={{ borderColor: "#93c5fd" }} placeholder="Custom styled select" />
<Select options={months} style={{ height: "32px", fontSize: "12px" }} placeholder="Small select" /> <Select options={months} style={{ height: "32px", fontSize: "12px" }} placeholder="Small select" />
@ -203,7 +205,7 @@ export const Test = () => {
{/* Label above */} {/* Label above */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Label Above</h2> <H2>Label Above</H2>
<VStack gap={4}> <VStack gap={4}>
<Select options={months} placeholder="Select month"> <Select options={months} placeholder="Select month">
Birth Month Birth Month
@ -219,7 +221,7 @@ export const Test = () => {
{/* Label to the left */} {/* Label to the left */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Label Left</h2> <H2>Label Left</H2>
<VStack gap={4}> <VStack gap={4}>
<Select labelPosition="left" options={months} placeholder="Select month"> <Select labelPosition="left" options={months} placeholder="Select month">
Month Month
@ -235,7 +237,7 @@ export const Test = () => {
{/* Label to the right */} {/* Label to the right */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Label Right</h2> <H2>Label Right</H2>
<VStack gap={4}> <VStack gap={4}>
<Select labelPosition="right" options={months} placeholder="Select month"> <Select labelPosition="right" options={months} placeholder="Select month">
Month Month
@ -248,7 +250,7 @@ export const Test = () => {
{/* Horizontal layout (like card form) */} {/* Horizontal layout (like card form) */}
<VStack gap={4}> <VStack gap={4}>
<h2 style={{ fontSize: "20px", fontWeight: "bold" }}>Horizontal Layout</h2> <H2>Horizontal Layout</H2>
<HStack gap={4}> <HStack gap={4}>
<Select options={months} placeholder="MM"> <Select options={months} placeholder="MM">
Expires Expires
@ -258,6 +260,6 @@ export const Test = () => {
</Select> </Select>
</HStack> </HStack>
</VStack> </VStack>
</VStack> </Section>
) )
} }

View File

@ -2,6 +2,9 @@ import type { TailwindSize } from "./types"
import "hono/jsx" import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx" import type { FC, PropsWithChildren, JSX } from "hono/jsx"
import { Grid } from "./grid" import { Grid } from "./grid"
import { Section } from "./section"
import { H2 } from "./text"
import { RedBox, GreenBox, BlueBox } from "./box"
export const VStack: FC<VStackProps> = (props) => { export const VStack: FC<VStackProps> = (props) => {
return ( return (
@ -64,10 +67,10 @@ export const Test = () => {
const crossAxisOpts: CrossAxisOpts[] = ["start", "center", "end", "stretch", "baseline"] const crossAxisOpts: CrossAxisOpts[] = ["start", "center", "end", "stretch", "baseline"]
return ( return (
<VStack gap={8} style={{ padding: "16px" }}> <Section gap={8} style={{ padding: "16px" }}>
{/* HStack layout matrix */} {/* HStack layout matrix */}
<VStack gap={2}> <VStack gap={2}>
<h2 style={{ fontSize: "18px", fontWeight: "bold" }}>HStack Layout</h2> <H2>HStack Layout</H2>
<div style={{ overflowX: "auto" }}> <div style={{ overflowX: "auto" }}>
<Grid cols={7} gap={1} style={{ gridTemplateColumns: "auto repeat(6, 1fr)" }}> <Grid cols={7} gap={1} style={{ gridTemplateColumns: "auto repeat(6, 1fr)" }}>
{/* Header row: blank + h labels */} {/* Header row: blank + h labels */}
@ -90,9 +93,9 @@ export const Test = () => {
v={v} v={v}
style={{ backgroundColor: "#f3f4f6", padding: "8px", height: "96px", border: "1px solid #9ca3af" }} style={{ backgroundColor: "#f3f4f6", padding: "8px", height: "96px", border: "1px solid #9ca3af" }}
> >
<div style={{ textAlign: "center", padding: "4px", backgroundColor: "#ef4444" }}>Aa</div> <RedBox>Aa</RedBox>
<div style={{ textAlign: "center", padding: "4px", backgroundColor: "#22c55e" }}>Aa</div> <GreenBox>Aa</GreenBox>
<div style={{ textAlign: "center", padding: "4px", backgroundColor: "#3b82f6" }}>Aa</div> <BlueBox>Aa</BlueBox>
</HStack> </HStack>
)), )),
])} ])}
@ -102,7 +105,7 @@ export const Test = () => {
{/* VStack layout matrix */} {/* VStack layout matrix */}
<VStack gap={2}> <VStack gap={2}>
<h2 style={{ fontSize: "18px", fontWeight: "bold" }}>VStack Layout</h2> <H2>VStack Layout</H2>
<div style={{ overflowX: "auto" }}> <div style={{ overflowX: "auto" }}>
<Grid cols={6} gap={1} style={{ gridTemplateColumns: "auto repeat(5, 1fr)" }}> <Grid cols={6} gap={1} style={{ gridTemplateColumns: "auto repeat(5, 1fr)" }}>
{/* Header row: blank + h labels */} {/* Header row: blank + h labels */}
@ -125,16 +128,16 @@ export const Test = () => {
h={h} h={h}
style={{ backgroundColor: "#f3f4f6", padding: "8px", height: "168px", border: "1px solid #9ca3af" }} style={{ backgroundColor: "#f3f4f6", padding: "8px", height: "168px", border: "1px solid #9ca3af" }}
> >
<div style={{ textAlign: "center", padding: "4px", backgroundColor: "#ef4444" }}>Aa</div> <RedBox>Aa</RedBox>
<div style={{ textAlign: "center", padding: "4px", backgroundColor: "#22c55e" }}>Aa</div> <GreenBox>Aa</GreenBox>
<div style={{ textAlign: "center", padding: "4px", backgroundColor: "#3b82f6" }}>Aa</div> <BlueBox>Aa</BlueBox>
</VStack> </VStack>
)), )),
])} ])}
</Grid> </Grid>
</div> </div>
</VStack> </VStack>
</VStack> </Section>
) )
} }

73
src/text.tsx Normal file
View File

@ -0,0 +1,73 @@
import "hono/jsx"
import type { FC, PropsWithChildren, JSX } from "hono/jsx"
type TextProps = PropsWithChildren & {
style?: JSX.CSSProperties
}
export const H1: FC<TextProps> = ({ children, style }) => (
<h1 style={{ fontSize: "24px", fontWeight: "bold", ...style }}>{children}</h1>
)
export const H2: FC<TextProps> = ({ children, style }) => (
<h2 style={{ fontSize: "20px", fontWeight: "bold", ...style }}>{children}</h2>
)
export const H3: FC<TextProps> = ({ children, style }) => (
<h3 style={{ fontSize: "18px", fontWeight: "600", ...style }}>{children}</h3>
)
export const H4: FC<TextProps> = ({ children, style }) => (
<h4 style={{ fontSize: "16px", fontWeight: "600", ...style }}>{children}</h4>
)
export const H5: FC<TextProps> = ({ children, style }) => (
<h5 style={{ fontSize: "14px", fontWeight: "500", ...style }}>{children}</h5>
)
export const Text: FC<TextProps> = ({ children, style }) => (
<p style={{ fontSize: "14px", ...style }}>{children}</p>
)
export const SmallText: FC<TextProps> = ({ children, style }) => (
<p style={{ fontSize: "12px", ...style }}>{children}</p>
)
export const Test = () => {
return (
<div style={{ padding: "24px", display: "flex", flexDirection: "column", gap: "32px" }}>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
<H1>Heading 1 (24px, bold)</H1>
<H2>Heading 2 (20px, bold)</H2>
<H3>Heading 3 (18px, semibold)</H3>
<H4>Heading 4 (16px, semibold)</H4>
<H5>Heading 5 (14px, medium)</H5>
<Text>Regular text (14px)</Text>
<SmallText>Small text (12px)</SmallText>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<H2>Custom Styling</H2>
<Text style={{ color: "#3b82f6" }}>Blue text with custom color</Text>
<Text style={{ fontWeight: "bold", fontStyle: "italic" }}>Bold italic text</Text>
<SmallText style={{ color: "#ef4444", textTransform: "uppercase" }}>
Red uppercase small text
</SmallText>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "8px", maxWidth: "600px" }}>
<H2>Typography Example</H2>
<H3>Article Title</H3>
<Text>
This is a paragraph of regular text. It demonstrates how the Text component can be used for body
content in articles, blog posts, or any other long-form content.
</Text>
<Text>
Multiple paragraphs can be stacked together to create readable content with consistent styling
throughout your application.
</Text>
<SmallText style={{ color: "#64748b" }}>Last updated: Today</SmallText>
</div>
</div>
)
}

View File

@ -25,8 +25,8 @@ app.get('/', c => {
}) })
function testFiles(): string[] { function testFiles(): string[] {
return readdirSync('./test') return readdirSync('./src')
.filter(x => x.endsWith('.tsx') && !x.startsWith('server')) .filter(x => x.endsWith('.tsx') && !x.startsWith('index'))
.map(x => x.replace('.tsx', '')) .map(x => x.replace('.tsx', ''))
.sort() .sort()
} }