fix: formatting
Some checks failed
CI / test (22) (push) Failing after 39s
CI / test (20) (push) Failing after 2m56s
CI / build (push) Has been skipped
CI / security (push) Failing after 2m30s
Deploy / docker-build (push) Failing after 9s
Deploy / build-and-deploy (push) Failing after 41s
Gitea CI / test (push) Failing after 33s
Gitea CI / docker-build (push) Has been skipped

This commit is contained in:
2025-08-14 14:16:04 +02:00
parent 82394e9a94
commit 788d500821
19 changed files with 252 additions and 245 deletions

View File

@@ -9,23 +9,23 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
node-version: [20, 22] node-version: [20, 22]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: '1.2.20' bun-version: '1.2.20'
- name: Verify Bun installation - name: Verify Bun installation
run: bun --version run: bun --version
- name: Cache dependencies - name: Cache dependencies
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@@ -35,25 +35,25 @@ jobs:
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-bun- ${{ runner.os }}-bun-
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Run linting - name: Run linting
run: bun run lint run: bun run lint
- name: Run type checking - name: Run type checking
run: bun run check run: bun run check
- name: Run unit tests - name: Run unit tests
run: bun run test:unit --run run: bun run test:unit --run
- name: Install Playwright browsers - name: Install Playwright browsers
run: bunx playwright install --with-deps run: bunx playwright install --with-deps
- name: Run E2E tests - name: Run E2E tests
run: bun run test:e2e run: bun run test:e2e
- name: Upload test results - name: Upload test results
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: failure() if: failure()
@@ -63,23 +63,23 @@ jobs:
test-results/ test-results/
playwright-report/ playwright-report/
retention-days: 30 retention-days: 30
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test needs: test
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: '1.2.20' bun-version: '1.2.20'
- name: Verify Bun installation - name: Verify Bun installation
run: bun --version run: bun --version
- name: Cache dependencies - name: Cache dependencies
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@@ -89,46 +89,46 @@ jobs:
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-bun- ${{ runner.os }}-bun-
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Build application - name: Build application
run: bun run build run: bun run build
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: build-files name: build-files
path: build/ path: build/
retention-days: 7 retention-days: 7
security: security:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: '1.2.20' bun-version: '1.2.20'
- name: Verify Bun installation - name: Verify Bun installation
run: bun --version run: bun --version
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Run security audit - name: Run security audit
run: bun audit run: bun audit
continue-on-error: true continue-on-error: true
- name: Run CodeQL Analysis - name: Run CodeQL Analysis
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v3
with: with:
languages: javascript languages: javascript
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v3

View File

@@ -8,28 +8,28 @@ on:
jobs: jobs:
build-and-deploy: build-and-deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
pages: write pages: write
id-token: write id-token: write
environment: environment:
name: github-pages name: github-pages
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: '1.2.20' bun-version: '1.2.20'
- name: Verify Bun installation - name: Verify Bun installation
run: bun --version run: bun --version
- name: Cache dependencies - name: Cache dependencies
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@@ -39,21 +39,21 @@ jobs:
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-bun- ${{ runner.os }}-bun-
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Build application - name: Build application
run: bun run build run: bun run build
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v5
- name: Upload to GitHub Pages - name: Upload to GitHub Pages
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v3
with: with:
path: build/ path: build/
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v4
@@ -61,21 +61,21 @@ jobs:
docker-build: docker-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/master' if: github.event_name == 'push' && github.ref == 'refs/heads/master'
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry - name: Log in to Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata - name: Extract metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -86,7 +86,7 @@ jobs:
type=ref,event=pr type=ref,event=pr
type=sha type=sha
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@@ -96,4 +96,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max

View File

@@ -9,34 +9,34 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: '1.2.20' bun-version: '1.2.20'
- name: Verify Bun installation - name: Verify Bun installation
run: bun --version run: bun --version
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Run linting - name: Run linting
run: bun run lint run: bun run lint
- name: Run type checking - name: Run type checking
run: bun run check run: bun run check
- name: Run unit tests - name: Run unit tests
run: bun run test:unit --run run: bun run test:unit --run
- name: Install Playwright browsers - name: Install Playwright browsers
run: bunx playwright install --with-deps run: bunx playwright install --with-deps
- name: Run E2E tests - name: Run E2E tests
run: bun run test:e2e run: bun run test:e2e
@@ -44,21 +44,21 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test needs: test
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Log in to Container Registry - name: Log in to Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ${{ vars.DOCKER_REGISTRY }} registry: ${{ vars.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v4 uses: docker/build-push-action@v4
with: with:
@@ -69,4 +69,4 @@ jobs:
${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:latest ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:latest
${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }} ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max

View File

@@ -94,17 +94,18 @@ The static adapter is configured in `svelte.config.js`. Modify settings as neede
```js ```js
adapter: adapter({ adapter: adapter({
pages: 'build', // Output directory pages: 'build', // Output directory
assets: 'build', // Assets directory assets: 'build', // Assets directory
fallback: undefined, // SPA fallback page fallback: undefined, // SPA fallback page
precompress: false, // Enable gzip/brotli precompress: false, // Enable gzip/brotli
strict: true // Strict prerendering strict: true // Strict prerendering
}) });
``` ```
### Nginx ### Nginx
Production nginx configuration in `nginx.conf` includes: Production nginx configuration in `nginx.conf` includes:
- Gzip compression - Gzip compression
- Static asset caching - Static asset caching
- Security headers - Security headers

View File

@@ -2,10 +2,10 @@ services:
app: app:
build: . build: .
ports: ports:
- "3000:80" - '3000:80'
restart: unless-stopped restart: unless-stopped
container_name: svelte-static-app container_name: svelte-static-app
# Optional: Add a development service # Optional: Add a development service
dev: dev:
build: build:
@@ -13,11 +13,11 @@ services:
target: builder target: builder
command: npm run dev -- --host 0.0.0.0 command: npm run dev -- --host 0.0.0.0
ports: ports:
- "5173:5173" - '5173:5173'
volumes: volumes:
- .:/app - .:/app
- /app/node_modules - /app/node_modules
environment: environment:
- NODE_ENV=development - NODE_ENV=development
profiles: profiles:
- dev - dev

View File

@@ -1,121 +1,121 @@
@import "tailwindcss"; @import 'tailwindcss';
@import "tw-animate-css"; @import 'tw-animate-css';
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
:root { :root {
--radius: 0.625rem; --radius: 0.625rem;
--background: oklch(1 0 0); --background: oklch(1 0 0);
--foreground: oklch(0.145 0 0); --foreground: oklch(0.145 0 0);
--card: oklch(1 0 0); --card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0); --card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0); --popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0); --popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0); --primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0); --primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0); --secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0); --secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0); --muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0); --muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0); --accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0); --accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325); --destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0); --border: oklch(0.922 0 0);
--input: oklch(0.922 0 0); --input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0); --ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116); --chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704); --chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392); --chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429); --chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08); --chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0); --sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0); --sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0); --sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0); --sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0); --sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0); --sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0); --sidebar-ring: oklch(0.708 0 0);
} }
.dark { .dark {
--background: oklch(0.145 0 0); --background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0); --foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0); --card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0); --card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0); --popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0); --popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0); --primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0); --primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0); --secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0); --secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0); --muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0); --muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0); --accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0); --accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216); --destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%); --border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%); --input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0); --ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376); --chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48); --chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08); --chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9); --chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439); --chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0); --sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0); --sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376); --sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0); --sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0); --sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%); --sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0); --sidebar-ring: oklch(0.556 0 0);
} }
@theme inline { @theme inline {
--radius-sm: calc(var(--radius) - 4px); --radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px); --radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius); --radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px); --radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background); --color-background: var(--background);
--color-foreground: var(--foreground); --color-foreground: var(--foreground);
--color-card: var(--card); --color-card: var(--card);
--color-card-foreground: var(--card-foreground); --color-card-foreground: var(--card-foreground);
--color-popover: var(--popover); --color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground); --color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary); --color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground); --color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary); --color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground); --color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted); --color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground); --color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent); --color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground); --color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive); --color-destructive: var(--destructive);
--color-border: var(--border); --color-border: var(--border);
--color-input: var(--input); --color-input: var(--input);
--color-ring: var(--ring); --color-ring: var(--ring);
--color-chart-1: var(--chart-1); --color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2); --color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3); --color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4); --color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5); --color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar); --color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border); --color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring); --color-sidebar-ring: var(--sidebar-ring);
} }
@layer base { @layer base {
* { * {
@apply border-border outline-ring/50; @apply border-border outline-ring/50;
} }
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
} }

View File

@@ -1,36 +1,36 @@
<script lang="ts" module> <script lang="ts" module>
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements"; import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from "tailwind-variants"; import { type VariantProps, tv } from 'tailwind-variants';
export const buttonVariants = tv({ export const buttonVariants = tv({
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
destructive: destructive:
"bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white", 'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white',
outline: outline:
"bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border", 'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border',
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: "text-primary underline-offset-4 hover:underline", link: 'text-primary underline-offset-4 hover:underline'
}, },
size: { size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3", default: 'h-9 px-4 py-2 has-[>svg]:px-3',
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5", sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
lg: "h-10 rounded-md px-6 has-[>svg]:px-4", lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
icon: "size-9", icon: 'size-9'
}, }
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: 'default',
size: "default", size: 'default'
}, }
}); });
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"]; export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>["size"]; export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> & export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & { WithElementRef<HTMLAnchorAttributes> & {
@@ -42,11 +42,11 @@
<script lang="ts"> <script lang="ts">
let { let {
class: className, class: className,
variant = "default", variant = 'default',
size = "default", size = 'default',
ref = $bindable(null), ref = $bindable(null),
href = undefined, href = undefined,
type = "button", type = 'button',
disabled, disabled,
children, children,
...restProps ...restProps
@@ -60,7 +60,7 @@
class={cn(buttonVariants({ variant, size }), className)} class={cn(buttonVariants({ variant, size }), className)}
href={disabled ? undefined : href} href={disabled ? undefined : href}
aria-disabled={disabled} aria-disabled={disabled}
role={disabled ? "link" : undefined} role={disabled ? 'link' : undefined}
tabindex={disabled ? -1 : undefined} tabindex={disabled ? -1 : undefined}
{...restProps} {...restProps}
> >

View File

@@ -2,8 +2,8 @@ import Root, {
type ButtonProps, type ButtonProps,
type ButtonSize, type ButtonSize,
type ButtonVariant, type ButtonVariant,
buttonVariants, buttonVariants
} from "./button.svelte"; } from './button.svelte';
export { export {
Root, Root,
@@ -13,5 +13,5 @@ export {
buttonVariants, buttonVariants,
type ButtonProps, type ButtonProps,
type ButtonSize, type ButtonSize,
type ButtonVariant, type ButtonVariant
}; };

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="card-action" data-slot="card-action"
class={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)} class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -10,6 +10,6 @@
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script> </script>
<div bind:this={ref} data-slot="card-content" class={cn("px-6", className)} {...restProps}> <div bind:this={ref} data-slot="card-content" class={cn('px-6', className)} {...restProps}>
{@render children?.()} {@render children?.()}
</div> </div>

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<p <p
bind:this={ref} bind:this={ref}
data-slot="card-description" data-slot="card-description"
class={cn("text-muted-foreground text-sm", className)} class={cn('text-sm text-muted-foreground', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="card-footer" data-slot="card-footer"
class={cn("[.border-t]:pt-6 flex items-center px-6", className)} class={cn('flex items-center px-6 [.border-t]:pt-6', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="card-header" data-slot="card-header"
class={cn( class={cn(
"@container/card-header has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6 grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6", '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
className className
)} )}
{...restProps} {...restProps}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="card-title" data-slot="card-title"
class={cn("font-semibold leading-none", className)} class={cn('leading-none font-semibold', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="card" data-slot="card"
class={cn( class={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", 'flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm',
className className
)} )}
{...restProps} {...restProps}

View File

@@ -1,10 +1,10 @@
import Root from "./card.svelte"; import Root from './card.svelte';
import Content from "./card-content.svelte"; import Content from './card-content.svelte';
import Description from "./card-description.svelte"; import Description from './card-description.svelte';
import Footer from "./card-footer.svelte"; import Footer from './card-footer.svelte';
import Header from "./card-header.svelte"; import Header from './card-header.svelte';
import Title from "./card-title.svelte"; import Title from './card-title.svelte';
import Action from "./card-action.svelte"; import Action from './card-action.svelte';
export { export {
Root, Root,
@@ -21,5 +21,5 @@ export {
Footer as CardFooter, Footer as CardFooter,
Header as CardHeader, Header as CardHeader,
Title as CardTitle, Title as CardTitle,
Action as CardAction, Action as CardAction
}; };

View File

@@ -1,13 +1,13 @@
import { clsx, type ClassValue } from "clsx"; import { clsx, type ClassValue } from 'clsx';
import { twMerge } from "tailwind-merge"; import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T; export type WithoutChild<T> = T extends { child?: any } ? Omit<T, 'child'> : T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, "children"> : T; export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T;
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>; export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null }; export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };

View File

@@ -1 +1 @@
export const prerender = true; export const prerender = true;

View File

@@ -1,11 +1,17 @@
<script lang="ts"> <script lang="ts">
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '$lib/components/ui/card/index.js'; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '$lib/components/ui/card/index.js';
</script> </script>
<div class="container mx-auto p-8"> <div class="container mx-auto p-8">
<h1 class="text-4xl font-bold mb-8">Welcome to SvelteKit with shadcn-svelte</h1> <h1 class="mb-8 text-4xl font-bold">Welcome to SvelteKit with shadcn-svelte</h1>
<div class="grid gap-6 md:grid-cols-2"> <div class="grid gap-6 md:grid-cols-2">
<Card> <Card>
<CardHeader> <CardHeader>
@@ -16,7 +22,7 @@
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p class="mb-4">This template includes:</p> <p class="mb-4">This template includes:</p>
<ul class="list-disc list-inside space-y-2"> <ul class="list-inside list-disc space-y-2">
<li>Svelte 5</li> <li>Svelte 5</li>
<li>TypeScript</li> <li>TypeScript</li>
<li>Tailwind CSS 4</li> <li>Tailwind CSS 4</li>
@@ -29,9 +35,7 @@
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>shadcn-svelte</CardTitle> <CardTitle>shadcn-svelte</CardTitle>
<CardDescription> <CardDescription>Beautiful components built with Radix and Tailwind CSS</CardDescription>
Beautiful components built with Radix and Tailwind CSS
</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p class="mb-4">Ready-to-use components:</p> <p class="mb-4">Ready-to-use components:</p>
@@ -46,7 +50,9 @@
<div class="mt-8"> <div class="mt-8">
<p class="text-muted-foreground"> <p class="text-muted-foreground">
Visit <a href="https://svelte.dev/docs/kit" class="underline hover:no-underline">svelte.dev/docs/kit</a> Visit <a href="https://svelte.dev/docs/kit" class="underline hover:no-underline"
>svelte.dev/docs/kit</a
>
to read the documentation to read the documentation
</p> </p>
</div> </div>