WIP: adding microformat data

This commit is contained in:
Tony Sullivan 2023-09-15 10:00:08 -05:00
parent 3bd197746b
commit d77bd5d447
9 changed files with 75 additions and 54 deletions

View file

@ -1,7 +1,5 @@
---
import ArticleHeader from './ArticleHeader.astro';
import Icon from './Icon.astro';
import Prose from './Prose.astro';
import SplitLayout from './SplitLayout.astro';
import type { Article, Post } from '../content/config.js';
@ -17,7 +15,7 @@ const { Content, headings } = await article.render();
<article>
<ArticleHeader {article} class="header" />
<SplitLayout reverse>
<div class="p-content">
<div class="e-content">
<Content />
</div>
@ -46,11 +44,11 @@ const { Content, headings } = await article.render();
padding-block: var(--theme-space-md);
}
.p-content > :global(* + *) {
.e-content > :global(* + *) {
margin-block-start: 1em;
}
.p-content > :global(p:first-child::first-letter) {
.e-content > :global(p:first-child::first-letter) {
float: left;
font-size: 3.5rem;
padding-inline-end: 0.5rem;
@ -58,7 +56,7 @@ const { Content, headings } = await article.render();
font-weight: bold;
}
.p-content :global(img, video, figure) {
.e-content :global(img, video, figure) {
margin-inline: auto;
}
@ -86,7 +84,7 @@ const { Content, headings } = await article.render();
display: block;
}
.p-content > :global(* + *) {
.e-content > :global(* + *) {
margin-block-start: 1.5em;
}
}

View file

@ -21,19 +21,19 @@ const { text: readingTime } = await getReadingTime(article.body);
<div class="cover">
{
article.data.cover && (
<Image src={article.data.cover.src} alt={article.data.cover.alt} />
<Image src={article.data.cover.src} alt={article.data.cover.alt} class="u-photo" />
)
}
<div class="author">
<Image src={settings.avatar.src} alt={settings.avatar.alt} width={80} />
<p class="u-name">{settings.name}</p>
<p class="u-nickname">{settings.username}</p>
<div class="p-author h-card">
<Image src={settings.avatar.src} alt={settings.avatar.alt} width={80} class="u-photo" />
<p class="p-name">{settings.name}</p>
<p class="p-nickname">{settings.username}</p>
</div>
</div>
<div class="meta">
<FormattedDate date={article.data.pubDate} />
<FormattedDate date={article.data.pubDate} class="dt-published" />
<span>•</span>
<p>
<Icon icon="clock" size="1rem" />
@ -41,7 +41,7 @@ const { text: readingTime } = await getReadingTime(article.body);
</p>
</div>
<h1>{article.data.title}</h1>
<h1 class="p-name">{article.data.title}</h1>
{article.data.categories?.length > 0 && <TagList tags={article.data.categories} />}
</header>
@ -73,7 +73,7 @@ const { text: readingTime } = await getReadingTime(article.body);
border-radius: inherit;
}
.author {
.p-author {
grid-column: 1;
grid-row: 1;
display: flex;
@ -96,23 +96,23 @@ const { text: readingTime } = await getReadingTime(article.body);
margin-block-end: var(--theme-space-sm);
}
.author {
.p-author {
justify-self: end;
margin-inline-end: var(--theme-space-xl);
}
}
.author img {
.p-author img {
width: var(--theme-space-lg);
height: var(--theme-space-lg);
}
.author .u-name {
.p-author .p-name {
font-family: var(--font-brand);
font-weight: bold;
}
.author .u-nickname {
.p-author .p-nickname {
color: var(--theme-gray-300);
font-size: var(--theme-text-sm);
font-weight: 600;

View file

@ -18,28 +18,28 @@ const cover = 'cover' in post.data && post.data.cover;
const title = 'title' in post.data && post.data.title;
---
<article {...attrs}>
<header>
<Image {...settings.avatar} width={120} class="u-image" />
<strong class="u-name">{settings.name}</strong>
<div class="u-nickname">
<article class="h-entry" {...attrs}>
<header class="p-author h-card">
<Image {...settings.avatar} width={120} class="u-photo" />
<strong class="p-name">{settings.name}</strong>
<div class="p-nickname">
{settings.username}
<FormattedDate date={pubDate} />
<FormattedDate date={pubDate} class="dt-published" />
</div>
{categories.length > 0 && <TagList tags={categories} class="tags" />}
</header>
{cover && <Image {...cover} width={1060} />}
{title && <h3>{title}</h3>}
{cover && <Image {...cover} width={1060} class="u-photo" />}
{title && <h3 class="p-name">{title}</h3>}
{
isArticle(post) ? (
<p>{post.data.description}</p>
<p class="p-summary">{post.data.description}</p>
) : (
post.render().then(({ Content }) => <Content />)
post.render().then(({ Content }) => <div class="e-content"><Content /></div>)
)
}
<footer>
<a href={`/post/${post.slug}/`}>Full {post.data.type === 'article' ? 'article' : 'note'}</a>
<a href={`/post/${post.slug}/`} class="u-url">Full {post.collection === 'articles' ? 'article' : 'note'}</a>
<a href={`javascript: navigator.clipboard.writeText(window.location.href + "post/${post.slug}/");`}>
<Icon icon="share" size="1.5rem" />
<span class="sr-only">Share this post</span>
@ -65,19 +65,19 @@ const title = 'title' in post.data && post.data.title;
row-gap: var(--theme-space-2xs);
column-gap: var(--theme-space-sm);
}
.u-image {
.u-photo {
grid-area: avatar;
width: var(--theme-space-xl);
height: var(--theme-space-xl);
border-radius: var(--theme-radius-full);
background-color: var(--theme-shade-subtle);
}
.u-name {
.p-name {
grid-area: name;
font-family: var(--font-brand);
font-weight: bold;
}
.u-nickname {
.p-nickname {
grid-area: nickname;
font-size: var(--theme-text-sm);
color: var(--theme-gray-300);

View file

@ -1,5 +1,7 @@
---
interface Props {
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'time'> {
date: Date;
}

View file

@ -9,7 +9,11 @@ const { tags, ...attrs } = Astro.props;
---
<ul {...attrs}>
{tags.map((tag) => <li>{tag}</li>)}
{tags.map((tag) => (
<li>
<a href={`/category/${tag}/`} rel="category tag" class="p-category">{tag}</a>
</li>
))}
</ul>
<style>

View file

@ -6,14 +6,14 @@ import Icon from './Icon.astro';
const socialLinks = Object.entries(settings.social);
---
<div class="profile">
<div class="h-card">
<div>
<div class="avatar">
<div class="u-photo">
<Image {...settings.avatar} width={220} />
</div>
<h1>
{settings.name}
<small>{settings.username}</small>
<span class="p-name">{settings.name}</span>
<small class="p-nickname">{settings.username}</small>
</h1>
</div>
<div class="bio-sections">
@ -37,7 +37,7 @@ const socialLinks = Object.entries(settings.social);
<p>
<Icon icon="location-point" color="var(--theme-accent-dark)" size="1.75rem" />
<span class="sr-only">Location</span>
{settings.location}
<span class="p-locality">{settings.location}</span>
</p>
)
}
@ -46,7 +46,7 @@ const socialLinks = Object.entries(settings.social);
<p>
<Icon icon="link-h" color="var(--theme-accent-dark)" size="1.75rem" />
<span class="sr-only">Homepage</span>
<a href={settings.homepage}>{settings.homepage.replace(/^https?:\/\/(www\.)?/, '')}</a>
<a href={settings.homepage} class="u-url">{settings.homepage.replace(/^https?:\/\/(www\.)?/, '')}</a>
</p>
)
}
@ -57,9 +57,10 @@ const socialLinks = Object.entries(settings.social);
</div>
<ul class="social">
{
socialLinks.map(([key, { url }]) => (
socialLinks.map(([key, { url, title }]) => (
<li>
<a href={url}>
<a href={url} rel="me">
<span class="sr-only">{title}</span>
<Icon icon={`${key}`} size="1.75rem" />
</a>
</li>
@ -70,18 +71,18 @@ const socialLinks = Object.entries(settings.social);
</div>
<style>
.profile {
.h-card {
display: flex;
flex-direction: column;
gap: var(--theme-space-md);
}
.avatar {
.u-photo {
display: inline-block;
position: relative;
}
.avatar::after {
.u-photo::after {
border-radius: var(--theme-radius-full);
position: absolute;
content: '';
@ -89,7 +90,7 @@ const socialLinks = Object.entries(settings.social);
border: 3px solid var(--theme-text);
}
.avatar img {
.u-photo img {
width: 110px;
height: 110px;
}

View file

@ -16,6 +16,7 @@ const articles = defineCollection({
})
.required({
// requiring the description for articles, this will be shown as the short preview text on cards
title: true,
description: true
})
.strict(),

View file

@ -1,4 +1,13 @@
import { getCollection } from 'astro:content';
import type { Article, Note } from '../content/config';
export function sortPosts(order: 'asc' | 'desc' = 'desc') {
return function(a: Article | Note, b: Article | Note) {
return order === 'asc'
? a.data.pubDate.getTime() - b.data.pubDate.getTime()
: b.data.pubDate.getTime() - a.data.pubDate.getTime()
}
}
/** Get everything in your posts collection, sorted by date. */
export async function getSortedPosts(order: 'asc' | 'desc' = 'desc') {
@ -6,8 +15,10 @@ export async function getSortedPosts(order: 'asc' | 'desc' = 'desc') {
getCollection('articles'),
getCollection('notes'),
])
.then((collections) => collections.flat())
posts.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
if (order === 'asc') posts.reverse();
.then((collections) => collections
.flat()
.sort(sortPosts(order)
));
return posts;
}

View file

@ -1,12 +1,14 @@
import rss from '@astrojs/rss';
import type { APIContext } from 'astro';
import { getSortedPosts } from '../helpers/getSortedPosts';
import { sortPosts } from '../helpers/getSortedPosts';
import settings from '../settings';
import { getCollection } from 'astro:content';
const { title, description } = settings.rss;
export async function get(context: APIContext) {
const posts = await getSortedPosts();
export async function GET(context: APIContext) {
const posts = await getCollection('articles');
return rss({
// `<title>` field in output xml
title,
@ -17,7 +19,9 @@ export async function get(context: APIContext) {
site: context.site!.href,
// Array of `<item>`s in output xml
// See "Generating items" section for examples using content collections and glob imports
items: posts.map(({ data, slug }) => ({ ...data, link: `/post/${slug}` })),
items: posts
.sort(sortPosts())
.map(({ data, slug }) => ({ ...data, link: `/post/${slug}` })),
stylesheet: '/rss/styles.xsl',
});
}