mirror of
https://github.com/LukeHagar/Sveltey.git
synced 2025-12-06 04:21:38 +00:00
Styling and breaking out the components, also readme updates
This commit is contained in:
81
README.md
81
README.md
@@ -25,6 +25,10 @@ A modern, production-ready SaaS template built with [SvelteKit 2](https://kit.sv
|
|||||||
- [🔧 Configuration](#-configuration)
|
- [🔧 Configuration](#-configuration)
|
||||||
- [Environment Variables](#environment-variables)
|
- [Environment Variables](#environment-variables)
|
||||||
- [Supabase Setup](#supabase-setup)
|
- [Supabase Setup](#supabase-setup)
|
||||||
|
- [Email Setup (Resend)](#email-setup-resend)
|
||||||
|
- [Email Features](#email-features)
|
||||||
|
- [Example Usage](#example-usage)
|
||||||
|
- [Email Template example](#email-template-example)
|
||||||
- [Stripe Setup](#stripe-setup)
|
- [Stripe Setup](#stripe-setup)
|
||||||
- [Analytics Setup (Plausible)](#analytics-setup-plausible)
|
- [Analytics Setup (Plausible)](#analytics-setup-plausible)
|
||||||
- [Current Configuration](#current-configuration)
|
- [Current Configuration](#current-configuration)
|
||||||
@@ -119,6 +123,7 @@ Visit `http://localhost:5173` and start building your SaaS!
|
|||||||
- **UI Components**: Skeleton UI
|
- **UI Components**: Skeleton UI
|
||||||
- **Styling**: Tailwind CSS
|
- **Styling**: Tailwind CSS
|
||||||
- **Analytics**: Plausible Analytics
|
- **Analytics**: Plausible Analytics
|
||||||
|
- **Email**: Resend
|
||||||
<!-- - **Payments**: Stripe -->
|
<!-- - **Payments**: Stripe -->
|
||||||
- **Deployment**: Vercel/Netlify ready
|
- **Deployment**: Vercel/Netlify ready
|
||||||
- **Language**: TypeScript
|
- **Language**: TypeScript
|
||||||
@@ -159,6 +164,9 @@ Create a `.env` file in the root directory:
|
|||||||
PUBLIC_SUPABASE_URL=your_supabase_url
|
PUBLIC_SUPABASE_URL=your_supabase_url
|
||||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||||
|
|
||||||
|
# Resend (for email)
|
||||||
|
RESEND_API_KEY=your_resend_api_key
|
||||||
|
|
||||||
# Stripe // coming soon
|
# Stripe // coming soon
|
||||||
PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
|
PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
|
||||||
STRIPE_SECRET_KEY=your_stripe_secret_key
|
STRIPE_SECRET_KEY=your_stripe_secret_key
|
||||||
@@ -173,6 +181,79 @@ STRIPE_WEBHOOK_SECRET=your_webhook_secret
|
|||||||
3. Run the included SQL migrations
|
3. Run the included SQL migrations
|
||||||
4. Set up your authentication providers
|
4. Set up your authentication providers
|
||||||
|
|
||||||
|
### Email Setup (Resend)
|
||||||
|
|
||||||
|
Sveltey uses [Resend](https://resend.com/) for reliable email delivery with excellent developer experience.
|
||||||
|
|
||||||
|
1. **Create a Resend account** at [resend.com](https://resend.com/)
|
||||||
|
2. **Get your API key** from the Resend dashboard
|
||||||
|
3. **Add to environment variables**:
|
||||||
|
```env
|
||||||
|
RESEND_API_KEY=re_your_api_key_here
|
||||||
|
```
|
||||||
|
4. **Verify your domain** (optional but recommended for production):
|
||||||
|
- Add your domain in the Resend dashboard
|
||||||
|
- Configure DNS records as instructed
|
||||||
|
- This removes the "via resend.com" branding and improves deliverability
|
||||||
|
|
||||||
|
#### Email Features
|
||||||
|
|
||||||
|
- **Transactional Emails**: Password resets, welcome emails, notifications
|
||||||
|
- **Template Support**: Beautiful HTML email templates
|
||||||
|
- **Delivery Tracking**: Monitor email delivery and engagement
|
||||||
|
- **High Deliverability**: Excellent inbox placement rates
|
||||||
|
- **Simple API**: Easy integration with SvelteKit API routes
|
||||||
|
|
||||||
|
#### Example Usage
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/routes/api/send-email/+server.ts
|
||||||
|
import { RESEND_API_KEY } from '$env/static/private';
|
||||||
|
import { Resend } from 'resend';
|
||||||
|
|
||||||
|
const resend = new Resend(RESEND_API_KEY);
|
||||||
|
|
||||||
|
export async function POST({ request }) {
|
||||||
|
const { to, subject, html } = await request.json();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await resend.emails.send({
|
||||||
|
from: 'noreply@yourdomain.com',
|
||||||
|
to,
|
||||||
|
subject,
|
||||||
|
html
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ success: true, data }), {
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return new Response(JSON.stringify({ error: error.message }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Email Template example
|
||||||
|
|
||||||
|
Create reusable email templates in `src/lib/emails/`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/lib/emails/welcome.ts
|
||||||
|
export const welcomeEmail = (userName: string) => `
|
||||||
|
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||||
|
<h1 style="color: #333;">Welcome to Sveltey, ${userName}!</h1>
|
||||||
|
<p>Thank you for joining our platform. We're excited to have you on board.</p>
|
||||||
|
<a href="https://yourdomain.com/dashboard"
|
||||||
|
style="background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px;">
|
||||||
|
Get Started
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
### Stripe Setup
|
### Stripe Setup
|
||||||
|
|
||||||
1. Create a Stripe account
|
1. Create a Stripe account
|
||||||
|
|||||||
90
src/lib/components/Footer.svelte
Normal file
90
src/lib/components/Footer.svelte
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="bg-surface-100-850-token border-surface-500 mt-20 border-t">
|
||||||
|
<div class="container mx-auto px-4 py-12 md:px-6">
|
||||||
|
<div class="grid grid-cols-1 gap-8 md:grid-cols-4">
|
||||||
|
<!-- Brand Column -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="bg-primary-500 flex h-8 w-8 items-center justify-center rounded-lg">
|
||||||
|
<span class="text-lg font-bold text-white">S</span>
|
||||||
|
</div>
|
||||||
|
<span class="text-xl font-bold">Sveltey</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm opacity-75">
|
||||||
|
The complete SvelteKit & Supabase SaaS template. Launch your next project in minutes, not
|
||||||
|
months.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Product Links -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h3 class="font-semibold">Product</h3>
|
||||||
|
<div class="space-y-2 text-sm">
|
||||||
|
<a href="/pricing" class="block opacity-75 transition-opacity hover:opacity-100"
|
||||||
|
>Pricing</a
|
||||||
|
>
|
||||||
|
<a href="/blog" class="block opacity-75 transition-opacity hover:opacity-100">Blog</a>
|
||||||
|
<a href="/dashboard" class="block opacity-75 transition-opacity hover:opacity-100"
|
||||||
|
>Dashboard</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Support Links -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h3 class="font-semibold">Support</h3>
|
||||||
|
<div class="space-y-2 text-sm">
|
||||||
|
<a
|
||||||
|
href="https://github.com/LukeHagar/sveltey"
|
||||||
|
class="block opacity-75 transition-opacity hover:opacity-100"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
<a href="/contact" class="block opacity-75 transition-opacity hover:opacity-100">
|
||||||
|
Contact
|
||||||
|
</a>
|
||||||
|
<a href="/help" class="block opacity-75 transition-opacity hover:opacity-100">
|
||||||
|
Help Center
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Legal Links -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h3 class="font-semibold">Legal</h3>
|
||||||
|
<div class="space-y-2 text-sm">
|
||||||
|
<a href="/privacy" class="block opacity-75 transition-opacity hover:opacity-100">
|
||||||
|
Privacy Policy
|
||||||
|
</a>
|
||||||
|
<a href="/terms" class="block opacity-75 transition-opacity hover:opacity-100">
|
||||||
|
Terms of Service
|
||||||
|
</a>
|
||||||
|
<a href="/cookies" class="block opacity-75 transition-opacity hover:opacity-100">
|
||||||
|
Cookie Policy
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer Bottom -->
|
||||||
|
<div
|
||||||
|
class="border-surface-300-600-token mt-8 flex flex-col items-center justify-between border-t pt-8 md:flex-row"
|
||||||
|
>
|
||||||
|
<p class="text-sm opacity-50">© 2025 Sveltey. All rights reserved.</p>
|
||||||
|
<div class="mt-4 flex flex-wrap items-center justify-center gap-4 md:mt-0">
|
||||||
|
<span class="text-sm opacity-50">Built with</span>
|
||||||
|
<div class="flex items-center gap-2 text-sm opacity-75">
|
||||||
|
<a class="anchor" href="https://kit.svelte.dev/" target="_blank">SvelteKit</a>
|
||||||
|
<span>•</span>
|
||||||
|
<a class="anchor" href="https://supabase.com/" target="_blank">Supabase</a>
|
||||||
|
<span>•</span>
|
||||||
|
<a class="anchor" href="https://skeleton.dev/" target="_blank">Skeleton UI</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
@@ -9,11 +9,6 @@
|
|||||||
|
|
||||||
let { session } = $derived(data);
|
let { session } = $derived(data);
|
||||||
|
|
||||||
// Helper function to check if a path is active
|
|
||||||
function isActivePath(path: string): boolean {
|
|
||||||
return page.url.pathname === path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to get navigation link classes
|
// Helper function to get navigation link classes
|
||||||
function getNavClasses(path: string): string {
|
function getNavClasses(path: string): string {
|
||||||
return `btn btn-sm flex items-center gap-2 ${page.url.pathname === path ? 'cursor-default disabled' : ''}`;
|
return `btn btn-sm flex items-center gap-2 ${page.url.pathname === path ? 'cursor-default disabled' : ''}`;
|
||||||
@@ -23,7 +18,7 @@
|
|||||||
<header
|
<header
|
||||||
class="bg-surface-50-950-token border-surface-200-700-token sticky top-0 z-50 border-b backdrop-blur-2xl"
|
class="bg-surface-50-950-token border-surface-200-700-token sticky top-0 z-50 border-b backdrop-blur-2xl"
|
||||||
>
|
>
|
||||||
<nav class="container mx-auto px-6 py-4">
|
<nav class="container mx-auto px-2 py-2 md:py-4 md:px-6">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<!-- Left side - Brand and Main navigation -->
|
<!-- Left side - Brand and Main navigation -->
|
||||||
<div class="flex items-center gap-8">
|
<div class="flex items-center gap-8">
|
||||||
@@ -95,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Authentication Buttons -->
|
<!-- Authentication Buttons -->
|
||||||
<div class="hidden md:flex items-center gap-1 md:gap-3 ">
|
<div class="hidden items-center gap-1 md:flex md:gap-3">
|
||||||
<a href="/auth" class={getNavClasses('/auth')} aria-label="Sign in or register">
|
<a href="/auth" class={getNavClasses('/auth')} aria-label="Sign in or register">
|
||||||
<User class="size-4" aria-hidden="true" />
|
<User class="size-4" aria-hidden="true" />
|
||||||
<span class="">Sign In / Register</span>
|
<span class="">Sign In / Register</span>
|
||||||
@@ -104,12 +99,12 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="block pt-4 md:hidden">
|
<div class="block pt-2 md:hidden">
|
||||||
<nav class="flex flex-wrap items-center justify-center gap-4">
|
<nav class="flex flex-wrap items-center justify-center gap-1">
|
||||||
<a href="/" class={getNavClasses('/')} aria-label="Go to homepage">
|
<a href="/" class={getNavClasses('/')} aria-label="Go to homepage">
|
||||||
<Home class="size-4" aria-hidden="true" />
|
<Home class="size-4" aria-hidden="true" />
|
||||||
Home
|
Home
|
||||||
</a>
|
</a>
|
||||||
<a href="/pricing" class={getNavClasses('/pricing')} aria-label="View pricing plans">
|
<a href="/pricing" class={getNavClasses('/pricing')} aria-label="View pricing plans">
|
||||||
<DollarSign class="size-4" aria-hidden="true" />
|
<DollarSign class="size-4" aria-hidden="true" />
|
||||||
Pricing
|
Pricing
|
||||||
@@ -131,7 +126,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<a href="/auth" class={getNavClasses('/auth')} aria-label="Sign in or register">
|
<a href="/auth" class={getNavClasses('/auth')} aria-label="Sign in or register">
|
||||||
<User class="size-4" aria-hidden="true" />
|
<User class="size-4" aria-hidden="true" />
|
||||||
Sign In / Register
|
Sign In / Up
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
{#each featuredPosts as post}
|
{#each featuredPosts as post}
|
||||||
<a href="/blog/{post.slug}" class="card preset-outlined-primary-500 p-6 md:p-8 space-y-4 hover:scale-105 hover:shadow-2xl transition-all duration-300 group">
|
<a href="/blog/{post.slug}" class="card preset-outlined-primary-500 p-6 md:p-8 space-y-4 hover:scale-105 hover:shadow-2xl transition-all duration-300 group flex flex-col">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center gap-1 text-sm opacity-75">
|
<div class="flex items-center gap-1 text-sm opacity-75">
|
||||||
<Calendar class="size-4" />
|
<Calendar class="size-4" />
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="opacity-75">{post.excerpt}</p>
|
<p class="opacity-75 grow">{post.excerpt}</p>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap gap-1">
|
<div class="flex flex-wrap gap-1">
|
||||||
{#each post.tags.slice(0, 2) as tag}
|
{#each post.tags.slice(0, 2) as tag}
|
||||||
<span class="badge preset-outlined-surface-200-800 text-xs flex items-center gap-1">
|
<span class="badge preset-outlined-surface-500 text-xs flex items-center gap-1">
|
||||||
<Tag class="size-3" />
|
<Tag class="size-3" />
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
{#if post.tags.length > 0}
|
{#if post.tags.length > 0}
|
||||||
<div class="flex flex-wrap gap-1">
|
<div class="flex flex-wrap gap-1">
|
||||||
{#each post.tags.slice(0, 3) as tag}
|
{#each post.tags.slice(0, 3) as tag}
|
||||||
<span class="badge preset-outlined-surface-200-800 text-xs flex items-center gap-1">
|
<span class="badge preset-outlined-surface-500 text-xs flex items-center gap-1">
|
||||||
<Tag class="size-3" />
|
<Tag class="size-3" />
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="card preset-outlined-surface-200-800 p-8 md:p-12 text-center max-w-2xl mx-auto space-y-4">
|
<div class="card preset-outlined-surface-500 p-8 md:p-12 text-center max-w-2xl mx-auto space-y-4">
|
||||||
<BookOpen class="size-16 mx-auto text-primary-500 opacity-50" />
|
<BookOpen class="size-16 mx-auto text-primary-500 opacity-50" />
|
||||||
<h2 class="h3">No blog posts yet</h2>
|
<h2 class="h3">No blog posts yet</h2>
|
||||||
<p class="opacity-75">
|
<p class="opacity-75">
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
Enterprise teams with specific requirements can get in touch for custom pricing and features.
|
Enterprise teams with specific requirements can get in touch for custom pricing and features.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a href="mailto:sales@example.com" class="btn btn-lg preset-filled-secondary-500 text-surface-50-950">
|
<a href="mailto:sales@example.com" class="btn btn-lg preset-filled-secondary-500">
|
||||||
<Mail class="size-5" />
|
<Mail class="size-5" />
|
||||||
<span>Contact Sales</span>
|
<span>Contact Sales</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Error Description -->
|
<!-- Error Description -->
|
||||||
<div class="card preset-outlined-surface-200-800 p-8 space-y-4">
|
<div class="card preset-outlined-surface-500 p-8 space-y-4">
|
||||||
<p class="text-lg opacity-75">{errorInfo.description}</p>
|
<p class="text-lg opacity-75">{errorInfo.description}</p>
|
||||||
{#if message}
|
{#if message}
|
||||||
<div class="card preset-outlined-error-500 p-4">
|
<div class="card preset-outlined-error-500 p-4">
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
<button
|
<button
|
||||||
onclick={() => window.history.back()}
|
onclick={() => window.history.back()}
|
||||||
class="btn preset-outlined-surface-200-800 flex items-center gap-2"
|
class="btn preset-outlined-surface-500 flex items-center gap-2"
|
||||||
aria-label="Go back to previous page"
|
aria-label="Go back to previous page"
|
||||||
title="Go back to previous page"
|
title="Go back to previous page"
|
||||||
>
|
>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onclick={() => window.location.reload()}
|
onclick={() => window.location.reload()}
|
||||||
class="btn preset-outlined-surface-200-800 flex items-center gap-2"
|
class="btn preset-outlined-surface-500 flex items-center gap-2"
|
||||||
aria-label="Reload current page"
|
aria-label="Reload current page"
|
||||||
title="Reload current page"
|
title="Reload current page"
|
||||||
>
|
>
|
||||||
@@ -103,14 +103,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Help Section -->
|
<!-- Help Section -->
|
||||||
<div class="text-sm opacity-50 space-y-2">
|
<div class="text-sm space-y-2">
|
||||||
<p>Still having trouble? Here are some helpful links:</p>
|
<p>Still having trouble? Here are some helpful links:</p>
|
||||||
<div class="flex items-center justify-center gap-4">
|
<div class="flex items-center justify-center gap-4">
|
||||||
<a href="/blog" class="hover:opacity-75 transition-opacity">Blog</a>
|
<a href="/blog" class="anchor">Blog</a>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<a href="/contact" class="hover:opacity-75 transition-opacity">Contact Support</a>
|
<a href="/contact" class="anchor">Contact Support</a>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<a href="/help" class="hover:opacity-75 transition-opacity">Help Center</a>
|
<a href="/help" class="anchor">Help Center</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';
|
||||||
|
import { Modal, Toaster } from '@skeletonlabs/skeleton-svelte';
|
||||||
|
import { MetaTags, deepMerge } from 'svelte-meta-tags';
|
||||||
import { invalidate } from '$app/navigation';
|
import { invalidate } from '$app/navigation';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { toaster } from '$lib';
|
|
||||||
import Header from '$lib/components/Header.svelte';
|
|
||||||
import { Modal, Toaster } from '@skeletonlabs/skeleton-svelte';
|
|
||||||
import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';
|
|
||||||
import 'prism-themes/themes/prism-vsc-dark-plus.css';
|
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { MetaTags, deepMerge } from 'svelte-meta-tags';
|
import { toaster } from '$lib';
|
||||||
|
|
||||||
|
import Footer from '$lib/components/Footer.svelte';
|
||||||
|
import Header from '$lib/components/Header.svelte';
|
||||||
|
|
||||||
|
import 'prism-themes/themes/prism-vsc-dark-plus.css';
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
|
|
||||||
injectSpeedInsights();
|
try {
|
||||||
|
injectSpeedInsights();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
let { data, children } = $props();
|
let { data, children } = $props();
|
||||||
let { session, supabase } = $derived(data);
|
let { session, supabase } = $derived(data);
|
||||||
@@ -37,16 +44,6 @@
|
|||||||
return `${baseClasses} preset-ghost-surface-200-800`;
|
return `${baseClasses} preset-ghost-surface-200-800`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get mobile navigation link classes
|
|
||||||
function getMobileNavLinkClasses(path: string): string {
|
|
||||||
const isActive = isActivePath(path);
|
|
||||||
const baseClasses = 'btn flex items-center justify-start gap-2';
|
|
||||||
|
|
||||||
if (isActive) {
|
|
||||||
return `${baseClasses} preset-filled-primary-500 text-on-primary-500 cursor-default`;
|
|
||||||
}
|
|
||||||
return `${baseClasses} preset-ghost-surface-200-800`;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Sync client-side session with server-side on mount
|
// Sync client-side session with server-side on mount
|
||||||
@@ -67,97 +64,13 @@
|
|||||||
<Toaster {toaster}></Toaster>
|
<Toaster {toaster}></Toaster>
|
||||||
<Modal />
|
<Modal />
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
<Header {data} />
|
<Header {data} />
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="min-h-screen p-4">
|
<main class="min-h-screen p-4">
|
||||||
{@render children()}
|
{@render children()}
|
||||||
<!-- Pass session to child pages -->
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="bg-surface-100-850-token border-surface-200-700-token mt-20 border-t">
|
<Footer />
|
||||||
<div class="container mx-auto px-6 py-12">
|
|
||||||
<div class="grid grid-cols-1 gap-8 md:grid-cols-4">
|
|
||||||
<!-- Brand Column -->
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<div class="bg-primary-500 flex h-8 w-8 items-center justify-center rounded-lg">
|
|
||||||
<span class="text-lg font-bold text-white">S</span>
|
|
||||||
</div>
|
|
||||||
<span class="text-xl font-bold">Sveltey</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-sm opacity-75">
|
|
||||||
The complete SvelteKit & Supabase SaaS template. Launch your next project in minutes, not
|
|
||||||
months.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Product Links -->
|
|
||||||
<div class="space-y-4">
|
|
||||||
<h3 class="font-semibold">Product</h3>
|
|
||||||
<div class="space-y-2 text-sm">
|
|
||||||
<a href="/pricing" class="block opacity-75 transition-opacity hover:opacity-100"
|
|
||||||
>Pricing</a
|
|
||||||
>
|
|
||||||
<a href="/blog" class="block opacity-75 transition-opacity hover:opacity-100">Blog</a>
|
|
||||||
<a href="/dashboard" class="block opacity-75 transition-opacity hover:opacity-100"
|
|
||||||
>Dashboard</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Support Links -->
|
|
||||||
<div class="space-y-4">
|
|
||||||
<h3 class="font-semibold">Support</h3>
|
|
||||||
<div class="space-y-2 text-sm">
|
|
||||||
<a
|
|
||||||
href="https://github.com/LukeHagar/sveltey"
|
|
||||||
class="block opacity-75 transition-opacity hover:opacity-100"
|
|
||||||
>
|
|
||||||
GitHub
|
|
||||||
</a>
|
|
||||||
<a href="/contact" class="block opacity-75 transition-opacity hover:opacity-100">
|
|
||||||
Contact
|
|
||||||
</a>
|
|
||||||
<a href="/help" class="block opacity-75 transition-opacity hover:opacity-100">
|
|
||||||
Help Center
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Legal Links -->
|
|
||||||
<div class="space-y-4">
|
|
||||||
<h3 class="font-semibold">Legal</h3>
|
|
||||||
<div class="space-y-2 text-sm">
|
|
||||||
<a href="/privacy" class="block opacity-75 transition-opacity hover:opacity-100">
|
|
||||||
Privacy Policy
|
|
||||||
</a>
|
|
||||||
<a href="/terms" class="block opacity-75 transition-opacity hover:opacity-100">
|
|
||||||
Terms of Service
|
|
||||||
</a>
|
|
||||||
<a href="/cookies" class="block opacity-75 transition-opacity hover:opacity-100">
|
|
||||||
Cookie Policy
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer Bottom -->
|
|
||||||
<div
|
|
||||||
class="border-surface-300-600-token mt-8 flex flex-col items-center justify-between border-t pt-8 md:flex-row"
|
|
||||||
>
|
|
||||||
<p class="text-sm opacity-50">© 2025 Sveltey. All rights reserved.</p>
|
|
||||||
<div class="mt-4 flex flex-wrap items-center justify-center gap-4 md:mt-0">
|
|
||||||
<span class="text-sm opacity-50">Built with</span>
|
|
||||||
<div class="flex items-center gap-2 text-sm opacity-75">
|
|
||||||
<a class="anchor" href="https://kit.svelte.dev/" target="_blank">SvelteKit</a>
|
|
||||||
<span>•</span>
|
|
||||||
<a class="anchor" href="https://supabase.com/" target="_blank">Supabase</a>
|
|
||||||
<span>•</span>
|
|
||||||
<a class="anchor" href="https://skeleton.dev/" target="_blank">Skeleton UI</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|||||||
@@ -57,11 +57,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<hr class="hr max-w-48" />
|
<div class="flex justify-center">
|
||||||
|
<hr class="hr border-surface-500 max-w-64" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Stats Section -->
|
<!-- Stats Section -->
|
||||||
<section>
|
<section>
|
||||||
<div class="flex gap-20 text-center flex-wrap font-bold justify-between justify-center">
|
<div class="flex gap-20 text-center flex-wrap font-bold justify-center">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<span class="text-7xl">99%</span>
|
<span class="text-7xl">99%</span>
|
||||||
<p class="text-primary-500">Faster Development</p>
|
<p class="text-primary-500">Faster Development</p>
|
||||||
@@ -81,7 +83,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<hr class="hr max-w-48" />
|
<div class="flex justify-center">
|
||||||
|
<hr class="hr border-surface-500 max-w-64" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Features Section -->
|
<!-- Features Section -->
|
||||||
<section class="flex flex-wrap gap-4">
|
<section class="flex flex-wrap gap-4">
|
||||||
@@ -111,7 +115,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<hr class="hr max-w-48" />
|
<div class="flex justify-center">
|
||||||
|
<hr class="hr border-surface-500 max-w-64" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Technologies Section -->
|
<!-- Technologies Section -->
|
||||||
<section class="space-y-8 text-center">
|
<section class="space-y-8 text-center">
|
||||||
@@ -144,7 +150,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<hr class="hr max-w-48" />
|
<div class="flex justify-center">
|
||||||
|
<hr class="hr border-surface-500 max-w-64" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Call to Action Section -->
|
<!-- Call to Action Section -->
|
||||||
<section class="grid grid-cols-1 items-center gap-4 md:grid-cols-[1fr_auto]">
|
<section class="grid grid-cols-1 items-center gap-4 md:grid-cols-[1fr_auto]">
|
||||||
|
|||||||
Reference in New Issue
Block a user