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:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Repository Checkout
|
||||
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
|
||||
- name: Set up Docker Buildx
|
||||
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
|
||||
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"
|
||||
- name: Log in to Gitea registry
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
timeout-minutes: 10
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
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
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
.output/
|
||||
.nitro/
|
||||
.tanstack/
|
||||
|
||||
# generated router types
|
||||
src/routeTree.gen.ts
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
@@ -11,7 +15,7 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
bun-debug.log*
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
@@ -22,3 +26,6 @@ pnpm-debug.log*
|
||||
|
||||
# jetbrains setting folder
|
||||
.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
|
||||
bun create astro@latest -- --template with-tailwindcss
|
||||
bun install
|
||||
bun run dev
|
||||
```
|
||||
|
||||
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-tailwindcss)
|
||||
[](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)
|
||||
Visit <http://localhost:3000>.
|
||||
|
||||
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",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"imports": {
|
||||
"#/*": "./src/*"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
"dev": "vite dev --port 3000 --host",
|
||||
"build": "vite build",
|
||||
"start": "bun .output/server/index.mjs",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"format": "biome format",
|
||||
"lint": "biome lint",
|
||||
"check": "biome check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.3.0",
|
||||
"@fontsource/inter": "^5.2.5",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"astro": "^5.8.0",
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"@tanstack/react-router": "latest",
|
||||
"@tanstack/react-start": "latest",
|
||||
"@tanstack/router-plugin": "^1.168.9",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"tailwindcss": "^4.1.3"
|
||||
"clsx": "^2.1.1",
|
||||
"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"?-->
|
||||
<svg
|
||||
id="Ebene_1"
|
||||
data-name="Ebene 1"
|
||||
export default function Logo() {
|
||||
return (
|
||||
<svg
|
||||
aria-label="unom Logo"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 643.6 382.49"
|
||||
>
|
||||
>
|
||||
<title>unom</title>
|
||||
<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"
|
||||
></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"
|
||||
></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"
|
||||
></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"
|
||||
></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"
|
||||
></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
|
||||
|
||||
Angaben gemäß § 5 TMG
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
layout: "@/layouts/MarkdownLayout.astro"
|
||||
title: Datenschutzerklärung
|
||||
---
|
||||
|
||||
# Datenschutzerklärung
|
||||
|
||||
## 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 "./page-transition.css" layer(base);
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--main: oklch(100 0 0);
|
||||
--secondary: oklch(0.85 0 0);
|
||||
--brand: oklch(0.5609 0.2483 280.67);
|
||||
--neutral: oklch(0.155 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-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;
|
||||
|
||||
--radius: 0.625rem;
|
||||
@@ -51,19 +52,6 @@
|
||||
--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 {
|
||||
:root {
|
||||
--container-max-section: 1000px;
|
||||
@@ -93,11 +81,7 @@
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-main font-display;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-3xl font-bold;
|
||||
@apply text-main font-display text-3xl font-bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@@ -120,10 +104,6 @@
|
||||
@apply decoration-main text-main;
|
||||
}
|
||||
|
||||
[astro-icon] {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
td {
|
||||
@apply px-3 py-1 border;
|
||||
}
|
||||
@@ -154,4 +134,30 @@
|
||||
@apply max-w-[600px] text-base text-secondary;
|
||||
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": {
|
||||
"baseUrl": "./src",
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"@/*": ["./src/*"],
|
||||
"./*": ["./*"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "@astrojs/ts-plugin"
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"types": ["vite/client"],
|
||||
|
||||
"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