mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 09:56:57 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c199bb15ef
|
|||
|
5ab1d94576
|
|||
|
a4b75dc1b0
|
|||
|
56368fc8f3
|
|||
|
0a71f4e4be
|
|||
|
ea11678365
|
|||
|
aa4e1844a3
|
|||
|
ce9bc0314c
|
|||
|
4cbe1f404e
|
|||
|
74e33f70c0
|
|||
|
c14909d116
|
|||
|
7f8cc180c3
|
Generated
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="BiomeSettings">
|
||||||
|
<option name="applySafeFixesOnSave" value="true" />
|
||||||
|
<option name="enableLspFormat" value="true" />
|
||||||
|
<option name="formatOnSave" value="true" />
|
||||||
|
<option name="sortImportOnSave" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
|
"$schema": "https://biomejs.dev/schemas/2.4.5/schema.json",
|
||||||
"vcs": {
|
"vcs": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"clientKind": "git",
|
"clientKind": "git",
|
||||||
|
|||||||
Generated
-5439
File diff suppressed because it is too large
Load Diff
+7
-2
@@ -15,6 +15,8 @@
|
|||||||
"check": "biome check"
|
"check": "biome check"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/inter": "^5.2.8",
|
||||||
|
"@socialgouv/matomo-next": "^1.13.1",
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
"@tanstack/react-devtools": "latest",
|
"@tanstack/react-devtools": "latest",
|
||||||
"@tanstack/react-router": "latest",
|
"@tanstack/react-router": "latest",
|
||||||
@@ -22,11 +24,14 @@
|
|||||||
"@tanstack/react-router-ssr-query": "latest",
|
"@tanstack/react-router-ssr-query": "latest",
|
||||||
"@tanstack/react-start": "latest",
|
"@tanstack/react-start": "latest",
|
||||||
"@tanstack/router-plugin": "^1.132.0",
|
"@tanstack/router-plugin": "^1.132.0",
|
||||||
"lucide-react": "^0.545.0",
|
"@unpic/react": "^1.0.2",
|
||||||
|
"lichess-pgn-viewer": "^2.4.5",
|
||||||
|
"lucide-react": "^1.3.0",
|
||||||
"nitro": "npm:nitro-nightly@latest",
|
"nitro": "npm:nitro-nightly@latest",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"tailwindcss": "^4.1.18"
|
"tailwindcss": "^4.1.18",
|
||||||
|
"zod": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.4.5",
|
"@biomejs/biome": "2.4.5",
|
||||||
|
|||||||
Generated
+4124
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="64.000000pt" height="64.000000pt" viewBox="0 0 64.000000 64.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,64.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#ffffff" stroke="white">
|
||||||
|
<path d="M460 605 c0 -8 -7 -15 -15 -15 -10 0 -15 -10 -15 -30 0 -20 5 -30 15
|
||||||
|
-30 20 0 19 -16 -1 -24 -12 -4 -15 -14 -11 -34 3 -15 1 -34 -4 -44 -16 -29 0
|
||||||
|
-48 41 -48 42 0 44 -2 55 -80 4 -25 9 -53 11 -62 5 -16 -2 -18 -46 -18 -40 0
|
||||||
|
-51 3 -47 13 12 32 19 130 9 123 -6 -3 -14 -32 -18 -62 -4 -36 -13 -62 -25
|
||||||
|
-72 -11 -9 -19 -22 -19 -28 0 -7 -7 -14 -15 -18 -8 -3 -15 -17 -15 -31 0 -25
|
||||||
|
0 -25 90 -25 53 0 90 4 90 10 0 6 -33 10 -80 10 -47 0 -80 4 -80 10 0 6 43 10
|
||||||
|
110 10 67 0 110 -4 110 -10 0 -5 -9 -10 -20 -10 -11 0 -20 -4 -20 -10 0 -5 14
|
||||||
|
-10 30 -10 25 0 30 4 30 25 0 14 -7 28 -15 31 -8 4 -15 12 -15 20 0 7 -7 17
|
||||||
|
-16 22 -11 6 -21 35 -29 83 -11 64 -11 76 2 91 9 10 12 22 6 30 -4 7 -8 29 -8
|
||||||
|
48 0 32 -3 35 -32 38 -25 2 -33 8 -33 23 0 10 -7 19 -15 19 -8 0 -15 5 -15 10
|
||||||
|
0 6 7 10 15 10 8 0 15 7 15 15 0 8 5 15 10 15 6 0 10 -7 10 -15 0 -8 7 -15 15
|
||||||
|
-15 8 0 15 -4 15 -10 0 -5 -7 -10 -15 -10 -8 0 -15 -4 -15 -10 0 -5 11 -10 25
|
||||||
|
-10 21 0 25 5 25 30 0 20 -5 30 -15 30 -8 0 -15 7 -15 15 0 10 -10 15 -30 15
|
||||||
|
-20 0 -30 -5 -30 -15z m66 -131 c-3 -9 -6 -20 -6 -25 0 -5 -13 -9 -30 -9 -16
|
||||||
|
0 -30 4 -30 9 0 5 -3 16 -6 25 -5 13 2 16 36 16 34 0 41 -3 36 -16z m14 -64
|
||||||
|
c0 -5 -22 -10 -50 -10 -27 0 -50 5 -50 10 0 6 23 10 50 10 28 0 50 -4 50 -10z
|
||||||
|
m30 -220 c0 -6 -33 -10 -80 -10 -47 0 -80 4 -80 10 0 6 33 10 80 10 47 0 80
|
||||||
|
-4 80 -10z"/>
|
||||||
|
<path d="M118 519 c-56 -29 -78 -80 -78 -179 0 -47 4 -80 10 -80 6 0 10 34 10
|
||||||
|
83 0 64 4 89 20 115 11 17 24 32 30 32 14 0 13 -5 -10 -42 -17 -28 -20 -51
|
||||||
|
-20 -160 0 -79 4 -128 10 -128 6 0 10 49 10 126 0 135 9 169 41 159 22 -7 23
|
||||||
|
-2 8 27 -16 29 -2 34 20 7 22 -25 28 -11 11 22 -9 16 -7 19 14 19 29 0 76 -19
|
||||||
|
76 -32 0 -10 -3 -10 -34 2 -16 6 -27 6 -31 0 -4 -6 -2 -10 3 -10 5 0 30 -16
|
||||||
|
56 -35 48 -37 64 -78 26 -68 -22 6 -28 -12 -7 -20 24 -10 -21 -7 -47 3 -34 13
|
||||||
|
-55 4 -62 -27 -6 -21 2 -33 50 -77 45 -42 56 -58 56 -84 0 -27 -4 -32 -24 -32
|
||||||
|
-17 0 -27 9 -36 30 -13 32 -37 56 -46 47 -4 -3 3 -18 15 -33 11 -15 21 -31 21
|
||||||
|
-35 0 -5 -31 -9 -68 -9 -70 0 -92 -10 -92 -41 0 -9 -7 -19 -15 -23 -17 -6 -21
|
||||||
|
-56 -5 -56 6 0 10 9 10 20 0 19 7 20 150 20 143 0 150 -1 150 -20 0 -11 5 -20
|
||||||
|
10 -20 16 0 12 50 -5 56 -8 4 -15 14 -15 24 0 10 -7 23 -15 30 -8 7 -15 28
|
||||||
|
-15 48 0 30 -8 43 -57 87 -41 38 -54 57 -49 70 4 11 12 15 24 10 46 -18 85
|
||||||
|
-18 98 0 22 30 17 71 -11 94 -20 15 -24 25 -20 44 5 20 1 27 -28 41 -46 21
|
||||||
|
-93 20 -139 -5z m190 -421 c3 -16 -8 -18 -117 -18 -94 0 -121 3 -121 13 0 25
|
||||||
|
14 28 124 25 96 -3 111 -5 114 -20z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="64.000000pt" height="64.000000pt" viewBox="0 0 64.000000 64.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,64.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#6f884d" stroke="black">
|
||||||
|
<path d="M460 605 c0 -8 -7 -15 -15 -15 -10 0 -15 -10 -15 -30 0 -20 5 -30 15
|
||||||
|
-30 20 0 19 -16 -1 -24 -12 -4 -15 -14 -11 -34 3 -15 1 -34 -4 -44 -16 -29 0
|
||||||
|
-48 41 -48 42 0 44 -2 55 -80 4 -25 9 -53 11 -62 5 -16 -2 -18 -46 -18 -40 0
|
||||||
|
-51 3 -47 13 12 32 19 130 9 123 -6 -3 -14 -32 -18 -62 -4 -36 -13 -62 -25
|
||||||
|
-72 -11 -9 -19 -22 -19 -28 0 -7 -7 -14 -15 -18 -8 -3 -15 -17 -15 -31 0 -25
|
||||||
|
0 -25 90 -25 53 0 90 4 90 10 0 6 -33 10 -80 10 -47 0 -80 4 -80 10 0 6 43 10
|
||||||
|
110 10 67 0 110 -4 110 -10 0 -5 -9 -10 -20 -10 -11 0 -20 -4 -20 -10 0 -5 14
|
||||||
|
-10 30 -10 25 0 30 4 30 25 0 14 -7 28 -15 31 -8 4 -15 12 -15 20 0 7 -7 17
|
||||||
|
-16 22 -11 6 -21 35 -29 83 -11 64 -11 76 2 91 9 10 12 22 6 30 -4 7 -8 29 -8
|
||||||
|
48 0 32 -3 35 -32 38 -25 2 -33 8 -33 23 0 10 -7 19 -15 19 -8 0 -15 5 -15 10
|
||||||
|
0 6 7 10 15 10 8 0 15 7 15 15 0 8 5 15 10 15 6 0 10 -7 10 -15 0 -8 7 -15 15
|
||||||
|
-15 8 0 15 -4 15 -10 0 -5 -7 -10 -15 -10 -8 0 -15 -4 -15 -10 0 -5 11 -10 25
|
||||||
|
-10 21 0 25 5 25 30 0 20 -5 30 -15 30 -8 0 -15 7 -15 15 0 10 -10 15 -30 15
|
||||||
|
-20 0 -30 -5 -30 -15z m66 -131 c-3 -9 -6 -20 -6 -25 0 -5 -13 -9 -30 -9 -16
|
||||||
|
0 -30 4 -30 9 0 5 -3 16 -6 25 -5 13 2 16 36 16 34 0 41 -3 36 -16z m14 -64
|
||||||
|
c0 -5 -22 -10 -50 -10 -27 0 -50 5 -50 10 0 6 23 10 50 10 28 0 50 -4 50 -10z
|
||||||
|
m30 -220 c0 -6 -33 -10 -80 -10 -47 0 -80 4 -80 10 0 6 33 10 80 10 47 0 80
|
||||||
|
-4 80 -10z"/>
|
||||||
|
<path d="M118 519 c-56 -29 -78 -80 -78 -179 0 -47 4 -80 10 -80 6 0 10 34 10
|
||||||
|
83 0 64 4 89 20 115 11 17 24 32 30 32 14 0 13 -5 -10 -42 -17 -28 -20 -51
|
||||||
|
-20 -160 0 -79 4 -128 10 -128 6 0 10 49 10 126 0 135 9 169 41 159 22 -7 23
|
||||||
|
-2 8 27 -16 29 -2 34 20 7 22 -25 28 -11 11 22 -9 16 -7 19 14 19 29 0 76 -19
|
||||||
|
76 -32 0 -10 -3 -10 -34 2 -16 6 -27 6 -31 0 -4 -6 -2 -10 3 -10 5 0 30 -16
|
||||||
|
56 -35 48 -37 64 -78 26 -68 -22 6 -28 -12 -7 -20 24 -10 -21 -7 -47 3 -34 13
|
||||||
|
-55 4 -62 -27 -6 -21 2 -33 50 -77 45 -42 56 -58 56 -84 0 -27 -4 -32 -24 -32
|
||||||
|
-17 0 -27 9 -36 30 -13 32 -37 56 -46 47 -4 -3 3 -18 15 -33 11 -15 21 -31 21
|
||||||
|
-35 0 -5 -31 -9 -68 -9 -70 0 -92 -10 -92 -41 0 -9 -7 -19 -15 -23 -17 -6 -21
|
||||||
|
-56 -5 -56 6 0 10 9 10 20 0 19 7 20 150 20 143 0 150 -1 150 -20 0 -11 5 -20
|
||||||
|
10 -20 16 0 12 50 -5 56 -8 4 -15 14 -15 24 0 10 -7 23 -15 30 -8 7 -15 28
|
||||||
|
-15 48 0 30 -8 43 -57 87 -41 38 -54 57 -49 70 4 11 12 15 24 10 46 -18 85
|
||||||
|
-18 98 0 22 30 17 71 -11 94 -20 15 -24 25 -20 44 5 20 1 27 -28 41 -46 21
|
||||||
|
-93 20 -139 -5z m190 -421 c3 -16 -8 -18 -117 -18 -94 0 -121 3 -121 13 0 25
|
||||||
|
14 28 124 25 96 -3 111 -5 114 -20z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,28 @@
|
|||||||
|
import { Image } from "@unpic/react";
|
||||||
|
import buyMeACoffeeLogo from "@/assets/images/buymeacoffeelogo.svg?url";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CoffeeWidget = ({ height, width }: IProps) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="https://buymeacoffee.com/owenreesdev"
|
||||||
|
target="_blank"
|
||||||
|
className="ml-auto flex items-center gap-2 text-sm"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={buyMeACoffeeLogo}
|
||||||
|
alt="Buy Me A Coffee Logo"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
/>
|
||||||
|
<p>Support This Project</p>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CoffeeWidget;
|
||||||
+31
-37
@@ -1,44 +1,38 @@
|
|||||||
export default function Footer() {
|
import { Link } from "@tanstack/react-router";
|
||||||
const year = new Date().getFullYear()
|
import { Image } from "@unpic/react";
|
||||||
|
|
||||||
|
import CoffeeWidget from "#/components/CoffeeWidget.tsx";
|
||||||
|
import footerLogo from "@/assets/images/footerLogo.svg?url";
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="mt-20 border-t border-[var(--line)] px-4 pb-14 pt-10 text-[var(--sea-ink-soft)]">
|
<footer className="text-white w-full grid-cols-2 items-center bg-(--accent) px-10 py-5">
|
||||||
<div className="page-wrap flex flex-col items-center justify-between gap-4 text-center sm:flex-row sm:text-left">
|
<div className="flex w-full items-center">
|
||||||
<p className="m-0 text-sm">
|
<Link to="/">
|
||||||
© {year} Your name here. All rights reserved.
|
<Image
|
||||||
|
src={footerLogo}
|
||||||
|
alt="ChessScribe Logo"
|
||||||
|
width={10}
|
||||||
|
height={10}
|
||||||
|
className="hidden h-10 w-10 sm:block"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
<div>
|
||||||
|
<p data-testid="copyright">
|
||||||
|
Copyright © 2023 - {new Date().getFullYear()}
|
||||||
</p>
|
</p>
|
||||||
<p className="island-kicker m-0">Built with TanStack Start</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex justify-center gap-4">
|
<div className="ml-auto flex gap-2">
|
||||||
<a
|
<Link to="/" target="_blank" rel="noopener noreferrer">
|
||||||
href="https://x.com/tan_stack"
|
Github
|
||||||
target="_blank"
|
</Link>
|
||||||
rel="noreferrer"
|
|
||||||
className="rounded-xl p-2 text-[var(--sea-ink-soft)] transition hover:bg-[var(--link-bg-hover)] hover:text-[var(--sea-ink)]"
|
<Link to="/" target="_blank" rel="noopener noreferrer">
|
||||||
>
|
Mail
|
||||||
<span className="sr-only">Follow TanStack on X</span>
|
</Link>
|
||||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
</div>
|
||||||
<path
|
<CoffeeWidget width={20} height={50} />
|
||||||
fill="currentColor"
|
|
||||||
d="M12.6 1h2.2L10 6.48 15.64 15h-4.41L7.78 9.82 3.23 15H1l5.14-5.84L.72 1h4.52l3.12 4.73L12.6 1zm-.77 12.67h1.22L4.57 2.26H3.26l8.57 11.41z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://github.com/TanStack"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="rounded-xl p-2 text-[var(--sea-ink-soft)] transition hover:bg-[var(--link-bg-hover)] hover:text-[var(--sea-ink)]"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to TanStack GitHub</span>
|
|
||||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-67
@@ -1,78 +1,45 @@
|
|||||||
import { Link } from '@tanstack/react-router'
|
import { Link } from "@tanstack/react-router";
|
||||||
import ThemeToggle from './ThemeToggle'
|
import { Image } from "@unpic/react";
|
||||||
|
|
||||||
|
import logo from "@/assets/images/logo.svg?url";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-50 border-b border-[var(--line)] bg-[var(--header-bg)] px-4 backdrop-blur-lg">
|
<header>
|
||||||
<nav className="page-wrap flex flex-wrap items-center gap-x-3 gap-y-2 py-3 sm:py-4">
|
<nav className="w-full max-w-screen-2xl place-self-center p-8">
|
||||||
<h2 className="m-0 flex-shrink-0 text-base font-semibold tracking-tight">
|
<div className="container mx-auto flex items-center justify-between">
|
||||||
<Link
|
<div className="flex items-center">
|
||||||
to="/"
|
<Link to="/" className="flex flex-row items-center justify-center">
|
||||||
className="inline-flex items-center gap-2 rounded-full border border-[var(--chip-line)] bg-[var(--chip-bg)] px-3 py-1.5 text-sm text-[var(--sea-ink)] no-underline shadow-[0_8px_24px_rgba(30,90,72,0.08)] sm:px-4 sm:py-2"
|
<Image
|
||||||
>
|
src={logo}
|
||||||
<span className="h-2 w-2 rounded-full bg-[linear-gradient(90deg,#56c6be,#7ed3bf)]" />
|
alt="ChessScribe Logo"
|
||||||
TanStack Start
|
width={50}
|
||||||
|
height={50}
|
||||||
|
className="mr-4"
|
||||||
|
/>
|
||||||
|
<span className="nav-title hidden text-2xl font-extrabold sm:block">
|
||||||
|
ChessScribe
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="order-3 flex w-full flex-wrap items-center gap-x-4 gap-y-1 pb-1 text-sm font-semibold sm:order-none sm:w-auto sm:flex-nowrap sm:pb-0">
|
|
||||||
<Link
|
|
||||||
to="/"
|
|
||||||
className="nav-link"
|
|
||||||
activeProps={{ className: 'nav-link is-active' }}
|
|
||||||
>
|
|
||||||
Home
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/about"
|
|
||||||
className="nav-link"
|
|
||||||
activeProps={{ className: 'nav-link is-active' }}
|
|
||||||
>
|
|
||||||
About
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href="https://tanstack.com/start/latest/docs/framework/react/overview"
|
|
||||||
className="nav-link"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Docs
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="ml-auto flex items-center gap-1.5 sm:gap-2">
|
<div>
|
||||||
<a
|
<ul className="flex flex-1 items-center gap-14 font-semibold">
|
||||||
href="https://x.com/tan_stack"
|
<li>
|
||||||
target="_blank"
|
<Link to="/chessboard" className="nav-item">
|
||||||
rel="noreferrer"
|
Chessboard
|
||||||
className="hidden rounded-xl p-2 text-[var(--sea-ink-soft)] transition hover:bg-[var(--link-bg-hover)] hover:text-[var(--sea-ink)] sm:block"
|
</Link>
|
||||||
>
|
</li>
|
||||||
<span className="sr-only">Follow TanStack on X</span>
|
|
||||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="24" height="24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12.6 1h2.2L10 6.48 15.64 15h-4.41L7.78 9.82 3.23 15H1l5.14-5.84L.72 1h4.52l3.12 4.73L12.6 1zm-.77 12.67h1.22L4.57 2.26H3.26l8.57 11.41z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://github.com/TanStack"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="hidden rounded-xl p-2 text-[var(--sea-ink-soft)] transition hover:bg-[var(--link-bg-hover)] hover:text-[var(--sea-ink)] sm:block"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Go to TanStack GitHub</span>
|
|
||||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="24" height="24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ThemeToggle />
|
<li>
|
||||||
|
<Link to="/contact" className="nav-item">
|
||||||
|
Contact
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { trackAppRouter } from "@socialgouv/matomo-next";
|
||||||
|
import { useLocation, useSearch } from "@tanstack/react-router";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const MATOMO_URL = import.meta.env.NEXT_PUBLIC_MATOMO_URL;
|
||||||
|
const MATOMO_SITE_ID = import.meta.env.NEXT_PUBLIC_MATOMO_SITE_ID;
|
||||||
|
|
||||||
|
export function MatomoAnalytics() {
|
||||||
|
const location = useLocation();
|
||||||
|
const searchParams = useSearch({ strict: false });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
|
||||||
|
const serializedParams = new URLSearchParams(
|
||||||
|
searchParams as Record<string, string>,
|
||||||
|
);
|
||||||
|
|
||||||
|
trackAppRouter({
|
||||||
|
url: MATOMO_URL || "",
|
||||||
|
siteId: MATOMO_SITE_ID || "",
|
||||||
|
pathname: location.pathname,
|
||||||
|
searchParams: serializedParams,
|
||||||
|
enableHeatmapSessionRecording: false,
|
||||||
|
enableHeartBeatTimer: true,
|
||||||
|
cleanUrl: true,
|
||||||
|
disableCookies: true,
|
||||||
|
debug: import.meta.env.DEV,
|
||||||
|
});
|
||||||
|
}, [location.pathname, searchParams]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
type ThemeMode = 'light' | 'dark' | 'auto'
|
|
||||||
|
|
||||||
function getInitialMode(): ThemeMode {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return 'auto'
|
|
||||||
}
|
|
||||||
|
|
||||||
const stored = window.localStorage.getItem('theme')
|
|
||||||
if (stored === 'light' || stored === 'dark' || stored === 'auto') {
|
|
||||||
return stored
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'auto'
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyThemeMode(mode: ThemeMode) {
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
||||||
const resolved = mode === 'auto' ? (prefersDark ? 'dark' : 'light') : mode
|
|
||||||
|
|
||||||
document.documentElement.classList.remove('light', 'dark')
|
|
||||||
document.documentElement.classList.add(resolved)
|
|
||||||
|
|
||||||
if (mode === 'auto') {
|
|
||||||
document.documentElement.removeAttribute('data-theme')
|
|
||||||
} else {
|
|
||||||
document.documentElement.setAttribute('data-theme', mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.style.colorScheme = resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ThemeToggle() {
|
|
||||||
const [mode, setMode] = useState<ThemeMode>('auto')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const initialMode = getInitialMode()
|
|
||||||
setMode(initialMode)
|
|
||||||
applyThemeMode(initialMode)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (mode !== 'auto') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const media = window.matchMedia('(prefers-color-scheme: dark)')
|
|
||||||
const onChange = () => applyThemeMode('auto')
|
|
||||||
|
|
||||||
media.addEventListener('change', onChange)
|
|
||||||
return () => {
|
|
||||||
media.removeEventListener('change', onChange)
|
|
||||||
}
|
|
||||||
}, [mode])
|
|
||||||
|
|
||||||
function toggleMode() {
|
|
||||||
const nextMode: ThemeMode =
|
|
||||||
mode === 'light' ? 'dark' : mode === 'dark' ? 'auto' : 'light'
|
|
||||||
setMode(nextMode)
|
|
||||||
applyThemeMode(nextMode)
|
|
||||||
window.localStorage.setItem('theme', nextMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
const label =
|
|
||||||
mode === 'auto'
|
|
||||||
? 'Theme mode: auto (system). Click to switch to light mode.'
|
|
||||||
: `Theme mode: ${mode}. Click to switch mode.`
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={toggleMode}
|
|
||||||
aria-label={label}
|
|
||||||
title={label}
|
|
||||||
className="rounded-full border border-[var(--chip-line)] bg-[var(--chip-bg)] px-3 py-1.5 text-sm font-semibold text-[var(--sea-ink)] shadow-[0_8px_22px_rgba(30,90,72,0.08)] transition hover:-translate-y-0.5"
|
|
||||||
>
|
|
||||||
{mode === 'auto' ? 'Auto' : mode === 'dark' ? 'Dark' : 'Light'}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+35
-17
@@ -9,12 +9,18 @@
|
|||||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||||
|
|
||||||
import { Route as rootRouteImport } from './routes/__root'
|
import { Route as rootRouteImport } from './routes/__root'
|
||||||
import { Route as AboutRouteImport } from './routes/about'
|
import { Route as ContactRouteImport } from './routes/contact'
|
||||||
|
import { Route as ChessboardRouteImport } from './routes/chessboard'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
|
|
||||||
const AboutRoute = AboutRouteImport.update({
|
const ContactRoute = ContactRouteImport.update({
|
||||||
id: '/about',
|
id: '/contact',
|
||||||
path: '/about',
|
path: '/contact',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ChessboardRoute = ChessboardRouteImport.update({
|
||||||
|
id: '/chessboard',
|
||||||
|
path: '/chessboard',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const IndexRoute = IndexRouteImport.update({
|
const IndexRoute = IndexRouteImport.update({
|
||||||
@@ -25,37 +31,48 @@ const IndexRoute = IndexRouteImport.update({
|
|||||||
|
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/about': typeof AboutRoute
|
'/chessboard': typeof ChessboardRoute
|
||||||
|
'/contact': typeof ContactRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/about': typeof AboutRoute
|
'/chessboard': typeof ChessboardRoute
|
||||||
|
'/contact': typeof ContactRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/about': typeof AboutRoute
|
'/chessboard': typeof ChessboardRoute
|
||||||
|
'/contact': typeof ContactRoute
|
||||||
}
|
}
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
fullPaths: '/' | '/about'
|
fullPaths: '/' | '/chessboard' | '/contact'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to: '/' | '/about'
|
to: '/' | '/chessboard' | '/contact'
|
||||||
id: '__root__' | '/' | '/about'
|
id: '__root__' | '/' | '/chessboard' | '/contact'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
AboutRoute: typeof AboutRoute
|
ChessboardRoute: typeof ChessboardRoute
|
||||||
|
ContactRoute: typeof ContactRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
interface FileRoutesByPath {
|
interface FileRoutesByPath {
|
||||||
'/about': {
|
'/contact': {
|
||||||
id: '/about'
|
id: '/contact'
|
||||||
path: '/about'
|
path: '/contact'
|
||||||
fullPath: '/about'
|
fullPath: '/contact'
|
||||||
preLoaderRoute: typeof AboutRouteImport
|
preLoaderRoute: typeof ContactRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/chessboard': {
|
||||||
|
id: '/chessboard'
|
||||||
|
path: '/chessboard'
|
||||||
|
fullPath: '/chessboard'
|
||||||
|
preLoaderRoute: typeof ChessboardRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/': {
|
'/': {
|
||||||
@@ -70,7 +87,8 @@ declare module '@tanstack/react-router' {
|
|||||||
|
|
||||||
const rootRouteChildren: RootRouteChildren = {
|
const rootRouteChildren: RootRouteChildren = {
|
||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
AboutRoute: AboutRoute,
|
ChessboardRoute: ChessboardRoute,
|
||||||
|
ContactRoute: ContactRoute,
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
|
|||||||
+22
-21
@@ -1,61 +1,62 @@
|
|||||||
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
|
import { TanStackDevtools } from "@tanstack/react-devtools";
|
||||||
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
|
import { createRootRoute, HeadContent, Scripts } from "@tanstack/react-router";
|
||||||
import { TanStackDevtools } from '@tanstack/react-devtools'
|
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
|
||||||
import Footer from '../components/Footer'
|
import { type ReactNode, Suspense } from "react";
|
||||||
import Header from '../components/Header'
|
import { MatomoAnalytics } from "#/components/MatomoAnalytics.tsx";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
import appCss from '../styles.css?url'
|
import Header from "../components/Header";
|
||||||
|
import appCss from "../styles.css?url";
|
||||||
const THEME_INIT_SCRIPT = `(function(){try{var stored=window.localStorage.getItem('theme');var mode=(stored==='light'||stored==='dark'||stored==='auto')?stored:'auto';var prefersDark=window.matchMedia('(prefers-color-scheme: dark)').matches;var resolved=mode==='auto'?(prefersDark?'dark':'light'):mode;var root=document.documentElement;root.classList.remove('light','dark');root.classList.add(resolved);if(mode==='auto'){root.removeAttribute('data-theme')}else{root.setAttribute('data-theme',mode)}root.style.colorScheme=resolved;}catch(e){}})();`
|
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
head: () => ({
|
head: () => ({
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
charSet: 'utf-8',
|
charSet: "utf-8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'viewport',
|
name: "viewport",
|
||||||
content: 'width=device-width, initial-scale=1',
|
content: "width=device-width, initial-scale=1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'TanStack Start Starter',
|
title: "ChessScribe",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
rel: 'stylesheet',
|
rel: "stylesheet",
|
||||||
href: appCss,
|
href: appCss,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
shellComponent: RootDocument,
|
shellComponent: RootDocument,
|
||||||
})
|
});
|
||||||
|
|
||||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
function RootDocument({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<head>
|
<head>
|
||||||
<script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />
|
|
||||||
<HeadContent />
|
<HeadContent />
|
||||||
</head>
|
</head>
|
||||||
<body className="font-sans antialiased [overflow-wrap:anywhere] selection:bg-[rgba(79,184,178,0.24)]">
|
<body className="font-sans antialiased">
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
<TanStackDevtools
|
<TanStackDevtools
|
||||||
config={{
|
config={{
|
||||||
position: 'bottom-right',
|
position: "bottom-right",
|
||||||
}}
|
}}
|
||||||
plugins={[
|
plugins={[
|
||||||
{
|
{
|
||||||
name: 'Tanstack Router',
|
name: "Tanstack Router",
|
||||||
render: <TanStackRouterDevtoolsPanel />,
|
render: <TanStackRouterDevtoolsPanel />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Scripts />
|
<Scripts />
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<MatomoAnalytics />
|
||||||
|
</Suspense>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/about')({
|
|
||||||
component: About,
|
|
||||||
})
|
|
||||||
|
|
||||||
function About() {
|
|
||||||
return (
|
|
||||||
<main className="page-wrap px-4 py-12">
|
|
||||||
<section className="island-shell rounded-2xl p-6 sm:p-8">
|
|
||||||
<p className="island-kicker mb-2">About</p>
|
|
||||||
<h1 className="display-title mb-3 text-4xl font-bold text-[var(--sea-ink)] sm:text-5xl">
|
|
||||||
A small starter with room to grow.
|
|
||||||
</h1>
|
|
||||||
<p className="m-0 max-w-3xl text-base leading-8 text-[var(--sea-ink-soft)]">
|
|
||||||
TanStack Start gives you type-safe routing, server functions, and
|
|
||||||
modern SSR defaults. Use this as a clean foundation, then layer in
|
|
||||||
your own routes, styling, and add-ons.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/chessboard')({
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return <div>Hello "/chessboard"!</div>
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/contact')({
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return <div>Hello "/contact"!</div>
|
||||||
|
}
|
||||||
+78
-78
@@ -1,87 +1,87 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createFileRoute('/')({ component: App })
|
export const Route = createFileRoute("/")({ component: App });
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<main className="page-wrap px-4 pb-8 pt-14">
|
<main className="page-wrap px-4 pb-8 pt-14">
|
||||||
<section className="island-shell rise-in relative overflow-hidden rounded-[2rem] px-6 py-10 sm:px-10 sm:py-14">
|
{/*<section className="island-shell rise-in relative overflow-hidden rounded-[2rem] px-6 py-10 sm:px-10 sm:py-14">*/}
|
||||||
<div className="pointer-events-none absolute -left-20 -top-24 h-56 w-56 rounded-full bg-[radial-gradient(circle,rgba(79,184,178,0.32),transparent_66%)]" />
|
{/* <div className="pointer-events-none absolute -left-20 -top-24 h-56 w-56 rounded-full bg-[radial-gradient(circle,rgba(79,184,178,0.32),transparent_66%)]" />*/}
|
||||||
<div className="pointer-events-none absolute -bottom-20 -right-20 h-56 w-56 rounded-full bg-[radial-gradient(circle,rgba(47,106,74,0.18),transparent_66%)]" />
|
{/* <div className="pointer-events-none absolute -bottom-20 -right-20 h-56 w-56 rounded-full bg-[radial-gradient(circle,rgba(47,106,74,0.18),transparent_66%)]" />*/}
|
||||||
<p className="island-kicker mb-3">TanStack Start Base Template</p>
|
{/* <p className="island-kicker mb-3">TanStack Start Base Template</p>*/}
|
||||||
<h1 className="display-title mb-5 max-w-3xl text-4xl leading-[1.02] font-bold tracking-tight text-[var(--sea-ink)] sm:text-6xl">
|
{/* <h1 className="display-title mb-5 max-w-3xl text-4xl leading-[1.02] font-bold tracking-tight text-[var(--sea-ink)] sm:text-6xl">*/}
|
||||||
Start simple, ship quickly.
|
{/* Start simple, ship quickly.*/}
|
||||||
</h1>
|
{/* </h1>*/}
|
||||||
<p className="mb-8 max-w-2xl text-base text-[var(--sea-ink-soft)] sm:text-lg">
|
{/* <p className="mb-8 max-w-2xl text-base text-[var(--sea-ink-soft)] sm:text-lg">*/}
|
||||||
This base starter intentionally keeps things light: two routes, clean
|
{/* This base starter intentionally keeps things light: two routes, clean*/}
|
||||||
structure, and the essentials you need to build from scratch.
|
{/* structure, and the essentials you need to build from scratch.*/}
|
||||||
</p>
|
{/* </p>*/}
|
||||||
<div className="flex flex-wrap gap-3">
|
{/* <div className="flex flex-wrap gap-3">*/}
|
||||||
<a
|
{/* <a*/}
|
||||||
href="/about"
|
{/* href="/about"*/}
|
||||||
className="rounded-full border border-[rgba(50,143,151,0.3)] bg-[rgba(79,184,178,0.14)] px-5 py-2.5 text-sm font-semibold text-[var(--lagoon-deep)] no-underline transition hover:-translate-y-0.5 hover:bg-[rgba(79,184,178,0.24)]"
|
{/* className="rounded-full border border-[rgba(50,143,151,0.3)] bg-[rgba(79,184,178,0.14)] px-5 py-2.5 text-sm font-semibold text-[var(--lagoon-deep)] no-underline transition hover:-translate-y-0.5 hover:bg-[rgba(79,184,178,0.24)]"*/}
|
||||||
>
|
{/* >*/}
|
||||||
About This Starter
|
{/* About This Starter*/}
|
||||||
</a>
|
{/* </a>*/}
|
||||||
<a
|
{/* <a*/}
|
||||||
href="https://tanstack.com/router"
|
{/* href="https://tanstack.com/router"*/}
|
||||||
target="_blank"
|
{/* target="_blank"*/}
|
||||||
rel="noopener noreferrer"
|
{/* rel="noopener noreferrer"*/}
|
||||||
className="rounded-full border border-[rgba(23,58,64,0.2)] bg-white/50 px-5 py-2.5 text-sm font-semibold text-[var(--sea-ink)] no-underline transition hover:-translate-y-0.5 hover:border-[rgba(23,58,64,0.35)]"
|
{/* className="rounded-full border border-[rgba(23,58,64,0.2)] bg-white/50 px-5 py-2.5 text-sm font-semibold text-[var(--sea-ink)] no-underline transition hover:-translate-y-0.5 hover:border-[rgba(23,58,64,0.35)]"*/}
|
||||||
>
|
{/* >*/}
|
||||||
Router Guide
|
{/* Router Guide*/}
|
||||||
</a>
|
{/* </a>*/}
|
||||||
</div>
|
{/* </div>*/}
|
||||||
</section>
|
{/*</section>*/}
|
||||||
|
|
||||||
<section className="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
{/*<section className="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">*/}
|
||||||
{[
|
{/* {[*/}
|
||||||
[
|
{/* [*/}
|
||||||
'Type-Safe Routing',
|
{/* 'Type-Safe Routing',*/}
|
||||||
'Routes and links stay in sync across every page.',
|
{/* 'Routes and links stay in sync across every page.',*/}
|
||||||
],
|
{/* ],*/}
|
||||||
[
|
{/* [*/}
|
||||||
'Server Functions',
|
{/* 'Server Functions',*/}
|
||||||
'Call server code from your UI without creating API boilerplate.',
|
{/* 'Call server code from your UI without creating API boilerplate.',*/}
|
||||||
],
|
{/* ],*/}
|
||||||
[
|
{/* [*/}
|
||||||
'Streaming by Default',
|
{/* 'Streaming by Default',*/}
|
||||||
'Ship progressively rendered responses for faster experiences.',
|
{/* 'Ship progressively rendered responses for faster experiences.',*/}
|
||||||
],
|
{/* ],*/}
|
||||||
[
|
{/* [*/}
|
||||||
'Tailwind Native',
|
{/* 'Tailwind Native',*/}
|
||||||
'Design quickly with utility-first styling and reusable tokens.',
|
{/* 'Design quickly with utility-first styling and reusable tokens.',*/}
|
||||||
],
|
{/* ],*/}
|
||||||
].map(([title, desc], index) => (
|
{/* ].map(([title, desc], index) => (*/}
|
||||||
<article
|
{/* <article*/}
|
||||||
key={title}
|
{/* key={title}*/}
|
||||||
className="island-shell feature-card rise-in rounded-2xl p-5"
|
{/* className="island-shell feature-card rise-in rounded-2xl p-5"*/}
|
||||||
style={{ animationDelay: `${index * 90 + 80}ms` }}
|
{/* style={{ animationDelay: `${index * 90 + 80}ms` }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<h2 className="mb-2 text-base font-semibold text-[var(--sea-ink)]">
|
{/* <h2 className="mb-2 text-base font-semibold text-[var(--sea-ink)]">*/}
|
||||||
{title}
|
{/* {title}*/}
|
||||||
</h2>
|
{/* </h2>*/}
|
||||||
<p className="m-0 text-sm text-[var(--sea-ink-soft)]">{desc}</p>
|
{/* <p className="m-0 text-sm text-[var(--sea-ink-soft)]">{desc}</p>*/}
|
||||||
</article>
|
{/* </article>*/}
|
||||||
))}
|
{/* ))}*/}
|
||||||
</section>
|
{/*</section>*/}
|
||||||
|
|
||||||
<section className="island-shell mt-8 rounded-2xl p-6">
|
{/*<section className="island-shell mt-8 rounded-2xl p-6">*/}
|
||||||
<p className="island-kicker mb-2">Quick Start</p>
|
{/* <p className="island-kicker mb-2">Quick Start</p>*/}
|
||||||
<ul className="m-0 list-disc space-y-2 pl-5 text-sm text-[var(--sea-ink-soft)]">
|
{/* <ul className="m-0 list-disc space-y-2 pl-5 text-sm text-[var(--sea-ink-soft)]">*/}
|
||||||
<li>
|
{/* <li>*/}
|
||||||
Edit <code>src/routes/index.tsx</code> to customize the home page.
|
{/* Edit <code>src/routes/index.tsx</code> to customize the home page.*/}
|
||||||
</li>
|
{/* </li>*/}
|
||||||
<li>
|
{/* <li>*/}
|
||||||
Update <code>src/components/Header.tsx</code> and{' '}
|
{/* Update <code>src/components/Header.tsx</code> and{' '}*/}
|
||||||
<code>src/components/Footer.tsx</code> for brand links.
|
{/* <code>src/components/Footer.tsx</code> for brand links.*/}
|
||||||
</li>
|
{/* </li>*/}
|
||||||
<li>
|
{/* <li>*/}
|
||||||
Add routes in <code>src/routes</code> and tweak visual tokens in{' '}
|
{/* Add routes in <code>src/routes</code> and tweak visual tokens in{' '}*/}
|
||||||
<code>src/styles.css</code>.
|
{/* <code>src/styles.css</code>.*/}
|
||||||
</li>
|
{/* </li>*/}
|
||||||
</ul>
|
{/* </ul>*/}
|
||||||
</section>
|
{/*</section>*/}
|
||||||
</main>
|
</main>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-208
@@ -1,78 +1,26 @@
|
|||||||
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap");
|
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@plugin "@tailwindcss/typography";
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
|
/* inter-latin-wght-normal */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter Variable';
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
font-weight: 100 900;
|
||||||
|
src: url('@fontsource-variable/inter/files/inter-latin-wght-normal.woff2') format('woff2-variations');
|
||||||
|
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans: "Manrope", ui-sans-serif, system-ui, sans-serif;
|
--font-sans: "Inter Variable", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--sea-ink: #173a40;
|
--base-content: #1f2937;
|
||||||
--sea-ink-soft: #416166;
|
--neutral-content: #4b5563;
|
||||||
--lagoon: #4fb8b2;
|
--accent: #6f884d;
|
||||||
--lagoon-deep: #328f97;
|
|
||||||
--palm: #2f6a4a;
|
|
||||||
--sand: #e7f0e8;
|
|
||||||
--foam: #f3faf5;
|
|
||||||
--surface: rgba(255, 255, 255, 0.74);
|
|
||||||
--surface-strong: rgba(255, 255, 255, 0.9);
|
|
||||||
--line: rgba(23, 58, 64, 0.14);
|
|
||||||
--inset-glint: rgba(255, 255, 255, 0.82);
|
|
||||||
--kicker: rgba(47, 106, 74, 0.9);
|
|
||||||
--bg-base: #e7f3ec;
|
--bg-base: #e7f3ec;
|
||||||
--header-bg: rgba(251, 255, 248, 0.84);
|
--header-bg: rgba(251, 255, 248, 0.84);
|
||||||
--chip-bg: rgba(255, 255, 255, 0.8);
|
|
||||||
--chip-line: rgba(47, 106, 74, 0.18);
|
|
||||||
--link-bg-hover: rgba(255, 255, 255, 0.9);
|
|
||||||
--hero-a: rgba(79, 184, 178, 0.36);
|
|
||||||
--hero-b: rgba(47, 106, 74, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[data-theme="dark"] {
|
|
||||||
--sea-ink: #d7ece8;
|
|
||||||
--sea-ink-soft: #afcdc8;
|
|
||||||
--lagoon: #60d7cf;
|
|
||||||
--lagoon-deep: #8de5db;
|
|
||||||
--palm: #6ec89a;
|
|
||||||
--sand: #0f1a1e;
|
|
||||||
--foam: #101d22;
|
|
||||||
--surface: rgba(16, 30, 34, 0.8);
|
|
||||||
--surface-strong: rgba(15, 27, 31, 0.92);
|
|
||||||
--line: rgba(141, 229, 219, 0.18);
|
|
||||||
--inset-glint: rgba(194, 247, 238, 0.14);
|
|
||||||
--kicker: #b8efe5;
|
|
||||||
--bg-base: #0a1418;
|
|
||||||
--header-bg: rgba(10, 20, 24, 0.8);
|
|
||||||
--chip-bg: rgba(13, 28, 32, 0.9);
|
|
||||||
--chip-line: rgba(141, 229, 219, 0.24);
|
|
||||||
--link-bg-hover: rgba(24, 44, 49, 0.8);
|
|
||||||
--hero-a: rgba(96, 215, 207, 0.18);
|
|
||||||
--hero-b: rgba(110, 200, 154, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root:not([data-theme="light"]) {
|
|
||||||
--sea-ink: #d7ece8;
|
|
||||||
--sea-ink-soft: #afcdc8;
|
|
||||||
--lagoon: #60d7cf;
|
|
||||||
--lagoon-deep: #8de5db;
|
|
||||||
--palm: #6ec89a;
|
|
||||||
--sand: #0f1a1e;
|
|
||||||
--foam: #101d22;
|
|
||||||
--surface: rgba(16, 30, 34, 0.8);
|
|
||||||
--surface-strong: rgba(15, 27, 31, 0.92);
|
|
||||||
--line: rgba(141, 229, 219, 0.18);
|
|
||||||
--inset-glint: rgba(194, 247, 238, 0.14);
|
|
||||||
--kicker: #b8efe5;
|
|
||||||
--bg-base: #0a1418;
|
|
||||||
--header-bg: rgba(10, 20, 24, 0.8);
|
|
||||||
--chip-bg: rgba(13, 28, 32, 0.9);
|
|
||||||
--chip-line: rgba(141, 229, 219, 0.24);
|
|
||||||
--link-bg-hover: rgba(24, 44, 49, 0.8);
|
|
||||||
--hero-a: rgba(96, 215, 207, 0.18);
|
|
||||||
--hero-b: rgba(110, 200, 154, 0.12);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -83,166 +31,62 @@ html,
|
|||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
background-color: var(--bg-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--sea-ink);
|
font-family: var(--font-sans), sans-serif;
|
||||||
font-family: var(--font-sans);
|
|
||||||
background-color: var(--bg-base);
|
background-color: var(--bg-base);
|
||||||
background:
|
|
||||||
radial-gradient(1100px 620px at -8% -10%, var(--hero-a), transparent 58%),
|
|
||||||
radial-gradient(1050px 620px at 112% -12%, var(--hero-b), transparent 62%),
|
|
||||||
radial-gradient(720px 380px at 50% 115%, rgba(79, 184, 178, 0.1), transparent 68%),
|
|
||||||
linear-gradient(180deg, color-mix(in oklab, var(--sand) 68%, white) 0%, var(--foam) 44%, var(--bg-base) 100%);
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
.nav-container {
|
||||||
content: "";
|
background-color: var(--header-bg);
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: -1;
|
|
||||||
opacity: 0.28;
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 20% 15%, rgba(255, 255, 255, 0.8), transparent 34%),
|
|
||||||
radial-gradient(circle at 78% 26%, rgba(79, 184, 178, 0.2), transparent 42%),
|
|
||||||
radial-gradient(circle at 42% 82%, rgba(47, 106, 74, 0.14), transparent 36%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body::after {
|
.nav-title {
|
||||||
content: "";
|
color: var(--accent);
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: -1;
|
|
||||||
opacity: 0.14;
|
|
||||||
background-image:
|
|
||||||
linear-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px),
|
|
||||||
linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
|
|
||||||
background-size: 28px 28px;
|
|
||||||
mask-image: radial-gradient(circle at 50% 30%, black, transparent 78%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
.nav-item {
|
||||||
color: var(--lagoon-deep);
|
color: var(--neutral-content);
|
||||||
text-decoration-color: rgba(50, 143, 151, 0.4);
|
|
||||||
text-decoration-thickness: 1px;
|
|
||||||
text-underline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
&:hover {
|
||||||
color: #246f76;
|
color: var(--accent);
|
||||||
}
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
code {
|
text-underline-offset: 8px;
|
||||||
font-size: 0.9em;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
background: color-mix(in oklab, var(--surface-strong) 82%, white 18%);
|
|
||||||
border-radius: 7px;
|
|
||||||
padding: 2px 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-wrap {
|
|
||||||
width: min(1080px, calc(100% - 2rem));
|
|
||||||
margin-inline: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-title {
|
|
||||||
font-family: "Fraunces", Georgia, serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.island-shell {
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
background: linear-gradient(165deg, var(--surface-strong), var(--surface));
|
|
||||||
box-shadow:
|
|
||||||
0 1px 0 var(--inset-glint) inset,
|
|
||||||
0 22px 44px rgba(30, 90, 72, 0.1),
|
|
||||||
0 6px 18px rgba(23, 58, 64, 0.08);
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card {
|
|
||||||
background: linear-gradient(165deg, color-mix(in oklab, var(--surface-strong) 93%, white 7%), var(--surface));
|
|
||||||
box-shadow:
|
|
||||||
0 1px 0 var(--inset-glint) inset,
|
|
||||||
0 18px 34px rgba(30, 90, 72, 0.1),
|
|
||||||
0 4px 14px rgba(23, 58, 64, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
border-color: color-mix(in oklab, var(--lagoon-deep) 35%, var(--line));
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
.island-shell,
|
|
||||||
a {
|
|
||||||
transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease,
|
|
||||||
transform 180ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.island-kicker {
|
|
||||||
letter-spacing: 0.16em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 0.69rem;
|
|
||||||
color: var(--kicker);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--sea-ink-soft);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: -6px;
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
transform: scaleX(0);
|
|
||||||
transform-origin: left;
|
|
||||||
background: linear-gradient(90deg, var(--lagoon), #7ed3bf);
|
|
||||||
transition: transform 170ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover,
|
|
||||||
.nav-link.is-active {
|
|
||||||
color: var(--sea-ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover::after,
|
|
||||||
.nav-link.is-active::after {
|
|
||||||
transform: scaleX(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.nav-link::after {
|
|
||||||
bottom: -4px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-footer {
|
/*a {*/
|
||||||
border-top: 1px solid var(--line);
|
/* text-decoration-thickness: 1px;*/
|
||||||
background: color-mix(in oklab, var(--header-bg) 84%, transparent 16%);
|
/* text-underline-offset: 2px;*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
|
/*a:hover {*/
|
||||||
|
/* color: #246f76;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.page-wrap {*/
|
||||||
|
/* width: min(1080px, calc(100% - 2rem));*/
|
||||||
|
/* margin-inline: auto;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*button,*/
|
||||||
|
/*a {*/
|
||||||
|
/* transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease,*/
|
||||||
|
/* transform 180ms ease;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*@media (max-width: 640px) {*/
|
||||||
|
/* .nav-link::after {*/
|
||||||
|
/* bottom: -4px;*/
|
||||||
|
/* }*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
.rise-in {
|
.rise-in {
|
||||||
animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
|||||||
Reference in New Issue
Block a user