Files
blog/frontend/app/layouts/default.vue
T
davide 4e8d1a892e Use the CorralitoComing seal as the site logo and favicon
The seal already carries the name, so it replaces the text wordmark in the
header and opens the footer. Small icon sizes use the central emblem only: the
circular lettering is unreadable below 48px.
2026-08-25 13:25:13 +02:00

191 lines
4.1 KiB
Vue

<script setup lang="ts">
import type { Category } from '#shared/types/blog'
const { data: categories } = await useFetch<Category[]>('/api/categories', {
key: 'nav-categories',
default: () => [],
})
</script>
<template>
<div class="page">
<a class="skip-link" href="#main">Skip to content</a>
<header class="site-header">
<div class="container">
<p class="wordmark">
<NuxtLink to="/">
<img src="/logo.png" :alt="SITE_NAME" width="400" height="400">
</NuxtLink>
</p>
<p class="tagline kicker">{{ SITE_DESCRIPTION }}</p>
</div>
<nav class="site-nav" aria-label="Main navigation">
<ul class="container">
<li><NuxtLink to="/blog">All articles</NuxtLink></li>
<li v-for="category in categories" :key="category.slug">
<NuxtLink :to="`/category/${category.slug}`">{{ category.name }}</NuxtLink>
</li>
</ul>
</nav>
</header>
<main id="main" class="container site-main">
<slot />
</main>
<footer class="site-footer">
<div class="container footer-inner">
<div class="footer-block">
<img class="footer-logo" src="/logo.png" :alt="SITE_NAME" width="400" height="400">
<p class="muted">{{ SITE_DESCRIPTION }}</p>
</div>
<nav class="footer-block" aria-label="Categories">
<p class="kicker">Categories</p>
<ul>
<li v-for="category in categories" :key="category.slug">
<NuxtLink :to="`/category/${category.slug}`">{{ category.name }}</NuxtLink>
</li>
<li v-if="!categories.length" class="muted">Nothing published yet</li>
</ul>
</nav>
<nav class="footer-block" aria-label="Sections">
<p class="kicker">Browse</p>
<ul>
<li><NuxtLink to="/">Home</NuxtLink></li>
<li><NuxtLink to="/blog">All articles</NuxtLink></li>
</ul>
</nav>
</div>
<p class="container copyright kicker">
&copy; {{ new Date().getFullYear() }} {{ SITE_NAME }}. All rights reserved.
</p>
</footer>
</div>
</template>
<style scoped>
.page {
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* Header: centred wordmark over a thin rule of links. */
.site-header {
padding-top: 2.5rem;
text-align: center;
border-bottom: 1px solid var(--color-border);
}
.wordmark {
margin: 0;
}
.wordmark a {
display: inline-block;
text-decoration: none;
}
/* The seal carries the name itself, so it stands in for a text wordmark. */
.wordmark img {
width: clamp(6rem, 14vw, 8.5rem);
height: auto;
}
.tagline {
margin-top: 0.6rem;
}
.site-nav {
margin-top: 2rem;
border-top: 1px solid var(--color-border);
}
.site-nav ul {
display: flex;
flex-wrap: wrap;
gap: 0.25rem 2rem;
justify-content: center;
padding-block: 0.9rem;
margin: 0;
list-style: none;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.site-nav a {
text-decoration: none;
color: var(--color-body);
padding-block: 0.25rem;
border-bottom: 1px solid transparent;
}
.site-nav a:hover,
.site-nav a.router-link-active {
color: var(--color-ink);
border-bottom-color: var(--color-ink);
}
.site-main {
flex: 1;
padding-block: 3.5rem 5rem;
}
/* Footer: columns, then a centred copyright line. */
.site-footer {
padding-block: 3.5rem 2rem;
background: var(--color-surface);
border-top: 1px solid var(--color-border);
}
.footer-inner {
display: grid;
gap: 2.5rem;
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
padding-bottom: 2.5rem;
}
.footer-block p:last-of-type {
margin-bottom: 0;
}
.footer-logo {
width: 4.5rem;
height: auto;
margin-bottom: 1rem;
}
.footer-block ul {
padding: 0;
margin: 0.9rem 0 0;
list-style: none;
font-size: 0.95rem;
}
.footer-block li + li {
margin-top: 0.35rem;
}
.footer-block a {
text-decoration: none;
}
.footer-block a:hover {
text-decoration: underline;
text-underline-offset: 0.2em;
}
.copyright {
padding-top: 1.75rem;
text-align: center;
border-top: 1px solid var(--color-border);
}
</style>