add: basic blog layout

Basic Blog Layout to work on
This commit is contained in:
webfussel 2025-06-11 20:41:34 +02:00
parent 9d691974ce
commit 91b59e4ebe
15 changed files with 278 additions and 3 deletions

View file

@ -0,0 +1,32 @@
.BlogAuthor {
display: flex;
align-items: center;
gap: 1rem;
& .meta {
display: flex;
flex-direction: column;
gap: .2rem;
& .name {
font-weight: bold;
}
& .date {
font-size: .8rem;
}
}
& .image {
--size: 50px;
height: var(--size);
width: var(--size);
border-radius: 50%;
overflow: hidden;
& img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
}

View file

@ -0,0 +1,44 @@
.BlogCard {
overflow: hidden;
border-radius: 1rem;
background: var(--color-orange-black);
display: flex;
flex-direction: column;
height: 100%;
transition: 150ms;
gap: 1rem;
&:hover {
scale: 1.05;
}
& .card-content {
display: flex;
flex-direction: column;
height: 100%;
padding: 0 1.5rem 1rem;
gap: 1.5rem;
}
& .chip {
margin-bottom: 1rem;
}
& footer {
margin-top: auto;
display: flex;
flex-direction: column;
gap: .5rem;
}
& .tags {
display: flex;
flex-wrap: wrap;
gap: .5rem;
opacity: .5;
& .tag {
color: var(--color-orange);
}
}
}

View file

@ -142,7 +142,7 @@ span.chip {
border-radius: 999px;
font-size: 1rem;
height: max-content;
padding: .5em 1em;
padding: .2em 1em;
user-select: none;
display: flex;
align-items: center;
@ -288,6 +288,12 @@ span.chip {
align-items: center;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
}
.tip-container .tip {
scale: 0;
position: absolute;

View file

@ -0,0 +1,29 @@
<template>
<aside class="BlogAuthor">
<div class="image">
<img :src="image" :alt="`Bild von ${name}`" />
</div>
<div class="meta">
<span class="name">{{ name }}</span>
<span class="date">{{ dateFormatted }}</span>
</div>
</aside>
</template>
<script setup lang="ts">
type Props = {
name : string
image : string
date : string
}
const { date } = defineProps<Props>()
const formatter = new Intl.DateTimeFormat('de-DE', {
year: 'numeric',
month: 'long',
day: '2-digit',
})
const dateFormatted = computed(() => formatter.format(new Date(date)))
</script>

View file

@ -0,0 +1,42 @@
<template>
<NuxtLink :to="link" class="BlogCard z-2">
<img :src="image" alt=" " aria-hidden="true" />
<div class="card-content">
<header>
<span class="chip">{{ category }}</span>
<h2>{{ title }}</h2>
<small>{{ description }}</small>
</header>
<main>
<ContentRenderer :value="excerpt" />
</main>
<footer>
<BlogAuthor :name="author.name" :image="author.image" :date="date" />
<div class="tags">
<span>tags</span>
<span class="tag" v-for="tag in tags">{{tag}}</span>
</div>
</footer>
</div>
</NuxtLink>
</template>
<script setup lang="ts">
type Props = {
title : string
description : string
image : string
date : string
excerpt : { type: string, children?: any }
link : string
tags : string[]
category : string
author : {
name : string
image : string
}
}
defineProps<Props>()
</script>

View file

@ -8,7 +8,7 @@
<script setup lang="ts">
type Props = {
title : string
titleTag ?: 'strong' | 'h3'
titleTag ?: 'strong' | 'h3' | 'h2'
}
defineProps<Props>()

11
app/pages/blog/[slug].vue Normal file
View file

@ -0,0 +1,11 @@
<template>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

25
app/pages/blog/index.vue Normal file
View file

@ -0,0 +1,25 @@
<template>
<section id="blog" class="Blog content">
<div class="grid">
<BlogCard v-for="article in articles" v-bind="makeBlogCard(article)" />
</div>
</section>
</template>
<script setup lang="ts">
const { data } = await useAsyncData('articles', () => queryCollection('blog').limit(10).all())
const articles = data.value!
const makeBlogCard = (article : typeof articles[0]) => ({
title: article.title,
description: article.description,
image: article.meta.image as string,
date: article.meta.date as string,
excerpt: article.excerpt,
link: article.path,
tags: article.meta.tags as string[],
category: article.meta.category as string,
author: article.meta.author as { name: string, image: string },
})
</script>

View file

@ -23,4 +23,10 @@ export const navigation = [
icon: 'ph:chats-circle-duotone',
aria: 'Link dieser Seite: Kontakt'
},
{
to: `/blog/`,
label: 'Blog',
icon: 'ph:book-open-user-duotone',
aria: 'Link dieser Seite: Blog'
},
]