From 2ad458c018da8c261fa4a88d4822440c5254d3ee Mon Sep 17 00:00:00 2001 From: h Date: Tue, 8 Sep 2026 19:15:15 +0200 Subject: [PATCH] Initial template --- .gitignore | 2 + README.md | 10 ++ copier.yml | 48 +++++++ template/.claude/rules/svelte.md | 23 ++++ template/.claude/rules/ultracite.md | 123 +++++++++++++++++ template/.claude/settings.json | 15 ++ template/.mcp.json | 8 ++ template/frontend/.dockerignore | 5 + template/frontend/.env.example | 1 + template/frontend/.gitignore | 23 ++++ template/frontend/.npmrc | 1 + template/frontend/CLAUDE.md | 6 + template/frontend/Makefile.jinja | 17 +++ template/frontend/biome.jsonc.jinja | 44 ++++++ .../frontend/docker-compose.local.yml.jinja | 8 ++ .../frontend/docker-compose.prod.yml.jinja | 15 ++ template/frontend/docker-compose.yml.jinja | 34 +++++ template/frontend/package.json.jinja | 41 ++++++ template/frontend/src/app.d.ts.jinja | 13 ++ template/frontend/src/app.html | 13 ++ template/frontend/src/lib/api/client.ts | 56 ++++++++ template/frontend/src/lib/index.ts | 2 + ...ry == 'shadcn' %}utils.ts{% endif %}.jinja | 15 ++ .../frontend/src/routes/+layout.svelte.jinja | 12 ++ .../frontend/src/routes/+page.svelte.jinja | 13 ++ template/frontend/src/routes/layout.css.jinja | 129 ++++++++++++++++++ ... == 'static' %}+layout.ts{% endif %}.jinja | 2 + template/frontend/svelte.config.js.jinja | 36 +++++ template/frontend/tsconfig.json.jinja | 20 +++ template/frontend/vite.config.ts | 15 ++ ...rve != 'api' %}Dockerfile{% endif %}.jinja | 55 ++++++++ ...ve == 'static' %}serve.ts{% endif %}.jinja | 36 +++++ ...shadcn' %}components.json{% endif %}.jinja | 20 +++ .../.gitignore | 3 + .../{{ _copier_conf.answers_file }}.jinja | 1 + 35 files changed, 865 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 copier.yml create mode 100644 template/.claude/rules/svelte.md create mode 100644 template/.claude/rules/ultracite.md create mode 100644 template/.claude/settings.json create mode 100644 template/.mcp.json create mode 100644 template/frontend/.dockerignore create mode 100644 template/frontend/.env.example create mode 100644 template/frontend/.gitignore create mode 100644 template/frontend/.npmrc create mode 100644 template/frontend/CLAUDE.md create mode 100644 template/frontend/Makefile.jinja create mode 100644 template/frontend/biome.jsonc.jinja create mode 100644 template/frontend/docker-compose.local.yml.jinja create mode 100644 template/frontend/docker-compose.prod.yml.jinja create mode 100644 template/frontend/docker-compose.yml.jinja create mode 100644 template/frontend/package.json.jinja create mode 100644 template/frontend/src/app.d.ts.jinja create mode 100644 template/frontend/src/app.html create mode 100644 template/frontend/src/lib/api/client.ts create mode 100644 template/frontend/src/lib/index.ts create mode 100644 template/frontend/src/lib/{% if ui_library == 'shadcn' %}utils.ts{% endif %}.jinja create mode 100644 template/frontend/src/routes/+layout.svelte.jinja create mode 100644 template/frontend/src/routes/+page.svelte.jinja create mode 100644 template/frontend/src/routes/layout.css.jinja create mode 100644 template/frontend/src/routes/{% if frontend_adapter == 'static' %}+layout.ts{% endif %}.jinja create mode 100644 template/frontend/svelte.config.js.jinja create mode 100644 template/frontend/tsconfig.json.jinja create mode 100644 template/frontend/vite.config.ts create mode 100644 template/frontend/{% if serve != 'api' %}Dockerfile{% endif %}.jinja create mode 100644 template/frontend/{% if serve == 'static' %}serve.ts{% endif %}.jinja create mode 100644 template/frontend/{% if ui_library == 'shadcn' %}components.json{% endif %}.jinja create mode 100644 template/{% if include_impeccable %}.impeccable{% endif %}/.gitignore create mode 100644 template/{{ _copier_conf.answers_file }}.jinja diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4befed3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..59691f8 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# frontend-svelte + +Copier template for a `frontend/` part: SvelteKit on Bun with Tailwind v4, ultracite (biome) and optional shadcn-svelte, served by a Bun server with SSR, as a static SPA behind `serve.ts`, or as a static SPA that the backend api serves from `frontend/build`. + +Ships `frontend/docker-compose.yml` with root-relative paths, `docker-compose.local.yml` (published ports) and `docker-compose.prod.yml` (the `caddy` network), a `Makefile` with `fmt`, `check`, `build` and `dev`, `.env.example`, `CLAUDE.md`, `.claude/rules` for Svelte and ultracite, the Svelte MCP server and `.impeccable/` scaffolding. Meant to be applied on top of the `infra` template. + +```sh +copier copy --trust -d project_name="My Thing" -d serve=bun +copier update -a .copier-answers.frontend.yml +``` diff --git a/copier.yml b/copier.yml new file mode 100644 index 0000000..46eb519 --- /dev/null +++ b/copier.yml @@ -0,0 +1,48 @@ +_subdirectory: template +_templates_suffix: .jinja +_answers_file: .copier-answers.frontend.yml +_envops: + keep_trailing_newline: true + +project_name: + type: str + help: Project name + +project_slug: + type: str + help: Package name, snake_case + default: "{{ project_name | lower | replace(' ', '_') | replace('-', '_') | replace('.', '_') }}" + +serve: + type: str + help: How the frontend is served + choices: + Bun server with SSR (svelte-adapter-bun): bun + Static SPA behind serve.ts (adapter-static): static + Static SPA served by the backend api: api + default: bun + +ui_library: + type: str + choices: + none (Tailwind v4): none + shadcn-svelte: shadcn + default: none + +include_impeccable: + type: bool + help: .impeccable/ scaffolding for the impeccable skill + default: true + +install_deps: + type: bool + help: bun install after generation + default: true + +frontend_adapter: + type: str + when: false + default: "{{ 'bun' if serve == 'bun' else 'static' }}" + +_tasks: + - "{% if _copier_operation == 'copy' and install_deps %}sh -c 'cd frontend && bun install'{% else %}true{% endif %}" diff --git a/template/.claude/rules/svelte.md b/template/.claude/rules/svelte.md new file mode 100644 index 0000000..4711cd0 --- /dev/null +++ b/template/.claude/rules/svelte.md @@ -0,0 +1,23 @@ +You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively: + +## Available Svelte MCP Tools: + +### 1. list-sections + +Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths. +When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections. + +### 2. get-documentation + +Retrieves full documentation content for specific sections. Accepts single or multiple sections. +After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task. + +### 3. svelte-autofixer + +Analyzes Svelte code and returns issues and suggestions. +You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned. + +### 4. playground-link + +Generates a Svelte Playground link with the provided code. +After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project. diff --git a/template/.claude/rules/ultracite.md b/template/.claude/rules/ultracite.md new file mode 100644 index 0000000..05ab812 --- /dev/null +++ b/template/.claude/rules/ultracite.md @@ -0,0 +1,123 @@ +# Ultracite Code Standards + +This project uses **Ultracite**, a zero-config preset that enforces strict code quality standards through automated formatting and linting. + +## Quick Reference + +- **Format code**: `bun x ultracite fix` +- **Check for issues**: `bun x ultracite check` +- **Diagnose setup**: `bun x ultracite doctor` + +Biome (the underlying engine) provides robust linting and formatting. Most issues are automatically fixable. + +--- + +## Core Principles + +Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity. + +### Type Safety & Explicitness + +- Use explicit types for function parameters and return values when they enhance clarity +- Prefer `unknown` over `any` when the type is genuinely unknown +- Use const assertions (`as const`) for immutable values and literal types +- Leverage TypeScript's type narrowing instead of type assertions +- Use meaningful variable names instead of magic numbers - extract constants with descriptive names + +### Modern JavaScript/TypeScript + +- Use arrow functions for callbacks and short functions +- Prefer `for...of` loops over `.forEach()` and indexed `for` loops +- Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access +- Prefer template literals over string concatenation +- Use destructuring for object and array assignments +- Use `const` by default, `let` only when reassignment is needed, never `var` + +### Async & Promises + +- Always `await` promises in async functions - don't forget to use the return value +- Use `async/await` syntax instead of promise chains for better readability +- Handle errors appropriately in async code with try-catch blocks +- Don't use async functions as Promise executors + +### React & JSX + +- Use function components over class components +- Call hooks at the top level only, never conditionally +- Specify all dependencies in hook dependency arrays correctly +- Use the `key` prop for elements in iterables (prefer unique IDs over array indices) +- Nest children between opening and closing tags instead of passing as props +- Don't define components inside other components +- Use semantic HTML and ARIA attributes for accessibility: + - Provide meaningful alt text for images + - Use proper heading hierarchy + - Add labels for form inputs + - Include keyboard event handlers alongside mouse events + - Use semantic elements (`