more of the article layout + layout refactor

This commit is contained in:
Tony Sullivan 2023-09-13 09:36:44 -05:00
parent 635fb1510c
commit ae23274489
8 changed files with 386 additions and 291 deletions

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,131 @@
---
import type { HTMLAttributes } from 'astro/types';
import { Image } from 'astro:assets';
import { CollectionEntry } from 'astro:content';
import getReadingTime from 'reading-time';
import FormattedDate from './FormattedDate.astro';
import Icon from './Icon.astro';
import TagList from './TagList.astro';
import settings from '../settings.js';
interface Props extends HTMLAttributes<'header'> {
article: CollectionEntry<'posts'>;
}
const { article, ...attrs } = Astro.props;
const { text: readingTime } = await getReadingTime(article.body);
---
<header {...attrs}>
<a href="/" class="back">
<Icon icon="arrow-left" color="var(--theme-accent-dark)" size="1.25rem" />
<span>Back to feed</span>
</a>
<div class="cover">
{
article.data.cover && (
<Image src={article.data.cover.src} alt={article.data.cover.alt} class="cover" />
)
}
<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>
</div>
<div class="meta">
<FormattedDate date={article.data.pubDate} />
<span>•</span>
<p>
<Icon icon="clock" size="1rem" />
<span>{readingTime}</span>
</p>
</div>
<h1>{article.data.title}</h1>
{article.data.tags?.length > 0 && <TagList tags={article.data.tags} />}
</header>
<style>
header {
display: flex;
flex-direction: column;
gap: 1rem;
}
.back {
text-decoration: none;
font-weight: bold;
}
.back {
display: flex;
align-items: center;
gap: 0.5rem;
margin-inline-start: -4px;
}
.cover {
position: relative;
width: 100%;
min-height: 150px;
background: var(--theme-gradient-main);
border-radius: 1.25rem;
margin-bottom: 1.25rem;
}
.cover > img {
width: 100%;
height: 100%;
aspect-ratio: 2.777;
object-fit: cover;
}
.author {
position: absolute;
display: flex;
align-items: center;
gap: 0.5rem;
bottom: 0%;
right: 3.125rem;
translate: 0 50%;
box-shadow: var(--theme-shadow-md);
background-color: var(--theme-accent-light);
padding: 0.75rem 1.5rem;
border-radius: 1.25rem;
border: 3px solid var(--theme-accent-dark);
}
.author img {
width: 2.5rem;
height: 2.5rem;
}
.author .u-name {
font-family: var(--font-brand);
font-weight: bold;
}
.author .u-nickname {
color: var(--theme-gray-300);
font-size: var(--theme-text-sm);
font-weight: 600;
}
.meta {
display: flex;
align-items: center;
gap: 1rem;
font-size: var(--theme-text-sm);
color: var(--theme-gray-200);
}
.meta svg {
margin-block-end: 0.25rem;
}
</style>

View file

@ -20,9 +20,7 @@ import ThemeToggle from './ThemeToggle.astro';
font-weight: 700;
font-family: var(--theme-font-brand);
text-decoration: none;
color: transparent;
background: var(--theme-gradient-main);
background-clip: text;
color: var(--theme-accent-dark);
}
.site-name:hover {

View file

@ -0,0 +1,33 @@
---
import type { HTMLAttributes } from 'astro/types';
export interface Props extends HTMLAttributes<'article'> {}
---
<article {...Astro.props}>
<slot />
</article>
<style>
article > :global(* + *) {
margin-block-start: 1em;
}
article > :global(p:first-child::first-letter) {
float: left;
font-size: 3.5rem;
padding-inline-end: 0.5rem;
padding-block-start: 0.35rem;
font-weight: bold;
}
article :global(img, video, figure) {
margin-inline: auto;
}
@media (min-width: 50em) {
article > :global(* + *) {
margin-block-start: 1.5em;
}
}
</style>

View file

@ -1,4 +1,5 @@
---
import { Image } from 'astro:assets';
import settings from '../settings';
import Icon from './Icon.astro';
@ -10,7 +11,7 @@ const socialLinks = Object.entries(settings.social) as SocialEntry[];
<div class="profile">
<div>
<div class="avatar">
<img width="110" height="110" {...settings.avatar} />
<Image {...settings.avatar} width={220} />
</div>
<h1>
{settings.name}
@ -36,11 +37,7 @@ const socialLinks = Object.entries(settings.social) as SocialEntry[];
{
settings.location && (
<p>
<Icon
icon="location-point"
color="var(--theme-accent-dark)"
size="1.75rem"
/>
<Icon icon="location-point" color="var(--theme-accent-dark)" size="1.75rem" />
<span class="sr-only">Location</span>
{settings.location}
</p>
@ -49,15 +46,9 @@ const socialLinks = Object.entries(settings.social) as SocialEntry[];
{
settings.homepage && (
<p>
<Icon
icon="link-h"
color="var(--theme-accent-dark)"
size="1.75rem"
/>
<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}>{settings.homepage.replace(/^https?:\/\/(www\.)?/, '')}</a>
</p>
)
}
@ -101,7 +92,8 @@ const socialLinks = Object.entries(settings.social) as SocialEntry[];
}
.avatar img {
height: auto;
width: 110px;
height: 110px;
}
h1 {

View file

@ -1,11 +1,9 @@
---
import { Image } from 'astro:assets';
import type { CollectionEntry } from 'astro:content';
import getReadingTime from 'reading-time';
import Layout, { type Props as LayoutProps } from './Base.astro';
import FormattedDate from '../components/FormattedDate.astro';
import TagList from '../components/TagList.astro';
import ArticleHeader from '../components/ArticleHeader.astro';
import Icon from '../components/Icon.astro';
import Prose from '../components/Prose.astro';
export interface Props extends LayoutProps {
article: CollectionEntry<'posts'>;
@ -16,34 +14,17 @@ export interface Props extends LayoutProps {
const { article, next, prev } = Astro.props;
const { Content, headings } = await article.render();
const readingTime = getReadingTime(article.body).text;
---
<Layout ...Astro.props} wrapperReverse>
<header>
<a href="/" class="back">
<Icon icon="arrow-left" color="var(--theme-accent-dark)" size="1.25rem" />
<span>Back to feed</span>
</a>
{article.data.cover && <Image src={article.data.cover.src} alt={article.data.cover.alt} class="cover" />}
<div class="meta">
<FormattedDate date={article.data.pubDate} />
<span>•</span>
<p>
<Icon icon="clock" size="1rem" />
<span>{readingTime}</span>
</p>
</div>
<h1>{article.data.title}</h1>
{article.data.tags?.length > 0 && (
<TagList tags={article.data.tags} />
)}
</header>
<div class="content">
<ArticleHeader {article} class="header" />
<Prose class="content">
<Content />
</div>
{headings.length > 0 && (
</Prose>
{
headings.length > 0 && (
<aside>
<h3>Table of contents</h3>
<ul>
@ -54,80 +35,35 @@ const readingTime = getReadingTime(article.body).text;
))}
</ul>
</aside>
)}
{(next || prev) && (
)
}
{
(next || prev) && (
<footer>
{prev && <a href={`/post/${prev.slug}`} class="prev">
{prev && (
<a href={`/post/${prev.slug}`} class="prev">
<Icon icon="arrow-left" size="1.25rem" color="var(--theme-accent-dark)" />
<span>Previous: {prev.data.title}</span>
</a>}
{next && <a href={`/post/${next.slug}`} class="next">
</a>
)}
{next && (
<a href={`/post/${next.slug}`} class="next">
<span>Next: {next.data.title}</span>
<Icon icon="arrow-right" size="1.25rem" color="var(--theme-accent-dark)" />
</a>}
</footer>
</a>
)}
</footer>
)
}
</Layout>
<style>
header, footer {
.header,
footer {
grid-column: 1 / -1;
}
header {
display: flex;
flex-direction: column;
gap: 1rem;
}
.content > :global(* + *) {
margin-block-start: 1em;
}
.content > :global(p:first-child::first-letter) {
float: left;
font-size: 3.5rem;
padding-inline-end: .5rem;
padding-block-start: .35rem;
font-weight: bold;
}
.content :global(img, video, figure) {
margin-inline: auto;
}
.meta {
display: flex;
align-items: center;
gap: 1rem;
font-size: var(--theme-text-sm);
color: var(--theme-gray-200);
}
.meta svg {
margin-block-end: 0.25rem;
}
.back {
text-decoration: none;
font-weight: bold;
}
.back {
display: flex;
align-items: center;
gap: 0.5rem;
margin-inline-start: -4px;
}
.cover {
height: auto;
aspect-ratio: 2.777;
object-fit: cover;
border-radius: 1.25rem;
margin-block-start: 0.5rem;
}
footer {
display: flex;
justify-content: space-between;
@ -165,9 +101,5 @@ const readingTime = getReadingTime(article.body).text;
aside {
display: block;
}
.content > :global(* + *) {
margin-block-start: 1.5em;
}
}
</style>

View file

@ -1,5 +1,6 @@
---
import type { GetStaticPathsOptions, Page } from 'astro';
import { Image } from 'astro:assets';
import type { CollectionEntry } from 'astro:content';
import Pagination from '../components/Pagination.astro';
import TagList from '../components/TagList.astro';
@ -28,11 +29,11 @@ const { page } = Astro.props;
<article class="card">
<header>
<div style="display: flex;gap:0.875rem;align-items:center;">
<img
style="border-radius: var(--theme-radius-full);background-color:var(--theme-shade-subtle);"
width="60"
height="60"
{...settings.avatar}
<Image
src={settings.avatar.src}
width={120}
class="u-avatar"
alt={settings.avatar.alt}
/>
<div>
<p style="font-family:var(--theme-font-brand);font-weight:700;font-size:var(--theme-text-lg)">
@ -89,4 +90,10 @@ const { page } = Astro.props;
header > * + * {
margin-top: 0.625rem;
}
.u-avatar {
width: 3.75rem;
height: 3.75rem;
border-radius: var(--theme-radius-full);
background-color: var(--theme-shade-subtle);
}
</style>

View file

@ -1,8 +1,10 @@
import avatar from './assets/avatar.webp'
export default {
name: 'Houston Astro',
username: '@houston',
avatar: {
src: '/avatar.webp',
src: avatar,
alt: 'Astro mascot Houston smiling',
},
rss: {