Update .gitignore to include .cursorindexingignore and DESIGN-BRIEF.md

This commit is contained in:
Nicholai 2025-11-26 17:19:03 -07:00
parent de940920fd
commit 1000fe89e3
27 changed files with 8199 additions and 0 deletions

2
.gitignore vendored
View File

@ -22,3 +22,5 @@ dump/**/nohup.out
*.tmp
*.swp
*~
.cursorindexingignore
astro-website/DESIGN-BRIEF.md

4
.specstory/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# SpecStory project identity file
/.project.json
# SpecStory explanation file
/.what-is-this.md

24
astro-website/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/

0
astro-website/README.md Normal file
View File

View File

@ -0,0 +1,15 @@
// @ts-check
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [tailwindcss()],
},
integrations: [react()],
});

View File

@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles/global.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}

7235
astro-website/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
{
"name": "nuke-telemetry",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/mdx": "^4.3.12",
"@astrojs/react": "^4.4.2",
"@radix-ui/react-slot": "^1.2.4",
"@tailwindcss/vite": "^4.1.17",
"@types/canvas-confetti": "^1.9.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"astro": "^5.16.1",
"canvas-confetti": "^1.9.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.555.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.17",
"@radix-ui/react-accordion": "1.2.2",
"@radix-ui/react-alert-dialog": "1.1.4",
"@radix-ui/react-aspect-ratio": "1.1.1",
"@radix-ui/react-avatar": "1.1.2",
"@radix-ui/react-checkbox": "1.1.3",
"@radix-ui/react-collapsible": "1.1.2",
"@radix-ui/react-context-menu": "2.2.4",
"@radix-ui/react-dialog": "1.1.4",
"@radix-ui/react-dropdown-menu": "2.1.4",
"@radix-ui/react-hover-card": "1.1.4",
"@radix-ui/react-label": "2.1.1",
"@radix-ui/react-menubar": "1.1.4",
"@radix-ui/react-navigation-menu": "1.2.3",
"@radix-ui/react-popover": "1.1.4",
"@radix-ui/react-progress": "1.1.1",
"@radix-ui/react-radio-group": "1.2.2",
"@radix-ui/react-scroll-area": "1.2.2",
"@radix-ui/react-select": "2.1.4",
"@radix-ui/react-separator": "1.1.1",
"@radix-ui/react-slider": "1.2.2",
"@radix-ui/react-switch": "1.1.2",
"@radix-ui/react-tabs": "1.1.2",
"@radix-ui/react-toast": "1.2.4",
"@radix-ui/react-toggle": "1.1.1",
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"@vercel/analytics": "1.3.1",
"autoprefixer": "^10.4.20",
"cmdk": "1.0.4",
"date-fns": "4.1.0",
"embla-carousel-react": "8.5.1",
"input-otp": "1.4.1",
"next": "16.0.3",
"next-themes": "^0.4.6",
"react-day-picker": "9.8.0",
"react-hook-form": "^7.60.0",
"react-resizable-panels": "^2.1.7",
"recharts": "2.15.4",
"sonner": "^1.7.4",
"tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2",
"zod": "3.25.76"
},
"devDependencies": {
"tw-animate-css": "^1.4.0",
"@tailwindcss/postcss": "^4.1.9",
"@types/node": "^22",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8.5",
"tailwindcss": "^4.1.9",
"typescript": "^5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@ -0,0 +1,18 @@
interface AsciiChartProps {
data: number[]
label: string
}
export function AsciiChart({ data, label }: AsciiChartProps) {
const max = Math.max(...data)
return (
<div className="font-mono text-xs">
<div className="flex items-end gap-px h-16 mb-2">
{data.map((value, i) => (
<div key={i} className="w-1 bg-primary/60" style={{ height: `${(value / max) * 100}%` }} />
))}
</div>
<p className="text-muted-foreground">{label}</p>
</div>
)
}

View File

@ -0,0 +1,19 @@
import type React from "react"
interface PrefixItemProps {
children: React.ReactNode
highlight?: string
}
export function PrefixItem({ children, highlight }: PrefixItemProps) {
return (
<div className="flex gap-3 font-mono text-sm">
<span className="text-muted-foreground">[+]</span>
<span>
{highlight && <span className="text-primary font-medium">{highlight}</span>}
{highlight && " "}
<span className="text-muted-foreground">{children}</span>
</span>
</div>
)
}

View File

@ -0,0 +1,16 @@
export { SurrealButton } from "./surreal-button"
export {
SurrealCard,
SurrealCardHeader,
SurrealCardTitle,
SurrealCardDescription,
SurrealCardContent,
SurrealCardFooter,
} from "./surreal-card"
export { SurrealInput } from "./surreal-input"
export { SurrealBadge } from "./surreal-badge"
export { SurrealAvatar } from "./surreal-avatar"
export { SurrealToggle } from "./surreal-toggle"
export { SurrealProgress } from "./surreal-progress"
export { SurrealDivider } from "./surreal-divider"
export { SurrealSkeleton } from "./surreal-skeleton"

View File

@ -0,0 +1,34 @@
import { cn } from "@/lib/utils"
interface SurrealAvatarProps {
src?: string
alt?: string
fallback?: string
size?: "sm" | "md" | "lg"
className?: string
}
export function SurrealAvatar({ src, alt, fallback, size = "md", className }: SurrealAvatarProps) {
const sizeClasses = {
sm: "w-8 h-8 text-xs",
md: "w-10 h-10 text-sm",
lg: "w-14 h-14 text-base",
}
return (
<div
className={cn(
"relative overflow-hidden rounded-full bg-secondary flex items-center justify-center",
"ring-2 ring-primary/30 transition-all duration-200 hover:ring-primary/60",
sizeClasses[size],
className,
)}
>
{src ? (
<img src={src || "/placeholder.svg"} alt={alt || "Avatar"} className="w-full h-full object-cover" />
) : (
<span className="font-medium text-secondary-foreground uppercase">{fallback?.slice(0, 2) || "?"}</span>
)}
</div>
)
}

View File

@ -0,0 +1,26 @@
import { cn } from "@/lib/utils"
import type { ReactNode } from "react"
interface SurrealBadgeProps {
children: ReactNode
variant?: "default" | "primary" | "secondary" | "outline"
className?: string
}
export function SurrealBadge({ children, variant = "default", className }: SurrealBadgeProps) {
return (
<span
className={cn(
"inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-colors",
{
"bg-primary text-primary-foreground": variant === "default" || variant === "primary",
"bg-secondary text-secondary-foreground": variant === "secondary",
"border border-primary text-primary bg-transparent": variant === "outline",
},
className,
)}
>
{children}
</span>
)
}

View File

@ -0,0 +1,41 @@
import { cn } from "@/lib/utils"
import { forwardRef, type ButtonHTMLAttributes } from "react"
interface SurrealButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "ghost" | "outline"
size?: "sm" | "md" | "lg"
}
export const SurrealButton = forwardRef<HTMLButtonElement, SurrealButtonProps>(
({ className, variant = "primary", size = "md", children, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
"relative inline-flex items-center justify-center font-medium transition-all duration-300 ease-out",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background",
"disabled:pointer-events-none disabled:opacity-50",
"rounded-lg",
{
"bg-primary text-primary-foreground hover:brightness-110 hover:scale-[1.02] active:scale-[0.98] shadow-lg shadow-primary/30":
variant === "primary",
"bg-secondary text-secondary-foreground hover:bg-secondary/80 hover:scale-[1.02] active:scale-[0.98]":
variant === "secondary",
"bg-transparent text-foreground hover:bg-secondary/50": variant === "ghost",
"border-2 border-primary bg-transparent text-primary hover:bg-primary/10": variant === "outline",
},
{
"px-3 py-1.5 text-sm": size === "sm",
"px-5 py-2.5 text-base": size === "md",
"px-7 py-3.5 text-lg": size === "lg",
},
className,
)}
{...props}
>
{children}
</button>
)
},
)
SurrealButton.displayName = "SurrealButton"

View File

@ -0,0 +1,44 @@
import { cn } from "@/lib/utils"
import type { ReactNode } from "react"
interface SurrealCardProps {
children: ReactNode
className?: string
glow?: boolean
}
export function SurrealCard({ children, className, glow = false }: SurrealCardProps) {
return (
<div
className={cn(
"relative overflow-hidden rounded-xl bg-card p-6 border border-border",
"transition-all duration-300 ease-out",
"hover:border-primary/50 hover:translate-y-[-2px]",
glow && "shadow-lg shadow-primary/20",
className,
)}
>
{children}
</div>
)
}
export function SurrealCardHeader({ children, className }: { children: ReactNode; className?: string }) {
return <div className={cn("mb-4", className)}>{children}</div>
}
export function SurrealCardTitle({ children, className }: { children: ReactNode; className?: string }) {
return <h3 className={cn("text-xl font-semibold text-foreground", className)}>{children}</h3>
}
export function SurrealCardDescription({ children, className }: { children: ReactNode; className?: string }) {
return <p className={cn("text-sm text-muted-foreground mt-1", className)}>{children}</p>
}
export function SurrealCardContent({ children, className }: { children: ReactNode; className?: string }) {
return <div className={cn("text-foreground", className)}>{children}</div>
}
export function SurrealCardFooter({ children, className }: { children: ReactNode; className?: string }) {
return <div className={cn("mt-6 flex items-center gap-3", className)}>{children}</div>
}

View File

@ -0,0 +1,20 @@
import { cn } from "@/lib/utils"
interface SurrealDividerProps {
className?: string
label?: string
}
export function SurrealDivider({ className, label }: SurrealDividerProps) {
if (label) {
return (
<div className={cn("flex items-center gap-4", className)}>
<div className="flex-1 h-px bg-border" />
<span className="text-xs text-muted-foreground uppercase tracking-wider">{label}</span>
<div className="flex-1 h-px bg-border" />
</div>
)
}
return <div className={cn("h-px w-full bg-border", className)} />
}

View File

@ -0,0 +1,36 @@
import { cn } from "@/lib/utils"
import { forwardRef, type InputHTMLAttributes } from "react"
interface SurrealInputProps extends InputHTMLAttributes<HTMLInputElement> {
label?: string
error?: string
}
export const SurrealInput = forwardRef<HTMLInputElement, SurrealInputProps>(
({ className, label, error, id, ...props }, ref) => {
return (
<div className="flex flex-col gap-2">
{label && (
<label htmlFor={id} className="text-sm font-medium text-foreground">
{label}
</label>
)}
<input
ref={ref}
id={id}
className={cn(
"w-full rounded-lg bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground",
"border border-border transition-all duration-200",
"focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent",
"hover:border-primary/50",
error && "border-destructive focus:ring-destructive",
className,
)}
{...props}
/>
{error && <span className="text-sm text-destructive">{error}</span>}
</div>
)
},
)
SurrealInput.displayName = "SurrealInput"

View File

@ -0,0 +1,24 @@
import { cn } from "@/lib/utils"
interface SurrealProgressProps {
value: number
max?: number
className?: string
showLabel?: boolean
}
export function SurrealProgress({ value, max = 100, className, showLabel = false }: SurrealProgressProps) {
const percentage = Math.min(Math.max((value / max) * 100, 0), 100)
return (
<div className={cn("w-full", className)}>
<div className="relative h-2 w-full overflow-hidden rounded-full bg-secondary">
<div
className="h-full bg-primary transition-all duration-500 ease-out rounded-full"
style={{ width: `${percentage}%` }}
/>
</div>
{showLabel && <span className="mt-1 text-xs text-muted-foreground">{Math.round(percentage)}%</span>}
</div>
)
}

View File

@ -0,0 +1,22 @@
import { cn } from "@/lib/utils"
interface SurrealSkeletonProps {
className?: string
variant?: "text" | "circular" | "rectangular"
}
export function SurrealSkeleton({ className, variant = "rectangular" }: SurrealSkeletonProps) {
return (
<div
className={cn(
"animate-pulse bg-secondary/70",
{
"h-4 w-full rounded": variant === "text",
"rounded-full": variant === "circular",
"rounded-lg": variant === "rectangular",
},
className,
)}
/>
)
}

View File

@ -0,0 +1,42 @@
import { cn } from "@/lib/utils"
import { useState } from "react"
interface SurrealToggleProps {
defaultChecked?: boolean
onChange?: (checked: boolean) => void
label?: string
className?: string
}
export function SurrealToggle({ defaultChecked = false, onChange, label, className }: SurrealToggleProps) {
const [checked, setChecked] = useState(defaultChecked)
const handleToggle = () => {
const newValue = !checked
setChecked(newValue)
onChange?.(newValue)
}
return (
<label className={cn("inline-flex items-center gap-3 cursor-pointer", className)}>
<button
type="button"
role="switch"
aria-checked={checked}
onClick={handleToggle}
className={cn(
"relative w-12 h-6 rounded-full transition-all duration-300",
checked ? "bg-primary" : "bg-secondary",
)}
>
<span
className={cn(
"absolute top-1 w-4 h-4 rounded-full bg-foreground transition-all duration-300",
checked ? "left-7" : "left-1",
)}
/>
</button>
{label && <span className="text-sm text-foreground">{label}</span>}
</label>
)
}

View File

@ -0,0 +1,16 @@
---
import '../styles/global.css';
const { content } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>{content.title}</title>
</head>
<body>
<slot />
</body>
</html>

View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@ -0,0 +1,295 @@
---
import '../styles/global.css'
import { Button } from "@/components/ui/button"
import { SurrealButton } from "@/components/surreal/surreal-button"
import { SurrealCard, SurrealCardHeader, SurrealCardTitle, SurrealCardDescription, SurrealCardContent, SurrealCardFooter, } from "@/components/surreal/surreal-card"
import { SurrealInput } from "@/components/surreal/surreal-input"
import { SurrealBadge } from "@/components/surreal/surreal-badge"
import { SurrealAvatar } from "@/components/surreal/surreal-avatar"
import { SurrealToggle } from "@/components/surreal/surreal-toggle"
import { SurrealProgress } from "@/components/surreal/surreal-progress"
import { SurrealDivider } from "@/components/surreal/surreal-divider"
import { SurrealSkeleton } from "@/components/surreal/surreal-skeleton"
import { AsciiChart } from "@/components/AsciiChart"
import { PrefixItem } from "@/components/PrefixItem"
// Sample data for ASCII charts
const chartData1 = [3, 5, 8, 4, 9, 7, 6, 8, 5, 9, 4, 7, 8, 6, 9, 5, 7, 8, 4, 6]
const chartData2 = [2, 4, 6, 8, 7, 5, 9, 6, 4, 8, 7, 5, 3, 6, 8, 9, 7, 5, 4, 6]
const chartData3 = [5, 7, 4, 8, 6, 9, 5, 7, 8, 4, 6, 9, 7, 5, 8, 6, 4, 7, 9, 5]
---
<div class="min-h-screen font-mono">
<nav class="border-b border-border">
<div class="max-w-4xl mx-auto px-6 py-4 flex items-center justify-between">
<span class="font-bold tracking-tight">
surreal<span class="text-primary">ui</span>
</span>
<div class="flex items-center gap-6 text-sm text-muted-foreground">
<a href="#" class="hover:text-foreground transition-colors">
GitHub <span class="text-primary">[2k]</span>
</a>
<a href="#" class="hover:text-foreground transition-colors">
Docs
</a>
<a href="#" class="hover:text-foreground transition-colors">
Components
</a>
</div>
</div>
</nav>
<div class="max-w-4xl mx-auto px-6">
<section class="py-20">
<p class="text-xs text-muted-foreground mb-4">What's new in the latest release</p>
<h1 class="text-2xl font-bold mb-4 text-balance">The component library built for the surreal</h1>
<p class="text-sm text-muted-foreground max-w-md mb-8 leading-relaxed">
Surreal UI is fully customizable, giving you control and freedom to use any color, any style, and any
component.
</p>
<SurrealButton variant="outline" size="sm">
Read docs →
</SurrealButton>
<div class="mt-10 border border-border rounded-md overflow-hidden">
<div class="flex gap-4 px-4 py-2 border-b border-border text-xs">
<span class="text-foreground">npm</span>
<span class="text-muted-foreground">yarn</span>
<span class="text-muted-foreground">pnpm</span>
<span class="text-muted-foreground">bun</span>
</div>
<div class="px-4 py-3 bg-card/50">
<code class="text-sm">
npx surreal-ui@latest <span class="text-primary">init</span>
</code>
</div>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">What is Surreal UI?</p>
<p class="text-sm text-muted-foreground mb-8 max-w-lg leading-relaxed">
Surreal UI is a component library that helps you build beautiful interfaces with a unique coral and teal
aesthetic.
</p>
<div class="space-y-3">
<PrefixItem highlight="Buttons">Four variants with smooth hover transitions</PrefixItem>
<PrefixItem highlight="Cards">Bordered containers with optional glow effect</PrefixItem>
<PrefixItem highlight="Inputs">Form elements with floating labels and validation</PrefixItem>
<PrefixItem highlight="Badges">Status indicators in multiple styles</PrefixItem>
<PrefixItem highlight="Avatars">User representations with fallback initials</PrefixItem>
<PrefixItem highlight="Toggles">Accessible switches for boolean settings</PrefixItem>
<PrefixItem highlight="Progress">Visual feedback for loading states</PrefixItem>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">The open source component library</p>
<p class="text-sm text-muted-foreground mb-10 max-w-lg">
[+] With over <span class="text-primary font-medium">2,000</span> GitHub stars,{" "}
<span class="text-primary font-medium">50</span> contributors, and almost{" "}
<span class="text-primary font-medium">200</span> commits, Surreal UI is used and trusted by over{" "}
<span class="text-primary font-medium">10,000</span> developers every month.
</p>
<div class="grid grid-cols-3 gap-8">
<AsciiChart data={chartData1} label="Fig 1. 2K GitHub Stars" />
<AsciiChart data={chartData2} label="Fig 2. 50 Contributors" />
<AsciiChart data={chartData3} label="Fig 3. 10,000 Monthly devs" />
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">Buttons</p>
<div class="border border-border rounded-md p-8 mt-4">
<div class="flex flex-wrap gap-4 mb-8">
<SurrealButton variant="primary">primary</SurrealButton>
<SurrealButton variant="secondary">secondary</SurrealButton>
<SurrealButton variant="outline">outline</SurrealButton>
<SurrealButton variant="ghost">ghost</SurrealButton>
</div>
<SurrealDivider className="my-8" />
<p class="text-xs text-muted-foreground mb-4">[+] sizes</p>
<div class="flex flex-wrap items-center gap-4">
<SurrealButton size="sm">small</SurrealButton>
<SurrealButton size="md">medium</SurrealButton>
<SurrealButton size="lg">large</SurrealButton>
</div>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">Cards</p>
<div class="grid md:grid-cols-2 gap-6 mt-4">
<SurrealCard glow>
<SurrealCardHeader>
<SurrealCardTitle className="lowercase font-mono text-sm">with glow</SurrealCardTitle>
<SurrealCardDescription className="font-mono">subtle coral glow effect on hover</SurrealCardDescription>
</SurrealCardHeader>
<SurrealCardContent>
<p class="text-muted-foreground text-sm font-mono leading-relaxed">
[+] A luminous shadow that creates depth and draws attention to important content.
</p>
</SurrealCardContent>
<SurrealCardFooter>
<SurrealButton size="sm">explore</SurrealButton>
</SurrealCardFooter>
</SurrealCard>
<SurrealCard>
<SurrealCardHeader>
<div class="flex items-center justify-between">
<SurrealCardTitle className="lowercase font-mono text-sm">standard</SurrealCardTitle>
<SurrealBadge>new</SurrealBadge>
</div>
<SurrealCardDescription className="font-mono">clean minimal styling</SurrealCardDescription>
</SurrealCardHeader>
<SurrealCardContent>
<p class="text-muted-foreground text-sm font-mono leading-relaxed">
[+] The default card with hover interactions and smooth border transitions.
</p>
</SurrealCardContent>
<SurrealCardFooter>
<SurrealButton size="sm" variant="outline">
action
</SurrealButton>
</SurrealCardFooter>
</SurrealCard>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">Form elements</p>
<div class="border border-border rounded-md p-8 mt-4">
<div class="grid md:grid-cols-2 gap-6 mb-8">
<SurrealInput label="Email address" placeholder="you@example.com" type="email" />
<SurrealInput label="Password" placeholder="enter password" type="password" />
</div>
<SurrealDivider className="my-8" />
<p class="text-xs text-muted-foreground mb-4">[+] toggles</p>
<div class="flex flex-wrap gap-8">
<SurrealToggle label="notifications" defaultChecked />
<SurrealToggle label="dark mode" />
</div>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">Badges + Avatars</p>
<div class="grid md:grid-cols-2 gap-6 mt-4">
<div class="border border-border rounded-md p-6">
<p class="text-xs text-muted-foreground mb-4">[+] badge variants</p>
<div class="flex flex-wrap gap-3">
<SurrealBadge variant="primary">primary</SurrealBadge>
<SurrealBadge variant="secondary">secondary</SurrealBadge>
<SurrealBadge variant="outline">outline</SurrealBadge>
</div>
</div>
<div class="border border-border rounded-md p-6">
<p class="text-xs text-muted-foreground mb-4">[+] avatar sizes</p>
<div class="flex items-center gap-4">
<SurrealAvatar fallback="jd" size="sm" />
<SurrealAvatar fallback="ab" size="md" />
<SurrealAvatar fallback="xy" size="lg" />
</div>
</div>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-2">Loading states</p>
<div class="grid md:grid-cols-2 gap-6 mt-4">
<div class="border border-border rounded-md p-6">
<p class="text-xs text-muted-foreground mb-4">[+] progress bars</p>
<div class="space-y-4">
<SurrealProgress value={25} showLabel />
<SurrealProgress value={60} showLabel />
<SurrealProgress value={90} showLabel />
</div>
</div>
<div class="border border-border rounded-md p-6">
<p class="text-xs text-muted-foreground mb-4">[+] skeleton loaders</p>
<div class="space-y-4">
<div class="flex items-center gap-3">
<SurrealSkeleton variant="circular" className="w-10 h-10" />
<div class="flex-1 space-y-2">
<SurrealSkeleton variant="text" className="w-3/4" />
<SurrealSkeleton variant="text" className="w-1/2" />
</div>
</div>
<SurrealSkeleton variant="rectangular" className="h-16 w-full" />
</div>
</div>
</div>
</section>
<SurrealDivider />
<section class="py-16">
<p class="text-xs text-muted-foreground mb-6">FAQ</p>
<div class="space-y-2">
{[
"What is Surreal UI?",
"How do I install Surreal UI?",
"Do I need to configure Tailwind?",
"Can I customize the colors?",
"Is Surreal UI open source?",
].map((question, i) => (
<div key={i} class="flex items-center gap-2 py-2">
<span class="w-1 h-1 rounded-full bg-muted-foreground" />
<span class="text-sm font-medium">{question}</span>
</div>
))}
</div>
</section>
<SurrealDivider />
<footer class="py-16">
<p class="text-xs text-muted-foreground mb-2">Surreal UI will be available on npm soon</p>
<p class="text-sm text-muted-foreground mb-6">Join the waitlist for early access.</p>
<div class="flex gap-2 max-w-md">
<SurrealInput placeholder="Email address" className="flex-1" />
<SurrealButton variant="outline" size="sm">
Subscribe
</SurrealButton>
</div>
<SurrealDivider className="my-12" />
<div class="flex items-center justify-between text-sm text-muted-foreground">
<div class="flex gap-8">
<a href="#" class="hover:text-foreground transition-colors">
GitHub <span class="text-primary">[2K]</span>
</a>
<a href="#" class="hover:text-foreground transition-colors">
Docs
</a>
<a href="#" class="hover:text-foreground transition-colors">
Discord
</a>
<a href="#" class="hover:text-foreground transition-colors">
X
</a>
</div>
</div>
<p class="text-xs text-muted-foreground mt-8">2025 Surreal UI • Brand</p>
</footer>
</div>
</div>

View File

@ -0,0 +1,128 @@
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
/* Retro Surrealist Theme - Inspired by coral sun & teal mountains */
:root {
--background: oklch(0.25 0.04 195);
/* Deep teal */
--foreground: oklch(0.95 0.01 195);
/* Light teal-white */
--card: oklch(0.2 0.04 195);
/* Darker teal */
--card-foreground: oklch(0.95 0.01 195);
--popover: oklch(0.18 0.04 195);
--popover-foreground: oklch(0.95 0.01 195);
--primary: oklch(0.65 0.2 25);
/* Coral red */
--primary-foreground: oklch(0.15 0.03 195);
--secondary: oklch(0.3 0.05 195);
/* Mid teal */
--secondary-foreground: oklch(0.95 0.01 195);
--muted: oklch(0.22 0.03 195);
--muted-foreground: oklch(0.65 0.03 195);
--accent: oklch(0.6 0.18 25);
/* Coral accent */
--accent-foreground: oklch(0.15 0.03 195);
--destructive: oklch(0.55 0.22 25);
--destructive-foreground: oklch(0.95 0.01 195);
--border: oklch(0.35 0.05 195);
--input: oklch(0.28 0.04 195);
--ring: oklch(0.65 0.2 25);
--chart-1: oklch(0.65 0.2 25);
--chart-2: oklch(0.5 0.08 195);
--chart-3: oklch(0.4 0.06 195);
--chart-4: oklch(0.7 0.15 30);
--chart-5: oklch(0.55 0.12 20);
--radius: 0.5rem;
--sidebar: oklch(0.18 0.04 195);
--sidebar-foreground: oklch(0.95 0.01 195);
--sidebar-primary: oklch(0.65 0.2 25);
--sidebar-primary-foreground: oklch(0.15 0.03 195);
--sidebar-accent: oklch(0.3 0.05 195);
--sidebar-accent-foreground: oklch(0.95 0.01 195);
--sidebar-border: oklch(0.35 0.05 195);
--sidebar-ring: oklch(0.65 0.2 25);
}
.dark {
--background: oklch(0.15 0.04 195);
--foreground: oklch(0.95 0.01 195);
--card: oklch(0.12 0.04 195);
--card-foreground: oklch(0.95 0.01 195);
--popover: oklch(0.1 0.04 195);
--popover-foreground: oklch(0.95 0.01 195);
--primary: oklch(0.65 0.2 25);
--primary-foreground: oklch(0.1 0.03 195);
--secondary: oklch(0.25 0.05 195);
--secondary-foreground: oklch(0.95 0.01 195);
--muted: oklch(0.18 0.03 195);
--muted-foreground: oklch(0.6 0.03 195);
--accent: oklch(0.6 0.18 25);
--accent-foreground: oklch(0.1 0.03 195);
--destructive: oklch(0.55 0.22 25);
--destructive-foreground: oklch(0.95 0.01 195);
--border: oklch(0.3 0.05 195);
--input: oklch(0.22 0.04 195);
--ring: oklch(0.65 0.2 25);
--sidebar: oklch(0.12 0.04 195);
--sidebar-foreground: oklch(0.95 0.01 195);
--sidebar-primary: oklch(0.65 0.2 25);
--sidebar-primary-foreground: oklch(0.1 0.03 195);
--sidebar-accent: oklch(0.25 0.05 195);
--sidebar-accent-foreground: oklch(0.95 0.01 195);
--sidebar-border: oklch(0.3 0.05 195);
--sidebar-ring: oklch(0.65 0.2 25);
}
@theme inline {
--font-sans: "Geist", "Geist Fallback";
--font-mono: "Geist Mono", "Geist Mono Fallback";
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

View File

@ -0,0 +1,20 @@
{
"extends": "astro/tsconfigs/strict",
"include": [
".astro/types.d.ts",
"**/*"
],
"exclude": [
"dist"
],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react",
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}