Compare commits

...

15 commits

Author SHA1 Message Date
Tony Sullivan
a1b7287c98 bugfix: full width images in card body 2023-09-25 15:38:09 -05:00
Tony Sullivan
97ac38e3bb disabling view transitions for now 2023-09-25 15:31:17 -05:00
Tony Sullivan
9addc2cbf1 mobile layout for card header 2023-09-25 15:28:02 -05:00
Tony Sullivan
6800e399ed some basic view transition support 2023-09-25 15:28:02 -05:00
Tony Sullivan
1d9faff6fe a few styling fixes 2023-09-25 15:28:02 -05:00
Tony Sullivan
d77bd5d447 WIP: adding microformat data 2023-09-25 15:28:02 -05:00
Tony Sullivan
3bd197746b nit: fix spacing in note post page 2023-09-25 15:28:02 -05:00
Tony Sullivan
ff1efb37a4 simplifying the footer, reducing font sizes slightly 2023-09-25 15:28:01 -05:00
Tony Sullivan
8c5280937d refactoring the layout, adding font & spacer scaling 2023-09-25 15:28:01 -05:00
Tony Sullivan
01bef5d14b WIP: refactoring the types to distinguish between articles and notes 2023-09-25 15:28:01 -05:00
Tony Sullivan
ae23274489 more of the article layout + layout refactor 2023-09-25 15:28:01 -05:00
Tony Sullivan
635fb1510c missed a file in the last commit 2023-09-25 15:28:01 -05:00
Tony Sullivan
de8fee50fd WIP: adding the styled layout for blog articles 2023-09-25 15:28:01 -05:00
Tony Sullivan
58b48a9bfe updating for latest Astro deps 2023-09-25 15:28:01 -05:00
Chris Swithinbank
07ccbe03a6 Bring project over into monorepo 2023-09-25 15:28:01 -05:00
48 changed files with 1851 additions and 0 deletions

21
examples/social-feed/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

View file

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

View file

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View file

@ -0,0 +1,46 @@
# Astro Starter Kit: Social Feed
```
npm create astro@latest -- --template social-feed
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/social-feed)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/s/github/withastro/astro/tree/latest/examples/social-feed)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View file

@ -0,0 +1,6 @@
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({
site: 'https://www.example.com',
});

View file

@ -0,0 +1,18 @@
{
"name": "@example/social-feed",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/rss": "^3.0.0",
"astro": "^3.0.1",
"reading-time": "^1.5.0"
}
}

View file

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 36 36">
<path fill="#000" d="M22.25 4h-8.5a1 1 0 0 0-.96.73l-5.54 19.4a.5.5 0 0 0 .62.62l5.05-1.44a2 2 0 0 0 1.38-1.4l3.22-11.66a.5.5 0 0 1 .96 0l3.22 11.67a2 2 0 0 0 1.38 1.39l5.05 1.44a.5.5 0 0 0 .62-.62l-5.54-19.4a1 1 0 0 0-.96-.73Z"/>
<path fill="url(#gradient)" d="M18 28a7.63 7.63 0 0 1-5-2c-1.4 2.1-.35 4.35.6 5.55.14.17.41.07.47-.15.44-1.8 2.93-1.22 2.93.6 0 2.28.87 3.4 1.72 3.81.34.16.59-.2.49-.56-.31-1.05-.29-2.46 1.29-3.25 3-1.5 3.17-4.83 2.5-6-.67.67-2.6 2-5 2Z"/>
<defs>
<linearGradient id="gradient" x1="16" x2="16" y1="32" y2="24" gradientUnits="userSpaceOnUse">
<stop stop-color="#000"/>
<stop offset="1" stop-color="#000" stop-opacity="0"/>
</linearGradient>
</defs>
<style>
@media (prefers-color-scheme:dark){:root{filter:invert(100%)}}
</style>
</svg>

After

Width:  |  Height:  |  Size: 873 B

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,58 @@
---
import ArticleHeader from './ArticleHeader.astro';
import type { Article } from '../content/config.js';
export interface Props {
article: Article;
}
const { article } = Astro.props;
const { Content } = await article.render();
---
<article>
<ArticleHeader {article} class="header" />
<div class="e-content">
<Content />
</div>
</article>
<style>
article {
display: flex;
flex-direction: column;
gap: var(--theme-space-lg);
padding-block: var(--theme-space-md);
width: 100%;
max-width: var(--theme-size-content-3);
margin-inline: auto;
}
.e-content {
max-width: var(--theme-size-content-3);
margin-inline: auto;
}
.e-content > :global(* + *) {
margin-block-start: 1em;
}
.e-content > :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;
}
.e-content :global(img, video, figure) {
margin-inline: auto;
}
@media (min-width: 50em) {
.e-content > :global(* + *) {
margin-block-start: 1.5em;
}
}
</style>

View file

@ -0,0 +1,132 @@
---
import type { HTMLAttributes } from 'astro/types';
import { Image } from 'astro:assets';
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';
import type { Article } from '../content/config.js';
interface Props extends HTMLAttributes<'header'> {
article: Article;
}
const { article, ...attrs } = Astro.props;
const { text: readingTime } = await getReadingTime(article.body);
---
<header {...attrs}>
<div class="cover">
{
article.data.cover && (
<Image src={article.data.cover.src} alt={article.data.cover.alt} class="u-photo" />
)
}
<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} class="dt-published" />
<span>•</span>
<p>
<Icon icon="clock" size="1rem" />
<span>{readingTime}</span>
</p>
</div>
<h1 class="p-name">article.data.title}</h1>
{article.data.categories?.length > 0 && <TagList tags={article.data.categories} />}
</header>
<style>
header {
display: flex;
flex-direction: column;
gap: var(--theme-space-sm);
}
.cover {
display: grid;
width: 100%;
min-height: 150px;
background: var(--theme-gradient-main);
border-radius: var(--theme-radius-xl);
margin-block-end: var(--theme-space-md);
}
.cover > img {
grid-column: 1;
grid-row: 1;
width: 100%;
min-height: inherit;
height: 100%;
aspect-ratio: 2.777;
object-fit: cover;
border-radius: inherit;
}
.p-author {
grid-column: 1;
grid-row: 1;
display: flex;
align-items: center;
gap: var(--theme-space-2xs);
width: fit-content;
height: fit-content;
align-self: end;
justify-self: center;
transform: translateY(50%);
box-shadow: var(--theme-shadow-md);
background-color: var(--theme-accent-light);
padding: var(--theme-space-3xs) var(--theme-space-md);
border-radius: var(--theme-radius-xl);
border: 3px solid var(--theme-accent-dark);
}
@media (min-width: 640px) {
.cover {
margin-block-end: var(--theme-space-sm);
}
.p-author {
justify-self: end;
margin-inline-end: var(--theme-space-xl);
}
}
.p-author img {
width: var(--theme-space-lg);
height: var(--theme-space-lg);
}
.p-author .p-name {
font-family: var(--font-brand);
font-weight: bold;
}
.p-author .p-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: var(--theme-space-3xs);
}
</style>

View file

@ -0,0 +1,132 @@
---
import type { HTMLAttributes } from 'astro/types';
import { Image } from 'astro:assets';
import FormattedDate from './FormattedDate.astro';
import Icon from './Icon.astro';
import TagList from './TagList.astro';
import settings from '../settings.js';
import { isArticle, type Post } from '../content/config.js';
export interface Props extends HTMLAttributes<'article'> {
post: Post;
}
const { post, class: className, ...attrs } = Astro.props;
const { pubDate, categories = [] } = post.data;
const cover = 'cover' in post.data && post.data.cover;
const title = 'title' in post.data && post.data.title;
const postUrl = `/post/${post.slug}/`;
---
<article class:list={['h-entry', className]} transition:name={`card-${post.slug}`} {...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} class="dt-published" />
</div>
{categories.length > 0 && <TagList tags={categories} class="tags" />}
</header>
{cover && <Image {...cover} width={1060} class="u-photo" />}
{title && <h3 class="p-name">{title}</h3>}
{
isArticle(post) ? (
<p class="p-summary">{post.data.description}</p>
) : (
post.render().then(({ Content }) => (
<div class="e-content">
<Content />
</div>
))
)
}
<footer>
{
Astro.url.pathname !== postUrl && (
<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>
</a>
</footer>
</article>
<style>
article {
max-width: var(--theme-size-content-2);
padding: var(--theme-space-sm) var(--theme-space-sm) var(--theme-space-md);
background-color: var(--theme-bg-accent);
border-radius: var(--theme-radius-xl);
box-shadow: var(--theme-shadow-md);
}
article > :global(* + *) {
margin-block-start: var(--theme-space-sm);
}
header {
display: grid;
grid-template-columns: auto auto 1fr;
grid-template-areas: 'avatar name' 'avatar nickname' 'tags tags';
align-items: center;
row-gap: var(--theme-space-3xs);
column-gap: var(--theme-space-sm);
}
.h-card .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);
}
.p-name {
grid-area: name;
font-family: var(--font-brand);
font-weight: bold;
}
.p-nickname {
grid-area: nickname;
font-size: var(--theme-text-sm);
color: var(--theme-gray-300);
}
.tags {
grid-area: tags;
margin-block-start: var(--theme-space-2xs);
}
article > .u-photo {
aspect-ratio: 21/9;
object-fit: cover;
border-radius: var(--theme-radius-lg);
}
.e-content > :global(* + *) {
margin-block-start: 1em;
}
.e-content :global(:where(img, picture, video)) {
width: 100%;
}
footer {
display: flex;
align-items: center;
justify-content: space-between;
}
footer > *:only-child {
margin-inline-start: auto;
}
@media (min-width: 50em) {
header {
grid-template-areas: 'avatar name nickname' 'avatar tags tags';
}
.tags {
margin-block-start: 0;
}
}
</style>

View file

@ -0,0 +1,30 @@
---
import settings from '../settings';
import Icon from './Icon.astro';
---
<footer>
<p>
{settings.name} © {new Date().getFullYear()}
</p>
<p>
Designed & Developed with ♥
</p>
</footer>
<style>
footer {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-top: auto;
border-top: 3px solid var(--theme-shade-subtle);
border-radius: var(--theme-radius-xl);
padding: var(--theme-space-md);
row-gap: var(--theme-space-sm);
column-gap: var(--theme-space-lg);
color: var(--theme-gray-200);
font-size: var(--theme-text-sm);
}
</style>

View file

@ -0,0 +1,19 @@
---
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'time'> {
date: Date;
}
const { date } = Astro.props;
---
<time datetime={date.toISOString()}>
{
date.toLocaleDateString('en-us', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}
</time>

View file

@ -0,0 +1,33 @@
---
import type { HTMLAttributes } from 'astro/types';
import settings from '../settings';
import ThemeToggle from './ThemeToggle.astro';
export interface Props extends HTMLAttributes<'header'> {}
---
<header {...Astro.props}>
<a class="site-name" href="/">{settings.username}</a>
<ThemeToggle />
</header>
<style>
header {
display: flex;
justify-content: space-between;
align-items: center;
padding-block: var(--theme-space-md);
}
.site-name {
font-size: var(--theme-text-lg);
font-weight: 700;
font-family: var(--theme-font-brand);
text-decoration: none;
color: var(--theme-accent-dark);
}
.site-name:hover {
text-decoration: 1px solid underline var(--theme-accent-dark);
}
</style>

View file

@ -0,0 +1,32 @@
---
import { iconPaths } from './IconPaths';
export interface Props {
icon: keyof typeof iconPaths;
color?: string;
size?: string;
}
const { icon, color, size } = Astro.props;
const iconPath = iconPaths[icon];
---
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="40"
viewBox="0 0 256 256"
aria-hidden="true"
stroke="currentcolor"
fill="currentcolor"
set:html={iconPath}
/>
<style define:vars={{ color, size }}>
svg {
color: var(--color, inherit);
vertical-align: middle;
width: var(--size, 1em);
height: var(--size, 1em);
}
</style>

View file

@ -0,0 +1,36 @@
/**
* Want to add more icons?
* 1. Find the icon you want as an SVG.
* 2. Scale it to 256×256px.
* 3. Paste the SVG code in your editor.
* 4. Remove the `<svg>` wrapper so you only have elements like `<path>`, `<circle>`, `<rect>` etc.
* 5. Remove any `stroke="{color}"` attributes
* 6. Replace any `fill="{color}"` attributes with `stroke="none"`
* (or add `stroke="none"` on shapes with no `fill` or `stroke` specified).
*/
export const iconPaths = {
'arrow-right': `<path d="m117.5 69.6 7-7c2.9-3 7.6-3 10.6 0l60.7 60.7c3 3 3 7.7 0 10.6L135 194.6c-3 3-7.7 3-10.6 0l-7-6.9c-3-3-2.9-7.8.2-10.7l37.6-35.9H65.5a7.5 7.5 0 0 1-7.5-7.5v-10c0-4.1 3.4-7.5 7.5-7.5h89.8l-37.6-35.8a7.4 7.4 0 0 1-.2-10.7Z"/>`,
'arrow-left': `<path d="m138.5 187-7 7c-2.9 3-7.6 3-10.6 0l-60.7-60.7c-3-3-3-7.7 0-10.6L121 62c3-3 7.7-3 10.6 0l7 7c3 2.9 2.9 7.7-.2 10.6l-37.6 35.9h89.8c4.1 0 7.5 3.3 7.5 7.5v10c0 4.2-3.4 7.5-7.5 7.5h-89.8l37.6 35.9a7.4 7.4 0 0 1 .2 10.7Z"/>`,
'rss-alt': `<path d="M71.8 153.01a28.65 28.65 0 1 0 40.49 40.5 28.65 28.65 0 0 0 0-40.5 29.41 29.41 0 0 0-40.49 0Zm27.02 27.03a9.55 9.55 0 0 1-13.56 0 9.55 9.55 0 0 1 0-13.56 9.55 9.55 0 0 1 13.56 0 9.54 9.54 0 0 1 0 13.56Zm-6.78-73.62a9.55 9.55 0 1 0 0 19.1 47.74 47.74 0 0 1 47.75 47.74 9.55 9.55 0 1 0 19.1 0 66.84 66.84 0 0 0-66.85-66.84Zm0-38.2a9.55 9.55 0 1 0 0 19.1 85.94 85.94 0 0 1 85.94 85.94 9.55 9.55 0 1 0 19.1 0A105.04 105.04 0 0 0 92.04 68.22Z"/>`,
'link-h': `<path d="M96.9 128.25a7.78 7.78 0 0 0 7.77 7.77h46.66a7.78 7.78 0 1 0 0-15.55h-46.66a7.77 7.77 0 0 0-7.78 7.78Zm15.55 23.32H89.12a23.33 23.33 0 0 1 0-46.65h23.33a7.78 7.78 0 1 0 0-15.55H89.12a38.88 38.88 0 0 0 0 77.76h23.33a7.78 7.78 0 1 0 0-15.56Zm54.43-62.2h-23.33a7.78 7.78 0 1 0 0 15.55h23.33a23.33 23.33 0 0 1 0 46.65h-23.33a7.77 7.77 0 1 0 0 15.56h23.33a38.88 38.88 0 1 0 0-77.76Z"/>`,
'location-point': `<path d="M193.8 110.2a66.1 66.1 0 1 0-112.5 53.1l41.3 41.3a7.8 7.8 0 0 0 11 0l41-41.3a65.8 65.8 0 0 0 19.2-53.1Zm-30 42.1L128 188.1l-35.8-35.8a50.5 50.5 0 0 1-7.5-61.9A51 51 0 0 1 128 65.8a50.3 50.3 0 0 1 50.3 45.8 50.5 50.5 0 0 1-14.5 40.7ZM128 82a35 35 0 1 0 0 70 35 35 0 0 0 0-70Zm0 54.4a19.4 19.4 0 1 1 0-38.9 19.4 19.4 0 0 1 0 38.9Z"/>`,
clock: `<path d="M158 118H138V78C138 75.3478 136.946 72.8043 135.071 70.929C133.196 69.0536 130.652 68 128 68C125.348 68 122.804 69.0536 120.929 70.929C119.054 72.8043 118 75.3478 118 78V128C118 130.652 119.054 133.196 120.929 135.071C122.804 136.946 125.348 138 128 138H158C160.653 138 163.195 136.946 165.07 135.071C166.947 133.196 168 130.652 168 128C168 125.348 166.947 122.804 165.07 120.929C163.195 119.054 160.653 118 158 118ZM128 28C108.222 28 88.8878 33.865 72.443 44.853C55.9981 55.8411 43.1808 71.459 35.612 89.7317C28.0434 108.004 26.063 128.111 29.9214 147.509C33.78 166.907 43.3042 184.725 57.2893 198.71C71.2746 212.696 89.093 222.221 108.491 226.078C127.889 229.938 147.996 227.957 166.269 220.387C184.541 212.819 200.158 200.002 211.147 183.557C222.134 167.112 228 147.778 228 128C228 114.868 225.413 101.864 220.387 89.7317C215.363 77.599 207.997 66.5752 198.71 57.2893C189.426 48.0035 178.402 40.6374 166.269 35.612C154.136 30.5866 141.132 28 128 28ZM128 208C112.177 208 96.7104 203.309 83.5544 194.517C70.3984 185.726 60.1446 173.232 54.0896 158.615C48.0347 143.997 46.4504 127.911 49.5371 112.393C52.624 96.8742 60.2432 82.6197 71.4315 71.4315C82.6197 60.2432 96.8742 52.624 112.393 49.5371C127.911 46.4504 143.997 48.0346 158.615 54.0896C173.232 60.1446 185.726 70.3984 194.517 83.5544C203.309 96.7102 208 112.177 208 128C208 149.217 199.571 169.566 184.568 184.568C169.566 199.571 149.217 208 128 208Z" fill="currentColor"/>`,
share: `<path d="M192 149.333C185.698 149.375 179.483 150.812 173.803 153.541C168.123 156.271 163.117 160.225 159.147 165.12L104.747 140.053C107.305 132.221 107.305 123.778 104.747 115.947L159.147 90.8799C165.564 98.6235 174.51 103.851 184.406 105.641C194.304 107.43 204.514 105.666 213.236 100.66C221.96 95.654 228.634 87.7276 232.08 78.2799C235.528 68.832 235.526 58.47 232.078 49.0229C228.629 39.5756 221.953 31.6505 213.23 26.6459C204.507 21.6415 194.296 19.8793 184.399 21.6706C174.504 23.4618 165.557 28.6913 159.141 36.4361C152.725 44.1809 149.252 53.9431 149.333 63.9999C149.365 66.541 149.615 69.0747 150.08 71.5732L93.76 97.4932C87.7556 91.622 80.1519 87.6534 71.9019 86.0844C63.6519 84.5155 55.1224 85.4161 47.3819 88.6732C39.6414 91.9305 33.0343 97.3995 28.3883 104.395C23.7423 111.391 21.264 119.602 21.264 128C21.264 136.398 23.7423 144.609 28.3883 151.604C33.0343 158.6 39.6414 164.069 47.3819 167.327C55.1224 170.583 63.6519 171.485 71.9019 169.916C80.1519 168.347 87.7556 164.378 93.76 158.507L150.08 184.427C149.615 186.925 149.365 189.459 149.333 192C149.333 200.438 151.836 208.688 156.524 215.704C161.212 222.721 167.876 228.19 175.673 231.419C183.469 234.648 192.047 235.493 200.324 233.846C208.601 232.2 216.203 228.136 222.17 222.17C228.137 216.203 232.201 208.6 233.846 200.324C235.493 192.047 234.649 183.469 231.419 175.672C228.19 167.876 222.721 161.212 215.705 156.524C208.688 151.836 200.438 149.333 192 149.333ZM192 42.6665C196.22 42.6665 200.344 43.9177 203.852 46.2618C207.36 48.606 210.095 51.9378 211.71 55.836C213.324 59.7341 213.746 64.0235 212.924 68.1618C212.1 72.3 210.068 76.1013 207.085 79.0849C204.101 82.0683 200.3 84.1001 196.162 84.9233C192.024 85.7464 187.734 85.324 183.836 83.7093C179.938 82.0947 176.606 79.3603 174.262 75.852C171.918 72.3437 170.667 68.2193 170.667 63.9999C170.667 58.3419 172.914 52.9157 176.915 48.9149C180.916 44.9142 186.342 42.6665 192 42.6665ZM64 149.333C59.7806 149.333 55.6561 148.082 52.1479 145.738C48.6396 143.394 45.9052 140.062 44.2906 136.164C42.6759 132.266 42.2535 127.976 43.0766 123.838C43.8998 119.699 45.9316 115.899 48.915 112.915C51.8986 109.932 55.6998 107.9 59.8381 107.076C63.9764 106.253 68.2657 106.676 72.1639 108.29C76.0621 109.905 79.3939 112.64 81.7381 116.148C84.0822 119.656 85.3334 123.78 85.3334 128C85.3334 133.658 83.0857 139.084 79.0849 143.085C75.0842 147.086 69.658 149.333 64 149.333ZM192 213.333C187.78 213.333 183.657 212.082 180.148 209.738C176.64 207.394 173.905 204.062 172.29 200.164C170.676 196.266 170.254 191.977 171.076 187.838C171.9 183.699 173.932 179.899 176.915 176.915C179.899 173.932 183.7 171.9 187.838 171.076C191.977 170.254 196.266 170.676 200.164 172.29C204.062 173.905 207.394 176.64 209.738 180.148C212.082 183.655 213.333 187.78 213.333 192C213.333 197.658 211.086 203.084 207.085 207.085C203.084 211.086 197.658 213.333 192 213.333Z" fill="currentColor"/>`,
user: `<path d="M156.9 133.5a46.7 46.7 0 1 0-57.7 0 77.8 77.8 0 0 0-48.4 63.6 7.8 7.8 0 0 0 15.5 1.7 62.2 62.2 0 0 1 123.7 0 7.8 7.8 0 0 0 7.8 7h.8a7.8 7.8 0 0 0 6.9-8.6 77.8 77.8 0 0 0-48.6-63.7ZM128 128a31.1 31.1 0 1 1 0-62.2 31.1 31.1 0 0 1 0 62.2Z"/>`,
heart: `<path d="M187.84 74.61A46.17 46.17 0 0 0 128 70.16a46.17 46.17 0 0 0-62.29 6.86 45.56 45.56 0 0 0 2.45 62.27l54.63 54.32a7.32 7.32 0 0 0 8.03 1.6c.9-.37 1.7-.92 2.39-1.6l54.63-54.32a45.68 45.68 0 0 0 13.48-32.34 45.48 45.48 0 0 0-13.48-32.34Zm-10.34 54.4L128 178.15l-49.5-49.14a31.15 31.15 0 0 1-6.8-33.89 31.24 31.24 0 0 1 11.47-14 31.51 31.51 0 0 1 17.33-5.34c8.26.02 16.17 3.3 22 9.11a7.34 7.34 0 0 0 10.41 0 31.42 31.42 0 0 1 42.74 1.6 31.05 31.05 0 0 1 1.26 42.52h.59Z"/>`,
'moon-stars': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="20" d="M216 112V64m24 24h-48m-24-64v32m16-16h-32m65 113A92 92 0 0 1 103 39h0a92 92 0 1 0 114 114Z"/>`,
sun: `<circle cx="128" cy="128" r="60" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="20"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="20" d="M128 36V16M63 63 49 49m-13 79H16m47 65-14 14m79 13v20m65-47 14 14m13-79h20m-47-65 14-14"/>`,
twitter: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 88c0-22 18.5-40.3 40.5-40a40 40 0 0 1 36.2 24H240l-32.3 32.3A127.9 127.9 0 0 1 80 224c-32 0-40-12-40-12s32-12 48-36c0 0-64-32-48-120 0 0 40 40 88 48Z"/>`,
codepen: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m232 101-104 59-104-59 100.1-56.8a8.3 8.3 0 0 1 7.8 0Z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m232 165-100.1 56.8a8.3 8.3 0 0 1-7.8 0L24 165l104-59Zm0-64v64M24 101v64m104-5v62.8m0-179.6V106"/>`,
github: `<g stroke-linecap="round" stroke-linejoin="round"><path fill="none" stroke-width="14.7" d="M55.7 167.2c13.9 1 21.3 13.1 22.2 14.6 4.2 7.2 10.4 9.6 18.3 7.1l1.1-3.4a60.3 60.3 0 0 1-25.8-11.9c-12-10.1-18-25.6-18-46.3"/><path fill="none" stroke-width="16" d="M61.4 205.1a24.5 24.5 0 0 1-3-6.1c-3.2-7.9-7.1-10.6-7.8-11.1l-1-.6c-2.4-1.6-9.5-6.5-7.2-13.9 1.4-4.5 6-7.2 12.3-7.2h.8c4 .3 7.6 1.5 10.7 3.2-9.1-10.1-13.6-24.3-13.6-42.3 0-11.3 3.5-21.7 10.1-30.4A46.7 46.7 0 0 1 65 67.3a8.3 8.3 0 0 1 5-4.7c2.8-.9 13.3-2.7 33.2 9.9a105 105 0 0 1 50.5 0c19.9-12.6 30.4-10.8 33.2-9.9 2.3.7 4.1 2.4 5 4.7 5 12.7 4 23.2 2.6 29.4 6.7 8.7 10 18.9 10 30.4 0 42.6-25.8 54.7-43.6 58.7 1.4 4.1 2.2 8.8 2.2 13.7l-.1 23.4v2.3"/><path fill="none" stroke-width="16" d="M160.9 185.7c1.4 4.1 2.2 8.8 2.2 13.7l-.1 23.4v2.3A98.6 98.6 0 1 0 61.4 205c-1.4-2.1-11.3-17.5-11.8-17.8-2.4-1.6-9.5-6.5-7.2-13.9 1.4-4.5 6-7.2 12.3-7.2h.8c4 .3 7.6 1.5 10.7 3.2-9.1-10.1-13.6-24.3-13.6-42.3 0-11.3 3.5-21.7 10.1-30.4A46.4 46.4 0 0 1 65 67.3a8.3 8.3 0 0 1 5-4.7c2.8-.9 13.3-2.7 33.2 9.9a105 105 0 0 1 50.5 0c19.9-12.6 30.4-10.8 33.2-9.9 2.3.7 4.1 2.4 5 4.7 5 12.7 4 23.2 2.6 29.4 6.7 8.7 10 18.9 10 30.4.1 42.6-25.8 54.7-43.6 58.6z"/><path fill="none" stroke-width="18.7" d="m170.1 203.3 17.3-12 17.2-18.7 9.5-26.6v-27.9l-9.5-27.5" /><path fill="none" stroke-width="22.7" d="m92.1 57.3 23.3-4.6 18.7-1.4 29.3 5.4m-110 32.6-8 16-4 21.4.6 20.3 3.4 13" /><path fill="none" stroke-width="13.3" d="M28.8 133a100 100 0 0 0 66.9 94.4v-8.7c-22.4 1.8-33-11.5-35.6-19.8-3.4-8.6-7.8-11.4-8.5-11.8"/></g>`,
twitch: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M165 200h-42a8 8 0 0 0-5 2l-46 38v-40H48a8 8 0 0 1-8-8V48a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8v108a8 8 0 0 1-3 6l-43 36a8 8 0 0 1-5 2Zm3-112v48m-48-48v48"/>`,
youtube: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m160 128-48-32v64l48-32z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M24 128c0 30 3 47 5 56a16 16 0 0 0 10 11c34 13 89 13 89 13s56 0 89-13a16 16 0 0 0 10-11c2-9 5-26 5-56s-3-47-5-56a16 16 0 0 0-10-11c-33-13-89-13-89-13s-55 0-89 13a16 16 0 0 0-10 11c-2 9-5 26-5 56Z"/>`,
dribbble: `<circle cx="128" cy="128" r="96" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M71 205a160 160 0 0 1 137-77l16 1m-36-76a160 160 0 0 1-124 59 165 165 0 0 1-30-3"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M86 42a161 161 0 0 1 74 177"/>`,
discord: `<circle stroke="none" cx="96" cy="144" r="12"/><circle stroke="none" cx="160" cy="144" r="12"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M74 80a175 175 0 0 1 54-8 175 175 0 0 1 54 8m0 96a175 175 0 0 1-54 8 175 175 0 0 1-54-8"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m155 182 12 24a8 8 0 0 0 9 4c25-6 46-16 61-30a8 8 0 0 0 3-8L206 59a8 8 0 0 0-5-5 176 176 0 0 0-30-9 8 8 0 0 0-9 5l-8 24m-53 108-12 24a8 8 0 0 1-9 4c-25-6-46-16-61-30a8 8 0 0 1-3-8L50 59a8 8 0 0 1 5-5 176 176 0 0 1 30-9 8 8 0 0 1 9 5l8 24"/>`,
linkedin: `<rect width="184" height="184" x="36" y="36" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" rx="8"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M120 112v64m-32-64v64m32-36a28 28 0 0 1 56 0v36"/><circle stroke="none" cx="88" cy="80" r="12"/>`,
instagram: `<circle cx="128" cy="128" r="40" fill="none" stroke-miterlimit="10" stroke-width="16"/><rect width="184" height="184" x="36" y="36" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" rx="48"/><circle cx="180" cy="76" r="12" stroke="none" />`,
tiktok: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M168 106a96 96 0 0 0 56 18V84a56 56 0 0 1-56-56h-40v128a28 28 0 1 1-40-25V89a68 68 0 1 0 80 67Z"/>`,
devto: `<path fill-rule="evenodd" d="M37 33c-2.2.5-4 2-4.7 4.2l-.3.8v181.8l.3.8c.7 2 2 3.3 4 4l.8.3H219l.7-.3c2-.7 3.3-2 4-4l.3-.8V38l-.3-.8c-.7-2-2-3.3-4-4l-.8-.3H37Zm43.6 58.3A20.7 20.7 0 0 1 98 109.6c.3 1.9.3 36.7 0 38.6a20.5 20.5 0 0 1-16 17.9c-2.6.5-1.8.5-14.9.6h-12V91h12.2c11 0 12.4 0 13.2.2Zm63.6 6.5v6.8H120v17.6h14.8v13.4H120v17.5h24.2v13.6h-30l-.8-.3a9.3 9.3 0 0 1-6.7-6.7l-.2-.9v-60l.2-.8c1-3.4 3.6-6 7-6.7.8-.2 1.8-.2 15.7-.2h14.8v6.7Zm27 8.6a268210 268210 0 0 1 9.5 36.4 5274.6 5274.6 0 0 0 13.5-51.6H209l-8.8 33a527.2 527.2 0 0 1-9.3 34.2 15 15 0 0 1-6.5 7.5c-1.3.6-2 .7-3.7.7-1.2 0-1.6 0-2.2-.2-3.2-.8-5.8-3.3-8-7.7l-.7-1.5-8.7-32.9-8.7-33c0-.2.3-.2 7.4-.2h7.4l4 15.3ZM68.7 128.8v24.3h9.5l.8-.3a9 9 0 0 0 4.7-3.4 7 7 0 0 0 1-2.4v-35.7l-.1-.8c-.6-2.3-1.9-3.8-4.3-5-1.8-.9-1.9-.9-7-1h-4.6v24.3Z" clip-rule="evenodd"/>`,
mastodon: `<path d="M219.3 74.8a49.7 49.7 0 0 0-43-40.4c-3.7-.6-17.6-2.5-49.8-2.5h-.3c-32.2 0-39.1 2-42.8 2.5-21.1 3-40.5 17.8-45.2 39a132.2 132.2 0 0 0-2 32.4c.6 15.1.7 30.3 2 45.3 1 10 2.7 20 5 29.8 4.5 18 22.6 33.1 40.3 39.2a108.8 108.8 0 0 0 65.1 1.4c4.7-1.5 10.3-3.2 14.4-6a.5.5 0 0 0 .2-.4v-14.7a.4.4 0 0 0-.4-.4h-.2a158 158 0 0 1-38.1 4.4c-22.1 0-28-10.3-29.8-14.7a45.1 45.1 0 0 1-2.4-12 .4.4 0 0 1 .4 0c12.3 3 24.9 4.4 37.5 4.4h9.1c12.7-.4 26.1-1 38.6-3.5.3 0 .6 0 .9-.2 19.7-3.7 38.5-15.5 40.4-45.3l.3-13.5c0-4 1.3-29.3-.2-44.8Zm-30.4 74.3h-20.7V99c0-10.6-4.5-16-13.6-16-10 0-15 6.4-15 19v27.5h-20.5V102c0-12.6-5-19-15-19-9 0-13.5 5.4-13.5 16v50H69.9V97.4A37 37 0 0 1 78 72c5.6-6.2 13-9.4 22.1-9.4 10.7 0 18.7 4 24 12.1l5.2 8.6 5.2-8.6c5.3-8 13.3-12 24-12 9 0 16.5 3.1 22.1 9.3a36.9 36.9 0 0 1 8.2 25.3V149Z"/>`,
};

View file

@ -0,0 +1,128 @@
---
import type { Page as Props } from 'astro';
import Icon from './Icon.astro';
const { currentPage, lastPage, url } = Astro.props;
const firstPage = 1;
interface Item {
page: number;
url: string;
current: boolean;
}
const makeItem = (page: number): Item => ({
page,
url: page === 1 ? '/' : `/${page}`,
current: page === currentPage,
});
const items: (Item | null)[] = [];
/** The number of pages to show before/after the current page. */
const beforeAfter = 1;
// Get range of pages around current page.
const min = Math.max(
Math.min(currentPage - beforeAfter, lastPage - 2 * beforeAfter - 1),
firstPage
);
const max = Math.min(Math.max(min + 2 * beforeAfter, firstPage + 2 * beforeAfter + 1), lastPage);
// Always include first page.
if (min > firstPage) items.push(makeItem(firstPage));
// Show “…” if the range starts at page 4 or higher.
if (min > firstPage + 2) items.push(null);
// Show page 2 if the range starts at page 3.
if (min === firstPage + 2) items.push(makeItem(firstPage + 1));
// Show an item for pages in range around current page.
for (let i = min; i <= max; i++) items.push(makeItem(i));
// Show “…” if the range ends more than 2 pages before the last page.
if (max < lastPage - 2) items.push(null);
// Show the penultimate page if the range ends 2 pages before the last page.
if (max === lastPage - 2) items.push(makeItem(lastPage - 1));
// Always include last page.
if (max < lastPage) items.push(makeItem(lastPage));
---
{
lastPage > 1 && (
<nav>
<ul>
<li class="pagination-arrow">
{url.prev === undefined ? (
<span>
<Icon icon="arrow-left" />
<span class="sr-only">Previous</span>
</span>
) : (
<a href={url.prev} rel="prev">
<Icon icon="arrow-left" />
<span class="sr-only">Previous</span>
</a>
)}
</li>
{items.map((item) => (
<li class="pagination-item">
{item ? (
<a href={item.url} aria-current={item.current}>
{item.page}
</a>
) : (
<span>…</span>
)}
</li>
))}
<li class="pagination-arrow">
{url.next === undefined ? (
<span>
<span class="sr-only">Next</span>
<Icon icon="arrow-right" />
</span>
) : (
<a href={url.next} rel="next">
<span class="sr-only">Next</span>
<Icon icon="arrow-right" />
</a>
)}
</li>
</ul>
</nav>
)
}
<style>
ul {
display: flex;
justify-content: center;
list-style: none;
gap: var(--theme-space-2xs);
padding: 0;
}
a {
text-decoration: none;
color: var(--theme-text);
}
.pagination-item > * {
border: 1.5px solid var(--theme-accent-dark);
border-radius: var(--theme-radius-base);
padding: 0.375rem 0.75rem;
background-color: var(--theme-bg-accent);
font-size: var(--theme-text-sm);
font-weight: 700;
box-shadow: var(--theme-shadow-sm);
}
.pagination-item > [aria-current='true'] {
background-color: var(--theme-accent-dark);
color: var(--theme-text-invert);
}
.pagination-arrow > * {
color: var(--theme-accent-dark);
opacity: 0.35;
font-size: var(--theme-text-xl);
display: flex;
}
.pagination-arrow > a {
opacity: 1;
}
</style>

View file

@ -0,0 +1,32 @@
---
import type { HTMLAttributes } from 'astro/types';
export interface Props extends HTMLAttributes<'div'> {
reverse?: boolean;
}
const { reverse = false, class: className, ...attrs } = Astro.props;
---
<div class:list={[className, { reverse }]} {...attrs}>
<slot />
</div>
<style>
div {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--theme-space-lg);
margin: 0 auto;
width: 100%;
max-width: var(--theme-size-content-3);
}
@media (min-width: 50em) {
div {
flex-direction: row;
align-items: initial;
}
}
</style>

View file

@ -0,0 +1,41 @@
---
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'ul'> {
tags: string[];
}
const { tags, ...attrs } = Astro.props;
---
<ul {...attrs}>
{
tags.map((tag) => (
<li>
<a href={`/category/${tag}/`} rel="category tag" class="p-category">
{tag}
</a>
</li>
))
}
</ul>
<style>
ul {
list-style: none;
padding: 0;
display: flex;
gap: var(--theme-space-3xs);
}
.p-category {
padding: var(--theme-space-3xs) var(--theme-space-2xs);
border-radius: var(--theme-radius-full);
background-color: var(--theme-accent-dark);
color: var(--theme-text-invert);
font-size: var(--theme-text-sm);
font-weight: 500;
}
.p-category::before {
content: '#';
}
</style>

View file

@ -0,0 +1,107 @@
---
import Icon from './Icon.astro';
---
<theme-toggle>
<button>
<span class="sr-only">Dark theme</span>
<span class="icon light"><Icon icon="sun" /></span>
<span class="icon dark"><Icon icon="moon-stars" /></span>
</button>
</theme-toggle>
<style>
button {
position: relative;
display: flex;
gap: 0.25rem;
border: 0;
border-radius: var(--theme-radius-full);
padding: 0.1875rem;
background-color: var(--theme-accent-light);
box-shadow: var(--theme-shadow-lg);
cursor: pointer;
}
button::before {
position: absolute;
inset: -0.125rem;
content: '';
border-radius: var(--theme-radius-full);
background: var(--theme-gradient-main);
z-index: -1;
}
.icon {
z-index: 1;
position: relative;
display: flex;
padding: 0.3125rem;
width: 1.625rem;
height: 1.625rem;
font-size: 1rem;
color: var(--theme-accent-dark);
}
.icon.light::before {
content: '';
z-index: -1;
position: absolute;
inset: 0;
background-color: var(--theme-accent-dark);
border-radius: 999rem;
}
:global(.theme-dark) .icon.light::before {
transform: translateX(calc(100% + 0.25rem));
}
:global(.theme-dark) .icon.dark,
:global(html:not(.theme-dark)) .icon.light,
button[aria-pressed='false'] .icon.light {
color: var(--theme-text-invert);
}
@media (prefers-reduced-motion: no-preference) {
.icon,
.icon.light::before {
transition: transform var(--theme-transition),
color var(--theme-transition);
}
}
@media (forced-colors: active) {
.icon.light::before {
background-color: SelectedItem;
}
}
</style>
<script>
class ThemeToggle extends HTMLElement {
constructor() {
super();
const button = this.querySelector('button')!;
/** Set the theme to dark/light mode. */
const setTheme = (dark: boolean) => {
document.documentElement.classList[dark ? 'add' : 'remove'](
'theme-dark'
);
button.setAttribute('aria-pressed', String(dark));
};
// Toggle the theme when a user clicks the button.
button.addEventListener('click', () => setTheme(!this.isDark()));
// Initialize button state to reflect current theme.
setTheme(this.isDark());
}
isDark() {
return document.documentElement.classList.contains('theme-dark');
}
}
customElements.define('theme-toggle', ThemeToggle);
</script>

View file

@ -0,0 +1,164 @@
---
import { Image } from 'astro:assets';
import settings from '../settings';
import Icon from './Icon.astro';
const socialLinks = Object.entries(settings.social);
---
<div class="h-card">
<div>
<div class="u-photo">
<Image {...settings.avatar} width={220} />
</div>
<h1>
<span class="p-name">{settings.name}</span>
<small class="p-nickname">{settings.username}</small>
</h1>
</div>
<div class="bio-sections">
<div class="bio">
<p>🚀 <a href="https://astro.build/">Astro</a> Mascot</p>
<p>😊 The cutest</p>
<p>🎨 Whimsical Speedy Web</p>
</div>
<div class="bio">
{
settings.pronouns && (
<p>
<Icon icon="user" color="var(--theme-accent-dark)" size="1.75rem" />
<span class="sr-only">Pronouns</span>
{settings.pronouns}
</p>
)
}
{
settings.location && (
<p>
<Icon icon="location-point" color="var(--theme-accent-dark)" size="1.75rem" />
<span class="sr-only">Location</span>
<span class="p-locality">{settings.location}</span>
</p>
)
}
{
settings.homepage && (
<p>
<Icon icon="link-h" color="var(--theme-accent-dark)" size="1.75rem" />
<span class="sr-only">Homepage</span>
<a href={settings.homepage} class="u-url">{settings.homepage.replace(/^https?:\/\/(www\.)?/, '')}</a>
</p>
)
}
<p>
<Icon icon="rss-alt" color="var(--theme-accent-dark)" size="1.75rem" />
<a href="/rss.xml">RSS Feed</a>
</p>
</div>
<ul class="social">
{
socialLinks.map(([key, { url, title }]) => (
<li>
<a href={url} rel="me">
<span class="sr-only">{title}</span>
<Icon icon={`${key}`} size="1.75rem" />
</a>
</li>
))
}
</ul>
</div>
</div>
<style>
.h-card {
display: flex;
flex-direction: column;
gap: var(--theme-space-md);
}
.u-photo {
display: inline-block;
position: relative;
}
.u-photo::after {
border-radius: var(--theme-radius-full);
position: absolute;
content: '';
inset: 0;
border: 3px solid var(--theme-text);
}
.u-photo img {
width: 110px;
height: 110px;
}
h1 {
display: flex;
flex-direction: column;
font-size: var(--theme-text-xl);
}
small {
font-size: var(--theme-text-base);
font-family: var(--theme-font-body);
color: var(--theme-gray-200);
}
.bio-sections {
display: grid;
grid-template-columns: 1fr;
align-items: flex-start;
gap: var(--theme-space-sm) var(--theme-space-md);
}
.bio-sections > :nth-child(2) {
grid-row: span 2;
}
.bio {
font-size: var(--theme-text-sm);
font-weight: 500;
}
.bio > * + * {
margin-top: var(--theme-space-xs);
}
.bio :global(svg) {
/* Slightly hacky way to avoid the icon height being included in the box calculation. */
margin: -50% 0;
}
.social {
display: flex;
flex-wrap: wrap;
gap: var(--theme-space-3xs);
list-style: none;
padding: 0;
grid-row: 2;
}
.social a {
color: var(--theme-accent-dark);
}
@media (min-width: 35em) {
.bio-sections {
grid-template-columns: 1fr 1fr;
}
.social {
grid-row: unset;
}
}
@media (min-width: 50em) {
.bio-sections {
display: flex;
flex-direction: column;
}
}
</style>

View file

@ -0,0 +1,29 @@
---
title: First post on my new site!
description: Dignissim eu sagittis aliquet magna sagittis. Eu etiam faucibus quis non. Laoreet amet aliquam enim sapien. Cras ac enim nulla morbi ultrices elementum metus neque nulla. Quis mi consectetur donec tempor habitant.
pubDate: 2023-01-01
categories: [keyboards, thoughts]
cover:
src: ../../assets/stock-1.jpg
alt: A laptop with a code editor open
---
Dignissim eu sagittis aliquet magna sagittis. Eu etiam faucibus quis non. Laoreet amet aliquam enim sapien. Cras ac enim nulla morbi ultrices elementum metus neque nulla. Quis mi consectetur donec tempor habitant.
Id at orci nulla nunc. Habitant a amet turpis facilisi purus cursus dui imperdiet. Integer fermentum amet nunc tristique scelerisque feugiat pellentesque phasellus. Feugiat euismod id varius id mattis ac ut. Donec aliquet fusce ut egestas vehicula sagittis.
Mauris vulputate tristique porttitor sed integer felis. In eget sodales lobortis laoreet molestie aliquet est aliquet tortor. Sodales erat rhoncus tellus mattis etiam nunc ornare. Nisi ut tellus elementum gravida dictum diam porta. Nulla porttitor magna duis pretium egestas nisl ornare.
## Heading One
Suscipit id porttitor laoreet purus semper fermentum libero cras magna. Non hendrerit lectus tincidunt condimentum. Sollicitudin pretium at vel nibh tempus proin faucibus donec sed. Habitant et risus auctor platea viverra tellus. Faucibus dapibus neque enim metus porttitor pulvinar pharetra amet sem. Nibh a facilisis pellentesque ut pellentesque. Amet semper ultrices est lacus facilisis pulvinar viverra. Quis velit cursus viverra varius vestibulum. Ultricies interdum id dapibus nunc bibendum vitae varius in. Lorem vel congue in amet et pellentesque sed facilisis. Id amet sed suspendisse tincidunt lacinia sit. Ut libero id ornare cursus porttitor elementum.
## Heading Number Two
Sagittis fames arcu tempor morbi sed mauris eu blandit cras. Risus amet nec auctor nunc pretium commodo. Dictum duis nascetur est molestie ullamcorper sit tempor. Vestibulum dictumst magna cursus aenean vitae ornare amet non.
Dui tortor viverra eu montes. Elit pretium pharetra aliquam pellentesque congue id morbi maecenas. Donec a egestas ipsum sit a ipsum. Quis vestibulum feugiat tincidunt justo tellus turpis. Luctus dignissim porta dictumst ut auctor neque in.
![Close-up of a mechanical keyboard with LED backlighting](../../assets/mechanical-keyboard.png)
Pellentesque faucibus faucibus magna tempus proin amet in viverra. Eros non ipsum justo pellentesque vestibulum morbi proin euismod. Sagittis in amet at aliquet facilisis.

View file

@ -0,0 +1,9 @@
---
title: Second time lucky
description: Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
pubDate: 2023-01-02
---
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.

View file

@ -0,0 +1,10 @@
---
title: Three is a magic number
description: Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
pubDate: 2023-01-03
cover:
src: ../../assets/stock-2.jpg
alt: A backlit multicolored mechanical keyboard
---
Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

View file

@ -0,0 +1,48 @@
import { rssSchema } from '@astrojs/rss';
import { defineCollection, z, type CollectionEntry } from 'astro:content';
const articles = defineCollection({
schema: ({ image }) => rssSchema
.extend({
cover: z
.object({
src: image().refine(
(img) => img.width >= 885,
'Cover image must be at least 885px wide.'
),
alt: z.string(),
})
.optional(),
})
.required({
// requiring the description for articles, this will be shown as the short preview text on cards
title: true,
description: true
})
.strict(),
})
const notes = defineCollection({
schema: rssSchema
.omit({
// notes are short, self-contained content without unique titles or descriptions
description: true,
title: true
})
.strict()
})
export const collections = { articles, notes };
export type Article = CollectionEntry<'articles'>;
export type Note = CollectionEntry<'notes'>;
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
export type Post = Article | Note;
export function isArticle(post: Post): post is Article {
return post.collection === 'articles'
}
export function isNote(post: Post): post is Note {
return post.collection === 'notes'
}

View file

@ -0,0 +1,7 @@
---
pubDate: 2023-01-10
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis commodo odio aenean sed. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aenean vel elit scelerisque mauris. Sapien eget mi proin sed libero enim sed.

View file

@ -0,0 +1,5 @@
---
pubDate: 2023-01-04
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Adipiscing elit ut aliquam purus sit amet luctus. Rhoncus aenean vel elit scelerisque mauris. Nulla pellentesque dignissim enim sit amet venenatis urna cursus.

View file

@ -0,0 +1,7 @@
---
pubDate: 2023-01-05
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pellentesque habitant morbi tristique senectus et netus et.
![Mechanical keyboard with LED backlighting](../../assets/mechanical-keyboard.png)

View file

@ -0,0 +1,5 @@
---
pubDate: 2023-01-06
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Donec adipiscing tristique risus nec feugiat in fermentum posuere. Mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus et netus. Cursus sit amet dictum sit amet justo donec.

View file

@ -0,0 +1,7 @@
---
pubDate: 2023-01-07
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat maecenas volutpat blandit aliquam etiam erat velit.
Read more in [the docs](https://docs.astro.build)

View file

@ -0,0 +1,7 @@
---
pubDate: 2023-01-08
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Adipiscing bibendum est ultricies integer quis auctor.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Semper viverra nam libero justo laoreet sit amet cursus sit.

View file

@ -0,0 +1,5 @@
---
pubDate: 2023-01-09
---
ultrices neque ornare aenean euismod elementum

View file

@ -0,0 +1,24 @@
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') {
const posts = await Promise.all([
getCollection('articles'),
getCollection('notes'),
])
.then((collections) => collections
.flat()
.sort(sortPosts(order)
));
return posts;
}

View file

@ -0,0 +1,89 @@
---
import settings from '../settings';
import '../style/theme.css';
import '../style/global.css';
import '../style/utilities.css';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
export interface Props {
title?: string;
}
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const { title = settings.name } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} />
<!-- RSS Feed Discovery -->
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500;700&display=swap"
rel="stylesheet"
/>
<script is:inline>
// This code is inlined in the head to make dark mode instant & blocking.
const getThemePreference = () => {
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
return localStorage.getItem('theme');
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
};
const isDark = getThemePreference() === 'dark';
document.documentElement.classList[isDark ? 'add' : 'remove']('theme-dark');
if (typeof localStorage !== 'undefined') {
// Watch the document element and persist user preference when it changes.
const observer = new MutationObserver(() => {
const isDark = document.documentElement.classList.contains('theme-dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
});
}
</script>
</head>
<body>
<Header class="header" />
<main>
<slot />
</main>
<Footer />
<style>
html {
scrollbar-gutter: stable;
}
body {
display: flex;
flex-direction: column;
gap: var(--theme-space-lg);
}
.header,
main {
padding-inline: var(--theme-space-sm-lg);
}
main {
width: 100%;
max-width: 55rem;
margin-inline: auto;
}
</style>
</body>
</html>

View file

@ -0,0 +1,16 @@
---
import Layout, { type Props as LayoutProps } from './Base.astro';
import UserProfile from '../components/UserProfile.astro';
import SplitLayout from '../components/SplitLayout.astro';
export interface Props extends LayoutProps {}
---
<Layout {...Astro.props}>
<SplitLayout>
<UserProfile />
<div>
<slot />
</div>
</SplitLayout>
</Layout>

View file

@ -0,0 +1,48 @@
---
import type { GetStaticPathsOptions, Page } from 'astro';
import Card from '../components/Card.astro';
import Pagination from '../components/Pagination.astro';
import { getSortedPosts } from '../helpers/getSortedPosts';
import Layout from '../layouts/Feed.astro';
import type { Post } from '../content/config.js';
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
return paginate(await getSortedPosts(), { pageSize: 3 });
}
interface Props {
page: Page<Post>;
}
const { page } = Astro.props;
---
<Layout>
<div class="stack">
<ol>
{
page.data.map((post) => (
<li>
<Card {post} />
</li>
))
}
</ol>
<Pagination {...page} />
</div>
</Layout>
<style>
.stack {
display: flex;
flex-direction: column;
gap: 2.5rem;
}
ol {
list-style: none;
padding: 0;
display: flex;
flex-direction: column;
gap: 1.25rem;
}
</style>

View file

@ -0,0 +1,102 @@
---
import Article from '../../components/Article.astro';
import Card from '../../components/Card.astro';
import Icon from '../../components/Icon.astro';
import { getSortedPosts } from '../../helpers/getSortedPosts';
import Layout from '../../layouts/Base.astro';
import { isArticle, type Post } from '../../content/config';
export async function getStaticPaths() {
const posts = await getSortedPosts();
return posts.map((post, idx) => ({
params: { slug: post.slug },
props: { post, prev: posts[idx - 1], next: posts[idx + 1] },
}));
}
interface Props {
post: Post;
prev?: Post;
next?: Post;
}
const { post, prev, next } = Astro.props;
const { title } = isArticle(post) && post.data;
---
<Layout {title}>
<a href="/" class="back">
<Icon icon="arrow-left" color="var(--theme-accent-dark)" size="var(--theme-space-md)" />
<span>Back to feed</span>
</a>
{isArticle(post) ? <Article article={post} /> : <Card {post} class="card" />}
{
(next || prev) && (
<footer>
{prev && (
<a href={`/post/${prev.slug}`} class="prev">
<Icon icon="arrow-left" size="var(--theme-space-md)" color="var(--theme-accent-dark)" />
<span>Previous post</span>
</a>
)}
{next && (
<a href={`/post/${next.slug}`} class="next">
<span>Next post</span>
<Icon
icon="arrow-right"
size="var(--theme-space-md)"
color="var(--theme-accent-dark)"
/>
</a>
)}
</footer>
)
}
</Layout>
<style>
.back {
text-decoration: none;
font-weight: bold;
}
.back {
display: flex;
align-items: center;
gap: var(--theme-space-2xs);
margin-inline-start: -4px;
}
.card {
width: 100%;
margin: var(--theme-space-md) auto;
}
.center {
margin-inline: auto;
}
.prev,
.next {
display: flex;
align-items: center;
gap: 0.5rem;
}
footer {
display: flex;
justify-content: space-between;
margin-inline: -4px;
}
footer a {
font-weight: 700;
text-decoration: none;
}
.next:first-child {
margin-inline-start: auto;
}
</style>

View file

@ -0,0 +1,27 @@
import rss from '@astrojs/rss';
import type { APIContext } from 'astro';
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 getCollection('articles');
return rss({
// `<title>` field in output xml
title,
// `<description>` field in output xml
description,
// Pull in your project "site" from the endpoint context
// https://docs.astro.build/en/reference/api-reference/#contextsite
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
.sort(sortPosts())
.map(({ data, slug }) => ({ ...data, link: `/post/${slug}` })),
stylesheet: '/rss/styles.xsl',
});
}

View file

@ -0,0 +1,66 @@
import { iconPaths } from './components/IconPaths.js'
import avatar from './assets/avatar.webp'
export interface Image {
src: ImageMetadata
alt: string
}
export interface Settings {
name: string
username: string
avatar: Image
rss: {
title: string,
description: string
},
pronouns?: string | undefined
location?: string | undefined
homepage?: string | undefined
social: Partial<{
[icon in keyof typeof iconPaths]: {
url: string,
title: string,
}
}>
}
const settings: Settings = {
name: 'Houston Astro',
username: '@houston',
avatar: {
src: avatar,
alt: 'Astro mascot Houston smiling',
},
rss: {
title: 'Houston Astros Feed',
description: 'Stay up-to-date with the latest posts from Houston Astro!',
},
pronouns: 'They/Them',
location: 'Space',
homepage: 'https://astro.build',
social: {
twitter: {
url: 'https://twitter.com/astrodotbuild',
title: 'Twitter'
},
github: {
url: 'https://github.com/withastro',
title: 'GitHub',
},
mastodon: {
url: 'https://m.webtoo.ls/@astro',
title: 'Mastodon'
},
youtube: {
url: 'https://www.youtube.com/@astrodotbuild',
title: 'YouTube',
},
discord: {
url: 'https://astro.build/chat',
title: 'Discord'
}
},
};
export default settings;

View file

@ -0,0 +1,45 @@
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
}
html,
body {
height: 100%;
}
body {
background-color: var(--theme-bg);
color: var(--theme-text);
font-size: var(--theme-text-base);
font-family: var(--theme-font-body);
-webkit-font-smoothing: antialiased;
line-height: 1.5;
}
img,
picture,
video,
canvas {
display: block;
max-width: 100%;
}
img {
height: auto;
}
a {
color: var(--theme-text);
}
h1,
h2,
h3,
h4,
h5 {
line-height: 1.2;
font-family: var(--theme-font-brand);
}

View file

@ -0,0 +1,120 @@
:root {
/* Colors */
--theme-bg: #eceaf5;
--theme-bg-accent: #f6f5fb;
--theme-text: #171b26;
--theme-text-invert: #fdfdfd;
--theme-gray-200: #505d84;
--theme-gray-400: #505d84;
--theme-gray-700: #505d84;
--theme-accent-light: #ddd6fc;
--theme-accent-medium: #8577eb;
--theme-accent-dark: #5a48d9;
--theme-shade-subtle: var(--theme-accent-light);
/* Gradients */
--theme-gradient-main: linear-gradient(83.21deg, var(--theme-accent-dark) 6.77%, #c561f6 93.75%);
--theme-gradient-text: linear-gradient(
83.21deg,
var(--theme-accent-dark) 10.42%,
#7a4fe2 76.04%,
#c561f6 100%
);
/* Shadows */
--theme-shadow-sm: 1px 1px 5px rgba(0, 0, 0, 0.1);
--theme-shadow-md: 2px 2px 10px rgba(0, 0, 0, 0.1);
--theme-shadow-lg: 2px 2px 20px rgba(0, 0, 0, 0.2);
/* Content Sizes */
--theme-size-content-1: 30ch;
--theme-size-content-2: 45ch;
--theme-size-content-3: 65ch;
/* Type Scale */
/* @link https://utopia.fyi/type/calculator?c=320,16,1.2,1240,20,1.2,5,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12 */
--theme-text-sm: clamp(0.83rem, calc(0.76rem + 0.36vw), 1.04rem);
--theme-text-base: clamp(1.00rem, calc(0.91rem + 0.43vw), 1.25rem);
--theme-text-lg: clamp(1.20rem, calc(1.10rem + 0.52vw), 1.50rem);
--theme-text-xl: clamp(1.44rem, calc(1.31rem + 0.63vw), 1.80rem);
--theme-text-2xl: clamp(1.73rem, calc(1.58rem + 0.75vw), 2.16rem);
--theme-text-3xl: clamp(2.07rem, calc(1.89rem + 0.90vw), 2.59rem);
--theme-text-4xl: clamp(2.49rem, calc(2.27rem + 1.08vw), 3.11rem);
/* @link https://utopia.fyi/type/calculator?c=320,16,1.2,1240,18,1.2,5,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12 */
--theme-text-sm: clamp(0.83rem, calc(0.80rem + 0.18vw), 0.94rem);
--theme-text-base: clamp(1.00rem, calc(0.96rem + 0.22vw), 1.13rem);
--theme-text-lg: clamp(1.20rem, calc(1.15rem + 0.26vw), 1.35rem);
--theme-text-xl: clamp(1.44rem, calc(1.38rem + 0.31vw), 1.62rem);
--theme-text-2xl: clamp(1.73rem, calc(1.65rem + 0.38vw), 1.94rem);
--theme-text-3xl: clamp(2.07rem, calc(1.98rem + 0.45vw), 2.33rem);
--theme-text-4xl: clamp(2.49rem, calc(2.38rem + 0.54vw), 2.80rem);
/* Fonts */
--__font-system: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--theme-font-body: var(--__font-system);
--theme-font-brand: 'Montserrat', var(--__font-system);
/* Corners */
--theme-radius-base: 0.3125rem;
--theme-radius-lg: 0.625rem;
--theme-radius-xl: 1.25rem;
--theme-radius-full: 999rem;
/* Transitions */
--theme-transition: 0.2s ease-in-out;
/* Spacing */
/* @link https://utopia.fyi/space/calculator?c=320,16,1.2,1240,18,1.2,5,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12 */
--theme-space-3xs: clamp(0.25rem, calc(0.23rem + 0.11vw), 0.31rem);
--theme-space-2xs: clamp(0.50rem, calc(0.48rem + 0.11vw), 0.56rem);
--theme-space-xs: clamp(0.75rem, calc(0.71rem + 0.22vw), 0.88rem);
--theme-space-sm: clamp(1.00rem, calc(0.96rem + 0.22vw), 1.13rem);
--theme-space-md: clamp(1.50rem, calc(1.43rem + 0.33vw), 1.69rem);
--theme-space-lg: clamp(2.00rem, calc(1.91rem + 0.43vw), 2.25rem);
--theme-space-xl: clamp(3.00rem, calc(2.87rem + 0.65vw), 3.38rem);
--theme-space-2xl: clamp(4.00rem, calc(3.83rem + 0.87vw), 4.50rem);
--theme-space-3xl: clamp(6.00rem, calc(5.74rem + 1.30vw), 6.75rem);
/* One-up pairs */
--theme-space-3xs-2xs: clamp(0.25rem, calc(0.14rem + 0.54vw), 0.56rem);
--theme-space-2xs-xs: clamp(0.50rem, calc(0.37rem + 0.65vw), 0.88rem);
--theme-space-xs-sm: clamp(0.75rem, calc(0.62rem + 0.65vw), 1.13rem);
--theme-space-sm-md: clamp(1.00rem, calc(0.76rem + 1.20vw), 1.69rem);
--theme-space-md-lg: clamp(1.50rem, calc(1.24rem + 1.30vw), 2.25rem);
--theme-space-lg-xl: clamp(2.00rem, calc(1.52rem + 2.39vw), 3.38rem);
--theme-space-xl-2xl: clamp(3.00rem, calc(2.48rem + 2.61vw), 4.50rem);
--theme-space-2xl-3xl: clamp(4.00rem, calc(3.04rem + 4.78vw), 6.75rem);
/* Custom pairs */
--theme-space-sm-lg: clamp(1.00rem, calc(0.57rem + 2.17vw), 2.25rem);
}
:root.theme-dark {
/* Colors (dark) */
--theme-bg: #141925;
--theme-bg-accent: #202c49;
--theme-text: #fdfdfd;
--theme-text-invert: #171b26;
--theme-gray-200: #c3cadb;
--theme-gray-400: #8490b5;
--theme-gray-700: #3d4663;
--theme-accent-light: #ebd2f8;
--theme-accent-medium: #c779ed;
--theme-accent-dark: #c561f6;
--theme-shade-subtle: var(--theme-bg-accent);
/* Gradients (dark) */
--theme-gradient-main: linear-gradient(83.21deg, var(--theme-accent-dark) 6.25%, #5a48d9 100%);
--theme-gradient-text: linear-gradient(
83.21deg,
var(--theme-accent-dark) 6.25%,
#b45df1 75.52%,
#5a48d9 100%
);
/* Shadows (dark) */
--theme-shadow-sm: 1px 1px 5px rgba(0, 0, 0, 0.1);
--theme-shadow-md: 4px 4px 10px rgba(9, 11, 17, 0.2);
--theme-shadow-lg: 4px 4px 30px rgba(0, 0, 0, 0.6);
}

View file

@ -0,0 +1,11 @@
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}

View file

@ -0,0 +1,3 @@
{
"extends": "astro/tsconfigs/base"
}

View file

@ -345,6 +345,18 @@ importers:
specifier: ^3.1.4 specifier: ^3.1.4
version: link:../../packages/astro version: link:../../packages/astro
examples/social-feed:
dependencies:
'@astrojs/rss':
specifier: ^3.0.0
version: link:../../packages/astro-rss
astro:
specifier: ^3.0.1
version: link:../../packages/astro
reading-time:
specifier: ^1.5.0
version: 1.5.0
examples/ssr: examples/ssr:
dependencies: dependencies:
'@astrojs/node': '@astrojs/node':