New Theme!

This commit is contained in:
Adam Matthiesen 2024-02-10 07:43:11 -08:00
parent 237e7d80f5
commit 384066e24b
42 changed files with 2306 additions and 333 deletions

View File

@ -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}}

View File

@ -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(),
]
}
})

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 &rarr;</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>

View File

@ -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>

View File

@ -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'>&larr; 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'>&larr; Back to blog</a>
</article>

View File

@ -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>

View File

@ -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>

View File

@ -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 &rarr;</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>

View File

@ -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'
>&larr; Go Home</a
>
</section>

View File

@ -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>

View File

@ -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: [],
},
]}
/>

View File

@ -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">
&larr; Prev
</a>
)}
{page.url.next && (
<a class="action__go-to-x" href={page.url.next} title="Go to Next">
Next &rarr;
</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 &rarr;</Button>
</div>
</SummaryCard>

View File

@ -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>

View File

@ -0,0 +1,18 @@
[
"#c084fc",
"#f472b6",
"#fb7185",
"#e879f9",
"#a78bfa",
"#818cf8",
"#60a5fa",
"#38bdf8",
"#22d3ee",
"#2dd4bf",
"#34d399",
"#4ade80",
"#a3e635",
"#facc15",
"#fb923c",
"#f87171"
]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

View File

@ -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>

View File

@ -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">&bull;</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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='/'>&larr; Back</Button>
</div>
<BlogList posts={posts} settings={settings} />
</main>
</Layout>

View File

@ -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>

View File

@ -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;
}

View File

@ -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
);

View File

@ -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",
})
],

View File

@ -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"
}
}

6
playground/uno.config.ts Normal file
View File

@ -0,0 +1,6 @@
import brutalTheme from '@matthiesenxyz/astro-ghostcms-brutalbyelian';
import { defineConfig } from 'unocss';
export default defineConfig({
presets: [ brutalTheme() ],
});

File diff suppressed because it is too large Load Diff