migrate from astro+netlify to tanstack start + self-hosted
Replace the Astro static site with a TanStack Start (Bun runtime) app and add Dockerfile + compose files so the site can be served from home-main-2 behind the home-reverse-proxy-1 Caddy instead of Netlify. CI workflow rewritten to build a container image and SSH-deploy to the home host.
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
node_modules
|
||||||
|
.output
|
||||||
|
.nitro
|
||||||
|
.tanstack
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.gitea
|
||||||
|
.github
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.log
|
||||||
|
bun-debug.log*
|
||||||
|
README.md
|
||||||
+69
-37
@@ -1,46 +1,78 @@
|
|||||||
name: Deploy to Netlify
|
name: Build & Deploy unom website
|
||||||
|
run-name: ${{ gitea.actor }} is deploying unom/website
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [main]
|
||||||
- main
|
workflow_dispatch:
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Repository Checkout
|
- name: Set up Docker Buildx
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.bun/install/cache
|
|
||||||
key: ${{ runner.os }}-${{ matrix.bun }}-bun-${{ hashFiles('**/bun.lockb') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-${{ matrix.bun }}-bun-
|
|
||||||
|
|
||||||
- name: Setup Bun
|
|
||||||
uses: oven-sh/setup-bun@v1
|
|
||||||
with:
|
|
||||||
bun-version: latest
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
env:
|
env:
|
||||||
UNOM_PKGS_TOKEN: ${{ secrets.UNOM_PKGS_TOKEN }}
|
BUILDER: builder-unom-website
|
||||||
|
run: |
|
||||||
|
cat > /tmp/buildkitd.toml <<'EOF'
|
||||||
|
[registry."docker.io"]
|
||||||
|
mirrors = ["192.168.1.52:5000"]
|
||||||
|
[registry."192.168.1.52:5000"]
|
||||||
|
http = true
|
||||||
|
insecure = true
|
||||||
|
EOF
|
||||||
|
docker buildx rm "$BUILDER" 2>/dev/null || true
|
||||||
|
docker buildx create --name "$BUILDER" --use --bootstrap \
|
||||||
|
--driver docker-container \
|
||||||
|
--config /tmp/buildkitd.toml
|
||||||
|
|
||||||
- name: Build
|
- name: Log in to Gitea registry
|
||||||
run: bun run build
|
|
||||||
|
|
||||||
- name: Deploy to Netlify
|
|
||||||
uses: nwtgck/actions-netlify@v2
|
|
||||||
with:
|
|
||||||
publish-dir: './dist'
|
|
||||||
production-branch: main
|
|
||||||
deploy-message: "Deploy from Gitea Actions"
|
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
timeout-minutes: 10
|
run: |
|
||||||
|
printf '%s' "$REGISTRY_TOKEN" | docker login git.unom.io -u "$REGISTRY_USER" --password-stdin
|
||||||
|
|
||||||
|
- name: Build & push
|
||||||
|
env:
|
||||||
|
BUILDER: builder-unom-website
|
||||||
|
IMAGE: git.unom.io/${{ gitea.repository }}
|
||||||
|
SHA: ${{ gitea.sha }}
|
||||||
|
run: |
|
||||||
|
docker buildx build \
|
||||||
|
--builder "$BUILDER" \
|
||||||
|
--push \
|
||||||
|
--file ./Dockerfile \
|
||||||
|
--tag "$IMAGE:latest" \
|
||||||
|
--tag "$IMAGE:$SHA" \
|
||||||
|
--cache-from "type=registry,ref=$IMAGE:cache" \
|
||||||
|
--cache-to "type=registry,ref=$IMAGE:cache,mode=min" \
|
||||||
|
.
|
||||||
|
|
||||||
|
- name: Tear down builder
|
||||||
|
if: always()
|
||||||
|
env:
|
||||||
|
BUILDER: builder-unom-website
|
||||||
|
run: |
|
||||||
|
docker buildx rm "$BUILDER" 2>/dev/null || true
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- name: Pull and start web
|
||||||
|
uses: appleboy/ssh-action@v1.2.5
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
username: ${{ secrets.DEPLOY_USER }}
|
||||||
|
port: ${{ secrets.DEPLOY_PORT }}
|
||||||
|
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
docker login git.unom.io -u ${{ secrets.REGISTRY_USER }} -p ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
cd ~/unom-website
|
||||||
|
git fetch origin main
|
||||||
|
git reset --hard origin/main
|
||||||
|
docker compose -f compose.production.yml pull web
|
||||||
|
docker compose -f compose.production.yml up -d --no-build web
|
||||||
|
|||||||
+10
-3
@@ -1,7 +1,11 @@
|
|||||||
# build output
|
# build output
|
||||||
dist/
|
dist/
|
||||||
# generated types
|
.output/
|
||||||
.astro/
|
.nitro/
|
||||||
|
.tanstack/
|
||||||
|
|
||||||
|
# generated router types
|
||||||
|
src/routeTree.gen.ts
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -11,7 +15,7 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
|
bun-debug.log*
|
||||||
|
|
||||||
# environment variables
|
# environment variables
|
||||||
.env
|
.env
|
||||||
@@ -22,3 +26,6 @@ pnpm-debug.log*
|
|||||||
|
|
||||||
# jetbrains setting folder
|
# jetbrains setting folder
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# editor
|
||||||
|
.vscode/
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
FROM oven/bun:alpine AS base
|
||||||
|
|
||||||
|
RUN apk update && apk add --no-cache libc6-compat
|
||||||
|
|
||||||
|
## INSTALLER - install deps and build
|
||||||
|
|
||||||
|
FROM base AS installer
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
RUN --mount=type=cache,target=/root/.bun/install/cache,sharing=shared \
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
## RUNNER - minimal production image
|
||||||
|
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 bunjs && \
|
||||||
|
adduser --system --uid 1001 web
|
||||||
|
|
||||||
|
COPY --from=installer --chown=web:bunjs /app/.output ./.output
|
||||||
|
|
||||||
|
USER web
|
||||||
|
|
||||||
|
CMD ["bun", ".output/server/index.mjs"]
|
||||||
@@ -1,13 +1,36 @@
|
|||||||
# Astro with Tailwind
|
# @unom/website
|
||||||
|
|
||||||
|
The unom.io marketing site. TanStack Start + Bun, deployed to home-main-2.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun create astro@latest -- --template with-tailwindcss
|
bun install
|
||||||
|
bun run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-tailwindcss)
|
Visit <http://localhost:3000>.
|
||||||
[](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/with-tailwindcss)
|
|
||||||
[](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/with-tailwindcss/devcontainer.json)
|
|
||||||
|
|
||||||
Astro comes with [Tailwind](https://tailwindcss.com) support out of the box. This example showcases how to style your Astro project with Tailwind.
|
## Production
|
||||||
|
|
||||||
For complete setup instructions, please see our [Tailwind Integration Guide](https://docs.astro.build/en/guides/integrations-guide/tailwind).
|
The repo is built into a container image (`git.unom.io/unom/website`) by Gitea
|
||||||
|
Actions on push to `main`, then deployed via SSH to `home-main-2`. The
|
||||||
|
container listens on port 3000 inside the network and is exposed on host port
|
||||||
|
3200, which Caddy on `home-reverse-proxy-1` reverse-proxies for unom.io and
|
||||||
|
www.unom.io.
|
||||||
|
|
||||||
|
Run the production image locally:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose -f compose.production.yml pull
|
||||||
|
docker compose -f compose.production.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required CI secrets
|
||||||
|
|
||||||
|
Set on the `unom/website` repo in Gitea Actions:
|
||||||
|
|
||||||
|
| Secret | Purpose |
|
||||||
|
| ------ | ------- |
|
||||||
|
| `REGISTRY_USER` / `REGISTRY_TOKEN` | Push to `git.unom.io` container registry |
|
||||||
|
| `DEPLOY_HOST` / `DEPLOY_USER` / `DEPLOY_PORT` / `DEPLOY_SSH_KEY` | SSH target on home-main-2 (private key matching the `unom-ci-deploy` authorized key) |
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
import { defineConfig } from 'astro/config';
|
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
|
||||||
import mdx from '@astrojs/mdx';
|
|
||||||
|
|
||||||
// https://astro.build/config
|
|
||||||
export default defineConfig({
|
|
||||||
site: "https://www.vspace.one",
|
|
||||||
vite: {
|
|
||||||
plugins: [tailwindcss()]
|
|
||||||
},
|
|
||||||
integrations: [
|
|
||||||
mdx(),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
|
||||||
|
"vcs": {
|
||||||
|
"enabled": false,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": false
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": false,
|
||||||
|
"includes": [
|
||||||
|
"**/src/**/*",
|
||||||
|
"**/vite.config.ts",
|
||||||
|
"!**/src/routeTree.gen.ts",
|
||||||
|
"!**/src/styles/**/*.css"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"parser": {
|
||||||
|
"tailwindDirectives": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "tab"
|
||||||
|
},
|
||||||
|
"assist": {
|
||||||
|
"actions": {
|
||||||
|
"source": {
|
||||||
|
"organizeImports": "on"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
name: unom-website-prod
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: git.unom.io/unom/website:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3200:3000"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
name: unom-website-dev
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
restart: unless-stopped
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
+32
-10
@@ -1,20 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "@unom/website",
|
"name": "@unom/website",
|
||||||
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"imports": {
|
||||||
|
"#/*": "./src/*"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "vite dev --port 3000 --host",
|
||||||
"build": "astro build",
|
"build": "vite build",
|
||||||
"preview": "astro preview",
|
"start": "bun .output/server/index.mjs",
|
||||||
"astro": "astro"
|
"typecheck": "tsc --noEmit",
|
||||||
|
"format": "biome format",
|
||||||
|
"lint": "biome lint",
|
||||||
|
"check": "biome check"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/mdx": "^4.3.0",
|
|
||||||
"@fontsource/inter": "^5.2.5",
|
"@fontsource/inter": "^5.2.5",
|
||||||
"@tailwindcss/vite": "^4.1.3",
|
"@tailwindcss/vite": "^4.3.0",
|
||||||
"astro": "^5.8.0",
|
"@tanstack/react-router": "latest",
|
||||||
|
"@tanstack/react-start": "latest",
|
||||||
|
"@tanstack/router-plugin": "^1.168.9",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"tailwind-merge": "^3.3.0",
|
"clsx": "^2.1.1",
|
||||||
"tailwindcss": "^4.1.3"
|
"nitro": "^3.0.260429-beta",
|
||||||
|
"react": "^19.2.6",
|
||||||
|
"react-dom": "^19.2.6",
|
||||||
|
"react-markdown": "^9.0.3",
|
||||||
|
"tailwind-merge": "^3.6.0",
|
||||||
|
"tailwindcss": "^4.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "2.4.15",
|
||||||
|
"@types/node": "^25.9.1",
|
||||||
|
"@types/react": "^19.2.15",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^6.0.2",
|
||||||
|
"typescript": "^6.0.3",
|
||||||
|
"vite": "^8.0.14",
|
||||||
|
"vite-tsconfig-paths": "^6.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 3.3 MiB |
@@ -0,0 +1,68 @@
|
|||||||
|
import Section from "./Section";
|
||||||
|
|
||||||
|
type NavigationItem =
|
||||||
|
| { type: "link"; path: string; label: string }
|
||||||
|
| { type: "text"; content: string };
|
||||||
|
|
||||||
|
type NavigationGroup = {
|
||||||
|
title: string;
|
||||||
|
className?: string;
|
||||||
|
items: Array<NavigationItem>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree: Array<NavigationGroup> = [
|
||||||
|
{
|
||||||
|
title: "Übersicht",
|
||||||
|
items: [{ type: "link", path: "/", label: "Startseite" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Rechtliches",
|
||||||
|
items: [
|
||||||
|
{ type: "link", path: "/legal/imprint", label: "Impressum" },
|
||||||
|
{ type: "link", path: "/legal/privacy", label: "Datenschutzerklärung" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Zugehöriges",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "https://enrico.buehler.earth",
|
||||||
|
label: "Enrico Bühler",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
className: "ml-auto mr-0 self-end",
|
||||||
|
items: [{ type: "text", content: "Made with ❤️ in Rottweil" }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="bg-neutral-accent">
|
||||||
|
<Section>
|
||||||
|
<div className="flex flex-row flex-wrap gap-12 w-full pb-8">
|
||||||
|
{tree.map((group) => (
|
||||||
|
<div key={group.title || "misc"} className={group.className}>
|
||||||
|
{group.title && <h3 className="mb-2">{group.title}</h3>}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{group.items.map((item, i) => {
|
||||||
|
if (item.type === "link") {
|
||||||
|
return (
|
||||||
|
<a key={`${item.path}-${i}`} href={item.path}>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <p key={`text-${i}`}>{item.content}</p>;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
import Logo from "./Logo";
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
return (
|
||||||
|
<header
|
||||||
|
id="nav-container"
|
||||||
|
className="fixed -top-1 w-full h-height-header z-50"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row justify-between items-center h-full w-full max-w-max-section m-auto px-section-main-x">
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="w-[120px] h-[120px] flex justify-center items-center rounded-3xl p-2 backdrop-blur-3xl"
|
||||||
|
>
|
||||||
|
<Logo />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { FC, ReactNode } from "react";
|
||||||
|
import Footer from "./Footer";
|
||||||
|
import Header from "./Header";
|
||||||
|
|
||||||
|
const Layout: FC<{ children: ReactNode; showHeader?: boolean }> = ({
|
||||||
|
children,
|
||||||
|
showHeader = true,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showHeader && <Header />}
|
||||||
|
<main className="min-h-screen">{children}</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
---
|
|
||||||
import Section from "../Section.astro";
|
|
||||||
|
|
||||||
enum NavigationItemType {
|
|
||||||
Link = 0,
|
|
||||||
Text = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
type NavigationItem =
|
|
||||||
| {
|
|
||||||
path: string;
|
|
||||||
label: string;
|
|
||||||
type: NavigationItemType.Link;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: NavigationItemType.Text;
|
|
||||||
content: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NavigationGroup = {
|
|
||||||
title: string;
|
|
||||||
class?: string;
|
|
||||||
items: Array<NavigationItem>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tree: Array<NavigationGroup> = [
|
|
||||||
{
|
|
||||||
title: "Übersicht",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
type: NavigationItemType.Link,
|
|
||||||
label: "Startseite",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Rechtliches",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
path: "/legal/imprint",
|
|
||||||
label: "Impressum",
|
|
||||||
type: NavigationItemType.Link,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/legal/privacy",
|
|
||||||
label: "Datenschutzerklärung",
|
|
||||||
type: NavigationItemType.Link,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Zugehöriges",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
path: "https://enrico.buehler.earth",
|
|
||||||
label: "Enrico Bühler",
|
|
||||||
type: NavigationItemType.Link,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "",
|
|
||||||
class: "ml-auto mr-0 self-end",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: NavigationItemType.Text,
|
|
||||||
content: "Made with ❤️ in Rottweil",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
---
|
|
||||||
|
|
||||||
<footer class="bg-neutral-accent">
|
|
||||||
<Section>
|
|
||||||
<div class="flex flex-row flex-wrap gap-12 w-full pb-8">
|
|
||||||
{
|
|
||||||
tree.map((group) => (
|
|
||||||
<div class={group.class}>
|
|
||||||
{group.title && <h3 class="mb-2">{group.title}</h3>}
|
|
||||||
<div class="flex flex-col">
|
|
||||||
{group.items.map((item) => {
|
|
||||||
switch (item.type) {
|
|
||||||
case NavigationItemType.Link:
|
|
||||||
return <a href={item.path}>{item.label}</a>;
|
|
||||||
case NavigationItemType.Text:
|
|
||||||
return <p>{item.content}</p>;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
</footer>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
import Logo from "../Logo/Logo.astro";
|
|
||||||
---
|
|
||||||
|
|
||||||
<header id="nav-container" class="fixed -top-1 w-full h-height-header z-50">
|
|
||||||
<div
|
|
||||||
class="flex flex-row justify-between items-center h-full w-full max-w-max-section m-auto px-section-main-x"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="/"
|
|
||||||
class="w-[120px] h-[120px] flex justify-center items-center rounded-3xl p-2 backdrop-blur-3xl"
|
|
||||||
>
|
|
||||||
<Logo />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import type { FC, ReactNode } from "react";
|
||||||
|
import Section from "./Section";
|
||||||
|
|
||||||
|
const LegalPage: FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<article className="markdown">{children}</article>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LegalPage;
|
||||||
@@ -1,28 +1,32 @@
|
|||||||
<!--?xml version="1.0" encoding="UTF-8"?-->
|
export default function Logo() {
|
||||||
|
return (
|
||||||
<svg
|
<svg
|
||||||
id="Ebene_1"
|
aria-label="unom Logo"
|
||||||
data-name="Ebene 1"
|
role="img"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 643.6 382.49"
|
viewBox="0 0 643.6 382.49"
|
||||||
>
|
>
|
||||||
|
<title>unom</title>
|
||||||
<path
|
<path
|
||||||
class="fill-main"
|
className="fill-main"
|
||||||
d="M128.61,230.82v25.76c-3.39,3.13-15.9,9.57-36.95,9.57s-33.56-6.44-36.95-9.57v-69.51c-6.51-1.15-13.96-1.89-22.4-1.89s-15.9.75-22.4,1.89v71.69c0,30.24,34.38,52.18,81.75,52.18s81.75-21.95,81.75-52.18v-27.9c.54-3.26,10.53-11.58,29.7-16.61v-45.87c-44.16,8.16-74.5,32.3-74.5,62.43Z"
|
d="M128.61,230.82v25.76c-3.39,3.13-15.9,9.57-36.95,9.57s-33.56-6.44-36.95-9.57v-69.51c-6.51-1.15-13.96-1.89-22.4-1.89s-15.9.75-22.4,1.89v71.69c0,30.24,34.38,52.18,81.75,52.18s81.75-21.95,81.75-52.18v-27.9c.54-3.26,10.53-11.58,29.7-16.61v-45.87c-44.16,8.16-74.5,32.3-74.5,62.43Z"
|
||||||
></path>
|
/>
|
||||||
<path
|
<path
|
||||||
class="fill-main"
|
className="fill-main"
|
||||||
d="M552.55,63.76c-27.7,0-50.91,7.57-65.32,19.94-12.48-7.97-28.41-13.93-46.65-17.17.74,3.66,1.14,7.42,1.14,11.28v34.8c19.17,5.03,29.16,13.33,29.7,16.56v52.56c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-63.83c3.41-3.08,15.75-9.34,36.33-9.34s32.92,6.26,36.33,9.34v63.83c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-66.1c0-30.06-34.12-51.88-81.13-51.88Z"
|
d="M552.55,63.76c-27.7,0-50.91,7.57-65.32,19.94-12.48-7.97-28.41-13.93-46.65-17.17.74,3.66,1.14,7.42,1.14,11.28v34.8c19.17,5.03,29.16,13.33,29.7,16.56v52.56c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-63.83c3.41-3.08,15.75-9.34,36.33-9.34s32.92,6.26,36.33,9.34v63.83c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-66.1c0-30.06-34.12-51.88-81.13-51.88Z"
|
||||||
></path>
|
/>
|
||||||
<path
|
<path
|
||||||
class="fill-main"
|
className="fill-main"
|
||||||
d="M322.42,370.08c-61.63,0-108.1-28.12-108.1-65.41V77.82c0-37.29,46.48-65.41,108.1-65.41s108.1,28.12,108.1,65.41v226.86c0,37.29-46.47,65.41-108.1,65.41ZM322.42,57.2c-41.19,0-62.5,15.85-63.31,20.66v226.81c.81,4.76,22.11,20.61,63.31,20.61s62.5-15.85,63.31-20.66V77.82c-.81-4.76-22.12-20.61-63.31-20.61Z"
|
d="M322.42,370.08c-61.63,0-108.1-28.12-108.1-65.41V77.82c0-37.29,46.48-65.41,108.1-65.41s108.1,28.12,108.1,65.41v226.86c0,37.29-46.47,65.41-108.1,65.41ZM322.42,57.2c-41.19,0-62.5,15.85-63.31,20.66v226.81c.81,4.76,22.11,20.61,63.31,20.61s62.5-15.85,63.31-20.66V77.82c-.81-4.76-22.12-20.61-63.31-20.61Z"
|
||||||
></path>
|
/>
|
||||||
<path
|
<path
|
||||||
class="fill-main"
|
className="fill-main"
|
||||||
d="M353.48,72.19c-32.05,10.89-52.91,31.4-53.44,56.03h-.03s0,.96,0,.96v32.77c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-32.73c.56-3.27,10.54-11.57,29.7-16.6v-31.07c-2.97-2.53-10.07-6.46-21.03-9.36Z"
|
d="M353.48,72.19c-32.05,10.89-52.91,31.4-53.44,56.03h-.03s0,.96,0,.96v32.77c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-32.73c.56-3.27,10.54-11.57,29.7-16.6v-31.07c-2.97-2.53-10.07-6.46-21.03-9.36Z"
|
||||||
></path>
|
/>
|
||||||
<path
|
<path
|
||||||
class="fill-main"
|
className="fill-main"
|
||||||
d="M322.42,290.86c8.44,0,15.9-.75,22.4-1.89v-58.15c0-30.13-30.35-54.26-74.5-62.43v45.87c19.17,5.03,29.16,13.33,29.7,16.56v58.15c6.51,1.15,13.96,1.89,22.4,1.89Z"
|
d="M322.42,290.86c8.44,0,15.9-.75,22.4-1.89v-58.15c0-30.13-30.35-54.26-74.5-62.43v45.87c19.17,5.03,29.16,13.33,29.7,16.56v58.15c6.51,1.15,13.96,1.89,22.4,1.89Z"
|
||||||
></path>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
|
|
||||||
<defs>
|
|
||||||
<style>
|
|
||||||
.cls-1 {
|
|
||||||
fill: #654eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cls-2 {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<rect class="cls-1" x="87.76" y="87.76" width="824.48" height="824.48" rx="200.39" ry="200.39"/>
|
|
||||||
<g>
|
|
||||||
<path class="cls-2" d="M306.81,545.97v25.76c-3.39,3.13-15.9,9.57-36.95,9.57s-33.56-6.44-36.95-9.57v-69.51c-6.51-1.15-13.96-1.89-22.4-1.89s-15.9.75-22.4,1.89v71.69c0,30.24,34.38,52.18,81.75,52.18s81.75-21.95,81.75-52.18v-27.9c.54-3.26,10.53-11.58,29.7-16.61v-45.87c-44.16,8.16-74.5,32.3-74.5,62.43Z"/>
|
|
||||||
<path class="cls-2" d="M730.75,378.92c-27.7,0-50.91,7.57-65.32,19.94-12.48-7.97-28.41-13.93-46.65-17.17.74,3.66,1.14,7.42,1.14,11.28v34.8c19.17,5.03,29.16,13.33,29.7,16.56v52.56c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-63.83c3.41-3.08,15.75-9.34,36.33-9.34s32.92,6.26,36.33,9.34v63.83c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-66.1c0-30.06-34.12-51.88-81.13-51.88Z"/>
|
|
||||||
<path class="cls-2" d="M500.62,685.24c-61.63,0-108.1-28.12-108.1-65.41v-226.86c0-37.29,46.48-65.41,108.1-65.41s108.1,28.12,108.1,65.41v226.86c0,37.29-46.47,65.41-108.1,65.41ZM500.62,372.36c-41.19,0-62.5,15.85-63.31,20.66v226.81c.81,4.76,22.11,20.61,63.31,20.61s62.5-15.85,63.31-20.66v-226.81c-.81-4.76-22.12-20.61-63.31-20.61Z"/>
|
|
||||||
<path class="cls-2" d="M531.68,387.34c-32.05,10.89-52.91,31.4-53.44,56.03h-.03s0,.96,0,.96v32.77c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-32.73c.56-3.27,10.54-11.57,29.7-16.6v-31.07c-2.97-2.53-10.07-6.46-21.03-9.36Z"/>
|
|
||||||
<path class="cls-2" d="M500.62,606.01c8.44,0,15.9-.75,22.4-1.89v-58.15c0-30.13-30.35-54.26-74.5-62.43v45.87c19.17,5.03,29.16,13.33,29.7,16.56v58.15c6.51,1.15,13.96,1.89,22.4,1.89Z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,28 @@
|
|||||||
|
export default function LogoQuadBG() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
aria-label="unom Logo"
|
||||||
|
role="img"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 1000 1000"
|
||||||
|
>
|
||||||
|
<title>unom</title>
|
||||||
|
<rect
|
||||||
|
fill="#654eff"
|
||||||
|
x="87.76"
|
||||||
|
y="87.76"
|
||||||
|
width="824.48"
|
||||||
|
height="824.48"
|
||||||
|
rx="200.39"
|
||||||
|
ry="200.39"
|
||||||
|
/>
|
||||||
|
<g fill="#fff">
|
||||||
|
<path d="M306.81,545.97v25.76c-3.39,3.13-15.9,9.57-36.95,9.57s-33.56-6.44-36.95-9.57v-69.51c-6.51-1.15-13.96-1.89-22.4-1.89s-15.9.75-22.4,1.89v71.69c0,30.24,34.38,52.18,81.75,52.18s81.75-21.95,81.75-52.18v-27.9c.54-3.26,10.53-11.58,29.7-16.61v-45.87c-44.16,8.16-74.5,32.3-74.5,62.43Z" />
|
||||||
|
<path d="M730.75,378.92c-27.7,0-50.91,7.57-65.32,19.94-12.48-7.97-28.41-13.93-46.65-17.17.74,3.66,1.14,7.42,1.14,11.28v34.8c19.17,5.03,29.16,13.33,29.7,16.56v52.56c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-63.83c3.41-3.08,15.75-9.34,36.33-9.34s32.92,6.26,36.33,9.34v63.83c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-66.1c0-30.06-34.12-51.88-81.13-51.88Z" />
|
||||||
|
<path d="M500.62,685.24c-61.63,0-108.1-28.12-108.1-65.41v-226.86c0-37.29,46.48-65.41,108.1-65.41s108.1,28.12,108.1,65.41v226.86c0,37.29-46.47,65.41-108.1,65.41ZM500.62,372.36c-41.19,0-62.5,15.85-63.31,20.66v226.81c.81,4.76,22.11,20.61,63.31,20.61s62.5-15.85,63.31-20.66v-226.81c-.81-4.76-22.12-20.61-63.31-20.61Z" />
|
||||||
|
<path d="M531.68,387.34c-32.05,10.89-52.91,31.4-53.44,56.03h-.03s0,.96,0,.96v32.77c6.51,1.15,13.96,1.89,22.4,1.89s15.9-.75,22.4-1.89v-32.73c.56-3.27,10.54-11.57,29.7-16.6v-31.07c-2.97-2.53-10.07-6.46-21.03-9.36Z" />
|
||||||
|
<path d="M500.62,606.01c8.44,0,15.9-.75,22.4-1.89v-58.15c0-30.13-30.35-54.26-74.5-62.43v45.87c19.17,5.03,29.16,13.33,29.7,16.56v58.15c6.51,1.15,13.96,1.89,22.4,1.89Z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
import type { HTMLAttributes } from "astro/types";
|
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
|
||||||
|
|
||||||
const section = cva("relative w-full", {
|
|
||||||
variants: {
|
|
||||||
padding: {
|
|
||||||
true: "px-section-main-x py-section-main-y",
|
|
||||||
false: "p-0",
|
|
||||||
},
|
|
||||||
maxWidth: {
|
|
||||||
true: "max-w-max-section mx-auto",
|
|
||||||
false: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export interface Props
|
|
||||||
extends HTMLAttributes<"section">,
|
|
||||||
VariantProps<typeof section> {}
|
|
||||||
|
|
||||||
const { padding = true, maxWidth = true, ...props } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<section {...props} class={section({ padding, maxWidth })}>
|
|
||||||
<slot />
|
|
||||||
</section>
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
import type { HTMLAttributes } from "react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const section = cva("relative w-full", {
|
||||||
|
variants: {
|
||||||
|
padding: {
|
||||||
|
true: "px-section-main-x py-section-main-y",
|
||||||
|
false: "p-0",
|
||||||
|
},
|
||||||
|
maxWidth: {
|
||||||
|
true: "max-w-max-section mx-auto",
|
||||||
|
false: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
padding: true,
|
||||||
|
maxWidth: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
type Props = HTMLAttributes<HTMLElement> & VariantProps<typeof section>;
|
||||||
|
|
||||||
|
export default function Section({
|
||||||
|
padding,
|
||||||
|
maxWidth,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
{...props}
|
||||||
|
className={cn(section({ padding, maxWidth }), className)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
---
|
|
||||||
layout: "@/layouts/MarkdownLayout.astro"
|
|
||||||
title: Impressum
|
|
||||||
---
|
|
||||||
|
|
||||||
# Impressum
|
# Impressum
|
||||||
|
|
||||||
Angaben gemäß § 5 TMG
|
Angaben gemäß § 5 TMG
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
---
|
|
||||||
layout: "@/layouts/MarkdownLayout.astro"
|
|
||||||
title: Datenschutzerklärung
|
|
||||||
---
|
|
||||||
|
|
||||||
# Datenschutzerklärung
|
# Datenschutzerklärung
|
||||||
|
|
||||||
## 1. Datenschutz auf einen Blick
|
## 1. Datenschutz auf einen Blick
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
import { cn } from "@/lib/utils.ts";
|
|
||||||
|
|
||||||
const anim = {
|
|
||||||
old: {
|
|
||||||
name: "pageLeave",
|
|
||||||
duration: "0.0s",
|
|
||||||
easing: "ease-out",
|
|
||||||
fillMode: "forwards",
|
|
||||||
},
|
|
||||||
new: {
|
|
||||||
name: "pageEnter",
|
|
||||||
duration: "0.7s",
|
|
||||||
easing: "cubic-bezier(0.23, 1, 0.32, 1)",
|
|
||||||
fillMode: "forwards",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
inset?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slideAnimation = {
|
|
||||||
forwards: anim,
|
|
||||||
backwards: anim,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { inset = true } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<main
|
|
||||||
transition:animate={slideAnimation}
|
|
||||||
class={cn("min-h-screen", inset ? "pt-height-header" : "")}
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</main>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
import "@/styles/markdown.css";
|
|
||||||
import Section from "@/components/Section.astro";
|
|
||||||
import MainLayout from "./MainLayout.astro";
|
|
||||||
import RootLayout from "./RootLayout.astro";
|
|
||||||
|
|
||||||
const { frontmatter } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<RootLayout title={frontmatter.title}>
|
|
||||||
<MainLayout>
|
|
||||||
<Section>
|
|
||||||
<slot />
|
|
||||||
</Section>
|
|
||||||
</MainLayout>
|
|
||||||
</RootLayout>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
import Footer from "@/components/Layout/Footer.astro";
|
|
||||||
import Header from "@/components/Layout/Header.astro";
|
|
||||||
import "@/styles/global.css";
|
|
||||||
import "@fontsource/inter";
|
|
||||||
import { ClientRouter } from "astro:transitions";
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
title: string;
|
|
||||||
showHeader?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title, showHeader = true } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="description" content="Kreative Webentwicklung aus Rottweil" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
||||||
<slot name="head" />
|
|
||||||
<script
|
|
||||||
is:inline
|
|
||||||
defer
|
|
||||||
data-domain="unom.io"
|
|
||||||
src="https://analytics.unom.io/js/plausible.js"></script>
|
|
||||||
<meta name="generator" content={Astro.generator} />
|
|
||||||
<title>unom - {title}</title>
|
|
||||||
<ClientRouter />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{showHeader && <Header transition:name="header" transition:persist />}
|
|
||||||
<slot />
|
|
||||||
<Footer />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
import RootLayout from "@/layouts/RootLayout.astro";
|
|
||||||
import "../styles/global.css";
|
|
||||||
import MainLayout from "@/layouts/MainLayout.astro";
|
|
||||||
import Landing from "@/sections/Landing/Landing.astro";
|
|
||||||
---
|
|
||||||
|
|
||||||
<RootLayout showHeader={false} title="Kreative Webentwicklung">
|
|
||||||
<MainLayout inset={false}>
|
|
||||||
<Landing />
|
|
||||||
</MainLayout>
|
|
||||||
</RootLayout>
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
|
||||||
|
import { routeTree } from "./routeTree.gen";
|
||||||
|
|
||||||
|
export function getRouter() {
|
||||||
|
const router = createTanStackRouter({
|
||||||
|
routeTree,
|
||||||
|
scrollRestoration: true,
|
||||||
|
defaultPreload: "intent",
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "@tanstack/react-router" {
|
||||||
|
interface Register {
|
||||||
|
router: ReturnType<typeof getRouter>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
createRootRoute,
|
||||||
|
HeadContent,
|
||||||
|
Scripts,
|
||||||
|
} from "@tanstack/react-router";
|
||||||
|
import "@fontsource/inter";
|
||||||
|
import Layout from "@/components/Layout";
|
||||||
|
import appCss from "../styles/globals.css?url";
|
||||||
|
|
||||||
|
const SITE_URL = "https://unom.io";
|
||||||
|
|
||||||
|
export const Route = createRootRoute({
|
||||||
|
head: () => ({
|
||||||
|
meta: [
|
||||||
|
{ charSet: "utf-8" },
|
||||||
|
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
||||||
|
{ name: "description", content: "Kreative Webentwicklung aus Rottweil" },
|
||||||
|
{ title: "unom - Kreative Webentwicklung" },
|
||||||
|
{ property: "og:title", content: "unom - Kreative Webentwicklung" },
|
||||||
|
{
|
||||||
|
property: "og:description",
|
||||||
|
content: "Kreative Webentwicklung aus Rottweil",
|
||||||
|
},
|
||||||
|
{ property: "og:url", content: SITE_URL },
|
||||||
|
{ property: "og:type", content: "website" },
|
||||||
|
],
|
||||||
|
links: [
|
||||||
|
{ rel: "stylesheet", href: appCss },
|
||||||
|
{ rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
|
||||||
|
],
|
||||||
|
scripts: [
|
||||||
|
{
|
||||||
|
defer: true,
|
||||||
|
"data-domain": "unom.io",
|
||||||
|
src: "https://analytics.unom.io/js/plausible.js",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
shellComponent: RootDocument,
|
||||||
|
});
|
||||||
|
|
||||||
|
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<HeadContent />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Layout>{children}</Layout>
|
||||||
|
<Scripts />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import Landing from "@/sections/Landing";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/")({
|
||||||
|
component: HomePage,
|
||||||
|
head: () => ({
|
||||||
|
meta: [{ title: "unom - Kreative Webentwicklung" }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function HomePage() {
|
||||||
|
return <Landing />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import LegalPage from "@/components/LegalPage";
|
||||||
|
import content from "@/content/legal/imprint.md?raw";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/legal/imprint")({
|
||||||
|
component: ImprintPage,
|
||||||
|
head: () => ({
|
||||||
|
meta: [{ title: "unom - Impressum" }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function ImprintPage() {
|
||||||
|
return (
|
||||||
|
<LegalPage>
|
||||||
|
<ReactMarkdown>{content}</ReactMarkdown>
|
||||||
|
</LegalPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import LegalPage from "@/components/LegalPage";
|
||||||
|
import content from "@/content/legal/privacy.md?raw";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/legal/privacy")({
|
||||||
|
component: PrivacyPage,
|
||||||
|
head: () => ({
|
||||||
|
meta: [{ title: "unom - Datenschutzerklärung" }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function PrivacyPage() {
|
||||||
|
return (
|
||||||
|
<LegalPage>
|
||||||
|
<ReactMarkdown>{content}</ReactMarkdown>
|
||||||
|
</LegalPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import LogoQuadBG from "@/components/LogoQuadBG";
|
||||||
|
import bgDark from "@/assets/unom_Logo_5_Dark.webp";
|
||||||
|
|
||||||
|
export default function Landing() {
|
||||||
|
return (
|
||||||
|
<div className="w-full h-screen object-cover">
|
||||||
|
<div className="w-full h-full flex items-center justify-center absolute">
|
||||||
|
<div className="w-[200px]">
|
||||||
|
<LogoQuadBG />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
src={bgDark}
|
||||||
|
width={3840}
|
||||||
|
height={2160}
|
||||||
|
alt="Ein 3D Rendering des unom Logos"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
import { Image } from "astro:assets";
|
|
||||||
import bgDark from "./unom_Logo_5_Dark.webp";
|
|
||||||
import LogoQuadBG from "@/components/Logo/LogoQuadBG.astro";
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="w-full h-screen object-cover">
|
|
||||||
<div class="w-full h-full flex items-center justify-center absolute">
|
|
||||||
<div class="w-[200px]">
|
|
||||||
<LogoQuadBG />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Image
|
|
||||||
class="h-full object-cover"
|
|
||||||
src={bgDark}
|
|
||||||
width={3840}
|
|
||||||
height={2160}
|
|
||||||
alt="Ein 3D Rendering des unom Logos"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import handler from "@tanstack/react-start/server-entry";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fetch(req: Request): Promise<Response> {
|
||||||
|
return handler.fetch(req);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
@import "./timing-functions.css" layer(base);
|
@import "./timing-functions.css" layer(base);
|
||||||
@import "./page-transition.css" layer(base);
|
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--main: oklch(100 0 0);
|
--main: oklch(100 0 0);
|
||||||
|
--secondary: oklch(0.85 0 0);
|
||||||
--brand: oklch(0.5609 0.2483 280.67);
|
--brand: oklch(0.5609 0.2483 280.67);
|
||||||
--neutral: oklch(0.155 0.0395 285.68);
|
--neutral: oklch(0.155 0.0395 285.68);
|
||||||
--neutral-accent: oklch(0.1 0.0395 285.68);
|
--neutral-accent: oklch(0.1 0.0395 285.68);
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
|
|
||||||
--font-display: "Ubuntu", ui-sans-serif, system-ui, sans-serif;
|
--font-display: "Ubuntu", ui-sans-serif, system-ui, sans-serif;
|
||||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||||
--font-mono: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
--font-mono:
|
||||||
|
Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||||
|
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
@@ -51,19 +52,6 @@
|
|||||||
--container-max-section: 100%;
|
--container-max-section: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @variant dark {
|
|
||||||
:root {
|
|
||||||
--main: oklch(1 0 0);
|
|
||||||
--secondary: oklch(0.8 0 0);
|
|
||||||
--primary: oklch(84.44% 0.2131 153.61);
|
|
||||||
--neutral: oklch(16.36% 0.0088 172.9);
|
|
||||||
--neutral-accent: oklch(19.73% 0.032 168.99);
|
|
||||||
--highlight: oklch(66.39% 0.2398 3.2);
|
|
||||||
--success: oklch(91.1% 0.1605 148.89);
|
|
||||||
--error: oklch(67.36% 0.2339 0.92);
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
@variant lg {
|
@variant lg {
|
||||||
:root {
|
:root {
|
||||||
--container-max-section: 1000px;
|
--container-max-section: 1000px;
|
||||||
@@ -93,11 +81,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@apply text-main font-display;
|
@apply text-main font-display text-3xl font-bold;
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
@apply text-3xl font-bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@@ -120,10 +104,6 @@
|
|||||||
@apply decoration-main text-main;
|
@apply decoration-main text-main;
|
||||||
}
|
}
|
||||||
|
|
||||||
[astro-icon] {
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
td {
|
||||||
@apply px-3 py-1 border;
|
@apply px-3 py-1 border;
|
||||||
}
|
}
|
||||||
@@ -154,4 +134,30 @@
|
|||||||
@apply max-w-[600px] text-base text-secondary;
|
@apply max-w-[600px] text-base text-secondary;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown {
|
||||||
|
& h1 {
|
||||||
|
@apply mb-4! mt-8!;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
@apply mb-4! mt-6!;
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
@apply mb-2! whitespace-break-spaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
@apply ml-4!;
|
||||||
|
}
|
||||||
|
|
||||||
|
& li {
|
||||||
|
@apply ml-4!;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
@reference "./global.css";
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
@apply mb-4! mt-8!;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5 {
|
|
||||||
@apply mb-4! mt-6!;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply mb-2! whitespace-break-spaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
@apply ml-4!;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
@apply ml-4!;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
@keyframes pageEnter {
|
|
||||||
from {
|
|
||||||
transform: translateY(40px);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes pageLeave {
|
|
||||||
from {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+21
-12
@@ -1,18 +1,27 @@
|
|||||||
{
|
{
|
||||||
"extends": "astro/tsconfigs/strictest",
|
"include": ["**/*.ts", "**/*.tsx"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "./src",
|
"target": "ES2022",
|
||||||
"strict": true,
|
"jsx": "react-jsx",
|
||||||
|
"module": "ESNext",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./src/*"],
|
||||||
|
"./*": ["./*"]
|
||||||
},
|
},
|
||||||
"plugins": [
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
{
|
"types": ["vite/client"],
|
||||||
"name": "@astrojs/ts-plugin"
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"jsx": "preserve",
|
|
||||||
"jsxImportSource": "solid-js"
|
|
||||||
},
|
|
||||||
"exclude": ["node_modules", "dist"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
||||||
|
import viteReact from "@vitejs/plugin-react";
|
||||||
|
import { nitro } from "nitro/vite";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
tsconfigPaths: true,
|
||||||
|
dedupe: ["react", "react-dom"],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
tailwindcss(),
|
||||||
|
tanstackStart(),
|
||||||
|
nitro({
|
||||||
|
preset: "bun",
|
||||||
|
}),
|
||||||
|
viteReact(),
|
||||||
|
],
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user