feat(frontend): assemble accessible application shell

This commit is contained in:
Justin Visser 2026-08-10 15:04:17 +02:00
parent 96c69507ec
commit 036ef3cc6f
27 changed files with 1852 additions and 13 deletions

View file

@ -0,0 +1,140 @@
<script setup lang="ts">
import type { AppMode, AuthState } from '../lib/models'
import AuthStatus from './AuthStatus.vue'
defineProps<{
auth: AuthState
mode: AppMode | null
healthFailed: boolean
}>()
defineEmits<{ logout: []; toggleDev: [] }>()
</script>
<template>
<header class="header">
<div class="inner">
<div class="brand">
<span class="wordmark">discovery<span class="accent">-by-</span>llm</span>
<span class="kicker">proof of concept</span>
</div>
<div class="controls">
<span class="mode" :class="{ failed: healthFailed }">
{{ mode ? `${mode} mode` : healthFailed ? 'mode unavailable' : 'checking mode' }}
</span>
<AuthStatus :auth="auth" @logout="$emit('logout')" />
<button class="button" type="button" @click="$emit('toggleDev')">
Dev panel <span class="key">Ctrl+D</span>
</button>
</div>
</div>
</header>
</template>
<style scoped>
.header {
z-index: 20;
flex: none;
padding: var(--s-5) var(--gutter);
background: var(--c-bg);
border-bottom: 1px solid var(--c-line);
}
.inner {
display: flex;
gap: var(--s-6);
align-items: center;
justify-content: space-between;
max-width: var(--measure);
margin: 0 auto;
}
.brand {
display: flex;
gap: var(--s-5);
align-items: baseline;
min-width: 0;
}
.wordmark {
font-family: var(--f-display);
font-size: var(--t-brand);
font-weight: 600;
letter-spacing: -0.02em;
}
.accent {
color: var(--c-accent);
}
.kicker,
.mode,
.button {
font-family: var(--f-mono);
font-size: var(--t-micro);
}
.kicker {
color: var(--c-text-faint);
font-size: var(--t-caps);
letter-spacing: var(--ls-caps);
text-transform: uppercase;
}
.controls {
display: flex;
flex: none;
gap: var(--s-4);
align-items: center;
white-space: nowrap;
}
.mode {
color: var(--c-accent-soft);
text-transform: uppercase;
}
.mode.failed {
color: var(--c-error);
}
.button {
min-height: 31px;
padding: 6px 11px;
color: var(--c-text-muted);
cursor: pointer;
background: transparent;
border: 1px solid var(--c-line);
border-radius: var(--r-md);
}
.button:hover {
color: var(--c-text);
border-color: var(--c-focus-border);
}
.key {
color: var(--c-text-ghost);
}
@media (max-width: 760px) {
.kicker,
.mode,
.key {
display: none;
}
}
@media (max-width: 560px) {
.button {
min-width: var(--tap-min);
min-height: var(--tap-min);
padding: 8px;
font-size: 0;
}
.button::after {
content: 'Dev';
font-size: var(--t-micro);
}
}
</style>