layout UI

This commit is contained in:
2026-02-28 01:46:14 +01:00
parent 88fe116bce
commit 55f90e1a3d
11 changed files with 204 additions and 26 deletions

View File

@@ -3,6 +3,12 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&family=Geist+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">

View File

@@ -0,0 +1,29 @@
<script lang="ts">
import { page } from '$app/state';
const user = $derived(page.data?.user);
</script>
<header
class="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-border bg-surface-900/80 px-6 backdrop-blur-md"
>
<div></div>
<div class="flex items-center gap-4">
{#if user}
<div class="flex items-center gap-3">
{#if user.image}
<img src={user.image} alt={user.name} class="h-7 w-7 rounded-full" />
{/if}
<span class="font-500 text-sm text-text-primary">{user.name}</span>
</div>
{:else}
<a
href="/api/auth/osu/login"
class="font-600 rounded-md bg-accent px-3.5 py-1.5 text-sm text-surface-900 transition-colors hover:bg-accent-hover"
>
Sign in with osu!
</a>
{/if}
</div>
</header>

View File

@@ -0,0 +1,57 @@
<script lang="ts">
import { page } from '$app/state';
const nav = [
{ href: '/', label: 'Dashboard', icon: '⌂' },
{ href: '/matches', label: 'Matches', icon: '⚔' },
{ href: '/mappools', label: 'Mappools', icon: '♫' },
{ href: '/teams', label: 'Teams', icon: '⚑' }
];
function isActive(href: string) {
if (href === '/') return page.url.pathname === '/';
return page.url.pathname.startsWith(href);
}
</script>
<aside
class="fixed top-0 left-0 z-40 flex h-full w-56 flex-col border-r border-border bg-surface-800"
>
<!-- Logo -->
<a href="/" class="flex items-center gap-2.5 px-5 py-5">
<div
class="font-800 flex h-8 w-8 items-center justify-center rounded-md bg-accent text-sm text-surface-900"
>
V
</div>
<span class="font-700 text-base tracking-tight text-text-primary">VASH</span>
</a>
<!-- Nav -->
<nav class="mt-2 flex flex-1 flex-col gap-0.5 px-3">
{#each nav as item}
<a
href={item.href}
class="font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors {isActive(
item.href
)
? 'bg-accent-dim text-accent'
: 'text-text-secondary hover:bg-surface-700 hover:text-text-primary'}"
>
<span class="text-base">{item.icon}</span>
{item.label}
</a>
{/each}
</nav>
<!-- Bottom -->
<div class="border-t border-border p-3">
<a
href="/settings"
class="font-500 flex items-center gap-3 rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-700 hover:text-text-primary"
>
<span class="text-base">⚙</span>
Settings
</a>
</div>
</aside>

View File

@@ -0,0 +1,7 @@
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
return {
user: locals.user ?? null
};
};

View File

@@ -0,0 +1,16 @@
<script lang="ts">
import Sidebar from '$lib/components/Sidebar.svelte';
import Header from '$lib/components/Header.svelte';
let { children } = $props();
</script>
<div class="flex min-h-dvh">
<Sidebar />
<div class="ml-56 flex flex-1 flex-col">
<Header />
<main class="flex-1 p-6">
{@render children()}
</main>
</div>
</div>

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import type { PageProps } from './$types';
import './layout.css';
let { data }: PageProps = $props();
</script>
<div class="mx-auto max-w-4xl">
<h1 class="font-700 text-2xl tracking-tight">
{#if data.user}
Welcome back, <span class="text-accent">{data.user.name}</span>
{:else}
Welcome to <span class="text-accent">VASH</span>
{/if}
</h1>
<p class="mt-2 text-sm text-text-secondary">Automated esports match management for osu!</p>
<div class="mt-8 grid grid-cols-3 gap-4">
<a
href="/matches"
class="group rounded-lg border border-border bg-surface-800 p-5 transition-colors hover:border-accent/40 hover:bg-surface-700"
>
<div class="text-2xl">⚔</div>
<h2 class="font-600 mt-3 text-sm">Matches</h2>
<p class="mt-1 text-xs text-text-secondary">Create and manage competitive matches</p>
</a>
<a
href="/mappools"
class="group rounded-lg border border-border bg-surface-800 p-5 transition-colors hover:border-accent/40 hover:bg-surface-700"
>
<div class="text-2xl">♫</div>
<h2 class="font-600 mt-3 text-sm">Mappools</h2>
<p class="mt-1 text-xs text-text-secondary">Build and share beatmap pools</p>
</a>
<a
href="/teams"
class="group rounded-lg border border-border bg-surface-800 p-5 transition-colors hover:border-accent/40 hover:bg-surface-700"
>
<div class="text-2xl">⚑</div>
<h2 class="font-600 mt-3 text-sm">Teams</h2>
<p class="mt-1 text-xs text-text-secondary">Manage your teams and rosters</p>
</a>
</div>
</div>

View File

@@ -0,0 +1,43 @@
@import 'tailwindcss';
@plugin '@tailwindcss/typography';
@theme {
--color-surface-900: #0a0a0f;
--color-surface-800: #12121a;
--color-surface-700: #1a1a25;
--color-surface-600: #24243a;
--color-accent: #ff66ab;
--color-accent-hover: #ff85be;
--color-accent-dim: #ff66ab33;
--color-text-primary: #eeeef0;
--color-text-secondary: #8888a0;
--color-border: #2a2a3d;
--font-display: 'Geist', system-ui, sans-serif;
--font-body: 'Geist', system-ui, sans-serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
html {
background-color: var(--color-surface-900);
color: var(--color-text-primary);
font-family: var(--font-body);
}
body {
min-height: 100dvh;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--color-surface-900);
}
::-webkit-scrollbar-thumb {
background: var(--color-surface-600);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-accent);
}

View File

@@ -1,9 +0,0 @@
<script lang="ts">
import './layout.css';
import favicon from '$lib/assets/favicon.svg';
let { children } = $props();
</script>
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
{@render children()}

View File

@@ -1,2 +0,0 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>

View File

@@ -1,2 +0,0 @@
@import 'tailwindcss';
@plugin '@tailwindcss/typography';

View File

@@ -1,13 +0,0 @@
import { page } from 'vitest/browser';
import { describe, expect, it } from 'vitest';
import { render } from 'vitest-browser-svelte';
import Page from './+page.svelte';
describe('/+page.svelte', () => {
it('should render h1', async () => {
render(Page);
const heading = page.getByRole('heading', { level: 1 });
await expect.element(heading).toBeInTheDocument();
});
});