lake-ozark-christian-church/
├── .codesandbox/ # CodeSandbox configuration
├── .git/ # Git version control
├── .vscode/ # VS Code workspace settings
├── public/ # Static assets served as-is
│ ├── favicon.ico
│ ├── favicon.svg
│ ├── LOCC-header.png
│ └── cards like.json
├── src/ # Source code
│ ├── blogs/ # Markdown blog posts
│ ├── components/ # Reusable Astro and React components
│ ├── layouts/ # Page layout templates
│ ├── pages/ # File-based routing (pages and API routes)
│ ├── ui/ # UI framework components (Header, Footer)
│ ├── utils/ # Utility functions and helpers
│ └── env.d.ts # TypeScript environment definitions
├── astro.config.mjs # Astro configuration
├── tailwind.config.mjs # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
└── README.md # Project documentation
```plaintext
## Core directories
### `src/pages/` - Routing and pages
The `src/pages/` directory is the heart of the routing system. Each file automatically becomes a route on the website.
```plaintext
src/pages/
├── index.astro # Homepage (/)
├── about.astro # About page (/about)
├── worship.astro # Worship page (/worship)
├── staff.astro # Staff page (/staff)
├── live.astro # Live stream page (/live)
├── videos.astro # Video gallery (/videos)
├── giving.astro # Giving page (/giving)
├── programs.astro # Programs page (/programs)
├── children-youth.astro # Children & Youth (/children-youth)
├── weddings.astro # Weddings page (/weddings)
├── lunch-bunch.astro # Lunch Bunch page (/lunch-bunch)
├── chimes.astro # Chimes newsletter (/chimes)
├── 404.astro # Not found page
├── 503.astro # Service unavailable page
├── report.astro # Report issue page (/report)
├── privacy.astro # Privacy policy (/privacy)
├── terms.astro # Terms of service (/terms)
├── admin/
│ └── thumbnail-generator.astro # Admin tool (/admin/thumbnail-generator)
├── api/
│ ├── videos.json.ts # API endpoint for video data
│ └── thumbnail-proxy.ts # Thumbnail proxy endpoint
├── blog/
│ ├── index.astro # Blog listing (/blog)
│ └── [ID].astro # Individual blog posts (/blog/1, /blog/2, etc.)
├── donate/
│ └── index.astro # Donation page (/donate)
├── internal/
│ └── ... # Internal pages
├── legal/
│ └── ... # Legal pages
└── wp-admin/
└── ... # WordPress admin redirects
```plaintext
<Note>
Files in `src/pages/` can be `.astro`, `.md`, or `.ts` (for API routes). Dynamic routes use bracket notation like `[ID].astro`.
</Note>
**Key features**:
- **File-based routing**: `about.astro` → `/about`
- **Dynamic routes**: `[ID].astro` uses route parameters
- **API routes**: `.ts` files export request handlers
- **Server-side rendering**: Pages can use SSR with `export const prerender = false`
**Example from index.astro**:
```astro src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
import PrayerRequestForm from '../components/PrayerRequestForm.astro';
import SalvationMarquee from '../components/SalvationMarquee';
import { isLocalVisitor } from '../utils/locationCheck';
// Enable server-side rendering for location checking
export const prerender = false;
// Check if visitor is local to Lake Ozark area
let isLocal = false;
try {
isLocal = await isLocalVisitor();
} catch (error) {
console.warn('Location check failed, defaulting to non-local:', error);
isLocal = false;
}
---
<Layout title="Home">
<!-- Page content -->
</Layout>
```plaintext
### `src/components/` - Reusable components
The components directory contains reusable Astro and React components used throughout the site:
```plaintext
src/components/
├── AccessibilityMenu.astro # Accessibility customization menu
├── AlertBanner.astro # Site-wide alert/announcement banner
├── AlertSmall.astro # Small inline alerts
├── CookieBanner.astro # Cookie consent banner
├── DonationForm.astro # Donation form with payment integration
├── ExternalLinkWarning.astro # Warning modal for external links
├── LocationMap.astro # Interactive map component
├── LocationPermission.astro # Location permission request
├── PrayerRequestForm.astro # Prayer request submission form
├── PrivacyBanner.astro # Privacy policy banner
├── SalvationMarquee.tsx # React component - testimonial marquee
├── SeasonalBanner.astro # Liturgical calendar-based banner
├── SelfXSS.astro # Console warning against self-XSS
├── SharePost.astro # Social sharing buttons
├── SundayCalendar.astro # Sunday service calendar
└── WeatherBanner.astro # Weather alert banner
```plaintext
<Note>
Components with `.tsx` extension are React components. All others are Astro components.
</Note>
**Example React component** (`SalvationMarquee.tsx`):
```tsx src/components/SalvationMarquee.tsx
import { useState } from 'react';
import { Marquee } from '@joycostudio/marquee/react';
interface Testimony {
name: string;
quote: string;
year?: string;
}
interface Props {
testimonies?: Testimony[];
}
export default function SalvationMarquee({ testimonies = defaultTestimonies }: Props) {
const [isPaused, setIsPaused] = useState(false);
return (
<div className="salvation-marquee-wrapper">
<Marquee speed={30} direction={-1} play={!isPaused}>
{/* Testimony cards */}
</Marquee>
</div>
);
}
```plaintext
**Example Astro component** usage:
```astro
---
import PrayerRequestForm from '../components/PrayerRequestForm.astro';
---
<div class="container mx-auto px-6 py-12">
<h2>Submit a Prayer Request</h2>
<PrayerRequestForm />
</div>
```plaintext
### `src/layouts/` - Page templates
Layouts provide consistent structure and styling across pages:
```plaintext
src/layouts/
├── Layout.astro # Primary layout for all standard pages
├── LegalLayout.astro # Layout for legal pages (privacy, terms)
└── LiveLayout.astro # Layout for live stream page
```plaintext
**The main Layout component** (`Layout.astro`):
```astro src/layouts/Layout.astro
---
interface Props {
title: string;
description?: string;
ogImage?: string;
type?: 'website' | 'article' | 'profile';
}
const {
title,
description = "Lake Ozark Christian Church - A welcoming Christian community...",
ogImage,
type = 'website'
} = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title} | Lake Ozark Christian Church</title>
<meta name="description" content={description} />
<!-- SEO, Open Graph, Schema.org, etc. -->
</head>
<body>
<Header />
<SeasonalBanner />
<main id="main-content">
<slot />
</main>
<Footer />
<PrivacyBanner />
<LocationPermission />
</body>
</html>
```plaintext
**Key features**:
- SEO optimization with meta tags and structured data
- Open Graph and Twitter Card support
- Accessibility features (skip links, ARIA labels)
- Sentry error tracking integration
- Consistent header and footer across all pages
### `src/utils/` - Utility functions
Utility functions provide reusable logic for common tasks:
```plaintext
src/utils/
├── liturgicalCalendar.ts # Liturgical season calculations
├── fetchYouTubeVideos.js # YouTube API integration
├── locationCheck.ts # Visitor location detection (TypeScript)
├── locationCheck.js # Location detection (JavaScript version)
└── clientThumbnailGenerator.js # Client-side thumbnail generation
```plaintext
**Example: Liturgical Calendar** (`liturgicalCalendar.ts`):
```typescript src/utils/liturgicalCalendar.ts
export interface LiturgicalSeason {
season: string;
color: string;
message?: string;
icon?: string;
countdown?: {
days: number;
holiday: string;
};
}
/**
* Determine liturgical season and color for a given date
* Based on Disciples of Christ liturgical calendar
*/
export function getLiturgicalSeason(date: Date): LiturgicalSeason {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
// Check for specific holidays
if (month === 12 && day === 25) {
return {
season: 'Christmas',
color: '#ffffff',
message: 'Have a merry Christmas from your LOCC family!',
icon: '🎄'
};
}
// Additional season logic...
}
```plaintext
**Usage in components**:
```astro src/components/SeasonalBanner.astro
---
import { getLiturgicalSeason } from '../utils/liturgicalCalendar';
const today = new Date();
const season = getLiturgicalSeason(today);
---
<div style={`background-color: ${season.color}`}>
<p>{season.message}</p>
</div>
```plaintext
### `src/ui/` - UI framework components
Core UI components that form the site's navigation and structure:
```plaintext
src/ui/
├── Header.astro # Site header with navigation
└── Footer.astro # Site footer with links and info
```plaintext
These components are included in the main `Layout.astro` and appear on every page.
### `src/blogs/` - Blog content
Markdown files for blog posts:
```plaintext
src/blogs/
├── 1.md # First blog post
├── 2.md # Second blog post
├── 3.md # Third blog post
└── 4.md # Fourth blog post
```plaintext
**Blog post structure**:
```markdown src/blogs/1.md
---
id: 1
title: "Welcome to Our New Website"
date: "2024-01-15"
author: "Church Staff"
excerpt: "We're excited to announce the launch of our new website..."
---
Content of the blog post goes here...
```plaintext
**Dynamic routing** (`src/pages/blog/[ID].astro`):
```astro src/pages/blog/[ID].astro
---
export async function getStaticPaths() {
const posts = await Astro.glob('../blogs/*.md');
return posts.map(post => ({
params: { ID: post.frontmatter.id },
props: { post }
}));
}
const { post } = Astro.props;
const { Content } = await post;
---
<Layout title={post.frontmatter.title}>
<article>
<h1>{post.frontmatter.title}</h1>
<Content />
</article>
</Layout>
```plaintext
### `public/` - Static assets
Static files served directly without processing:
```plaintext
public/
├── favicon.ico # Browser favicon
├── favicon.svg # SVG favicon
├── LOCC-header.png # Header logo image
└── cards like.json # JSON data file
```plaintext
<Warning>
Files in `public/` are served at the root path. `public/favicon.ico` becomes `/favicon.ico` in production.
</Warning>
## Configuration files
### `astro.config.mjs`
Main Astro configuration:
```javascript astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import react from '@astrojs/react';
import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
// Enable Tailwind CSS and React
integrations: [
tailwind(),
react()
],
// Use server-side rendering
output: 'server',
// Deploy to Vercel serverless
adapter: vercel(),
// Markdown configuration
markdown: {
shikiConfig: {
theme: 'github-light'
}
}
});
```plaintext
### `tailwind.config.mjs`
Tailwind CSS customization:
```javascript tailwind.config.mjs
export default {
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
],
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
colors: {
brand: {
DEFAULT: '#9e1e3e', // Main brand color
light: '#b84660', // Light shade
dark: '#841834', // Dark shade
}
}
},
},
plugins: [],
}
```plaintext
**Usage in components**:
```astro
<button class="bg-brand hover:bg-brand-dark text-white px-6 py-3 rounded">
Click Me
</button>
```plaintext
### `package.json`
Project dependencies and scripts:
```json package.json
{
"name": "lake-ozark-christian-church",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^5.16.4",
"@astrojs/react": "^4.4.2",
"@astrojs/tailwind": "^5.1.5",
"@astrojs/vercel": "^8.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwindcss": "^3.4.17",
"youtube-sr": "^4.3.12",
"@joycostudio/marquee": "^0.0.13",
"@sentry/astro": "^9.46.0"
}
}
```plaintext
## Architecture patterns
### Server-side rendering
Pages can opt into SSR for dynamic functionality:
```astro src/pages/example.astro
---
// Disable prerendering to enable SSR
export const prerender = false;
// This code runs on each request
const userIP = Astro.request.headers.get('x-forwarded-for');
---
<p>Your IP: {userIP}</p>
```plaintext
### Hybrid rendering
Mix static and dynamic pages:
- Static pages (default): Pre-rendered at build time
- Dynamic pages: Rendered on-demand with SSR
- API routes: Always server-side
### Component composition
Components are composed to build complex UIs:
```astro
---
import Layout from '../layouts/Layout.astro';
import Header from '../ui/Header.astro';
import PrayerRequestForm from '../components/PrayerRequestForm.astro';
import Footer from '../ui/Footer.astro';
---
<Layout title="Prayer">
<div class="container mx-auto">
<h1>Prayer Requests</h1>
<PrayerRequestForm />
</div>
</Layout>
```plaintext
## Next steps
Now that you understand the project structure:
<CardGroup cols={2}>
<Card title="Components reference" icon="puzzle-piece" href="/components/overview">
Explore all available components in detail
</Card>
<Card title="Pages and routing" icon="file" href="/pages/overview">
Learn more about creating and organizing pages
</Card>
<Card title="API routes" icon="code" href="/api-reference/overview">
Discover the API endpoints and how to create new ones
</Card>
<Card title="Styling guide" icon="paintbrush" href="/styling/overview">
Master Tailwind CSS and custom theming
</Card>
</CardGroup>