├── .eslintrc.json ├── bun.lockb ├── postcss.config.js ├── lib ├── utils.ts └── themes.ts ├── next.config.js ├── hooks └── use-theme.ts ├── app ├── components │ ├── layout.tsx │ ├── checkbox │ │ └── page.tsx │ ├── button │ │ └── page.tsx │ ├── select │ │ └── page.tsx │ ├── theme │ │ └── page.tsx │ ├── switch │ │ └── page.tsx │ ├── input │ │ └── page.tsx │ ├── radio │ │ └── page.tsx │ ├── toggle │ │ └── page.tsx │ ├── colorpicker │ │ └── page.tsx │ └── datepicker │ │ └── page.tsx ├── layout.tsx ├── globals.css ├── page.tsx └── manifest │ └── page.tsx ├── components ├── layouts │ └── components-layout.tsx ├── ui │ ├── aspect-ratio.tsx │ ├── toggle.tsx │ ├── radio-group.tsx │ ├── container.tsx │ ├── space.tsx │ ├── textarea.tsx │ ├── breadcrumb.tsx │ ├── skeleton.tsx │ ├── grid.tsx │ ├── checkbox.tsx │ ├── radio.tsx │ ├── spinner.tsx │ ├── switch.tsx │ ├── stack.tsx │ ├── divider.tsx │ ├── toast-provider.tsx │ ├── badge.tsx │ ├── input.tsx │ ├── button.tsx │ ├── alert.tsx │ ├── stats.tsx │ ├── tree.tsx │ ├── form.tsx │ ├── progress.tsx │ ├── modal.tsx │ ├── stepper.tsx │ ├── timeline.tsx │ ├── list.tsx │ ├── toast.tsx │ ├── pagination.tsx │ ├── select.tsx │ ├── color-picker.tsx │ ├── calendar.tsx │ ├── upload.tsx │ ├── tabs.tsx │ ├── table.tsx │ └── date-picker.tsx ├── feature-card.tsx ├── background-pattern.tsx ├── code-block.tsx ├── theme-provider.tsx └── sidebar.tsx ├── components.json ├── .gitignore ├── tsconfig.json ├── package.json └── tailwind.config.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddoemonn/parrot_ui/HEAD/bun.lockb -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: 'export', 4 | eslint: { 5 | ignoreDuringBuilds: true, 6 | }, 7 | images: { unoptimized: true }, 8 | }; 9 | 10 | module.exports = nextConfig; 11 | -------------------------------------------------------------------------------- /hooks/use-theme.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { ThemeContext } from '@/components/theme-provider'; 3 | 4 | export function useTheme() { 5 | const context = useContext(ThemeContext); 6 | if (context === undefined) { 7 | throw new Error('useTheme must be used within a ThemeProvider'); 8 | } 9 | return context; 10 | } -------------------------------------------------------------------------------- /app/components/layout.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Sidebar } from '@/components/sidebar'; 4 | 5 | export default function ComponentsLayout({ 6 | children, 7 | }: { 8 | children: React.ReactNode; 9 | }) { 10 | return ( 11 |
12 | 13 |
14 | {children} 15 |
16 |
17 | ); 18 | } -------------------------------------------------------------------------------- /components/layouts/components-layout.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Sidebar } from '@/components/sidebar'; 4 | 5 | export function ComponentsLayout({ 6 | children, 7 | }: { 8 | children: React.ReactNode; 9 | }) { 10 | return ( 11 |
12 | 13 |
14 | {children} 15 |
16 |
17 | ); 18 | } -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { cn } from '@/lib/utils'; 5 | import { useTheme } from '@/hooks/use-theme'; 6 | 7 | interface AspectRatioProps { 8 | ratio?: number; 9 | children: React.ReactNode; 10 | className?: string; 11 | } 12 | 13 | export function AspectRatio({ 14 | ratio = 16/9, 15 | children, 16 | className 17 | }: AspectRatioProps) { 18 | const { theme } = useTheme(); 19 | 20 | return ( 21 |
30 |
31 | {children} 32 |
33 |
34 | ); 35 | } -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './globals.css'; 2 | import type { Metadata } from 'next'; 3 | import { Inter } from 'next/font/google'; 4 | import { ThemeProvider } from '@/components/theme-provider'; 5 | import { ToastProvider } from '@/components/ui/toast-provider'; 6 | 7 | const inter = Inter({ subsets: ['latin'] }); 8 | 9 | export const metadata: Metadata = { 10 | title: 'ParrotUI - Modern React Component Library', 11 | description: 'A beautiful and feature-rich React component library', 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: { 17 | children: React.ReactNode; 18 | }) { 19 | return ( 20 | 21 | 22 | 23 | 24 | {children} 25 | 26 | 27 | 28 | 29 | ); 30 | } -------------------------------------------------------------------------------- /components/feature-card.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { motion } from 'framer-motion'; 4 | import { ReactNode } from 'react'; 5 | 6 | interface FeatureCardProps { 7 | icon: ReactNode; 8 | title: string; 9 | description: string; 10 | } 11 | 12 | export function FeatureCard({ icon, title, description }: FeatureCardProps) { 13 | return ( 14 | 18 |
19 |
20 | {icon} 21 |
22 |
23 |

{title}

24 |

{description}

25 |
26 |
27 |
28 | ); 29 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@next/swc-wasm-nodejs": "13.5.1", 13 | "autoprefixer": "^10.4.17", 14 | "class-variance-authority": "^0.7.0", 15 | "clsx": "^2.1.1", 16 | "framer-motion": "^10.18.0", 17 | "lucide-react": "^0.344.0", 18 | "next": "13.5.1", 19 | "next-themes": "^0.3.0", 20 | "postcss": "^8.4.35", 21 | "react": "18.2.0", 22 | "react-dom": "18.2.0", 23 | "react-syntax-highlighter": "^15.5.0", 24 | "tailwind-merge": "^2.2.1", 25 | "tailwindcss": "3.3.3", 26 | "tailwindcss-animate": "^1.0.7" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "22.9.1", 30 | "@types/react": "18.3.12", 31 | "@types/react-syntax-highlighter": "^15.5.11", 32 | "typescript": "5.6.3" 33 | } 34 | } -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | const config: Config = { 4 | darkMode: ['class'], 5 | content: [ 6 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 7 | './components/**/*.{js,ts,jsx,tsx,mdx}', 8 | './app/**/*.{js,ts,jsx,tsx,mdx}', 9 | ], 10 | theme: { 11 | extend: { 12 | colors: { 13 | primary: 'var(--primary)', 14 | secondary: 'var(--secondary)', 15 | accent: 'var(--accent)', 16 | background: 'var(--background)', 17 | text: 'var(--text)', 18 | muted: 'var(--muted)', 19 | border: 'var(--border)', 20 | error: 'var(--error)', 21 | success: 'var(--success)', 22 | warning: 'var(--warning)', 23 | info: 'var(--info)', 24 | }, 25 | borderRadius: { 26 | lg: 'var(--radius)', 27 | md: 'calc(var(--radius) - 2px)', 28 | sm: 'calc(var(--radius) - 4px)', 29 | }, 30 | }, 31 | }, 32 | plugins: [require('tailwindcss-animate')], 33 | }; 34 | 35 | export default config; -------------------------------------------------------------------------------- /components/ui/toggle.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { motion } from 'framer-motion'; 5 | import { cn } from '@/lib/utils'; 6 | 7 | interface ToggleProps { 8 | pressed?: boolean; 9 | onPressedChange?: (pressed: boolean) => void; 10 | disabled?: boolean; 11 | className?: string; 12 | children?: React.ReactNode; 13 | } 14 | 15 | export function Toggle({ 16 | pressed = false, 17 | onPressedChange, 18 | disabled = false, 19 | className, 20 | children, 21 | }: ToggleProps) { 22 | return ( 23 | !disabled && onPressedChange?.(!pressed)} 26 | className={cn( 27 | "inline-flex items-center justify-center rounded-xl px-4 py-2", 28 | "transition-all duration-200 text-sm font-medium", 29 | "bg-black/20 backdrop-blur-xl border border-white/20", 30 | pressed && "bg-gradient-to-r from-[#FF6B6B] to-[#FF8E53] border-transparent", 31 | disabled && "opacity-50 cursor-not-allowed", 32 | !disabled && "hover:bg-white/5", 33 | className 34 | )} 35 | > 36 | {children} 37 | 38 | ); 39 | } -------------------------------------------------------------------------------- /components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Radio } from './radio'; 5 | import { cn } from '@/lib/utils'; 6 | 7 | interface RadioOption { 8 | label: string; 9 | value: string; 10 | disabled?: boolean; 11 | } 12 | 13 | interface RadioGroupProps { 14 | options: RadioOption[]; 15 | value?: string; 16 | onChange?: (value: string) => void; 17 | name: string; 18 | className?: string; 19 | orientation?: 'horizontal' | 'vertical'; 20 | } 21 | 22 | export function RadioGroup({ 23 | options, 24 | value, 25 | onChange, 26 | name, 27 | className, 28 | orientation = 'vertical' 29 | }: RadioGroupProps) { 30 | return ( 31 |
36 | {options.map((option) => ( 37 | onChange?.(option.value)} 44 | disabled={option.disabled} 45 | /> 46 | ))} 47 |
48 | ); 49 | } -------------------------------------------------------------------------------- /components/background-pattern.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { motion } from 'framer-motion'; 4 | 5 | export function BackgroundPattern() { 6 | return ( 7 |
8 | 25 | 42 |
43 | ); 44 | } -------------------------------------------------------------------------------- /components/code-block.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; 4 | import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; 5 | 6 | interface CodeBlockProps { 7 | code: string; 8 | language: string; 9 | } 10 | 11 | export function CodeBlock({ code, language }: CodeBlockProps) { 12 | return ( 13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 33 | {code.trim()} 34 | 35 |
36 |
37 | ); 38 | } -------------------------------------------------------------------------------- /components/ui/container.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { motion } from 'framer-motion'; 5 | import { useTheme } from '@/hooks/use-theme'; 6 | import { cn } from '@/lib/utils'; 7 | 8 | interface ContainerProps extends React.HTMLAttributes { 9 | size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'; 10 | padding?: 'none' | 'sm' | 'md' | 'lg'; 11 | center?: boolean; 12 | as?: React.ElementType; 13 | } 14 | 15 | export function Container({ 16 | children, 17 | className, 18 | size = 'lg', 19 | padding = 'md', 20 | center = true, 21 | as: Component = 'div', 22 | ...props 23 | }: ContainerProps) { 24 | const { theme } = useTheme(); 25 | 26 | const sizeClasses = { 27 | sm: 'max-w-2xl', 28 | md: 'max-w-4xl', 29 | lg: 'max-w-6xl', 30 | xl: 'max-w-7xl', 31 | full: 'max-w-full' 32 | }; 33 | 34 | const paddingClasses = { 35 | none: 'px-0', 36 | sm: 'px-4', 37 | md: 'px-6', 38 | lg: 'px-8' 39 | }; 40 | 41 | return ( 42 | 56 | {children} 57 | 58 | ); 59 | } -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --primary: #FF6B6B; 7 | --secondary: #4ECDC4; 8 | --accent: #45B7D1; 9 | --background: #0A0A0A; 10 | --text: #FFFFFF; 11 | --muted: #666666; 12 | --border: #2A2A2A; 13 | --error: #FF4D4D; 14 | --success: #4CAF50; 15 | --warning: #FFA726; 16 | --info: #29B6F6; 17 | --radius: 1rem; 18 | } 19 | 20 | body { 21 | background-color: var(--background); 22 | color: var(--text); 23 | } 24 | 25 | /* Hide scrollbar for Chrome, Safari and Opera */ 26 | ::-webkit-scrollbar { 27 | display: none; 28 | } 29 | 30 | /* Hide scrollbar for IE, Edge and Firefox */ 31 | * { 32 | -ms-overflow-style: none; /* IE and Edge */ 33 | scrollbar-width: none; /* Firefox */ 34 | } 35 | 36 | /* Ensure content is still scrollable */ 37 | html, body { 38 | overflow-y: auto; 39 | overflow-x: hidden; 40 | } 41 | 42 | /* Add smooth scrolling for a better user experience */ 43 | html { 44 | scroll-behavior: smooth; 45 | } 46 | 47 | 48 | @keyframes progress-indeterminate { 49 | 0% { 50 | transform: translateX(-100%); 51 | } 52 | 100% { 53 | transform: translateX(100%); 54 | } 55 | } 56 | 57 | @keyframes progress-stripes { 58 | 0% { 59 | background-position: 1rem 0; 60 | } 61 | 100% { 62 | background-position: 0 0; 63 | } 64 | } 65 | 66 | .animate-progress-indeterminate { 67 | animation: progress-indeterminate 1.5s infinite linear; 68 | } 69 | 70 | .animate-progress { 71 | animation: progress-stripes 1s linear infinite; 72 | } -------------------------------------------------------------------------------- /lib/themes.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue } from 'clsx'; 2 | 3 | export type ThemeColors = { 4 | primary: string; 5 | secondary: string; 6 | accent: string; 7 | background: string; 8 | text: string; 9 | muted: string; 10 | border: string; 11 | error: string; 12 | success: string; 13 | warning: string; 14 | info: string; 15 | }; 16 | 17 | export type ThemeGradients = { 18 | primary: string; 19 | secondary: string; 20 | accent: string; 21 | }; 22 | 23 | export type ThemeConfig = { 24 | colors: ThemeColors; 25 | gradients: ThemeGradients; 26 | borderRadius: string; 27 | fontFamily: string; 28 | }; 29 | 30 | export const defaultTheme: ThemeConfig = { 31 | colors: { 32 | primary: '#FF6B6B', 33 | secondary: '#4ECDC4', 34 | accent: '#45B7D1', 35 | background: '#0A0A0A', 36 | text: '#FFFFFF', 37 | muted: '#666666', 38 | border: '#2A2A2A', 39 | error: '#FF4D4D', 40 | success: '#4CAF50', 41 | warning: '#FFA726', 42 | info: '#29B6F6' 43 | }, 44 | gradients: { 45 | primary: 'from-[#FF6B6B] to-[#FF8E53]', 46 | secondary: 'from-[#4ECDC4] to-[#45B7D1]', 47 | accent: 'from-[#A8E6CF] to-[#3EECAC]' 48 | }, 49 | borderRadius: '0.5rem', 50 | fontFamily: 'Inter, sans-serif' 51 | }; 52 | 53 | export const darkTheme: ThemeConfig = { 54 | ...defaultTheme, 55 | colors: { 56 | ...defaultTheme.colors, 57 | background: '#000000', 58 | text: '#FFFFFF', 59 | muted: '#888888', 60 | border: '#333333' 61 | } 62 | }; 63 | 64 | export const lightTheme: ThemeConfig = { 65 | ...defaultTheme, 66 | colors: { 67 | ...defaultTheme.colors, 68 | background: '#FFFFFF', 69 | text: '#000000', 70 | muted: '#666666', 71 | border: '#E0E0E0' 72 | } 73 | }; -------------------------------------------------------------------------------- /components/ui/space.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { cn } from '@/lib/utils'; 5 | import { useTheme } from '@/hooks/use-theme'; 6 | 7 | interface SpaceProps { 8 | size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; 9 | direction?: 'horizontal' | 'vertical'; 10 | className?: string; 11 | children?: React.ReactNode; 12 | } 13 | 14 | const spaceValues = { 15 | xs: '0.25rem', 16 | sm: '0.5rem', 17 | md: '1rem', 18 | lg: '1.5rem', 19 | xl: '2rem', 20 | '2xl': '3rem', 21 | '3xl': '4rem', 22 | '4xl': '6rem', 23 | }; 24 | 25 | export function Space({ 26 | size = 'md', 27 | direction = 'vertical', 28 | className, 29 | children 30 | }: SpaceProps) { 31 | const { theme } = useTheme(); 32 | 33 | const style = { 34 | '--space-size': spaceValues[size], 35 | } as React.CSSProperties; 36 | 37 | if (children) { 38 | return ( 39 |
47 | {React.Children.map(children, (child, index) => { 48 | if (index === React.Children.count(children) - 1) return child; 49 | return ( 50 | <> 51 | {child} 52 |
58 | 59 | ); 60 | })} 61 |
62 | ); 63 | } 64 | 65 | return ( 66 |
73 | ); 74 | } -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { cn } from '@/lib/utils'; 5 | import { useTheme } from '@/hooks/use-theme'; 6 | 7 | interface TextareaProps extends React.TextareaHTMLAttributes { 8 | error?: string; 9 | helperText?: string; 10 | resize?: 'none' | 'vertical' | 'horizontal' | 'both'; 11 | variant?: 'default' | 'filled' | 'outline' | 'ghost'; 12 | } 13 | 14 | export function Textarea({ 15 | className, 16 | error, 17 | helperText, 18 | resize = 'vertical', 19 | variant = 'default', 20 | disabled, 21 | ...props 22 | }: TextareaProps) { 23 | const { theme } = useTheme(); 24 | 25 | const variantStyles = { 26 | default: 'bg-white/5 border border-white/10', 27 | filled: 'bg-white/10 border-transparent', 28 | outline: 'bg-transparent border border-white/20', 29 | ghost: 'bg-transparent border-transparent hover:bg-white/5' 30 | }; 31 | 32 | return ( 33 |
34 |