chore: initialize project
Some checks failed
Deploy monie-landing (kaniko) / build-and-deploy (push) Failing after 13s
Some checks failed
Deploy monie-landing (kaniko) / build-and-deploy (push) Failing after 13s
This commit is contained in:
44
src/components/Footer.tsx
Normal file
44
src/components/Footer.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
export default function Footer() {
|
||||
const year = new Date().getFullYear()
|
||||
|
||||
return (
|
||||
<footer className="mt-20 border-t border-[var(--line)] px-4 pb-14 pt-10 text-[var(--sea-ink-soft)]">
|
||||
<div className="page-wrap flex flex-col items-center justify-between gap-4 text-center sm:flex-row sm:text-left">
|
||||
<p className="m-0 text-sm">
|
||||
© {year} Your name here. All rights reserved.
|
||||
</p>
|
||||
<p className="island-kicker m-0">Built with TanStack Start</p>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-center gap-4">
|
||||
<a
|
||||
href="https://x.com/tan_stack"
|
||||
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">Follow TanStack on X</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32">
|
||||
<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="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>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
80
src/components/Header.tsx
Normal file
80
src/components/Header.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import ParaglideLocaleSwitcher from './LocaleSwitcher.tsx'
|
||||
import ThemeToggle from './ThemeToggle'
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-[var(--line)] bg-[var(--header-bg)] px-4 backdrop-blur-lg">
|
||||
<nav className="page-wrap flex flex-wrap items-center gap-x-3 gap-y-2 py-3 sm:py-4">
|
||||
<h2 className="m-0 flex-shrink-0 text-base font-semibold tracking-tight">
|
||||
<Link
|
||||
to="/"
|
||||
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"
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-[linear-gradient(90deg,#56c6be,#7ed3bf)]" />
|
||||
TanStack Start
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="ml-auto flex items-center gap-1.5 sm:ml-0 sm:gap-2">
|
||||
<a
|
||||
href="https://x.com/tan_stack"
|
||||
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">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>
|
||||
<ParaglideLocaleSwitcher />
|
||||
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
<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-2 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>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
46
src/components/LocaleSwitcher.tsx
Normal file
46
src/components/LocaleSwitcher.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
// Locale switcher refs:
|
||||
// - Paraglide docs: https://inlang.com/m/gerre34r/library-inlang-paraglideJs
|
||||
// - Router example: https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#switching-locale
|
||||
import { getLocale, locales, setLocale } from '#/paraglide/runtime'
|
||||
import { m } from '#/paraglide/messages'
|
||||
|
||||
export default function ParaglideLocaleSwitcher() {
|
||||
const currentLocale = getLocale()
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '0.5rem',
|
||||
alignItems: 'center',
|
||||
color: 'inherit',
|
||||
}}
|
||||
aria-label={m.language_label()}
|
||||
>
|
||||
<span style={{ opacity: 0.85 }}>
|
||||
{m.current_locale({ locale: currentLocale })}
|
||||
</span>
|
||||
<div style={{ display: 'flex', gap: '0.25rem' }}>
|
||||
{locales.map((locale) => (
|
||||
<button
|
||||
key={locale}
|
||||
onClick={() => setLocale(locale)}
|
||||
aria-pressed={locale === currentLocale}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
padding: '0.35rem 0.75rem',
|
||||
borderRadius: '999px',
|
||||
border: '1px solid #d1d5db',
|
||||
background: locale === currentLocale ? '#0f172a' : 'transparent',
|
||||
color: locale === currentLocale ? '#f8fafc' : 'inherit',
|
||||
fontWeight: locale === currentLocale ? 700 : 500,
|
||||
letterSpacing: '0.01em',
|
||||
}}
|
||||
>
|
||||
{locale.toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
81
src/components/ThemeToggle.tsx
Normal file
81
src/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user