Compare commits

...

10 commits

Author SHA1 Message Date
f177bb3857
ci: configure forgejo ci for deployment
All checks were successful
deploy / build (push) Successful in 44s
deploy / deploy (push) Successful in 22s
2026-01-07 12:32:48 -05:00
98c35dabc5
docs: add badges to readme for license and status 2026-01-07 12:31:53 -05:00
39bb755f1e
refactor: update git link to point towards gitdab 2026-01-07 11:16:43 -05:00
a3b0b838c7
fix: add types to date objects 2026-01-04 14:38:03 -05:00
6f537de9e6
refactor: move copyright under toc on desktop 2025-12-20 21:30:05 -05:00
35ac6a7ae1
fix: remove overflow on home page 2025-12-19 01:21:08 -05:00
8e539ffc0f
chore: update flake inputs 2025-12-18 21:51:38 -05:00
e56608c81e
refactor: set theme color and blog desc 2025-12-18 21:30:01 -05:00
2dbadd360f
fix: ensure that anchors are not draggable 2025-12-18 21:11:53 -05:00
9dbaf7c321
refactor: improve svg and img loading 2025-12-18 19:49:13 -05:00
26 changed files with 201 additions and 134 deletions

View file

@ -0,0 +1,53 @@
name: deploy
on:
push:
branches:
- main
paths:
- src/**/*
- public/**/*
- .forgejo/workflows/deploy.yml
- astro.config.mjs
- tsconfig.json
- package.json
- bun.lock
jobs:
build:
runs-on: trixie
env:
TERM: dumb
NIX_INSTALLER_INIT: "none"
NIX_INSTALLER_NO_CONFIRM: "true"
NIX_INSTALLER_ENABLE_FLAKES: "true"
steps:
- name: Checkout repository
uses: https://code.forgejo.org/actions/checkout@v6.0.1
- name: Setup lix toolchain
run: curl -sL https://install.lix.systems/lix | sh -s -- install linux
- name: Install bun dependencies
run: nix develop -c bun install --frozen-lockfile
- name: Build static files for website
run: nix develop -c bun run build
- name: Upload static files
uses: https://code.forgejo.org/forgejo/upload-artifact@v5
with:
name: website-${{ forge.sha }}
retention-days: 1
path: dist
deploy:
needs: [build]
runs-on: trixie
env: { TERM: "dumb" }
container: { image: "node:current" }
steps:
- name: Download static files
uses: https://code.forgejo.org/forgejo/download-artifact@v7
with: { name: "website-${{ forge.sha }}", path: "dist" }
- name: Deploy website to cloudflare
uses: https://github.com/cloudflare/wrangler-action@v3.14.1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=website

View file

@ -1,41 +0,0 @@
stages:
- build
- deploy
workflow:
rules:
- changes:
- src/**/*
- public/**/*
- astro.config.mjs
- .gitlab-ci.yml
- tsconfig.json
- package.json
- flake.lock
- flake.nix
- bun.lock
build:
stage: build
image: alpine:edge
variables:
TERM: "dumb"
NIX_INSTALLER_NO_CONFIRM: "true"
NIX_INSTALLER_ENABLE_FLAKES: "true"
before_script:
- apk add --no-cache curl bash git xz
- curl -sL https://install.lix.systems/lix | sh -s -- install linux --init none
- source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
script:
- nix develop -c bun install
- nix develop -c bun run build
artifacts:
name: website-$CI_COMMIT_SHA
expire_in: 1 day
paths: [dist]
deploy:
stage: deploy
image: node:alpine
needs: [build]
script: npx wrangler pages deploy dist --project-name=website

View file

@ -1,4 +1,4 @@
# Website # website ![license] ![status]
The source code of my personal website and blog, made using the [Astro] static The source code of my personal website and blog, made using the [Astro] static
site generator. site generator.
@ -13,6 +13,9 @@ Due to the nature of the content in this repository, it uses multiple licenses.
All files that are in this repository must follow these licenses. All files that are in this repository must follow these licenses.
[status]: https://badge.hanna.lol/build/hanna/website/main
[license]: https://badge.hanna.lol/license/MPL-2.0
[astro]: https://astro.build [astro]: https://astro.build
[mpl-2.0]: https://choosealicense.com/licenses/mpl-2.0 [mpl-2.0]: https://choosealicense.com/licenses/mpl-2.0
[cc by-nc-nd 4.0]: https://creativecommons.org/licenses/by-nc-nd/4.0 [cc by-nc-nd 4.0]: https://creativecommons.org/licenses/by-nc-nd/4.0

View file

@ -5,4 +5,7 @@ import mdx from '@astrojs/mdx';
export default defineConfig({ export default defineConfig({
integrations: [mdx()], integrations: [mdx()],
image: {
service: { entrypoint: 'astro/assets/services/noop' },
},
}); });

6
flake.lock generated
View file

@ -21,11 +21,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1765903799, "lastModified": 1766025857,
"narHash": "sha256-1wbl0y7U8TvSHxDWME7o92bIspfuhjVaTOs27r54uc4=", "narHash": "sha256-Lav5jJazCW4mdg1iHcROpuXqmM94BWJvabLFWaJVJp0=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e8d16d2186d6ed9f047eb30948e97e7e01886d10", "rev": "def3da69945bbe338c373fddad5a1bb49cf199ce",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar-icon lucide-calendar"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/></svg>

Before

Width:  |  Height:  |  Size: 338 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-smile-plus-icon lucide-smile-plus"><path d="M22 11v1a10 10 0 1 1-9-10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" x2="9.01" y1="9" y2="9"/><line x1="15" x2="15.01" y1="9" y2="9"/><path d="M16 5h6"/><path d="M19 2v6"/></svg>

Before

Width:  |  Height:  |  Size: 424 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail-icon lucide-mail"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"/><rect x="2" y="4" width="20" height="16" rx="2"/></svg>

Before

Width:  |  Height:  |  Size: 324 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-user-icon lucide-file-user"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M16 22a4 4 0 0 0-8 0"/><circle cx="12" cy="15" r="3"/></svg>

Before

Width:  |  Height:  |  Size: 454 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-gamepad-directional-icon lucide-gamepad-directional"><path d="M11.146 15.854a1.207 1.207 0 0 1 1.708 0l1.56 1.56A2 2 0 0 1 15 18.828V21a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2.172a2 2 0 0 1 .586-1.414z"/><path d="M18.828 15a2 2 0 0 1-1.414-.586l-1.56-1.56a1.207 1.207 0 0 1 0-1.708l1.56-1.56A2 2 0 0 1 18.828 9H21a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1z"/><path d="M6.586 14.414A2 2 0 0 1 5.172 15H3a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2.172a2 2 0 0 1 1.414.586l1.56 1.56a1.207 1.207 0 0 1 0 1.708z"/><path d="M9 3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2.172a2 2 0 0 1-.586 1.414l-1.56 1.56a1.207 1.207 0 0 1-1.708 0l-1.56-1.56A2 2 0 0 1 9 5.172z"/></svg>

Before

Width:  |  Height:  |  Size: 822 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-gitlab-icon lucide-gitlab"><path d="m22 13.29-3.33-10a.42.42 0 0 0-.14-.18.38.38 0 0 0-.22-.11.39.39 0 0 0-.23.07.42.42 0 0 0-.14.18l-2.26 6.67H8.32L6.1 3.26a.42.42 0 0 0-.1-.18.38.38 0 0 0-.26-.08.39.39 0 0 0-.23.07.42.42 0 0 0-.14.18L2 13.29a.74.74 0 0 0 .27.83L12 21l9.69-6.88a.71.71 0 0 0 .31-.83Z"/></svg>

Before

Width:  |  Height:  |  Size: 505 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-heart-icon lucide-heart"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"/></svg>

Before

Width:  |  Height:  |  Size: 387 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user-lock-icon lucide-user-lock"><circle cx="10" cy="7" r="4"/><path d="M10.3 15H7a4 4 0 0 0-4 4v2"/><path d="M15 15.5V14a2 2 0 0 1 4 0v1.5"/><rect width="8" height="5" x="13" y="16" rx=".899"/></svg>

Before

Width:  |  Height:  |  Size: 395 B

View file

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 255 KiB

Before After
Before After

1
src/images/calendar.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar-icon lucide-calendar"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/></svg>

After

Width:  |  Height:  |  Size: 346 B

1
src/images/emoji.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-smile-plus-icon lucide-smile-plus"><path d="M22 11v1a10 10 0 1 1-9-10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" x2="9.01" y1="9" y2="9"/><line x1="15" x2="15.01" y1="9" y2="9"/><path d="M16 5h6"/><path d="M19 2v6"/></svg>

After

Width:  |  Height:  |  Size: 432 B

1
src/images/envelope.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail-icon lucide-mail"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"/><rect x="2" y="4" width="20" height="16" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 332 B

1
src/images/file-user.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-user-icon lucide-file-user"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M16 22a4 4 0 0 0-8 0"/><circle cx="12" cy="15" r="3"/></svg>

After

Width:  |  Height:  |  Size: 462 B

1
src/images/games.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-gamepad-directional-icon lucide-gamepad-directional"><path d="M11.146 15.854a1.207 1.207 0 0 1 1.708 0l1.56 1.56A2 2 0 0 1 15 18.828V21a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2.172a2 2 0 0 1 .586-1.414z"/><path d="M18.828 15a2 2 0 0 1-1.414-.586l-1.56-1.56a1.207 1.207 0 0 1 0-1.708l1.56-1.56A2 2 0 0 1 18.828 9H21a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1z"/><path d="M6.586 14.414A2 2 0 0 1 5.172 15H3a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2.172a2 2 0 0 1 1.414.586l1.56 1.56a1.207 1.207 0 0 1 0 1.708z"/><path d="M9 3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2.172a2 2 0 0 1-.586 1.414l-1.56 1.56a1.207 1.207 0 0 1-1.708 0l-1.56-1.56A2 2 0 0 1 9 5.172z"/></svg>

After

Width:  |  Height:  |  Size: 830 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-git-branch-icon lucide-git-branch"><line x1="6" x2="6" y1="3" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>

After

Width:  |  Height:  |  Size: 371 B

1
src/images/heart.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-heart-icon lucide-heart"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"/></svg>

After

Width:  |  Height:  |  Size: 395 B

1
src/images/user-lock.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user-lock-icon lucide-user-lock"><circle cx="10" cy="7" r="4"/><path d="M10.3 15H7a4 4 0 0 0-4 4v2"/><path d="M15 15.5V14a2 2 0 0 1 4 0v1.5"/><rect width="8" height="5" x="13" y="16" rx=".899"/></svg>

After

Width:  |  Height:  |  Size: 403 B

View file

@ -8,6 +8,7 @@ const { title, desc } = Astro.props;
<meta charset="utf-8"> <meta charset="utf-8">
<title>{title}</title> <title>{title}</title>
<meta name="description" content={desc}> <meta name="description" content={desc}>
<meta name="theme-color" content="#2986cc">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
<link rel="stylesheet" href="/css/global.css"> <link rel="stylesheet" href="/css/global.css">
@ -16,13 +17,13 @@ const { title, desc } = Astro.props;
<body> <body>
<div class="navbar"> <div class="navbar">
<div class="left"> <div class="left">
<h1><a href="/">Hanna Rose</a></h1> <h1><a draggable="false" href="/">Hanna Rose</a></h1>
</div> </div>
<div class="right"> <div class="right">
<ul class="pages"> <ul class="pages">
<li><a href="/">Home</a></li> <li><a draggable="false" href="/">Home</a></li>
<li><a href="/blog">Blog</a></li> <li><a draggable="false" href="/blog">Blog</a></li>
<li><a href="https://liberapay.com/hqnna">Donate</a></li> <li><a draggable="false" href="https://liberapay.com/hqnna">Donate</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -11,11 +11,12 @@ export async function getStaticPaths() {
})); }));
} }
const fmtDate = (date) => new Date(date).toLocaleString('en-US', { const fmtDate = (date: Date) =>
month: '2-digit', new Date(date).toLocaleString('en-US', {
day: '2-digit', month: '2-digit',
year: 'numeric' day: '2-digit',
}); year: 'numeric',
});
const { post } = Astro.props; const { post } = Astro.props;
const { Content, headings } = await post.render(); const { Content, headings } = await post.render();
@ -31,27 +32,33 @@ const { Content, headings } = await post.render();
</div> </div>
</div> </div>
<details class="toc" open> <div class="sidebar">
<summary>Table of Contents</summary> <details class="toc" open>
<ul> <summary>Table of Contents</summary>
{headings.filter(h => h.depth > 1 && h.depth < 4).map(h => ( <ul>
<li class={`depth-${h.depth}`}> {headings.filter(h => h.depth > 1 && h.depth < 4).map(h => (
<a href={`#${h.slug}`}>{h.text}</a> <li class={`depth-${h.depth}`}>
</li> <a draggable="false" href={`#${h.slug}`}>{h.text}</a>
))} </li>
</ul> ))}
</details> </ul>
</details>
<p class="copyright">
This blog post is licensed under
<a draggable="false" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">
CC BY-NC-ND 4.0</a>.
</p>
</div>
<section class="article"> <section class="article">
<Content /> <Content />
<p class="copyright">
This article is licensed under the
<a href="https://creativecommons.org/licenses/by-nc-nd/4.0/">
CC BY-NC-ND 4.0
</a>
license.
</p>
</section> </section>
<p class="copyright mobile">
This blog post is licensed under
<a draggable="false" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">
CC BY-NC-ND 4.0</a>.
</p>
</div> </div>
</Page> </Page>
@ -102,16 +109,63 @@ const { Content, headings } = await post.render();
} }
} }
.toc { .sidebar {
position: fixed; position: fixed;
left: max(20px, calc(50% - 24rem - 300px)); left: max(20px, calc(50% - 24rem - 300px));
max-height: calc(100vh - 140px); max-height: calc(100vh - 140px);
width: 250px;
display: flex;
flex-direction: column;
& > .copyright {
color: rgba(194, 200, 204, 0.4);
padding-top: var(--space-sm);
font-size: 10px;
& > a {
color: var(--text-muted);
text-decoration: none;
}
}
@media only screen and (max-width: 1400px) {
position: relative;
max-height: none;
width: 100%;
left: 0;
& > .copyright {
display: none;
}
}
}
.copyright.mobile {
display: none;
color: rgba(194, 200, 204, 0.4);
font-size: 10px;
border-top: 1px solid rgba(194, 200, 204, 0.1);
padding-top: 8px;
margin-top: var(--space-sm);
& > a {
color: var(--text-muted);
text-decoration: none;
}
@media only screen and (max-width: 1400px) {
display: block;
}
}
.toc {
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--bg-secondary); background: var(--bg-secondary);
border-radius: var(--radius); border-radius: var(--radius);
padding: var(--space-sm); padding: var(--space-sm);
overflow-y: auto; overflow-y: auto;
width: 250px; flex: 1;
max-height: calc(100vh - 200px);
& summary { & summary {
font-size: 14px; font-size: 14px;
@ -147,7 +201,6 @@ const { Content, headings } = await post.render();
font-size: 13px; font-size: 13px;
display: block; display: block;
padding: 2px 0; padding: 2px 0;
transition: color 0.2s;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -171,12 +224,9 @@ const { Content, headings } = await post.render();
@media only screen and (max-width: 1400px) { @media only screen and (max-width: 1400px) {
margin-top: 12px; margin-top: 12px;
position: relative;
overflow: visible; overflow: visible;
max-height: none; max-height: none;
width: calc(100% - 22px); flex: none;
left: 0;
top: 0;
&:not([open]) { &:not([open]) {
margin-bottom: 0; margin-bottom: 0;
@ -330,7 +380,8 @@ const { Content, headings } = await post.render();
text-decoration-color: transparent; text-decoration-color: transparent;
text-underline-offset: 2px; text-underline-offset: 2px;
color: var(--accent-blue); color: var(--accent-blue);
transition: text-decoration-color 0.25s ease-out; -webkit-user-drag: none;
user-drag: none;
} }
& :global(a:hover) { & :global(a:hover) {
@ -399,17 +450,5 @@ const { Content, headings } = await post.render();
& :global(blockquote p) { & :global(blockquote p) {
margin: 0px; margin: 0px;
} }
& > .copyright {
border-top: 1px solid rgba(194, 200, 204, 0.1);
color: rgba(194, 200, 204, 0.4);
padding-top: 8px;
font-size: 10px;
& > a {
color: var(--text-muted);
text-decoration: none;
}
}
} }
</style> </style>

View file

@ -1,23 +1,25 @@
--- ---
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import CalendarIcon from '../../images/calendar.svg';
import Page from '../../layouts/Page.astro'; import Page from '../../layouts/Page.astro';
const posts = (await getCollection('posts')) const posts = (await getCollection('posts'))
.filter(post => !post.data.draft) .filter((post) => !post.data.draft)
.sort((a, b) => { .sort((a, b) => {
const firstDate = new Date(a.data.date).valueOf(); const firstDate = new Date(a.data.date).valueOf();
const secondDate = new Date(b.data.date).valueOf(); const secondDate = new Date(b.data.date).valueOf();
return secondDate - firstDate; return secondDate - firstDate;
}); });
const fmtDate = (date) => new Date(date).toLocaleString('en-US', { const fmtDate = (date: Date) =>
month: '2-digit', new Date(date).toLocaleString('en-US', {
day: '2-digit', month: '2-digit',
year: 'numeric' day: '2-digit',
}); year: 'numeric',
});
--- ---
<Page title="Blog" desc=""> <Page title="Blog" desc="A list of all the blog posts I've written">
<div class="content"> <div class="content">
<section> <section>
<h1>Here be dragons.</h1> <h1>Here be dragons.</h1>
@ -31,11 +33,11 @@ const fmtDate = (date) => new Date(date).toLocaleString('en-US', {
<section> <section>
<ul class="posts"> <ul class="posts">
{posts.map((post) => ( {posts.map((post) => (
<li><a href={`/blog/${post.slug}`}> <li><a draggable="false" href={`/blog/${post.slug}`}>
<div class="metadata"> <div class="metadata">
<h1>{post.data.title}</h1> <h1>{post.data.title}</h1>
<p> <p>
<img src="/img/calendar.svg" alt="calendar icon"> <CalendarIcon class="icon" />
{fmtDate(post.data.date)} {fmtDate(post.data.date)}
</p> </p>
</div> </div>
@ -127,9 +129,10 @@ const fmtDate = (date) => new Date(date).toLocaleString('en-US', {
max-height: 23px; max-height: 23px;
color: var(--text-muted); color: var(--text-muted);
& > img { & > .icon {
margin-right: var(--space-xs); margin-right: var(--space-xs);
height: 12px; height: 12px;
width: 12px;
} }
} }
} }

View file

@ -1,42 +1,52 @@
--- ---
import { Image } from 'astro:assets';
import Page from '../layouts/Page.astro'; import Page from '../layouts/Page.astro';
import Avatar from '../images/avatar.png';
import EmojiIcon from '../images/emoji.svg';
import EnvelopeIcon from '../images/envelope.svg';
import FileUserIcon from '../images/file-user.svg';
import UserLockIcon from '../images/user-lock.svg';
import GitBranchIcon from '../images/git-branch.svg';
import GamesIcon from '../images/games.svg';
import HeartIcon from '../images/heart.svg';
const links = [ const links = [
{ {
name: 'GitLab', name: 'GitDab',
icon: 'gitlab', icon: GitBranchIcon,
url: 'https://gitlab.com/hqnna', url: 'https://gitdab.com/hanna',
}, },
{ {
name: 'Emojis', name: 'Emojis',
icon: 'emoji', icon: EmojiIcon,
url: 'https://discord.gg/qZQmTM5Skk', url: 'https://discord.gg/qZQmTM5Skk',
}, },
{ {
name: 'Steam', name: 'Steam',
icon: 'games', icon: GamesIcon,
url: 'https://steamcommunity.com/id/sapphicdoll', url: 'https://steamcommunity.com/id/sapphicdoll',
}, },
{ {
name: 'GPG Key', name: 'GPG Key',
icon: 'user-lock', icon: UserLockIcon,
url: '/files/key.txt', url: '/files/key.txt',
}, },
{ {
name: 'Resume', name: 'Resume',
icon: 'file-user', icon: FileUserIcon,
url: '/files/resume.pdf', url: '/files/resume.pdf',
}, },
{ {
name: 'Contact', name: 'Contact',
icon: 'envelope', icon: EnvelopeIcon,
url: 'mailto:me@hanna.lol', url: 'mailto:me@hanna.lol',
}, },
{ {
name: 'Donate', name: 'Donate',
icon: 'heart', icon: HeartIcon,
url: 'https://liberapay.com/hqnna', url: 'https://liberapay.com/hqnna',
} },
]; ];
--- ---
@ -46,15 +56,15 @@ const links = [
<div class="center"> <div class="center">
<div class="container"> <div class="container">
<div class="details"> <div class="details">
<img src="/img/avatar.png" alt="avatar"> <Image src={Avatar} alt="avatar" loading="eager" />
<div class="about"> <div class="about">
<h1>Hanna Rose</h1> <h1>Hanna Rose</h1>
<h2>Just your average software engineer</h2> <h2>Just your average software engineer</h2>
<ul class="socials"> <ul class="socials">
{links.map(link => ( {links.map(link => (
<li> <li>
<a draggable="false" href={link.url}> <a draggable="false" href={link.url} aria-label={link.name}>
<img src={`/img/${link.icon}.svg`} alt={`${link.name} icon`}> <link.icon class="icon" />
<span class="label">{link.name}</span> <span class="label">{link.name}</span>
</a> </a>
</li> </li>
@ -67,6 +77,10 @@ const links = [
</Page> </Page>
<style> <style>
html > body {
overflow-y: hidden;
}
.background { .background {
background-image: url("/img/background.jpg"); background-image: url("/img/background.jpg");
background-position: center; background-position: center;
@ -87,7 +101,7 @@ const links = [
align-items: center; align-items: center;
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
padding-top: 24px; padding-top: 20px;
max-height: 100%; max-height: 100%;
} }
} }
@ -184,7 +198,7 @@ const links = [
width: 32px; width: 32px;
height: 32px; height: 32px;
& img { & .icon {
width: 16px; width: 16px;
} }
@ -193,16 +207,8 @@ const links = [
border-color: var(--accent-blue); border-color: var(--accent-blue);
color: var(--accent-blue); color: var(--accent-blue);
& img { & .icon {
filter: stroke: var(--accent-blue);
brightness(0)
saturate(100%)
invert(70%)
sepia(37%)
saturate(4975%)
hue-rotate(200deg)
brightness(106%)
contrast(101%);
} }
} }
} }