Home






Hostinger Horizons



«name»: «web-app»,
«type»: «module»,
«version»: «0.0.0»,
«private»: true,
«scripts»: {
«dev»: «vite»,
«build»: «node tools/generate-llms.js || true && vite build»,
«preview»: «vite preview»
},
«dependencies»: {
«@radix-ui/react-alert-dialog»: «^1.0.5»,
«@radix-ui/react-avatar»: «^1.0.3»,
«@radix-ui/react-checkbox»: «^1.0.4»,
«@radix-ui/react-dialog»: «^1.0.5»,
«@radix-ui/react-dropdown-menu»: «^2.0.5»,
«@radix-ui/react-label»: «^2.0.2»,
«@radix-ui/react-slot»: «^1.0.2»,
«@radix-ui/react-tabs»: «^1.0.4»,
«@radix-ui/react-toast»: «^1.1.5»,
«@radix-ui/react-slider»: «^1.1.2»,
«class-variance-authority»: «^0.7.0»,
«clsx»: «^2.0.0»,
«framer-motion»: «^10.16.4»,
«lucide-react»: «^0.285.0»,
«react»: «^18.2.0»,
«react-dom»: «^18.2.0»,
«react-helmet»: «^6.1.0»,
«tailwind-merge»: «^1.14.0»,
«tailwindcss-animate»: «^1.0.7»
},
«devDependencies»: {
«@babel/generator»: «^7.27.0»,
«@babel/parser»: «^7.27.0»,
«@babel/traverse»: «^7.27.0»,
«@babel/types»: «^7.27.0»,
«@types/node»: «^20.8.3»,
«@types/react»: «^18.2.15»,
«@types/react-dom»: «^18.2.7»,
«@vitejs/plugin-react»: «^4.0.3»,
«autoprefixer»: «^10.4.16»,
«postcss»: «^8.4.31»,
«tailwindcss»: «^3.3.3»,
«vite»: «^4.4.5»,
«terser»: «^5.39.0»,
«eslint»: «^8.57.1»,
«eslint-config-react-app»: «^7.0.1″
}
}
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
import React, { useState, useEffect } from ‘react’;
import { Helmet } from ‘react-helmet’;
import { Toaster } from ‘@/components/ui/toaster’;

import Navbar from ‘@/components/Navbar’;
import HeroSection from ‘@/components/HeroSection’;
import StoryGrid from ‘@/components/StoryGrid’;
import StatsSection from ‘@/components/StatsSection’;
import Footer from ‘@/components/Footer’;

import { categories } from ‘@/data/categories’;
import { stories } from ‘@/data/stories’;

function App() {
const [selectedCategory, setSelectedCategory] = useState(‘all’);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState(»);

const filteredStories = stories.filter(story => {
const matchesCategory = selectedCategory === ‘all’ || story.category === selectedCategory;
const matchesSearch = story.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
story.excerpt.toLowerCase().includes(searchTerm.toLowerCase());
return matchesCategory && matchesSearch;
});

const handleCategoryFilter = (categoryId) => {
setSelectedCategory(categoryId);
setIsMenuOpen(false);
};

const toggleMenu = () => {
setIsMenuOpen(!isMenuOpen);
};

const handleSearchChange = (e) => {
setSearchTerm(e.target.value);
};

return (
<>

Horror Tales – Scary Stories and Urban Legends




);
}

export default App;
import React from ‘react’;
import { Skull } from ‘lucide-react’;
const Footer = () => {
return


Scary Legends

Immerse yourself in the most chilling stories and urban legends that have terrified
generations. Each tale has been carefully selected to offer you
the most authentic horror experience.

© 2024 Horror Tales

Chilling Stories

Urban Legends

;
};
export default Footer;
import React from ‘react’;
import { motion } from ‘framer-motion’;
import { Search, Ghost, Eye, Moon } from ‘lucide-react’;
const HeroSection = ({
searchTerm,
onSearchChange
}) => {
return

Scary Legends

Dive into the world of the most chilling stories,
mysterious urban legends, and paranormal tales that defy reality

{/* Search Bar */}



Urban Legends

Paranormal Experiences

Classic Horror

;
};
export default HeroSection;
import React from ‘react’;
import { motion, AnimatePresence } from ‘framer-motion’;
import { Skull, Menu, X } from ‘lucide-react’;
import { categories } from ‘@/data/categories’;
const Navbar = ({
selectedCategory,
onCategoryFilter,
isMenuOpen,
toggleMenu
}) => {
return

;
};
export default Navbar;
import React from ‘react’;
import { motion } from ‘framer-motion’;
import { BookOpen, Users, Ghost, Star } from ‘lucide-react’;

const statsData = [
{ number: «150+», label: «Horror Stories», icon: BookOpen },
{ number: «50K+», label: «Brave Readers», icon: Users },
{ number: «25+», label: «Urban Legends», icon: Ghost },
{ number: «4.8», label: «Average Rating», icon: Star }
];

const StatsSection = () => {
return (

{statsData.map((stat, index) => {
const Icon = stat.icon;
return (

{stat.number}
{stat.label}


);
})}

);
};

export default StatsSection;
import React, { useEffect } from ‘react’;
import { motion } from ‘framer-motion’;
import { Star, Ghost } from ‘lucide-react’;
import { toast } from ‘@/components/ui/use-toast’;
import { categories } from ‘@/data/categories’;

const StoryGrid = ({ filteredStories, selectedCategory }) => {
useEffect(() => {
const observerOptions = {
threshold: 0.1,
rootMargin: ‘0px 0px -50px 0px’
};

const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add(‘visible’);
}
});
}, observerOptions);

const elements = document.querySelectorAll(‘.scroll-fade’);
elements.forEach(el => observer.observe(el));

return () => observer.disconnect();
}, []);

const handleStoryClick = (story) => {
toast({
title: «🚧 Feature Not Implemented Yet»,
description: «This chilling story will be available soon! You can request it in your next prompt! 🚀»,
duration: 4000,
});
};

return (

Chilling Stories

{filteredStories.length} stories found
{selectedCategory !== ‘all’ && (

in {categories.find(c => c.id === selectedCategory)?.name}

)}

{filteredStories.map((story, index) => (

handleStoryClick(story)}
>

{`Image


{categories.find(c => c.id === story.category)?.name}

{story.rating}

{story.readTime}

{story.title}

{story.excerpt}


))}

{filteredStories.length === 0 && (

No stories found

Try different search terms or select a different category


)}

);
};

export default StoryGrid;
import { cn } from ‘@/lib/utils’;
import { Slot } from ‘@radix-ui/react-slot’;
import { cva } from ‘class-variance-authority’;
import React from ‘react’;

const buttonVariants = cva(
‘inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50’,
{
variants: {
variant: {
default: ‘bg-primary text-primary-foreground hover:bg-primary/90’,
destructive:
‘bg-destructive text-destructive-foreground hover:bg-destructive/90’,
outline:
‘border border-input bg-background hover:bg-accent hover:text-accent-foreground’,
secondary:
‘bg-secondary text-secondary-foreground hover:bg-secondary/80’,
ghost: ‘hover:bg-accent hover:text-accent-foreground’,
link: ‘text-primary underline-offset-4 hover:underline’,
},
size: {
default: ‘h-10 px-4 py-2’,
sm: ‘h-9 rounded-md px-3’,
lg: ‘h-11 rounded-md px-8’,
icon: ‘h-10 w-10’,
},
},
defaultVariants: {
variant: ‘default’,
size: ‘default’,
},
},
);

const Button = React.forwardRef(({ className, variant, size, asChild = false, …props }, ref) => {
const Comp = asChild ? Slot : ‘button’;
return (

);
});
Button.displayName = ‘Button’;

export { Button, buttonVariants };
import { cn } from ‘@/lib/utils’;
import * as ToastPrimitives from ‘@radix-ui/react-toast’;
import { cva } from ‘class-variance-authority’;
import { X } from ‘lucide-react’;
import React from ‘react’;

const ToastProvider = ToastPrimitives.Provider;

const ToastViewport = React.forwardRef(({ className, …props }, ref) => (

));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;

const toastVariants = cva(
‘data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(–radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(–radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full’,
{
variants: {
variant: {
default: ‘bg-background border’,
destructive:
‘group destructive border-destructive bg-destructive text-destructive-foreground’,
},
},
defaultVariants: {
variant: ‘default’,
},
},
);

const Toast = React.forwardRef(({ className, variant, …props }, ref) => {
return (

);
});
Toast.displayName = ToastPrimitives.Root.displayName;

const ToastAction = React.forwardRef(({ className, …props }, ref) => (

));
ToastAction.displayName = ToastPrimitives.Action.displayName;

const ToastClose = React.forwardRef(({ className, …props }, ref) => (



));
ToastClose.displayName = ToastPrimitives.Close.displayName;

const ToastTitle = React.forwardRef(({ className, …props }, ref) => (

));
ToastTitle.displayName = ToastPrimitives.Title.displayName;

const ToastDescription = React.forwardRef(({ className, …props }, ref) => (

));
ToastDescription.displayName = ToastPrimitives.Description.displayName;

export {
Toast,
ToastAction,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
};
import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from ‘@/components/ui/toast’;
import { useToast } from ‘@/components/ui/use-toast’;
import React from ‘react’;

export function Toaster() {
const { toasts } = useToast();

return (

{toasts.map(({ id, title, description, action, …props }) => {
return (

{title && {title}}
{description && (
{description}
)}

{action}


);
})}


);
}
import { useState, useEffect } from «react»

const TOAST_LIMIT = 1

let count = 0
function generateId() {
count = (count + 1) % Number.MAX_VALUE
return count.toString()
}

const toastStore = {
state: {
toasts: [],
},
listeners: [],

getState: () => toastStore.state,

setState: (nextState) => {
if (typeof nextState === ‘function’) {
toastStore.state = nextState(toastStore.state)
} else {
toastStore.state = { …toastStore.state, …nextState }
}

toastStore.listeners.forEach(listener => listener(toastStore.state))
},

subscribe: (listener) => {
toastStore.listeners.push(listener)
return () => {
toastStore.listeners = toastStore.listeners.filter(l => l !== listener)
}
}
}

export const toast = ({ …props }) => {
const id = generateId()

const update = (props) =>
toastStore.setState((state) => ({
…state,
toasts: state.toasts.map((t) =>
t.id === id ? { …t, …props } : t
),
}))

const dismiss = () => toastStore.setState((state) => ({
…state,
toasts: state.toasts.filter((t) => t.id !== id),
}))

toastStore.setState((state) => ({
…state,
toasts: [
{ …props, id, dismiss },
…state.toasts,
].slice(0, TOAST_LIMIT),
}))

return {
id,
dismiss,
update,
}
}

export function useToast() {
const [state, setState] = useState(toastStore.getState())

useEffect(() => {
const unsubscribe = toastStore.subscribe((state) => {
setState(state)
})

return unsubscribe
}, [])

useEffect(() => {
const timeouts = []

state.toasts.forEach((toast) => {
if (toast.duration === Infinity) {
return
}

const timeout = setTimeout(() => {
toast.dismiss()
}, toast.duration || 5000)

timeouts.push(timeout)
})

return () => {
timeouts.forEach((timeout) => clearTimeout(timeout))
}
}, [state.toasts])

return {
toast,
toasts: state.toasts,
}
}
import { Ghost, Skull, Eye, Moon, BookOpen } from ‘lucide-react’;

export const categories = [
{ id: ‘all’, name: ‘All Stories’, icon: BookOpen },
{ id: ‘urban-legends’, name: ‘Urban Legends’, icon: Ghost },
{ id: ‘paranormal’, name: ‘Paranormal’, icon: Eye },
{ id: ‘mysteries’, name: ‘Mysteries’, icon: Skull },
{ id: ‘classic-horror’, name: ‘Classic Horror’, icon: Moon }
];
export const stories = [
{
id: 1,
title: «The Lady in White»,
category: «urban-legends»,
excerpt: «An ethereal figure appearing on lonely roads, seeking revenge for her tragic death…»,
readTime: «5 min»,
rating: 4.8,
image: «Ghostly woman in a white dress walking on a dark road»
},
{
id: 2,
title: «The Cursed Mirror»,
category: «paranormal»,
excerpt: «An ancient mirror that reflects more than it should, showing visions from the other side…»,
readTime: «7 min»,
rating: 4.9,
image: «Antique mirror with an ornate frame in a dark room»
},
{
id: 3,
title: «The House on the Hill»,
category: «classic-horror»,
excerpt: «An abandoned mansion where the screams of the past still echo within its walls…»,
readTime: «12 min»,
rating: 4.7,
image: «Abandoned Victorian house on a foggy hill»
},
{
id: 4,
title: «The Elevator Boy»,
category: «urban-legends»,
excerpt: «On the non-existent 13th floor, a boy eternally waits for someone to press the button…»,
readTime: «6 min»,
rating: 4.6,
image: «Vintage elevator with illuminated buttons in a dark building»
},
{
id: 5,
title: «Voices on the Radio»,
category: «mysteries»,
excerpt: «Mysterious transmissions appearing on dead frequencies, with messages from the other world…»,
readTime: «8 min»,
rating: 4.5,
image: «Old radio tuning in a gloomy room»
},
{
id: 6,
title: «The Crying Doll»,
category: «paranormal»,
excerpt: «A porcelain doll that sheds tears of blood every midnight…»,
readTime: «4 min»,
rating: 4.8,
image: «Antique porcelain doll with red tears»
}
];
@tailwind base;
@tailwind components;
@tailwind utilities;

@import url(‘https://fonts.googleapis.com/css2?family=Frijole&family=Creepster&family=Inter:wght@300;400;500;600;700&display=swap’);

:root {
–blood-red: #8B0000;
–dark-red: #4A0000;
–ghost-white: #F8F8FF;
–shadow-gray: #2D2D2D;
–midnight-black: #0A0A0A;
–eerie-purple: #4B0082;
–fog-gray: #696969;
}

body {
@apply bg-gradient-to-br from-gray-900 via-black to-gray-800;
font-family: ‘Inter’, sans-serif;
color: var(–ghost-white);
overflow-x: hidden;
}

.creepy-font {
font-family: ‘Frijole’, cursive;
}

.horror-font {
font-family: ‘Creepster’, cursive;
}

.blood-drip {
background: linear-gradient(180deg, transparent 0%, var(–blood-red) 100%);
}

.fog-effect {
background: radial-gradient(ellipse at center, rgba(255,255,255,0.1) 0%, transparent 70%);
}

.shadow-text {
text-shadow: 2px 2px 4px rgba(0,0,0,0.8), 0 0 10px rgba(139,0,0,0.3);
}

.glow-red {
box-shadow: 0 0 20px rgba(139,0,0,0.5), inset 0 0 20px rgba(139,0,0,0.1);
}

.card-hover {
transition: all 0.3s ease;
}

.card-hover:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 10px 30px rgba(139,0,0,0.3);
}

.floating {
animation: float 6s ease-in-out infinite;
}

@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
}

.pulse-red {
animation: pulseRed 2s infinite;
}

@keyframes pulseRed {
0%, 100% { box-shadow: 0 0 5px rgba(139,0,0,0.5); }
50% { box-shadow: 0 0 20px rgba(139,0,0,0.8), 0 0 30px rgba(139,0,0,0.6); }
}

.flicker {
animation: flicker 3s infinite;
}

@keyframes flicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.8; }
75% { opacity: 0.9; }
}

.scroll-fade {
opacity: 0;
transform: translateY(30px);
transition: all 0.6s ease;
}

.scroll-fade.visible {
opacity: 1;
transform: translateY(0);
}
import { clsx } from ‘clsx’;
import { twMerge } from ‘tailwind-merge’;

export function cn(…inputs) {
return twMerge(clsx(inputs));
}
import React from ‘react’;
import ReactDOM from ‘react-dom/client’;
import App from ‘@/App’;
import ‘@/index.css’;

ReactDOM.createRoot(document.getElementById(‘root’)).render(



);
/** @type {import(‘tailwindcss’).Config} */
module.exports = {
darkMode: [‘class’],
content: [
‘./pages/**/*.{js,jsx}’,
‘./components/**/*.{js,jsx}’,
‘./app/**/*.{js,jsx}’,
‘./src/**/*.{js,jsx}’,
],
theme: {
container: {
center: true,
padding: ‘2rem’,
screens: {
‘2xl’: ‘1400px’,
},
},
extend: {
colors: {
border: ‘hsl(var(–border))’,
input: ‘hsl(var(–input))’,
ring: ‘hsl(var(–ring))’,
background: ‘hsl(var(–background))’,
foreground: ‘hsl(var(–foreground))’,
primary: {
DEFAULT: ‘hsl(var(–primary))’,
foreground: ‘hsl(var(–primary-foreground))’,
},
secondary: {
DEFAULT: ‘hsl(var(–secondary))’,
foreground: ‘hsl(var(–secondary-foreground))’,
},
destructive: {
DEFAULT: ‘hsl(var(–destructive))’,
foreground: ‘hsl(var(–destructive-foreground))’,
},
muted: {
DEFAULT: ‘hsl(var(–muted))’,
foreground: ‘hsl(var(–muted-foreground))’,
},
accent: {
DEFAULT: ‘hsl(var(–accent))’,
foreground: ‘hsl(var(–accent-foreground))’,
},
popover: {
DEFAULT: ‘hsl(var(–popover))’,
foreground: ‘hsl(var(–popover-foreground))’,
},
card: {
DEFAULT: ‘hsl(var(–card))’,
foreground: ‘hsl(var(–card-foreground))’,
},
},
borderRadius: {
lg: ‘var(–radius)’,
md: ‘calc(var(–radius) – 2px)’,
sm: ‘calc(var(–radius) – 4px)’,
},
keyframes: {
‘accordion-down’: {
from: { height: 0 },
to: { height: ‘var(–radix-accordion-content-height)’ },
},
‘accordion-up’: {
from: { height: ‘var(–radix-accordion-content-height)’ },
to: { height: 0 },
},
},
animation: {
‘accordion-down’: ‘accordion-down 0.2s ease-out’,
‘accordion-up’: ‘accordion-up 0.2s ease-out’,
},
},
},
plugins: [require(‘tailwindcss-animate’)],
};