Something went wrong. Try again.
mat's hack club statistical analysis statsclub.matmanna.dev
nuxt vue dashboard hackclub
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241<script setup lang="ts">import { useRoute } from "#imports";
const route = useRoute();
const navigation = [ { label: "Home", to: "/", match: ["/"] }, { label: "Hackatime Help", to: "/hackatime-help", match: ["/hackatime-help", "/hackatime"] }, { label: "#ship", to: "/ship", match: ["/ship"] }];
function isActive(item: { match: string[] }): boolean { return item.match.some((path) => route.path === path || route.path.startsWith(`${path}/`));}</script>
<template> <div class="app-shell"> <aside class="sidebar"> <div class="brand"> <div class="brand-mark">SC</div> <div class="brand-copy"> <p>StatsClub</p> <span>Mat's analysis projects</span> </div> </div>
<nav class="nav" aria-label="Primary"> <NuxtLink v-for="item in navigation" :key="item.to" :to="item.to" class="nav-link" :class="{ 'is-active': isActive(item) }" > {{ item.label }} </NuxtLink> </nav>
<div class="sidebar-footer"> <p>Green dashboards for Hack Club data, survey analysis, and community stats.</p> <a href="https://github.com/matmanna/hackatime-help-analysis" target="_blank" rel="noreferrer"> Star the repo </a> <a href="https://github.com/matmanna" target="_blank" rel="noreferrer"> Follow mat manna </a> </div> </aside>
<main class="content-shell"> <NuxtPage /> </main> </div></template>
<style>:root { color-scheme: dark; --bg: #041913; --bg-elevated: rgba(6, 35, 26, 0.88); --bg-panel: rgba(12, 58, 42, 0.84); --border: rgba(148, 214, 180, 0.16); --border-strong: rgba(148, 214, 180, 0.28); --text: #ecfff4; --muted: rgba(236, 255, 244, 0.72); --muted-strong: rgba(236, 255, 244, 0.86); --primary: #29d17d; --primary-soft: rgba(41, 209, 125, 0.2); --primary-glow: rgba(41, 209, 125, 0.32); --shadow: 0 30px 80px rgba(0, 0, 0, 0.35);}
html,body,#__nuxt { min-height: 100%;}
body { margin: 0; color: var(--text); background: var(--bg); font-family: Inter, "Segoe UI", "Helvetica Neue", Arial, sans-serif;}
a { color: inherit; text-decoration: none;}
button,input,select,textarea { font: inherit;}
.app-shell { min-height: 100vh; display: grid; grid-template-columns: 280px minmax(0, 1fr); align-items: start;}
.sidebar { position: sticky; top: 0; height: auto; max-height: 100vh; padding: 24px 18px; background: var(--bg-elevated); border-right: 1px solid var(--border); /* minimal: no large blur */ display: flex; flex-direction: column; gap: 28px;}
.brand { display: flex; align-items: center; gap: 14px;}
.brand-mark { width: 48px; height: 48px; border-radius: 16px; display: grid; place-items: center; background: transparent; border: 1px solid rgba(255,255,255,0.04); color: var(--text); font-weight: 700; letter-spacing: 0.06em;}
.brand-copy p { margin: 0; font-size: 1rem; font-weight: 700;}
.brand-copy span { display: block; margin-top: 3px; color: var(--muted); font-size: 0.9rem;}
.nav { display: grid; gap: 10px;}
.nav-link { padding: 14px 15px; border-radius: 16px; border: 1px solid transparent; color: var(--muted-strong); background: transparent; transition: transform 180ms ease, border-color 180ms ease, background 180ms ease, color 180ms ease;}
.nav-link:hover { transform: none; border-color: var(--border-strong); background: rgba(255, 255, 255, 0.02); color: var(--text);}
.nav-link.is-active { border-color: var(--primary); background: rgba(255,255,255,0.02); color: var(--text);}
.sidebar-footer { margin-top: auto; padding: 18px; border: 1px solid var(--border); border-radius: 22px; background: transparent; display: grid; gap: 10px;}
.sidebar-footer p { margin: 0 0 2px; color: var(--muted); font-size: 0.92rem; line-height: 1.5;}
.sidebar-footer a { color: var(--primary); font-size: 0.94rem;}
.sidebar-footer a:hover { color: #ffffff;}
.content-shell { min-width: 0; padding: 24px; overflow-x: hidden;}
@media (max-width: 960px) { .app-shell { grid-template-columns: 1fr; }
.sidebar { position: static; height: auto; border-right: none; border-bottom: 1px solid var(--border); }
.nav { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.content-shell { padding: 18px 16px 28px; }}
@media (max-width: 680px) { .sidebar { padding: 18px 14px; }
.nav { grid-template-columns: 1fr; }}</style>