feat: initialize project with Tailwind CSS, React, and TypeScript setup
- Added Tailwind CSS configuration with custom theme colors and animations. - Created global styles in index.css including custom scrollbar and button components. - Set up main entry point in main.tsx to render the App component. - Configured TypeScript with strict settings and path mapping. - Added support for high contrast mode and reduced motion in styles. - Included print styles for better printing experience.
This commit is contained in:
42
.env.example
Normal file
42
.env.example
Normal file
@@ -0,0 +1,42 @@
|
||||
# Environment Variables Template
|
||||
# Copy this file to .env and fill in your actual values
|
||||
|
||||
# Analytics
|
||||
VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX
|
||||
VITE_ANALYTICS_ENABLED=true
|
||||
|
||||
# Contact Information
|
||||
VITE_CONTACT_EMAIL=contact@miraclesinmotion.org
|
||||
VITE_PHONE_NUMBER=+15551234567
|
||||
VITE_ADDRESS="123 Community Way, Hometown, ST 12345"
|
||||
|
||||
# Donation Processing
|
||||
VITE_DONATION_ENDPOINT=https://api.donation-processor.com/v1
|
||||
VITE_PAYPAL_CLIENT_ID=your_paypal_client_id
|
||||
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_key
|
||||
|
||||
# Organization Details
|
||||
VITE_EIN=12-3456789
|
||||
VITE_ORG_NAME="Miracles In Motion"
|
||||
VITE_ORG_DESCRIPTION="A 501(c)3 non-profit providing essentials for student success"
|
||||
|
||||
# Social Media
|
||||
VITE_FACEBOOK_URL=https://facebook.com/miraclesinmotion
|
||||
VITE_INSTAGRAM_URL=https://instagram.com/miraclesinmotion
|
||||
VITE_TWITTER_URL=https://twitter.com/miraclesinmotion
|
||||
VITE_LINKEDIN_URL=https://linkedin.com/company/miraclesinmotion
|
||||
|
||||
# API Endpoints
|
||||
VITE_API_BASE_URL=https://api.miraclesinmotion.org
|
||||
VITE_VOLUNTEER_FORM_URL=https://forms.miraclesinmotion.org/volunteer
|
||||
VITE_CONTACT_FORM_URL=https://forms.miraclesinmotion.org/contact
|
||||
|
||||
# Feature Flags
|
||||
VITE_ENABLE_CHAT=false
|
||||
VITE_ENABLE_NEWSLETTER=true
|
||||
VITE_ENABLE_EVENTS=true
|
||||
VITE_ENABLE_BLOG=false
|
||||
|
||||
# Development
|
||||
VITE_DEV_MODE=true
|
||||
VITE_LOG_LEVEL=info
|
||||
20
.eslintrc.cjs
Normal file
20
.eslintrc.cjs
Normal file
@@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
}
|
||||
92
.gitignore
vendored
Normal file
92
.gitignore
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# OS generated files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# Build outputs
|
||||
build/
|
||||
dist/
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# IDE files
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Local environment files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
88
index.html
88
index.html
@@ -1,68 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Miracles In Motion | 501(c)3 Non-Profit Organization</title>
|
||||
<meta name="description" content="Miracles In Motion is a 501(c)3 non-profit organization dedicated to creating positive change in our community through compassionate action and support.">
|
||||
<meta name="keywords" content="non-profit, charity, 501c3, miracles in motion, community support, donations, volunteers">
|
||||
<meta name="description" content="Miracles In Motion is a 501(c)3 non-profit organization dedicated to creating positive change in our community through compassionate action and support." />
|
||||
<meta name="keywords" content="non-profit, charity, 501c3, miracles in motion, community support, donations, volunteers" />
|
||||
|
||||
<!-- Open Graph Meta Tags -->
|
||||
<meta property="og:title" content="Miracles In Motion | 501(c)3 Non-Profit Organization">
|
||||
<meta property="og:description" content="Creating positive change in our community through compassionate action and support.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://miraclesinmotion.org">
|
||||
<meta property="og:title" content="Miracles In Motion | 501(c)3 Non-Profit Organization" />
|
||||
<meta property="og:description" content="Creating positive change in our community through compassionate action and support." />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://miraclesinmotion.org" />
|
||||
<meta property="og:image" content="/og-image.png" />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<!-- Twitter Card Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Miracles In Motion | 501(c)3 Non-Profit Organization" />
|
||||
<meta name="twitter:description" content="Creating positive change in our community through compassionate action and support." />
|
||||
<meta name="twitter:image" content="/og-image.png" />
|
||||
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Favicon and Web App Manifest -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
|
||||
<!-- React and Babel -->
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
||||
<!-- Theme Color -->
|
||||
<meta name="theme-color" content="#ec4899" />
|
||||
|
||||
<!-- Framer Motion -->
|
||||
<script src="https://unpkg.com/framer-motion@10/dist/framer-motion.js"></script>
|
||||
<!-- Preconnect to external domains -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
<!-- Font Awesome for Icons -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.hero-gradient {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card-hover:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.smooth-scroll {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="smooth-scroll">
|
||||
<!-- Inter Font -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="text/babel" src="mim_web.jsx"></script>
|
||||
</body>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
4508
package-lock.json
generated
Normal file
4508
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"name": "miracles-in-motion-web",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "Public website for Miracles In Motion 501(c)3 non-profit organization",
|
||||
"main": "index.html",
|
||||
"scripts": {
|
||||
"dev": "live-server --port=3000",
|
||||
"build": "npm run copy-files",
|
||||
"copy-files": "mkdir -p dist && cp -r *.html *.jsx *.css *.js *.json assets/ dist/",
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview",
|
||||
"deploy": "npm run build && gh-pages -d dist"
|
||||
},
|
||||
"keywords": [
|
||||
@@ -16,7 +18,10 @@
|
||||
"miracles-in-motion",
|
||||
"community",
|
||||
"donations",
|
||||
"volunteers"
|
||||
"volunteers",
|
||||
"react",
|
||||
"vite",
|
||||
"tailwind"
|
||||
],
|
||||
"author": "Miracles In Motion",
|
||||
"license": "MIT",
|
||||
@@ -25,13 +30,27 @@
|
||||
"url": "https://github.com/Miracles-In-Motion/public-web.git"
|
||||
},
|
||||
"homepage": "https://miraclesinmotion.org",
|
||||
"devDependencies": {
|
||||
"live-server": "^1.2.2",
|
||||
"gh-pages": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"framer-motion": "^10.16.4"
|
||||
"framer-motion": "^10.16.16",
|
||||
"lucide-react": "^0.290.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"@vitejs/plugin-react": "^4.1.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"gh-pages": "^6.0.0",
|
||||
"postcss": "^8.4.31",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0"
|
||||
}
|
||||
}
|
||||
6
postcss.config.cjs
Normal file
6
postcss.config.cjs
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
13
public/favicon.svg
Normal file
13
public/favicon.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ec4899;stop-opacity:1" />
|
||||
<stop offset="50%" style="stop-color:#8b5cf6;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="32" height="32" rx="8" fill="url(#grad1)"/>
|
||||
<path d="M16 8L20.5 14H11.5L16 8Z" fill="white" opacity="0.9"/>
|
||||
<circle cx="16" cy="18" r="3" fill="white" opacity="0.9"/>
|
||||
<path d="M10 22L16 20L22 22L20 26H12L10 22Z" fill="white" opacity="0.9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 683 B |
15
public/robots.txt
Normal file
15
public/robots.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Block access to sensitive files
|
||||
Disallow: /.env
|
||||
Disallow: /src/
|
||||
Disallow: /node_modules/
|
||||
Disallow: /dist/
|
||||
Disallow: /*.log
|
||||
|
||||
# Sitemap
|
||||
Sitemap: https://miraclesinmotion.org/sitemap.xml
|
||||
|
||||
# Crawl delay (optional)
|
||||
Crawl-delay: 1
|
||||
28
public/site.webmanifest
Normal file
28
public/site.webmanifest
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "Miracles In Motion",
|
||||
"short_name": "MiraclesInMotion",
|
||||
"description": "A 501(c)3 non-profit providing essentials for student success",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#ec4899",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"categories": ["education", "social", "non-profit"],
|
||||
"lang": "en-US",
|
||||
"dir": "ltr",
|
||||
"orientation": "portrait-primary",
|
||||
"scope": "/",
|
||||
"related_applications": [],
|
||||
"prefer_related_applications": false
|
||||
}
|
||||
129
scripts/generate-og.mjs
Normal file
129
scripts/generate-og.mjs
Normal file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Generate Open Graph images for Miracles In Motion
|
||||
* This script creates social media preview images
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const OG_CONFIG = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
title: 'Miracles In Motion',
|
||||
subtitle: 'Essentials for Every Student',
|
||||
description: '501(c)3 Non-Profit Organization'
|
||||
}
|
||||
|
||||
/**
|
||||
* Create SVG template for OG image
|
||||
*/
|
||||
function createOGImageSVG(config = OG_CONFIG) {
|
||||
return `
|
||||
<svg width="${config.width}" height="${config.height}" viewBox="0 0 ${config.width} ${config.height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="bg-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ec4899;stop-opacity:1" />
|
||||
<stop offset="50%" style="stop-color:#8b5cf6;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="rgba(0,0,0,0.25)"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="100%" height="100%" fill="url(#bg-gradient)"/>
|
||||
|
||||
<!-- Pattern overlay -->
|
||||
<pattern id="dots" patternUnits="userSpaceOnUse" width="40" height="40">
|
||||
<circle cx="20" cy="20" r="2" fill="rgba(255,255,255,0.1)"/>
|
||||
</pattern>
|
||||
<rect width="100%" height="100%" fill="url(#dots)"/>
|
||||
|
||||
<!-- Content container -->
|
||||
<g transform="translate(80, 0)">
|
||||
<!-- Logo area -->
|
||||
<rect x="0" y="120" width="80" height="80" rx="20" fill="rgba(255,255,255,0.2)" filter="url(#shadow)"/>
|
||||
<circle cx="40" cy="160" r="20" fill="white"/>
|
||||
|
||||
<!-- Text content -->
|
||||
<text x="120" y="140" font-family="system-ui, -apple-system, sans-serif" font-size="48" font-weight="700" fill="white">
|
||||
${config.title}
|
||||
</text>
|
||||
<text x="120" y="180" font-family="system-ui, -apple-system, sans-serif" font-size="28" font-weight="400" fill="rgba(255,255,255,0.9)">
|
||||
${config.subtitle}
|
||||
</text>
|
||||
<text x="120" y="220" font-family="system-ui, -apple-system, sans-serif" font-size="20" font-weight="300" fill="rgba(255,255,255,0.8)">
|
||||
${config.description}
|
||||
</text>
|
||||
|
||||
<!-- Call to action -->
|
||||
<rect x="120" y="280" width="200" height="50" rx="25" fill="rgba(255,255,255,0.2)" filter="url(#shadow)"/>
|
||||
<text x="220" y="310" font-family="system-ui, -apple-system, sans-serif" font-size="18" font-weight="500" fill="white" text-anchor="middle">
|
||||
Learn More
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Bottom accent -->
|
||||
<rect x="0" y="580" width="100%" height="50" fill="rgba(0,0,0,0.1)"/>
|
||||
<text x="600" y="610" font-family="system-ui, -apple-system, sans-serif" font-size="16" fill="rgba(255,255,255,0.8)" text-anchor="middle">
|
||||
miraclesinmotion.org
|
||||
</text>
|
||||
</svg>
|
||||
`.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate OG images
|
||||
*/
|
||||
function generateOGImages() {
|
||||
const publicDir = path.join(process.cwd(), 'public')
|
||||
|
||||
// Ensure public directory exists
|
||||
if (!fs.existsSync(publicDir)) {
|
||||
fs.mkdirSync(publicDir, { recursive: true })
|
||||
}
|
||||
|
||||
// Create default OG image
|
||||
const defaultOG = createOGImageSVG()
|
||||
fs.writeFileSync(path.join(publicDir, 'og-image.svg'), defaultOG)
|
||||
|
||||
console.log('✅ Generated og-image.svg')
|
||||
|
||||
// Create page-specific OG images
|
||||
const pages = [
|
||||
{ name: 'donate', title: 'Donate', subtitle: 'Help Students Succeed' },
|
||||
{ name: 'volunteer', title: 'Volunteer', subtitle: 'Make a Difference' },
|
||||
{ name: 'stories', title: 'Stories', subtitle: 'Impact in Action' },
|
||||
]
|
||||
|
||||
pages.forEach(page => {
|
||||
const pageOG = createOGImageSVG({
|
||||
...OG_CONFIG,
|
||||
title: page.title,
|
||||
subtitle: page.subtitle
|
||||
})
|
||||
fs.writeFileSync(path.join(publicDir, `og-image-${page.name}.svg`), pageOG)
|
||||
console.log(`✅ Generated og-image-${page.name}.svg`)
|
||||
})
|
||||
|
||||
console.log('\n🎉 All OG images generated successfully!')
|
||||
console.log('\nNote: These are SVG files. For production, consider converting to PNG using a tool like:')
|
||||
console.log('- Puppeteer for programmatic conversion')
|
||||
console.log('- Online converters')
|
||||
console.log('- Design tools like Figma or Canva')
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
try {
|
||||
generateOGImages()
|
||||
} catch (error) {
|
||||
console.error('❌ Error generating OG images:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
export { generateOGImages, createOGImageSVG }
|
||||
1086
src/App.tsx
Normal file
1086
src/App.tsx
Normal file
File diff suppressed because it is too large
Load Diff
182
src/index.css
Normal file
182
src/index.css
Normal file
@@ -0,0 +1,182 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-feature-settings: 'rlig' 1, 'calt' 1;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(135deg, #ec4899, #8b5cf6);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(135deg, #db2777, #7c3aed);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Button Components */
|
||||
.btn-primary {
|
||||
@apply inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-primary-600 to-secondary-600 px-6 py-3 text-sm font-medium text-white shadow-lg shadow-primary-500/25 transition hover:scale-105 hover:shadow-xl focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply inline-flex items-center gap-2 rounded-full border border-neutral-300 bg-white/70 px-6 py-3 text-sm font-medium text-neutral-700 backdrop-blur transition hover:bg-white hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-neutral-500 focus:ring-offset-2 dark:border-white/20 dark:bg-white/10 dark:text-neutral-200 dark:hover:bg-white/20;
|
||||
}
|
||||
|
||||
.btn-white {
|
||||
@apply inline-flex items-center gap-2 rounded-full bg-white px-6 py-3 text-sm font-medium text-neutral-900 shadow-lg transition hover:scale-105 hover:shadow-xl focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2;
|
||||
}
|
||||
|
||||
.navlink {
|
||||
@apply text-sm font-medium text-neutral-600 transition hover:text-primary-600 dark:text-neutral-300 dark:hover:text-primary-400;
|
||||
}
|
||||
|
||||
.input {
|
||||
@apply w-full rounded-xl border border-white/30 bg-white/70 px-3 py-2 text-sm backdrop-blur transition focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 dark:border-white/10 dark:bg-white/10 dark:focus:border-primary-400;
|
||||
}
|
||||
|
||||
/* Card Components */
|
||||
.card {
|
||||
@apply rounded-2xl border border-white/30 bg-white/70 p-6 shadow-xl backdrop-blur dark:border-white/10 dark:bg-white/5;
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
@apply transition hover:-translate-y-1 hover:shadow-2xl;
|
||||
}
|
||||
|
||||
/* Section Header */
|
||||
.section-header {
|
||||
@apply mx-auto max-w-3xl text-center;
|
||||
}
|
||||
|
||||
.section-eyebrow {
|
||||
@apply text-sm uppercase tracking-wider text-primary-600 dark:text-primary-400;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
@apply mt-2 text-3xl font-bold tracking-tight sm:text-4xl;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
@apply mt-4 text-lg text-neutral-600 dark:text-neutral-300;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
/* Gradient Text */
|
||||
.gradient-text {
|
||||
@apply bg-gradient-to-r from-primary-500 via-primary-600 to-secondary-600 bg-clip-text text-transparent;
|
||||
}
|
||||
|
||||
/* Glass Effect */
|
||||
.glass {
|
||||
@apply bg-white/10 backdrop-blur-md;
|
||||
}
|
||||
|
||||
.glass-dark {
|
||||
@apply bg-black/10 backdrop-blur-md;
|
||||
}
|
||||
|
||||
/* Text Balance */
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* Animation Utilities */
|
||||
.animate-in {
|
||||
animation: animate-in 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes animate-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(1rem);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus States for Accessibility */
|
||||
.focus-visible\:ring-2:focus-visible {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 2px rgba(236, 72, 153, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* High Contrast Mode Support */
|
||||
@media (prefers-contrast: high) {
|
||||
.btn-primary {
|
||||
@apply border-2 border-current;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply border-2 border-current;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply border-2 border-current;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced Motion Support */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
.animate-marquee {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.animate-pulse-slow {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body {
|
||||
color: black !important;
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
color: black !important;
|
||||
background: none !important;
|
||||
-webkit-text-fill-color: initial !important;
|
||||
}
|
||||
}
|
||||
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
70
tailwind.config.cjs
Normal file
70
tailwind.config.cjs
Normal file
@@ -0,0 +1,70 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#fdf4ff',
|
||||
100: '#fae8ff',
|
||||
200: '#f5d0fe',
|
||||
300: '#f0abfc',
|
||||
400: '#e879f9',
|
||||
500: '#d946ef',
|
||||
600: '#c026d3',
|
||||
700: '#a21caf',
|
||||
800: '#86198f',
|
||||
900: '#701a75',
|
||||
},
|
||||
secondary: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
animation: {
|
||||
'marquee': 'marquee 30s linear infinite',
|
||||
'float': 'float 6s ease-in-out infinite',
|
||||
'pulse-slow': 'pulse-slow 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
},
|
||||
keyframes: {
|
||||
marquee: {
|
||||
'0%': { transform: 'translateX(100%)' },
|
||||
'100%': { transform: 'translateX(-100%)' },
|
||||
},
|
||||
float: {
|
||||
'0%, 100%': { transform: 'translateY(0px)' },
|
||||
'50%': { transform: 'translateY(-10px)' },
|
||||
},
|
||||
'pulse-slow': {
|
||||
'0%, 100%': { opacity: '1' },
|
||||
'50%': { opacity: '0.5' },
|
||||
},
|
||||
},
|
||||
boxShadow: {
|
||||
'glow': '0 0 20px rgba(236, 72, 153, 0.3)',
|
||||
'glow-lg': '0 0 40px rgba(236, 72, 153, 0.4)',
|
||||
},
|
||||
backdropBlur: {
|
||||
xs: '2px',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
],
|
||||
}
|
||||
31
tsconfig.json
Normal file
31
tsconfig.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
/* Path mapping */
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
10
tsconfig.node.json
Normal file
10
tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user