New Theme!
This commit is contained in:
parent
237e7d80f5
commit
384066e24b
|
@ -0,0 +1,52 @@
|
|||
name: NPM-Theme (Brutal By Elian)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publishnpm:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./packages/astro-ghostcms-brutalbyelian
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- name: publish-npm
|
||||
run: |
|
||||
# Change publish registry
|
||||
echo "$(jq '.publishConfig.registry = "https://registry.npmjs.org"' package.json)" > package.json
|
||||
# Publish package
|
||||
npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
|
||||
githubpackage:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./packages/astro-ghostcms-brutalbyelian
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: https://npm.pkg.github.com
|
||||
- name: publish-github
|
||||
run: |
|
||||
# Change publish registry
|
||||
echo "$(jq '.publishConfig.registry = "https://npm.pkg.github.com"' package.json)" > package.json
|
||||
# Publish package
|
||||
npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.ACTION_TOKEN}}
|
|
@ -0,0 +1,21 @@
|
|||
import { definePreset, presetIcons,
|
||||
presetWind, presetTypography } from 'unocss';
|
||||
|
||||
export default definePreset(() => {
|
||||
return {
|
||||
name: 'brutalTheme',
|
||||
presets: [
|
||||
presetWind(),
|
||||
presetIcons({
|
||||
collections: {
|
||||
//@ts-expect-error
|
||||
logos: () =>
|
||||
import('@iconify-json/logos/icons.json').then((i) => i.default),
|
||||
uil: () =>
|
||||
import('@iconify-json/uil/icons.json').then((l) => l.default),
|
||||
},
|
||||
}),
|
||||
presetTypography(),
|
||||
]
|
||||
}
|
||||
})
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"name": "@matthiesenxyz/astro-ghostcms-brutalbyelian",
|
||||
"description": "ElianCodes Brutal theme modified to work with Astro-GhostCMS",
|
||||
"version": "0.0.1",
|
||||
"homepage": "https://astro-ghostcms.xyz/",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"author": {
|
||||
"email": "adam@matthiesen.xyz",
|
||||
"name": "Adam Matthiesen - MatthiesenXYZ",
|
||||
"url": "https://matthiesen.xyz"
|
||||
},
|
||||
"keywords": [
|
||||
"astro-ghostcms"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/MatthiesenXYZ/astro-ghostcms.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/MatthiesenXYZ/astro-ghostcms/issues",
|
||||
"email": "issues@astro-ghostcms.xyz"
|
||||
},
|
||||
"main": "./brutalunocss-preset.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"brutalunocss-preset.ts"
|
||||
],
|
||||
"exports": {
|
||||
".": "./brutalunocss-preset.ts",
|
||||
"./index.astro": "./src/routes/index.astro",
|
||||
"./[slug].astro": "./src/routes/[slug].astro",
|
||||
"./tags.astro": "./src/routes/tags.astro",
|
||||
"./authors.astro": "./src/routes/authors.astro",
|
||||
"./tag/[slug].astro": "./src/routes/tag/[slug].astro",
|
||||
"./author/[slug].astro": "./src/routes/author/[slug].astro",
|
||||
"./archives/[...page].astro": "./src/routes/archives/[...page].astro"
|
||||
},
|
||||
"scripts": { },
|
||||
"peerDependencies": {
|
||||
"astro": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@resvg/resvg-js": "^2.6.0",
|
||||
"@typescript-eslint/parser": "^6.15.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-astro": "^0.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-astro": "^0.12.2",
|
||||
"sharp": "^0.32.6",
|
||||
"@matthiesenxyz/astro-ghostcms": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/logos": "^1.1.41",
|
||||
"@iconify-json/uil": "^1.1.8",
|
||||
"@matthiesenxyz/astro-ghostcms": "^3.1.8",
|
||||
"@unocss/astro": "^0.57.7",
|
||||
"@unocss/reset": "^0.57.7",
|
||||
"unocss": "^0.57.7",
|
||||
"@eliancodes/brutal-ui": "^0.2.3",
|
||||
"astro-font": "^0.0.72",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
import AuthorList from './AuthorList.astro';
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
|
||||
import type { Settings, Post, Author } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
export type Props = {
|
||||
authors: Author[];
|
||||
settings: Settings;
|
||||
isHome?: boolean;
|
||||
};
|
||||
const { authors, settings } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<section class='mt-8'>
|
||||
<Card>
|
||||
<div class='flex justify-between items-start'>
|
||||
<h2 class='text-2xl md:text-4xl lg:text-6xl mb-8 dm-serif'>
|
||||
Our Authors
|
||||
</h2>
|
||||
</div>
|
||||
<AuthorList authors={authors} settings={settings}/>
|
||||
</Card>
|
||||
</section>
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
export type Props = {
|
||||
name: string,
|
||||
image?: string | null,
|
||||
count: number,
|
||||
bio: string | null,
|
||||
location: string | null,
|
||||
website: string | null,
|
||||
twitter: string | null,
|
||||
facebook: string | null
|
||||
};
|
||||
|
||||
const {
|
||||
name, image, bio, location, website, twitter, facebook, count
|
||||
} = Astro.props as Props;
|
||||
---
|
||||
|
||||
<header class="author-card">
|
||||
{ image ? <img src={image} alt="Author Image" class="author-image"> : (
|
||||
<span class="author-image">
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path
|
||||
d="M3.513 18.998C4.749 15.504 8.082 13 12 13s7.251 2.504 8.487 5.998C18.47 21.442 15.417 23 12 23s-6.47-1.558-8.487-4.002zM12 12c2.21 0 4-2.79 4-5s-1.79-4-4-4-4 1.79-4 4 1.79 5 4 5z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
<div class="author-info">
|
||||
<div class="author-name text-ctp-teal">{name}</div>
|
||||
<div class="author-bio text-ctp-blue">
|
||||
{bio ? bio : count > 0? count + " Posts" : "No Posts"}
|
||||
</div>
|
||||
|
||||
{location && (
|
||||
<div class="author-location text-ctp-sapphire">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pin-map-fill text-ctp-flamingo inline" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M3.1 11.2a.5.5 0 0 1 .4-.2H6a.5.5 0 0 1 0 1H3.75L1.5 15h13l-2.25-3H10a.5.5 0 0 1 0-1h2.5a.5.5 0 0 1 .4.2l3 4a.5.5 0 0 1-.4.8H.5a.5.5 0 0 1-.4-.8z"/> <path fill-rule="evenodd" d="M4 4a4 4 0 1 1 4.5 3.969V13.5a.5.5 0 0 1-1 0V7.97A4 4 0 0 1 4 3.999z"/> </svg> {location}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="author-links">
|
||||
{website && (<a href={website} target="_blank" rel="noopener">Website</a>)}
|
||||
{twitter && (<a href={twitter} target="_blank" rel="noopener">Twitter</a>)}
|
||||
{facebook && (<a href={facebook} target="_blank" rel="noopener">Facebook</a>)}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.author-card {
|
||||
display: flex;
|
||||
margin-top: 8vmin;
|
||||
margin-bottom: 6vmin;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.author-image {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.author-info {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.author-bio {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.author-location {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.author-links {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.author-links a {
|
||||
text-decoration: none;
|
||||
color: #FFFFFF;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import AuthorSummaryCard from './AuthorSummaryCard.astro';
|
||||
import type { Settings, Post, Author } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
export type Props = {
|
||||
authors: Author[];
|
||||
settings: Settings;
|
||||
};
|
||||
const { authors, settings } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<ul class='grid md:grid-cols-2 lg:grid-cols-3 gap-8'>
|
||||
{
|
||||
authors.map((author: Author) => {
|
||||
return (
|
||||
<li>
|
||||
<AuthorSummaryCard author={author}, settings={settings} />
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
import SummaryCard from './AuthorSummaryCardHeader.astro';
|
||||
import type { Settings, Author } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import { getGhostImgPath, formatDate } from "../../utils";
|
||||
import { Pill } from '@eliancodes/brutal-ui';
|
||||
import Button from "../generic/button.astro"
|
||||
|
||||
interface Props {
|
||||
author: Author;
|
||||
settings: Settings;
|
||||
};
|
||||
|
||||
const { author, settings } = Astro.props;
|
||||
---
|
||||
|
||||
<SummaryCard
|
||||
title={author.name}
|
||||
imgAlt={author.name}
|
||||
imgSrc={getGhostImgPath(settings.url, author.profile_image || "", 100)}
|
||||
description={author.bio?author.bio:""}
|
||||
>
|
||||
<div class='flex justify-end my-4'>
|
||||
<Button href={`/author/${author.slug}/`}>More Info →</Button>
|
||||
</div>
|
||||
|
||||
<div class='hidden sm:inline-block'>
|
||||
<div class='flex justify-between items-center'>
|
||||
<ul class='flex gap-4 mt-2'>
|
||||
{author.count && author.count.posts && (
|
||||
<div class="sanchez">
|
||||
<Pill>{author.count.posts > 0 ? `${author.count.posts} posts` : "No posts"}</Pill>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</SummaryCard>
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
import { Image } from 'astro:assets';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
imgSrc: string | null;
|
||||
imgAlt: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const { title, imgAlt, imgSrc, description } = Astro.props;
|
||||
---
|
||||
|
||||
<Card color='white'>
|
||||
<div class='rounded-lg'>
|
||||
{imgSrc && (<Image
|
||||
src={imgSrc}
|
||||
alt={imgAlt}
|
||||
width={100}
|
||||
height={100}
|
||||
class='rounded h-54 w-56'
|
||||
/>)}
|
||||
</div>
|
||||
<h3 class='poppins text-lg font-bold md:text-xl'>{title}</h3>
|
||||
<p class='poppins line-clamp-4'>{description}</p>
|
||||
<slot />
|
||||
</Card>
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
interface Props {
|
||||
content: any;
|
||||
}
|
||||
|
||||
const { content } = Astro.props;
|
||||
---
|
||||
|
||||
<article class='prose-slate w-sm md:w-prose md:prose poppins'>
|
||||
<a href='/blog/' title='Back to blog'>← Back to blog</a>
|
||||
<p class='text-slate text-sm md:text-base'>
|
||||
Published on {content.pubDate} by {content.author}
|
||||
</p>
|
||||
<slot />
|
||||
<p class='text-slate'>Written by {content.author}</p>
|
||||
<a href='/blog/' title='Back to blog'>← Back to blog</a>
|
||||
</article>
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import BlogSummaryCard from './BlogSummaryCard.astro';
|
||||
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
export type Props = {
|
||||
posts: Post[];
|
||||
settings: Settings;
|
||||
};
|
||||
const { posts, settings } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<ul class='grid md:grid-cols-2 lg:grid-cols-3 gap-8'>
|
||||
{
|
||||
posts.map((post: Post) => {
|
||||
return (
|
||||
<li>
|
||||
<BlogSummaryCard post={post}, settings={settings} />
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
interface Props {
|
||||
headings: Array<Object>;
|
||||
}
|
||||
|
||||
const { headings } = Astro.props;
|
||||
---
|
||||
|
||||
<aside>
|
||||
<nav class='hidden lg:block sticky top-6'>
|
||||
<ul>
|
||||
{
|
||||
headings.map((heading: any) => (
|
||||
<li class='py-1'>
|
||||
{[...Array(heading.depth - 1)].map((_, _i) => (
|
||||
<span class='inline-block w-4' />
|
||||
))}
|
||||
<a
|
||||
class='hover:text-stone transition-all duration-150 ease-in-out text-dark poppins'
|
||||
href={`#${heading.slug}`}
|
||||
>
|
||||
{heading.text}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
import SummaryCard from '../generic/SummaryCard.astro';
|
||||
import type { Settings, Post, Tag } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import { getGhostImgPath, formatDate } from "../../utils";
|
||||
import { Pill } from '@eliancodes/brutal-ui';
|
||||
import Button from "../generic/button.astro"
|
||||
|
||||
interface Props {
|
||||
post: Post;
|
||||
settings: Settings;
|
||||
};
|
||||
|
||||
const { post, settings } = Astro.props;
|
||||
---
|
||||
|
||||
<SummaryCard
|
||||
title={post.title}
|
||||
imgAlt={post.feature_image_alt
|
||||
? post.feature_image_alt : post.feature_image_caption
|
||||
? post.feature_image_caption : post.title}
|
||||
imgSrc={getGhostImgPath(settings.url, post.feature_image || "", 800)}
|
||||
description={post.excerpt}
|
||||
>
|
||||
<div class='flex justify-end my-4'>
|
||||
<Button href={`/${post.slug}/`}>Read post →</Button>
|
||||
</div>
|
||||
|
||||
<div class='hidden sm:inline-block'>
|
||||
<p class='poppins mt-2'>tags:</p>
|
||||
<div class='flex justify-between items-center'>
|
||||
<ul class='flex gap-4 mt-2'>
|
||||
{
|
||||
post.tags && post.tags.map((tag) => {
|
||||
return (
|
||||
<li>
|
||||
<a class="sanchez" href={`/tag/${tag.slug}/`}>
|
||||
<Pill>{tag.name}</Pill>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</SummaryCard>
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
---
|
||||
|
||||
<section class='flex flex-col gap-8 justify-between'>
|
||||
<p class='text-9xl font-bold dm-serif'>404</p>
|
||||
<h2 class='text-4xl outfit'>Page Not Found</h2>
|
||||
<p class='text-xl sm:text-3xl sanchez'>
|
||||
Sorry, we couldn't find the page you were looking for.
|
||||
</p>
|
||||
<a
|
||||
href='/'
|
||||
title='Go back home'
|
||||
class='px-4 py-2 border-2 border-black hover:bg-red transition-colors duration-150 ease-in-out w-48 text-center poppins'
|
||||
>← Go Home</a
|
||||
>
|
||||
</section>
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
import { getGhostImgPath } from "../../utils";
|
||||
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
export type Props = {
|
||||
image: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
settings: Settings;
|
||||
transitionName?: string;
|
||||
};
|
||||
const { image, alt, caption = "", settings, transitionName } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<figure class="article-image">
|
||||
<img
|
||||
srcset={`
|
||||
${getGhostImgPath(settings.url, image, 300)} 300w,
|
||||
${getGhostImgPath(settings.url, image, 600)} 600w,
|
||||
`}
|
||||
sizes="(min-width: 300px) 600px, 92vw"
|
||||
src={getGhostImgPath(settings.url, image, 2000)}
|
||||
alt={alt}
|
||||
transition:name={transitionName}
|
||||
/>
|
||||
{caption && <figcaption class="text-ctp-overlay2"><Fragment set:html={caption}></figcaption>}
|
||||
</figure>
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
import { AstroFont } from "astro-font";
|
||||
---
|
||||
<AstroFont
|
||||
config={[
|
||||
{
|
||||
name: "Outfit",
|
||||
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Outfit&display=swap',
|
||||
display: "swap",
|
||||
selector: ".outfit",
|
||||
fallback: "sans-serif",
|
||||
src: [],
|
||||
},
|
||||
{
|
||||
display: "swap",
|
||||
name: "Poppins",
|
||||
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Poppins&display=swap',
|
||||
selector: ".poppins",
|
||||
fallback: "sans-serif",
|
||||
preload: true,
|
||||
src: [],
|
||||
},
|
||||
{
|
||||
display: "swap",
|
||||
name: "Righteous",
|
||||
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Righteous&display=swap',
|
||||
fallback: "sans-serif",
|
||||
selector: ".righteous",
|
||||
preload: true,
|
||||
src: [],
|
||||
},
|
||||
{
|
||||
display: "swap",
|
||||
name: "Sanchez",
|
||||
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Sanchez&display=swap',
|
||||
fallback: "serif",
|
||||
selector: ".sanchez",
|
||||
preload: true,
|
||||
src: [],
|
||||
},
|
||||
{
|
||||
display: "swap",
|
||||
fallback: "serif",
|
||||
selector: ".dm-serif",
|
||||
name: "DM Serif Text",
|
||||
googleFontsURL: 'https://fonts.googleapis.com/css2?family=DM+Serif+Text&display=swap',
|
||||
src: [],
|
||||
},
|
||||
]}
|
||||
/>
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
import type { Page } from 'astro';
|
||||
const { page } = Astro.props as {page: Page};
|
||||
---
|
||||
|
||||
<div class="page__actions">
|
||||
{page.url.prev && (
|
||||
<a class="action__go-to-x" href={page.url.prev} title="Go to Previous">
|
||||
← Prev
|
||||
</a>
|
||||
)}
|
||||
{page.url.next && (
|
||||
<a class="action__go-to-x" href={page.url.next} title="Go to Next">
|
||||
Next →
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* .page__actions {
|
||||
@apply flex justify-center md:justify-end py-6 gap-2;
|
||||
}
|
||||
.action__go-to-x {
|
||||
@apply text-base uppercase text-gray-500 dark:text-gray-400 hover:underline;
|
||||
} */
|
||||
</style>
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
import BlogList from '../blog/BlogList.astro';
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
|
||||
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
export type Props = {
|
||||
posts: Post[];
|
||||
settings: Settings;
|
||||
isHome?: boolean;
|
||||
};
|
||||
const { posts, settings } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<section class='mt-8'>
|
||||
<Card>
|
||||
<div class='flex justify-between items-start'>
|
||||
<h2 class='text-2xl md:text-4xl lg:text-6xl mb-8 dm-serif'>
|
||||
Recent Blogposts
|
||||
</h2>
|
||||
</div>
|
||||
<BlogList posts={posts} settings={settings}/>
|
||||
</Card>
|
||||
</section>
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<Card color='white'>
|
||||
<center>
|
||||
<h3 class='poppins text-lg md:text-xl'>{title}</h3>
|
||||
<p class='poppins line-clamp-4'>{description}</p>
|
||||
<slot />
|
||||
</center>
|
||||
</Card>
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
import { Image } from 'astro:assets';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
imgSrc: string | null;
|
||||
imgAlt: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const { title, imgAlt, imgSrc, description } = Astro.props;
|
||||
---
|
||||
|
||||
<Card color='white'>
|
||||
<h3 class='poppins text-lg md:text-xl'>{title}</h3>
|
||||
<div class='rounded-lg border-3 border-black my-4 h-56'>
|
||||
{imgSrc && (<Image
|
||||
src={imgSrc}
|
||||
alt={imgAlt}
|
||||
width={800}
|
||||
height={400}
|
||||
class='rounded h-full w-full object-cover'
|
||||
/>)}
|
||||
</div>
|
||||
<p class='poppins line-clamp-4'>{description}</p>
|
||||
<slot />
|
||||
</Card>
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
import SummaryCard from './SummaryCard-tag.astro';
|
||||
import type { Settings, Tag } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import Button from "./button.astro"
|
||||
|
||||
interface Props {
|
||||
tag: Tag;
|
||||
settings: Settings;
|
||||
};
|
||||
|
||||
const { tag } = Astro.props;
|
||||
---
|
||||
|
||||
<SummaryCard
|
||||
title={tag.name}
|
||||
description={tag.description?tag.description:""}
|
||||
>
|
||||
<div class='flex justify-end my-4'>
|
||||
<Button href={`/${tag.slug}/`}>See Posts →</Button>
|
||||
</div>
|
||||
|
||||
</SummaryCard>
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
interface Props {
|
||||
href: string;
|
||||
target?: '_blank' | '_self';
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
import colors from './colors.json';
|
||||
|
||||
if (Astro.props.target === undefined) {
|
||||
Astro.props.target = '_self';
|
||||
}
|
||||
|
||||
if (Astro.props.color === undefined) {
|
||||
Astro.props.color =
|
||||
colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
|
||||
const { href, target, color } = Astro.props;
|
||||
---
|
||||
|
||||
<style define:vars={{ color: color }}>
|
||||
a.brutal-btn {
|
||||
filter: drop-shadow(5px 5px 0 rgb(0 0 0 / 1));
|
||||
background-color: white;
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid black;
|
||||
transition: all;
|
||||
transition-duration: 0.5s;
|
||||
animation: ease-in-out;
|
||||
font-family: 'Sanchez', serif;
|
||||
}
|
||||
a.brutal-btn:hover {
|
||||
filter: drop-shadow(3px 3px 0 rgb(0 0 0 / 1));
|
||||
background-color: var(--color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<a
|
||||
href={href}
|
||||
target={target}
|
||||
class='brutal-btn'
|
||||
data-astro-reload
|
||||
>
|
||||
<slot />
|
||||
</a>
|
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
"#c084fc",
|
||||
"#f472b6",
|
||||
"#fb7185",
|
||||
"#e879f9",
|
||||
"#a78bfa",
|
||||
"#818cf8",
|
||||
"#60a5fa",
|
||||
"#38bdf8",
|
||||
"#22d3ee",
|
||||
"#2dd4bf",
|
||||
"#34d399",
|
||||
"#4ade80",
|
||||
"#a3e635",
|
||||
"#facc15",
|
||||
"#fb923c",
|
||||
"#f87171"
|
||||
]
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
import { facebook, getSettings, invariant, twitter } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
---
|
||||
|
||||
<section class='md:hidden'>
|
||||
<h2 class='hidden'>Socials section</h2>
|
||||
<ul class='flex justify-between'>
|
||||
{ settings.facebook && (
|
||||
<li class='bg-white px-4 py-2 text-green border-black border-2 rounded card-shadow'>
|
||||
<a href={facebook(settings.facebook)} target='_blank' title={`Go to Facebook`}>
|
||||
<div class:list={['i-uil-facebook', 'p-6']} />
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
{ settings.twitter && (
|
||||
<li class='bg-white px-4 py-2 text-green border-black border-2 rounded card-shadow'>
|
||||
<a href={twitter(settings.twitter)} target='_blank' title={`Go to Twitter "X"`}>
|
||||
<div class:list={['i-uil-twitter', 'p-6']} />
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
<li class='bg-white px-4 py-2 text-green border-black border-2 rounded card-shadow'>
|
||||
<a href='/rss.xml' target='_blank' title={`See our RSS Feed`}>
|
||||
<div class:list={['i-uil-rss', 'p-6']} />
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</section>
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
import { getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
|
||||
export interface Props {
|
||||
backToTop?: boolean;
|
||||
}
|
||||
|
||||
const { backToTop = false } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
backToTop && (
|
||||
<button
|
||||
class:list={[
|
||||
backToTop ? `backToTop` : null,
|
||||
'transition-300 z-50 opacity-0 fixed flex bottom-[10px] right-[30px] w-10 h-10 bg-white border border-black card-shadow',
|
||||
]}
|
||||
>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
height='100%'
|
||||
width='100%'
|
||||
stroke-width='1.5'
|
||||
stroke='currentColor'
|
||||
>
|
||||
<path
|
||||
stroke-linecap='round'
|
||||
stroke-linejoin='round'
|
||||
d='m4.5 15.75 7.5-7.5 7.5 7.5'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
<footer class='bg-black text-white p-6'>
|
||||
<h2 class='hidden'>Footer</h2>
|
||||
<p class="text-center text-sm text-slate-500">
|
||||
Copyright © {new Date().getFullYear()} - {settings.title}. All rights reserved.
|
||||
</p>
|
||||
<p class='outfit opacity-50%'>
|
||||
Brutal theme for Astro - by <a
|
||||
href='https://www.elian.codes/'
|
||||
target='_blank'
|
||||
class='text-blue'>ElianCodes</a
|
||||
>
|
||||
<p class='outfit opacity-50%'>
|
||||
Powered By Ghost
|
||||
</p>
|
||||
</p>
|
||||
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
.backToTop.active {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('astro:page-load', () => {
|
||||
const backToTop = document.querySelector('.backToTop');
|
||||
|
||||
const toggleBackToTopButton = () => {
|
||||
if (window.scrollY > 250) {
|
||||
backToTop?.classList.add('active');
|
||||
} else {
|
||||
backToTop?.classList.remove('active');
|
||||
}
|
||||
};
|
||||
|
||||
backToTop?.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', toggleBackToTopButton);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
import LocalFont from '../generic/LocalFont.astro';
|
||||
import { ViewTransitions } from 'astro:transitions';
|
||||
import { getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import { getOgImagePath } from "@matthiesenxyz/astro-ghostcms/satoriOG";
|
||||
|
||||
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
const Ghosttitle = settings.title;
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
ogImage?: URL;
|
||||
}
|
||||
|
||||
const ogI = new URL(getOgImagePath(Astro.url.pathname), Astro.url.origin).href;
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
|
||||
if (Astro.props.ogImage === undefined) {
|
||||
Astro.props.ogImage = new URL('/v1/generate/og/default.png', Astro.url);
|
||||
}
|
||||
|
||||
const { title, description, ogImage } = Astro.props;
|
||||
---
|
||||
|
||||
<head>
|
||||
<LocalFont />
|
||||
<meta charset='utf-8' />
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<meta name='generator' content={Astro.generator} />
|
||||
|
||||
<link
|
||||
rel='alternate'
|
||||
href='https://brutal.elian.codes/blog.xml'
|
||||
type='application/rss+xml'
|
||||
/>
|
||||
|
||||
<meta name='title' content={`${Ghosttitle} | ${title}`} />
|
||||
<meta name='description' content={description} />
|
||||
|
||||
<meta property='og:type' content='website' />
|
||||
<meta property='og:url' content={canonicalURL} />
|
||||
<meta property='og:title' content={title} />
|
||||
<meta property='og:description' content={description} />
|
||||
<meta property='og:image' content={ogI} />
|
||||
|
||||
<meta property='twitter:card' content='summary_large_image' />
|
||||
<meta property='twitter:url' content={canonicalURL} />
|
||||
<meta property='twitter:title' content={title} />
|
||||
<meta property='twitter:description' content={description} />
|
||||
<meta property='twitter:image' content={ogI} />
|
||||
|
||||
<link rel='canonical' href={canonicalURL} />
|
||||
<link rel='icon' type='image/svg' href='/favicon.svg' />
|
||||
|
||||
<title>{Ghosttitle} | {title}</title>
|
||||
|
||||
<ViewTransitions />
|
||||
|
||||
<slot />
|
||||
</head>
|
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
import { facebook, getSettings, invariant, twitter } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
|
||||
interface Props {
|
||||
pageTitle?: string;
|
||||
}
|
||||
|
||||
const { pageTitle } = Astro.props;
|
||||
---
|
||||
|
||||
<header class='border-b-4 border-black flex justify-between p-6 items-center'>
|
||||
{pageTitle && <h1 class='hidden'>{pageTitle}</h1>}
|
||||
<a href='/' title='Back to Home'>
|
||||
<p class='righteous md:text-4xl'>{settings.icon && <img src={settings.icon} width="48" class="inline">}{settings.title}</p>
|
||||
</a>
|
||||
<nav class='hidden md:inline-block'>
|
||||
<h2 class='hidden'>Navigation</h2>
|
||||
<ul class='flex gap-8 poppins'>
|
||||
{
|
||||
settings.navigation.map(({label, url}) => (
|
||||
<li>
|
||||
<a
|
||||
class='font-medium hover:text-green hover:underline transition-all duration-150 ease-in-out'
|
||||
href={url}
|
||||
title={`Go to ${label}`}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
{ settings.facebook && (
|
||||
<li>
|
||||
<a
|
||||
href={facebook(settings.facebook)}
|
||||
class='hover:text-green hover:underline transition-all duration-150 ease-in-out'
|
||||
target='_blank'
|
||||
title={`See @${settings.facebook} on Facebook`}
|
||||
>
|
||||
<div class:list={['i-uil-facebook', 'w-6 h-6']} />
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
{ settings.twitter && (
|
||||
<li>
|
||||
<a
|
||||
href={twitter(settings.twitter)}
|
||||
class='hover:text-green hover:underline transition-all duration-150 ease-in-out'
|
||||
target='_blank'
|
||||
title={`See @${settings.twitter} on Twitter "X"`}
|
||||
>
|
||||
<div class:list={['i-uil-twitter', 'w-6 h-6']} />
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
<li>
|
||||
<a
|
||||
href='/rss.xml'
|
||||
class='hover:text-green hover:underline transition-all duration-150 ease-in-out'
|
||||
target='_blank'
|
||||
title={`See our RSS Feed`}
|
||||
>
|
||||
<div class:list={['i-uil-rss', 'w-6 h-6']} />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class='md:hidden flex'>
|
||||
<h2 class='hidden'>Mobile Navigation</h2>
|
||||
<ul>
|
||||
{
|
||||
settings.navigation.map(({label, url}) => (
|
||||
<li>
|
||||
<a
|
||||
class='font-medium hover:text-green hover:underline transition-all duration-150 ease-in-out'
|
||||
href={url}
|
||||
title={`Go to ${label}`}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
|
@ -0,0 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
import BaseNavigation from '../components/layout/BaseNavigation.astro';
|
||||
import BaseFooter from '../components/layout/BaseFooter.astro';
|
||||
import BaseHead from '../components/layout/BaseHead.astro';
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
classList?: string;
|
||||
pageTitle?: string;
|
||||
ogImage?: URL;
|
||||
}
|
||||
|
||||
const { title, description, classList, pageTitle, ogImage } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang='en' class='h-full'>
|
||||
<BaseHead ogImage={ogImage} title={title} description={description} />
|
||||
|
||||
<body class:list={[classList]}>
|
||||
<BaseNavigation {pageTitle} />
|
||||
<slot />
|
||||
<BaseFooter />
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
import BlogPost from "../layouts/Default.astro"
|
||||
import { getAllPosts, getAllPages, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import type { InferGetStaticPropsType } from "astro";
|
||||
import RecentBlogPosts from "../components/generic/RecentBlogPosts.astro";
|
||||
import { formatDate } from "../utils";
|
||||
import FeatureImage from "../components/generic/Featureimage.astro"
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const [posts, pages, settings] = await Promise.all([getAllPosts(), await getAllPages(), await getSettings()]);
|
||||
const allPosts = [...posts, ...pages];
|
||||
return allPosts.map((post) => ({
|
||||
params: { slug: post.slug },
|
||||
props: { post, posts, settings },
|
||||
}));
|
||||
}
|
||||
|
||||
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
const {post, posts, settings} = Astro.props as Props;
|
||||
invariant(settings, "Settings are required");
|
||||
---
|
||||
<BlogPost title={post.title} description={post.excerpt}>
|
||||
<article class='prose-slate w-sm md:w-prose md:prose poppins pl-4'>
|
||||
|
||||
<header class="article-header gh-canvas">
|
||||
<div class="flex justify-between my-4">
|
||||
<section class="flex flex-grow align-middle">
|
||||
<div class="text-ctp-overlay2">
|
||||
{ post.primary_author && (
|
||||
<h4 class="text-ctp-teal">Author:
|
||||
{post.primary_author.name}
|
||||
</h4>
|
||||
)}
|
||||
<div class="text-ctp-overlay2">
|
||||
<time class="text-ctp-sapphire" datetime={formatDate(post.created_at)}
|
||||
>{formatDate(post.created_at)}
|
||||
</time>
|
||||
<span class="text-ctp-peach"
|
||||
><span class="text-ctp-overlay2">•</span>
|
||||
{post.reading_time} min read
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{post.feature_image && (
|
||||
<FeatureImage
|
||||
image={post.feature_image}
|
||||
alt={post.feature_image_alt ? post.feature_image_alt : post.title}
|
||||
caption={post.feature_image_caption || "" }
|
||||
settings={settings}
|
||||
transitionName={`img-${post.title}`}
|
||||
/>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<Fragment set:html={post.html} />
|
||||
</article>
|
||||
<section class='p-6'>
|
||||
<RecentBlogPosts posts={posts} settings={settings} />
|
||||
</section>
|
||||
</BlogPost>
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
import Layout from '../../layouts/Default.astro';
|
||||
import BlogList from '../../components/blog/BlogList.astro';
|
||||
import { getAllPosts, getSettings, invariant, type Post } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import type { GetStaticPathsOptions, Page } from "astro";
|
||||
import Paginator from "../../components/generic/Paginator.astro";
|
||||
|
||||
export async function getStaticPaths({ paginate }:GetStaticPathsOptions) {
|
||||
const posts = await getAllPosts();
|
||||
return paginate(posts, {
|
||||
pageSize: 5,
|
||||
});
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
page: Page<Post>
|
||||
};
|
||||
|
||||
const settings = await getSettings();
|
||||
invariant(settings, "Settings are required");
|
||||
|
||||
const title = settings.title;
|
||||
const description = settings.description;
|
||||
const { page } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<Layout
|
||||
title='Archives'
|
||||
description=`${title} | On this page you can find a collection of blogposts`
|
||||
pageTitle=`${title} | Archives`
|
||||
>
|
||||
<main class='bg-green p-6'>
|
||||
<BlogList posts={page.data} settings={settings} />
|
||||
<Paginator {page} />
|
||||
</main>
|
||||
</Layout>
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import Layout from '../../layouts/Default.astro';
|
||||
import AuthorDetailCard from '../../components/authors/AuthorDetailCard.astro';
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
import { getAllPosts, getAllAuthors, getSettings, invariant, type Post, type Author } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import type { InferGetStaticParamsType, InferGetStaticPropsType } from 'astro';
|
||||
import BlogList from "../../components/blog/BlogList.astro"
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getAllPosts();
|
||||
const { authors } = await getAllAuthors();
|
||||
const settings = await getSettings();
|
||||
|
||||
return authors.map((author: Author) => {
|
||||
const filteredPosts = posts.filter((post: Post) =>
|
||||
post.authors?.map((author) => author.slug).includes(author.slug)
|
||||
);
|
||||
return {
|
||||
params: { slug: author.slug },
|
||||
props: {
|
||||
posts: filteredPosts,
|
||||
settings,
|
||||
author,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
|
||||
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
|
||||
const { posts, settings, author } = Astro.props;
|
||||
invariant(settings, "Settings are required");
|
||||
const title = `Posts by author: ${author.name}`;
|
||||
const description = `All of the articles we've posted and linked so far under the author: ${author.name}`;
|
||||
---
|
||||
|
||||
<Layout
|
||||
title={title}
|
||||
pageTitle=`${settings.title} | ${title}`
|
||||
description={description}
|
||||
>
|
||||
<main class='bg-pink p-6'>
|
||||
<section id='about' class='col mt-4'>
|
||||
<h2 class='hidden'>{title}</h2>
|
||||
<div class='flex'>
|
||||
<Card>
|
||||
<AuthorDetailCard name={author.name} count={author.count?.posts || 0} image={author.profile_image} bio={author.bio} location={author.location} website={author.website} twitter={author.twitter} facebook={author.facebook}/>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
<br />
|
||||
<BlogList posts={posts} settings={settings} />
|
||||
</main>
|
||||
</Layout>
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import Layout from '../layouts/Default.astro';
|
||||
import AuthorCollection from '../components/authors/AuthorCollection.astro';
|
||||
import { getAllAuthors, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
const { authors } = await getAllAuthors();
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
const title = settings.title;
|
||||
const description = settings.description;
|
||||
|
||||
---
|
||||
|
||||
<Layout
|
||||
title='Authors'
|
||||
pageTitle=`${title} | Authors`
|
||||
description={description}
|
||||
>
|
||||
<main class='bg-pink p-6'>
|
||||
<AuthorCollection authors={authors} settings={settings} />
|
||||
</main>
|
||||
</Layout>
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
import Layout from '../layouts/Default.astro';
|
||||
import RecentBlogPosts from '../components/generic/RecentBlogPosts.astro';
|
||||
import MobileSocials from '../components/home/MobileSocials.astro';
|
||||
import { Card } from '@eliancodes/brutal-ui';
|
||||
import { getPosts, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
const { posts } = await getPosts();
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
const title = settings.title;
|
||||
const description = settings.description;
|
||||
|
||||
---
|
||||
|
||||
<Layout
|
||||
title='Home'
|
||||
pageTitle=`${title} | Home`
|
||||
description={description}
|
||||
>
|
||||
<main class='bg-pink p-6'>
|
||||
<MobileSocials />
|
||||
<section id='about' class='col mt-4'>
|
||||
<h2 class='hidden'>{title}</h2>
|
||||
<div class='flex'>
|
||||
<Card>
|
||||
<div class='flex flex-col justify-between items-start gap-4'>
|
||||
<p class='mt-4 outfit text-2xl md:text-5xl lg:text-7xl'>
|
||||
{title}
|
||||
</p>
|
||||
<p class='mt-2 outfit text-xl md:text-3xl lg:text-5xl'>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
<RecentBlogPosts posts={posts} settings={settings} />
|
||||
</main>
|
||||
</Layout>
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
import type { InferGetStaticParamsType, InferGetStaticPropsType } from 'astro';
|
||||
import Layout from '../../layouts/Default.astro';
|
||||
import BlogList from '../../components/blog/BlogList.astro';
|
||||
import { Button } from '@eliancodes/brutal-ui';
|
||||
|
||||
import { getAllPosts, getAllTags, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getAllPosts();
|
||||
const { tags } = await getAllTags();
|
||||
const settings = await getSettings();
|
||||
|
||||
return tags.map((tag) => {
|
||||
const filteredPosts = posts.filter((post) =>
|
||||
post.tags?.map((tag) => tag.slug).includes(tag.slug)
|
||||
);
|
||||
return {
|
||||
params: { slug: tag.slug },
|
||||
props: {
|
||||
posts: filteredPosts,
|
||||
settings,
|
||||
tag,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
|
||||
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
|
||||
const { posts, settings, tag } = Astro.props;
|
||||
invariant(settings, "Settings are required");
|
||||
---
|
||||
|
||||
<Layout
|
||||
title={`Blog: ${tag.name}`}
|
||||
description={`${settings.title} | All posts tagged with ${tag}`}
|
||||
pageTitle={`${settings.title} | Blogposts tagged with ${tag}`}
|
||||
>
|
||||
<main class='p-6 bg-purple grid gap-4'>
|
||||
<div>
|
||||
<Button href='/'>← Back</Button>
|
||||
</div>
|
||||
<BlogList posts={posts} settings={settings} />
|
||||
</main>
|
||||
</Layout>
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import Layout from '../layouts/Default.astro';
|
||||
import { getAllTags, getSettings, invariant } from "@matthiesenxyz/astro-ghostcms/api";
|
||||
import TagSummaryCard from '../components/generic/TagSummaryCard.astro';
|
||||
const { tags } = await getAllTags();
|
||||
const settings = await getSettings();
|
||||
invariant(settings, 'Settings not found');
|
||||
const title = settings.title;
|
||||
const description = settings.description;
|
||||
|
||||
---
|
||||
|
||||
<Layout
|
||||
title='Tags'
|
||||
pageTitle=`${title} | Tags`
|
||||
description={description}
|
||||
>
|
||||
<main class='bg-pink p-6'>
|
||||
{
|
||||
tags
|
||||
.filter((tag) => tag.slug && !tag.slug.startsWith("hash-"))
|
||||
.map((tag) => (
|
||||
<TagSummaryCard tag={tag}, settings={settings} />
|
||||
<br />
|
||||
))
|
||||
}
|
||||
</main>
|
||||
</Layout>
|
|
@ -0,0 +1,121 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.card-shadow {
|
||||
filter: drop-shadow(7px 7px 0 rgb(0 0 0 / 1));
|
||||
transition: all;
|
||||
transition-duration: 0.5s;
|
||||
animation: ease-in-out;
|
||||
}
|
||||
.card-shadow:hover {
|
||||
filter: drop-shadow(5px 5px 0 rgb(0 0 0 / 1));
|
||||
}
|
||||
|
||||
:root
|
||||
{ /* ---- ⚫️ Colors ⚪️ ---- */
|
||||
/* Base Tokens */
|
||||
--sys-color-white: #FFFFFF;
|
||||
--sys-color-black: #000000;
|
||||
|
||||
/* Theme Tokens */
|
||||
--primary: var(--sys-color-white);
|
||||
--secondary: var(--sys-color-black);
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 22px;
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: linear-gradient(45deg, var(--secondary) 25%, transparent 25%, transparent 75%, var(--secondary) 75%, var(--secondary)), linear-gradient(45deg, var(--secondary) 25%, transparent 25%, transparent 75%, var(--secondary) 75%, var(--secondary));
|
||||
background-color: var(--primary);
|
||||
background-size: 4px 4px;
|
||||
background-position: 0 0, 2px 2px;
|
||||
width: 10px;
|
||||
border-left: 3px solid var(--secondary);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
width: 20px;
|
||||
box-sizing: content-box;
|
||||
background-color: var(--primary);
|
||||
border: 2px solid var(--secondary);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button:horizontal:start:decrement,
|
||||
::-webkit-scrollbar-button:horizontal:end:increment,
|
||||
::-webkit-scrollbar-button:vertical:start:decrement,
|
||||
::-webkit-scrollbar-button:vertical:end:increment {
|
||||
display: block;
|
||||
}
|
||||
::-webkit-scrollbar-button:vertical:start {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='22' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M.5.5h21v22.375H.5z'/%3E%3Cpath fill='%23000' d='M1 23h20v-2H1zM1.375 12.375h5.5V11h-5.5zM6.875 17.875h6.875V16.5H6.875zM6.875 17.875v-5.5H5.5v5.5zM9.625 5.5V4.125H8.25V5.5zM11 4.125V2.75H9.625v1.375zM19.25 12.375V11h-1.375v1.375zM17.875 11V9.625H16.5V11zM16.5 9.625V8.25h-1.375v1.375zM15.125 8.25V6.875H13.75V8.25zM13.75 6.875V5.5h-1.375v1.375zM12.375 5.5V4.125H11V5.5zM8.25 6.875V5.5H6.875v1.375zM6.875 8.25V6.875H5.5V8.25zM5.5 9.625V8.25H4.125v1.375zM4.125 11V9.625H2.75V11z'/%3E%3Cpath fill='%23000' d='M2.75 12.375V11H1.375v1.375zM15.125 17.875v-5.5H13.75v5.5zM13.75 12.375h5.5V11h-5.5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:vertical:start:active {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='22' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M.5.5h21v22.38H.5z'/%3E%3Cpath fill='%23000' d='M1 23.005h20v-2H1zM1.375 12.378h5.5v-1.375h-5.5zM6.875 17.879h6.875V6.877H6.875zM6.875 17.879v-5.501H5.5v5.5zM9.625 5.501V4.126H8.25v1.375zM11 4.126V2.75H9.625v1.375zM19.25 12.378v-1.375h-1.375v1.375zM17.875 11.002V9.627H13.75v1.375zM16.5 9.627V8.252h-2.75v1.375zM15.125 8.252V6.877H13.75v1.375zM13.75 6.876V5.501h-1.375v1.375zM12.375 5.501V4.126h-2.75v1.375zM12.375 6.876V5.501h-5.5v1.375zM6.875 8.252V6.877H5.5v1.375zM6.875 9.627V8.252h-2.75v1.375zM6.875 11.002V9.627H2.75v1.375z'/%3E%3Cpath fill='%23000' d='M2.75 12.378v-1.375H1.375v1.375zM15.125 17.879v-5.501H13.75v5.5zM13.75 12.378h5.5v-1.375h-5.5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:vertical:end {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='22' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M.5 22.875h21V.5H.5z'/%3E%3Cpath fill='%23000' d='M1 .375h20v2H1zM1.375 11h5.5v1.375h-5.5zM6.875 5.5h6.875v1.375H6.875zM6.875 5.5V11H5.5V5.5zM9.625 17.875v1.375H8.25v-1.375zM11 19.25v1.375H9.625V19.25zM19.25 11v1.375h-1.375V11zM17.875 12.375v1.375H16.5v-1.375zM16.5 13.75v1.375h-1.375V13.75zM15.125 15.125V16.5H13.75v-1.375zM13.75 16.5v1.375h-1.375V16.5zM12.375 17.875v1.375H11v-1.375zM8.25 16.5v1.375H6.875V16.5zM6.875 15.125V16.5H5.5v-1.375zM5.5 13.75v1.375H4.125V13.75zM4.125 12.375v1.375H2.75v-1.375z'/%3E%3Cpath fill='%23000' d='M2.75 11v1.375H1.375V11zM15.125 5.5V11H13.75V5.5zM13.75 11h5.5v1.375h-5.5z'/%3E%3C/svg%3E");
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:vertical:end:active {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='22' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M.5 22.88h21V.5H.5z'/%3E%3Cpath fill='%23000' d='M1 .375h20v2H1zM1.375 11.002h5.5v1.375h-5.5zM6.875 5.501h6.875v11.002H6.875zM6.875 5.501v5.501H5.5v-5.5zM9.625 17.879v1.375H8.25v-1.375zM11 19.254v1.375H9.625v-1.375zM19.25 11.002v1.375h-1.375v-1.375zM17.875 12.378v1.375H13.75v-1.375zM16.5 13.753v1.375h-2.75v-1.375zM15.125 15.128v1.375H13.75v-1.375zM13.75 16.503v1.375h-1.375v-1.375zM12.375 17.879v1.375h-2.75v-1.375zM12.375 16.503v1.375h-5.5v-1.375zM6.875 15.128v1.375H5.5v-1.375zM6.875 13.753v1.375h-2.75v-1.375zM6.875 12.378v1.375H2.75v-1.375z'/%3E%3Cpath fill='%23000' d='M2.75 11.002v1.375H1.375v-1.375zM15.125 5.501v5.501H13.75v-5.5zM13.75 11.002h5.5v1.375h-5.5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:horizontal:start {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='23' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M.813 22.187v-21h22.375v21z'/%3E%3Cpath fill='%23000' d='M23.313 21.688v-20h-2v20zM12.688 21.313v-5.5h-1.376v5.5zM18.188 15.813V8.936h-1.375v6.876z'/%3E%3Cpath fill='%23000' d='M18.188 15.813h-5.5v1.374h5.5zM5.813 13.063H4.438v1.374h1.375zM4.438 11.688H3.063v1.374h1.374zM12.688 3.438h-1.376v1.374h1.376zM11.313 4.813H9.937v1.375h1.376zM9.938 6.188H8.562v1.375h1.376zM8.563 7.563H7.187v1.375h1.375zM7.188 8.938H5.813v1.374h1.375zM5.813 10.313H4.438v1.374h1.375zM7.188 14.438H5.813v1.374h1.375zM8.563 15.813H7.187v1.374h1.375zM9.938 17.188H8.562v1.375h1.376zM11.313 18.563H9.937v1.375h1.376zM12.688 19.938h-1.376v1.375h1.376zM18.188 7.563h-5.5v1.375h5.5z'/%3E%3Cpath fill='%23000' d='M12.688 8.938v-5.5h-1.376v5.5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:horizontal:start:active {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='23' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M.81 22.19v-21h22.38v21z'/%3E%3Cpath fill='%23000' d='M23.315 21.69v-20h-2v20zM12.688 21.315v-5.5h-1.375v5.5zM18.189 15.815V8.94H7.187v6.875z'/%3E%3Cpath fill='%23000' d='M18.189 15.815h-5.501v1.375h5.5zM5.811 13.065H4.436v1.375h1.375zM4.436 11.69H3.06v1.375h1.375zM12.688 3.44h-1.375v1.375h1.375zM11.312 4.815H9.937V8.94h1.375zM9.937 6.19H8.562v2.75h1.375z'/%3E%3Cpath fill='%23000' d='M8.562 7.565H7.187V8.94h1.375zM7.186 8.94H5.811v1.375h1.375zM5.811 10.315H4.436v2.75h1.375zM7.186 10.315H5.811v5.5h1.375zM8.562 15.815H7.187v1.375h1.375z'/%3E%3Cpath fill='%23000' d='M9.937 15.815H8.562v2.75h1.375zM11.312 15.815H9.937v4.125h1.375zM12.688 19.94h-1.375v1.375h1.375zM18.189 7.565h-5.501V8.94h5.5z'/%3E%3Cpath fill='%23000' d='M12.688 8.94v-5.5h-1.375v5.5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:horizontal:end {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='23' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M23.188 22.188v-21H.813v21z'/%3E%3Cpath fill='%23000' d='M.688 21.687v-20h2v20zM11.313 21.312v-5.5h1.375v5.5zM5.813 15.812V8.937h1.375v6.875zM5.813 15.813h5.5v1.375h-5.5zM18.188 13.063h1.375v1.375h-1.375zM19.563 11.688h1.375v1.375h-1.375zM11.313 3.438h1.375v1.375h-1.375zM12.688 4.813h1.375v1.375h-1.375zM14.063 6.188h1.375v1.375h-1.375zM15.438 7.563h1.375v1.375h-1.375zM16.813 8.938h1.375v1.375h-1.375zM18.188 10.313h1.375v1.375h-1.375zM16.813 14.438h1.375v1.375h-1.375zM15.438 15.813h1.375v1.375h-1.375zM14.063 17.188h1.375v1.375h-1.375zM12.688 18.563h1.375v1.375h-1.375z'/%3E%3Cpath fill='%23000' d='M11.313 19.938h1.375v1.375h-1.375zM5.813 7.563h5.5v1.375h-5.5zM11.313 8.937v-5.5h1.375v5.5z'/%3E%3C/svg%3E");
|
||||
height: 23.38px;
|
||||
}
|
||||
::-webkit-scrollbar-button:horizontal:end:active {
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='23' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' stroke='%23000' d='M23.19 22.19v-21H.81v21z'/%3E%3Cpath fill='%23000' d='M.685 21.69v-20h2v20zM11.312 21.315v-5.5h1.375v5.5zM5.811 15.815V8.94h11.002v6.875z'/%3E%3Cpath fill='%23000' d='M5.811 15.815h5.501v1.375H5.811zM18.189 13.065h1.375v1.375h-1.375zM19.564 11.69h1.375v1.375h-1.375zM11.312 3.44h1.375v1.375h-1.375zM12.688 4.815h1.375V8.94h-1.375zM14.063 6.19h1.375v2.75h-1.375zM15.438 7.565h1.375V8.94h-1.375zM16.814 8.94h1.375v1.375h-1.375zM18.189 10.315h1.375v2.75h-1.375zM16.814 10.315h1.375v5.5h-1.375zM15.438 15.815h1.375v1.375h-1.375zM14.063 15.815h1.375v2.75h-1.375zM12.688 15.815h1.375v4.125h-1.375zM11.312 19.94h1.375v1.375h-1.375zM5.811 7.565h5.501V8.94H5.811z'/%3E%3Cpath fill='%23000' d='M11.312 8.94v-5.5h1.375v5.5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
height: 23.38px;
|
||||
}
|
||||
|
||||
|
||||
.window-pane::-webkit-scrollbar {
|
||||
width: 22px;
|
||||
background-color: var(--primary);
|
||||
}
|
||||
.window-pane::-webkit-scrollbar-track {
|
||||
background: linear-gradient(45deg, var(--secondary) 25%, transparent 25%, transparent 75%, var(--secondary) 75%, var(--secondary)), linear-gradient(45deg, var(--secondary) 25%, transparent 25%, transparent 75%, var(--secondary) 75%, var(--secondary));
|
||||
background-color: var(--primary);
|
||||
background-size: 4px 4px;
|
||||
background-position: 0 0, 2px 2px;
|
||||
width: 10px;
|
||||
border-left: 4px solid var(--secondary);
|
||||
}
|
||||
.window-pane::-webkit-scrollbar-thumb {
|
||||
width: 20px;
|
||||
box-sizing: content-box;
|
||||
background-color: var(--primary);
|
||||
border: 2px solid var(--secondary);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
::selection
|
||||
{
|
||||
background-color: aliceblue;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
export const getGhostImgPath = (
|
||||
baseUrl: string,
|
||||
imgUrl: string,
|
||||
width = 0
|
||||
): string => {
|
||||
if (!imgUrl) return "";
|
||||
if (!imgUrl.startsWith(baseUrl)) {
|
||||
return imgUrl;
|
||||
}
|
||||
const relativePath = imgUrl.substring(`${baseUrl}content/images`.length);
|
||||
const cleanedBaseUrl = baseUrl.replace(/\/~/, "");
|
||||
if (width && width > 0) {
|
||||
return `${cleanedBaseUrl}content/images/size/w${width}/${relativePath}`;
|
||||
}
|
||||
return `${cleanedBaseUrl}content/images/${width}${relativePath}`;
|
||||
};
|
||||
|
||||
export const truncate = (input: string, size: number): string =>
|
||||
input.length > size ? `${input.substring(0, size)}...` : input;
|
||||
|
||||
export const formatDate = (dateInput: string): string => {
|
||||
const dateObject = new Date(dateInput);
|
||||
return dateObject.toDateString();
|
||||
};
|
||||
|
||||
export const uniqWith = <T>(
|
||||
arr: Array<T>,
|
||||
fn: (element: T, step: T) => number
|
||||
): Array<T> =>
|
||||
arr.filter(
|
||||
(element, index) => arr.findIndex((step) => fn(element, step)) === index
|
||||
);
|
|
@ -1,17 +1,20 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
import ghostcms from "@matthiesenxyz/astro-ghostcms";
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
//import tailwind from "@astrojs/tailwind";
|
||||
import UnoCSS from 'unocss/astro';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://demo.astro-ghostcms.xyz/",
|
||||
integrations: [tailwind(),
|
||||
integrations: [
|
||||
//tailwind(),
|
||||
UnoCSS({ injectReset: true }),
|
||||
ghostcms({
|
||||
disable404: false,
|
||||
disableRSS: false,
|
||||
disableRouteInjection: false,
|
||||
disableConsoleOutput: false,
|
||||
theme: "@matthiesenxyz/astro-ghostcms-catppuccin",
|
||||
theme: "@matthiesenxyz/astro-ghostcms-brutalbyelian",
|
||||
ghostURL: "https://ghostdemo.matthiesen.xyz",
|
||||
})
|
||||
],
|
||||
|
|
|
@ -14,11 +14,15 @@
|
|||
"astro": "^4.3.2",
|
||||
"@matthiesenxyz/astro-ghostcms-theme-default": "workspace:*",
|
||||
"@matthiesenxyz/astro-ghostcms-catppuccin": "workspace:*",
|
||||
"@matthiesenxyz/astro-ghostcms-brutalbyelian": "workspace:*",
|
||||
"@matthiesenxyz/astro-ghostcms": "workspace:*",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@unocss/astro": "^0.57.7",
|
||||
"tailwindcss": "^3.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
"@unocss/reset": "^0.57.7",
|
||||
"typescript": "^5.3.3",
|
||||
"unocss": "^0.57.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import brutalTheme from '@matthiesenxyz/astro-ghostcms-brutalbyelian';
|
||||
import { defineConfig } from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
presets: [ brutalTheme() ],
|
||||
});
|
1143
pnpm-lock.yaml
1143
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue