Clean up styles, fix and improve the blog post card layout
This commit is contained in:
+123
-86
@@ -44,7 +44,7 @@ if (data.value) {
|
||||
|
||||
// Generate the article's Open Graph image.
|
||||
defineOgImageComponent(
|
||||
'OgImage',
|
||||
'OgThumbnail',
|
||||
{
|
||||
title: data.value.title,
|
||||
description: data.value.description,
|
||||
@@ -59,55 +59,55 @@ if (data.value) {
|
||||
)
|
||||
|
||||
// Hydrate the rendered items.
|
||||
// onMounted(() => {
|
||||
// document.querySelectorAll('pre').forEach((pre) => {
|
||||
// const icon = document.createElement('iconify-icon')
|
||||
//
|
||||
// icon.setAttribute('icon', 'mdi:content-copy')
|
||||
// icon.setAttribute('inline', 'true')
|
||||
//
|
||||
// icon.classList.add('button')
|
||||
//
|
||||
// pre.appendChild(icon)
|
||||
// })
|
||||
//
|
||||
// document
|
||||
// .querySelectorAll('code:not(pre *), pre > iconify-icon.button')
|
||||
// .forEach((code) => {
|
||||
// if (code instanceof HTMLElement) {
|
||||
// code.onclick = () => {
|
||||
// const area = document.createElement('textarea')
|
||||
//
|
||||
// area.textContent =
|
||||
// code.nodeName && code.nodeName.toLowerCase() === 'code'
|
||||
// ? code.textContent
|
||||
// : code.parentElement!.textContent
|
||||
//
|
||||
// // It's necessary to create the textarea element every time you copy to get access to the select() method.
|
||||
// area.setSelectionRange(0, 99999) // An iOS gotcha.
|
||||
// area.select()
|
||||
//
|
||||
// // Copy the text inside the textarea.
|
||||
// navigator.clipboard.writeText(area.value)
|
||||
//
|
||||
// // TODO: Alert the user text has been successfully copied.
|
||||
//
|
||||
// // Remove the textarea element.
|
||||
// area.remove()
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// onUnmounted(() => {
|
||||
// document.querySelectorAll('code').forEach((code) => {
|
||||
// code.onclick = null
|
||||
// })
|
||||
//
|
||||
// document.querySelectorAll('pre').forEach((pre) => {
|
||||
// pre.querySelector('.clipboard')?.remove()
|
||||
// })
|
||||
// })
|
||||
onMounted(() => {
|
||||
document.querySelectorAll('pre').forEach((pre) => {
|
||||
const icon = document.createElement('iconify-icon')
|
||||
|
||||
icon.setAttribute('icon', 'mdi:content-copy')
|
||||
icon.setAttribute('inline', 'true')
|
||||
|
||||
icon.classList.add('button')
|
||||
|
||||
pre.appendChild(icon)
|
||||
})
|
||||
|
||||
document
|
||||
.querySelectorAll('code:not(pre *), pre > iconify-icon.button')
|
||||
.forEach((code) => {
|
||||
if (code instanceof HTMLElement) {
|
||||
code.onclick = () => {
|
||||
const area = document.createElement('textarea')
|
||||
|
||||
area.textContent =
|
||||
code.nodeName && code.nodeName.toLowerCase() === 'code'
|
||||
? code.textContent
|
||||
: code.parentElement!.textContent
|
||||
|
||||
// It's necessary to create the textarea element every time you copy to get access to the select() method.
|
||||
area.setSelectionRange(0, 99999) // An iOS gotcha.
|
||||
area.select()
|
||||
|
||||
// Copy the text inside the textarea.
|
||||
navigator.clipboard.writeText(area.value)
|
||||
|
||||
// TODO: Alert the user text has been successfully copied.
|
||||
|
||||
// Remove the textarea element.
|
||||
area.remove()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.querySelectorAll('code').forEach((code) => {
|
||||
code.onclick = null
|
||||
})
|
||||
|
||||
document.querySelectorAll('pre').forEach((pre) => {
|
||||
pre.querySelector('.clipboard')?.remove()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
useHead({
|
||||
@@ -140,56 +140,93 @@ useHead({
|
||||
</div>
|
||||
<article
|
||||
v-else-if="status === 'success' && data"
|
||||
class="flex-grow post fade-mask-sm flex flex-col gap-4 overflow-x-hidden overflow-y-auto sm:py-4 sm:pe-4"
|
||||
class="flex-grow post snap-normal fade-mask-sm flex flex-col gap-4 overflow-x-hidden overflow-y-auto sm:py-4 sm:pe-4"
|
||||
>
|
||||
<div
|
||||
class="relative flex flex-col justify-end items-start w-full min-h-[400px] rounded-xl accent-text-shadow post-preamble"
|
||||
:style="{ backgroundImage: 'url(' + thumbnail + ')' }"
|
||||
>
|
||||
<div class="p-2">
|
||||
<h3>{{ data.title }}</h3>
|
||||
<div class="flex flex-row flex-wrap gap-x-2 gap-y-0">
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<iconify-icon icon="mdi:calendar" />
|
||||
<small class="whitespace-nowrap">
|
||||
<strong>
|
||||
{{ formatDate(data.created, 'LLLL do, y – HH:mm') }}
|
||||
</strong>
|
||||
</small>
|
||||
</div>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<iconify-icon icon="mdi:clock-outline" />
|
||||
<small class="whitespace-nowrap">
|
||||
<strong>
|
||||
{{ data!.readingTime.text.split(' ')[0] + ' minutes to read' }}
|
||||
</strong>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NuxtLink
|
||||
to="/blog"
|
||||
class="absolute top-0 left-0 p-2 text-inherit no-underline"
|
||||
>
|
||||
<div class="grid-thumbnail grid max-w-[768px] snap-end">
|
||||
<img
|
||||
draggable="false"
|
||||
:src="thumbnail!"
|
||||
alt="Thumbnail"
|
||||
class="grid-thumbnail-image object-cover object-center rounded-xl aspect-[16/9] select-none"
|
||||
/>
|
||||
<NuxtLink to="/blog" class="grid-thumbnail-start button-back p-2">
|
||||
<iconify-icon
|
||||
icon="icon-park-solid:back"
|
||||
width="2em"
|
||||
height="2em"
|
||||
style="color: lavender"
|
||||
class="drop-shadow text-pink-200"
|
||||
/>
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
class="grid-thumbnail-end button-twitter p-2"
|
||||
:href="`https://twitter.com/share?url=${config.url}/blog/${slug}&text=${data.title}&hashtags=${data.tags.slice(0, 3).join(',').replace(/ /g, '')}`"
|
||||
target="_blank"
|
||||
class="absolute top-0 right-0 p-2"
|
||||
>
|
||||
<iconify-icon icon="logos:twitter" width="2em" height="2em" />
|
||||
<iconify-icon
|
||||
icon="logos:twitter"
|
||||
width="2em"
|
||||
height="2em"
|
||||
class="drop-shadow"
|
||||
/>
|
||||
</NuxtLink>
|
||||
<div
|
||||
class="grid-thumbnail-title accent-fade w-full rounded-b-lg p-2 mt-16"
|
||||
>
|
||||
<h3 class="mb-1">{{ data.title }}</h3>
|
||||
<div class="flex flex-row flex-wrap gap-x-2 gap-y-0">
|
||||
<small class="flex flex-row items-center gap-1 whitespace-nowrap">
|
||||
<iconify-icon icon="mdi:calendar" inline />
|
||||
<strong>
|
||||
{{ formatDate(data.created, 'HH:mm • LLLL do, y') }}
|
||||
</strong>
|
||||
</small>
|
||||
<small class="flex flex-row items-center gap-1 whitespace-nowrap">
|
||||
<iconify-icon icon="mdi:clock-outline" inline />
|
||||
<strong>
|
||||
{{ data!.readingTime.text.split(' ')[0] + ' minutes to read' }}
|
||||
</strong>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-content">
|
||||
<hr class="accent-text accent-gradient border-0 h-px" />
|
||||
<section class="page snap-start">
|
||||
<ContentRenderer :value="data" />
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<NotFound v-else />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.grid-thumbnail {
|
||||
&-image {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
|
||||
place-self: start center;
|
||||
|
||||
max-width: clamp(1px, 100%, 768px);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&-title {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
|
||||
place-self: end center;
|
||||
}
|
||||
|
||||
&-start {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
|
||||
place-self: start start;
|
||||
}
|
||||
|
||||
&-end {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
|
||||
place-self: start end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+31
-26
@@ -64,43 +64,48 @@ useHead({
|
||||
class="text-inherit hover:text-inherit no-underline"
|
||||
>
|
||||
<article
|
||||
class="post-box flex flex-col md:flex-row gap-4 rounded-xl h-full"
|
||||
class="post-box relative flex flex-col md:flex-row gap-4 p-2 border-2 rounded-xl transition-ease"
|
||||
>
|
||||
<div class="post-thumb">
|
||||
<img
|
||||
draggable="false"
|
||||
:src="
|
||||
post.thumbnail ??
|
||||
`/images/blog/thumbnails/${post._path!.split('/').at(-1)}.png`
|
||||
"
|
||||
:alt="post.title"
|
||||
class="rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<h3 class="post-title">{{ post.title }}</h3>
|
||||
<p class="post-description mb-0">{{ post.description }}</p>
|
||||
<img
|
||||
draggable="false"
|
||||
:src="
|
||||
post.thumbnail ??
|
||||
`/images/blog/thumbnails/${post._path!.split('/').at(-1)}.png`
|
||||
"
|
||||
:alt="post.title"
|
||||
class="md:flex-grow-[2] md:basis-0 xl:flex-grow-1 object-cover object-center aspect-[16/9] w-fit md:w-0 xl:w-fit h-full max-h-[300px] rounded-xl"
|
||||
/>
|
||||
<div class="md:flex-grow-[3] md:basis-0 xl:flex-grow-1 w-full">
|
||||
<h3 class="post-title md:line-clamp-2 md:overflow-ellipsis">
|
||||
{{ post.title }}
|
||||
</h3>
|
||||
<p
|
||||
class="post-description mb-0 md:line-clamp-4 md:overflow-ellipsis"
|
||||
>
|
||||
{{ post.description }}
|
||||
</p>
|
||||
|
||||
<div class="post-tags flex flex-row flex-wrap gap-2 py-2">
|
||||
<span
|
||||
<div class="flex flex-row flex-wrap gap-2 py-2">
|
||||
<small
|
||||
v-for="(tag, index) in post.tags.slice(0, 3)"
|
||||
:key="index"
|
||||
class="whitespace-nowrap"
|
||||
class="post-tag px-2 py-1 rounded-xl border whitespace-nowrap"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
<span v-if="post.tags.length > 3" class="whitespace-nowrap">
|
||||
</small>
|
||||
<small
|
||||
v-if="post.tags.length > 3"
|
||||
class="post-tag px-2 py-1 rounded-xl border whitespace-nowrap"
|
||||
>
|
||||
{{ post.tags.length - 3 }} more
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<hr class="accent-text accent-gradient border-0 h-px" />
|
||||
|
||||
<div class="post-details py-2">
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<iconify-icon icon="mdi:calendar" />
|
||||
<small class="whitespace-nowrap">
|
||||
{{ formatDate(post.created, 'LLLL do, y – HH:mm') }}
|
||||
{{ formatDate(post.created, 'HH:mm • LLLL do, y') }}
|
||||
</small>
|
||||
</div>
|
||||
<div
|
||||
@@ -109,14 +114,14 @@ useHead({
|
||||
>
|
||||
<iconify-icon icon="mdi:pencil" />
|
||||
<small class="whitespace-nowrap">
|
||||
{{ formatDate(post.updated, 'LLLL do, y – HH:mm') }}
|
||||
{{ formatDate(post.updated, 'HH:mm • LLLL do, y') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="post-pocket absolute bottom-0 right-0 uppercase rounded-tl-xl flex flex-row items-center gap-2 p-2"
|
||||
class="post-pocket border-l border-t absolute bottom-0 right-0 uppercase rounded-tl-xl flex flex-row items-center gap-2 p-2"
|
||||
>
|
||||
<iconify-icon icon="mdi:clock-outline" />
|
||||
<small class="whitespace-nowrap font-mono font-small">
|
||||
|
||||
Reference in New Issue
Block a user