initial commit from blog template

This commit is contained in:
Sam Tate 2024-08-27 21:34:51 +01:00
commit 758bbf0fa0
47 changed files with 9273 additions and 0 deletions

20
.gitignore vendored Normal file
View file

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

2
.npmrc Normal file
View file

@ -0,0 +1,2 @@
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true

6
.stackblitzrc Normal file
View file

@ -0,0 +1,6 @@
{
"startCommand": "npm start",
"env": {
"ENABLE_CJS_IMPORTS": true
}
}

4
.vscode/extensions.json vendored Normal file
View file

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

11
.vscode/launch.json vendored Normal file
View file

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

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Maxi Ferreira
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

64
README.md Normal file
View file

@ -0,0 +1,64 @@
# [Astro](https://astro.build) Blog Template
[![Screenshot](screenshot.png)](https://astro-blog-template.netlify.app/)
## 👉 Check out the ✨ [Live Demo](https://astro-blog-template.netlify.app/) ✨
## 👩‍🚀 Getting Started
### Locally
```
npm init astro -- --template Charca/astro-blog-template
```
### On StackBlitz
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/charca/astro-blog-template)
## ✨ Features:
- ✅ Astro 4.0
- ✅ Dark Mode
- ✅ Full Markdown support
- ✅ SEO-friendly setup with canonical URLs and OpenGraph data
- ✅ RSS 2.0 generation
- ✅ Sitemap.xml generation
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```
/
├── public/
│ ├── robots.txt
│ └── favicon.ico
├── src/
│ ├── components/
│ │ └── Tour.astro
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :---------------- | :------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3030` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
## 👀 Want to learn more?
Feel free to check [Astro's documentation](https://github.com/withastro/astro) or jump into Astro's [Discord server](https://astro.build/chat).

26
astro.config.mjs Normal file
View file

@ -0,0 +1,26 @@
import { defineConfig } from 'astro/config'
import svelte from '@astrojs/svelte'
import mdx from '@astrojs/mdx'
import remarkGfm from 'remark-gfm'
import remarkSmartypants from 'remark-smartypants'
import rehypeExternalLinks from 'rehype-external-links'
// https://astro.build/config
export default defineConfig({
site: 'https://astro-blog-template.netlify.app',
integrations: [mdx(), svelte()],
markdown: {
shikiConfig: {
theme: 'nord',
},
remarkPlugins: [remarkGfm, remarkSmartypants],
rehypePlugins: [
[
rehypeExternalLinks,
{
target: '_blank',
},
],
],
},
})

7861
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

20
package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "~-projects-cyclesam-site",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/mdx": "^2.0.0",
"@astrojs/svelte": "^5.0.0",
"astro": "^4.0.1",
"reading-time": "^1.5.0",
"rehype-external-links": "^3.0.0",
"remark-gfm": "^4.0.0",
"remark-smartypants": "^2.0.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View file

@ -0,0 +1,93 @@
Copyright 2016 The Merriweather Project Authors (https://github.com/EbenSorkin/Merriweather), with Reserved Font Name "Merriweather".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
public/assets/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
public/assets/social.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

2
public/robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-agent: *
Allow: /

11
sandbox.config.json Normal file
View file

@ -0,0 +1,11 @@
{
"infiniteLoopProtection": true,
"hardReloadOnChange": false,
"view": "browser",
"template": "node",
"container": {
"port": 3000,
"startScript": "start",
"node": "14"
}
}

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View file

@ -0,0 +1,54 @@
---
import { ViewTransitions } from 'astro:transitions'
import '../styles/fonts.css'
import '../styles/global.css'
export interface Props {
title: string
description: string
permalink: string
}
const { title, description, permalink } = Astro.props
const socialUrl = Astro.site.href + 'assets/social.png'
---
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={permalink} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={socialUrl} />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={permalink} />
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={socialUrl} />
<ViewTransitions />
<!-- This is intentionally inlined to avoid FOUC -->
<script is:inline>
const root = document.documentElement
const theme = localStorage.getItem('theme')
if (
theme === 'dark' ||
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
root.classList.add('theme-dark')
} else {
root.classList.remove('theme-dark')
}
</script>

28
src/components/Bio.astro Normal file
View file

@ -0,0 +1,28 @@
<div>
<img src="/assets/profile-pic.webp" alt="Headshot of Maxi Ferreira">
<p>
Hi, I'm <strong>Maxi</strong>. I'm a software engineer and data scientist based in Los Angeles.
You can follow me on <a href="https://www.twitter.com/Charca" target="_blank">Twitter</a>,
see some of my work on <a href="https://www.github.com/Charca" target="_blank">GitHub</a>,
or read more about me on <a href="https://www.maxiferreira.com" target="_blank">my website</a>.
</p>
</div>
<style>
div {
align-items: center;
display: flex;
}
img {
width: 100px;
height: 100px;
border-radius: 100px;
display: block;
margin-right: 20px;
}
p {
font-size: 1.125rem;
}
</style>

View file

@ -0,0 +1,19 @@
<footer>
<span>
&copy; {new Date().getFullYear()} Your Blog.
Powered by <a href="https://astro.build" target="_blank" rel="noopener">Astro</a>.
Template by <a href="https://www.twitter.com/Charca" target="_blank" rel="noopener">Maxi Ferreira</a>.
</span>
</footer>
<style>
footer {
color: var(--text-secondary);
font-size: .8em;
margin: 1em auto;
max-width: 1400px;
padding: 1em 2em;
text-align: center;
width: 100%;
}
</style>

View file

@ -0,0 +1,21 @@
---
import Logo from './Logo.astro'
import Nav from './Nav.astro'
const { current = '' } = Astro.props;
---
<style>
header {
display: flex;
margin: 0 auto;
max-width: 1400px;
padding: 2em;
width: 100%;
}
</style>
<header>
<Logo />
<Nav current={current} />
</header>

16
src/components/Logo.astro Normal file
View file

@ -0,0 +1,16 @@
<style>
img {
display: block;
width: 75px;
}
@media screen and (max-width: 520px) {
img {
display: none;
}
}
</style>
<a href="/">
<img alt="Blog Logo" src="/assets/logo.webp" width="75" height="50" />
</a>

65
src/components/Nav.astro Normal file
View file

@ -0,0 +1,65 @@
---
import ThemeToggleButton from './ThemeToggleButton.svelte';
const { current = '' } = Astro.props;
---
<style>
nav {
align-items: center;
display: flex;
flex: 1;
font-family: var(--font-family-sans);
font-weight: 700;
justify-content: flex-end;
text-transform: uppercase;
}
a {
color: inherit;
text-decoration: none;
padding: 10px 5px;
display: block;
position: relative;
margin-left: 20px;
min-width: 70px;
text-align: center;
}
a:not(.selected) {
opacity: 0.7;
}
a::before {
content: '';
position: absolute;
transition: transform .3s ease;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background: var(--text-secondary);
transform: scaleX(0);
}
a:hover::before,
.selected::before {
transform: scaleX(1);
}
.selected::before {
background: var(--primary-color);
}
.theme-toggle-container {
width: 75px;
}
</style>
<nav>
<a class={current === "" ? "selected" : ""} href='/'>home</a>
<a class={current === "about" ? "selected" : ""} href='/about'>about</a>
<a class={current === "blog" ? "selected" : ""} href='/blog'>blog</a>
<div class="theme-toggle-container">
<ThemeToggleButton client:load />
</div>
</nav>

View file

@ -0,0 +1,65 @@
<script>
const rootEl = typeof document !== 'undefined' ? document.documentElement : null;
const themes = ['light', 'dark'];
let theme = ''
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
theme = localStorage.getItem('theme');
} else if (typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
theme = 'dark';
}
function handleChange(event) {
theme = event.target.value;
localStorage.setItem('theme', theme);
}
$: if (rootEl && theme === 'light') {
rootEl.classList.remove('theme-dark');
} else if (rootEl && theme === 'dark') {
rootEl.classList.add('theme-dark');
}
const icons = [
`<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clip-rule="evenodd"
/>
</svg>`,
`<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
>
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>`,
];
</script>
<div class="theme-toggle">
{#each themes as t, i}
<label class={theme === t ? 'checked' : ''}>
{@html icons[i]}
<input
type="radio"
name="theme-toggle"
checked={theme === t}
value={t}
title={`Use ${t} theme`}
aria-label={`Use ${t} theme`}
on:change={handleChange}
/>
</label>
{/each}
</div>

View file

@ -0,0 +1,11 @@
---
title: Hello World 👋
publishDate: 30 Nov 2021
description: Every blog starts with a single post. This is yours. Make it great.
---
![Illustration of woman using a meditation app](/assets/blog/casual-life-3d-meditation-crystal.webp)
This post intentionally left blank.
Write what you want.

View file

@ -0,0 +1,204 @@
---
title: Markdown Test Page
publishDate: 01 Dec 2021
description: A sample page with the most common elements of an article, including headings, paragraphs, lists, and images. Use it as a starting point for applying your own styles.
---
![Illustration of woman using a computer receiving likes](/assets/blog/casual-life-3d-likes.webp)
This page is an adapted version of [markdown-test-page](https://github.com/fullpipe/markdown-test-page). It should give you an idea of how different elements are styled on this template.
## <a name="top"></a> Table of Contents
- [Headings](#Headings)
- [Paragraphs](#Paragraphs)
- [Blockquotes](#Blockquotes)
- [Lists](#Lists)
- [Horizontal rule](#Horizontal)
- [Table](#Table)
- [Code](#Code)
- [Inline elements](#Inline)
---
# <a name="Headings"></a>Headings
# Heading one
Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor. Aliquip et adipisicing sit sit fugiat commodo id sunt. Nostrud enim ad commodo incididunt cupidatat in ullamco ullamco Lorem cupidatat velit enim et Lorem. Ut laborum cillum laboris fugiat culpa sint irure do reprehenderit culpa occaecat. Exercitation esse mollit tempor magna aliqua in occaecat aliquip veniam reprehenderit nisi dolor in laboris dolore velit.
## Heading two
Aute officia nulla deserunt do deserunt cillum velit magna. Officia veniam culpa anim minim dolore labore pariatur voluptate id ad est duis quis velit dolor pariatur enim. Incididunt enim excepteur do veniam consequat culpa do voluptate dolor fugiat ad adipisicing sit. Labore officia est adipisicing dolore proident eiusmod exercitation deserunt ullamco anim do occaecat velit. Elit dolor consectetur proident sunt aliquip est do tempor quis aliqua culpa aute. Duis in tempor exercitation pariatur et adipisicing mollit irure tempor ut enim esse commodo laboris proident. Do excepteur laborum anim esse aliquip eu sit id Lorem incididunt elit irure ea nulla dolor et. Nulla amet fugiat qui minim deserunt enim eu cupidatat aute officia do velit ea reprehenderit.
### Heading three
Voluptate cupidatat cillum elit quis ipsum eu voluptate fugiat consectetur enim. Quis ut voluptate culpa ex anim aute consectetur dolore proident voluptate exercitation eiusmod. Esse in do anim magna minim culpa sint. Adipisicing ipsum consectetur proident ullamco magna sit amet aliqua aute fugiat laborum exercitation duis et.
#### Heading four
Commodo fugiat aliqua minim quis pariatur mollit id tempor. Non occaecat minim esse enim aliqua adipisicing nostrud duis consequat eu adipisicing qui. Minim aliquip sit excepteur ipsum consequat laborum pariatur excepteur. Veniam fugiat et amet ad elit anim laborum duis mollit occaecat et et ipsum et reprehenderit. Occaecat aliquip dolore adipisicing sint labore occaecat officia fugiat. Quis adipisicing exercitation exercitation eu amet est laboris sunt nostrud ipsum reprehenderit ullamco. Enim sint ut consectetur id anim aute voluptate exercitation mollit dolore magna magna est Lorem. Ut adipisicing adipisicing aliqua ullamco voluptate labore nisi tempor esse magna incididunt.
##### Heading five
Veniam enim esse amet veniam deserunt laboris amet enim consequat. Minim nostrud deserunt cillum consectetur commodo eu enim nostrud ullamco occaecat excepteur. Aliquip et ut est commodo enim dolor amet sint excepteur. Amet ad laboris laborum deserunt sint sunt aliqua commodo ex duis deserunt enim est ex labore ut. Duis incididunt velit adipisicing non incididunt adipisicing adipisicing. Ad irure duis nisi tempor eu dolor fugiat magna et consequat tempor eu ex dolore. Mollit esse nisi qui culpa ut nisi ex proident culpa cupidatat cillum culpa occaecat anim. Ut officia sit ea nisi ea excepteur nostrud ipsum et nulla.
###### Heading six
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
[[Top]](#top)
# <a name="Paragraphs"></a>Paragraphs
Incididunt ex adipisicing ea ullamco consectetur in voluptate proident fugiat tempor deserunt reprehenderit ullamco id dolore laborum. Do laboris laboris minim incididunt qui consectetur exercitation adipisicing dolore et magna consequat magna anim sunt. Officia fugiat Lorem sunt pariatur incididunt Lorem reprehenderit proident irure. Dolore ipsum aliqua mollit ad officia fugiat sit eu aliquip cupidatat ipsum duis laborum laborum fugiat esse. Voluptate anim ex dolore deserunt ea ex eiusmod irure. Occaecat excepteur aliqua exercitation aliquip dolor esse eu eu.
Officia dolore laborum aute incididunt commodo nisi velit est est elit et dolore elit exercitation. Enim aliquip magna id ipsum aliquip consectetur ad nulla quis. Incididunt pariatur dolor consectetur cillum enim velit cupidatat laborum quis ex.
Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in. Ex labore nulla velit officia ullamco Lorem Lorem id do. Dolore ullamco ipsum magna dolor pariatur voluptate ipsum id occaecat ipsum. Dolore tempor quis duis commodo quis quis enim.
[[Top]](#top)
# <a name="Blockquotes"></a>Blockquotes
Ad nisi laborum aute cupidatat magna deserunt eu id laboris id. Aliquip nulla cupidatat sint ex Lorem mollit laborum dolor amet est ut esse aute. Nostrud ex consequat id incididunt proident ipsum minim duis aliqua ut ex et ad quis. Laborum sint esse cillum anim nulla cillum consectetur aliqua sit. Nisi excepteur cillum labore amet excepteur commodo enim occaecat consequat ipsum proident exercitation duis id in.
> Ipsum et cupidatat mollit exercitation enim duis sunt irure aliqua reprehenderit mollit. Pariatur Lorem pariatur laboris do culpa do elit irure. Eiusmod amet nulla voluptate velit culpa et aliqua ad reprehenderit sit ut.
Labore ea magna Lorem consequat aliquip consectetur cillum duis dolore. Et veniam dolor qui incididunt minim amet laboris sit. Dolore ad esse commodo et dolore amet est velit ut nisi ea. Excepteur ea nulla commodo dolore anim dolore adipisicing eiusmod labore id enim esse quis mollit deserunt est. Minim ea culpa voluptate nostrud commodo proident in duis aliquip minim.
> Qui est sit et reprehenderit aute est esse enim aliqua id aliquip ea anim. Pariatur sint reprehenderit mollit velit voluptate enim consectetur sint enim. Quis exercitation proident elit non id qui culpa dolore esse aliquip consequat.
Ipsum excepteur cupidatat sunt minim ad eiusmod tempor sit.
> Deserunt excepteur adipisicing culpa pariatur cillum laboris ullamco nisi fugiat cillum officia. In cupidatat nulla aliquip tempor ad Lorem Lorem quis voluptate officia consectetur pariatur ex in est duis. Mollit id esse est elit exercitation voluptate nostrud nisi laborum magna dolore dolore tempor in est consectetur.
Adipisicing voluptate ipsum culpa voluptate id aute laboris labore esse fugiat veniam ullamco occaecat do ut. Tempor et esse reprehenderit veniam proident ipsum irure sit ullamco et labore ea excepteur nulla labore ut. Ex aute minim quis tempor in eu id id irure ea nostrud dolor esse.
[[Top]](#top)
# <a name="Lists"></a>Lists
### Ordered List
1. Longan
2. Lychee
3. Excepteur ad cupidatat do elit laborum amet cillum reprehenderit consequat quis.
Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip aliquip velit pariatur dolore.
4. Marionberry
5. Melon
- Cantaloupe
- Honeydew
- Watermelon
6. Miracle fruit
7. Mulberry
### Unordered List
- Olive
- Orange
- Blood orange
- Clementine
- Papaya
- Ut aute ipsum occaecat nisi culpa Lorem id occaecat cupidatat id id magna laboris ad duis. Fugiat cillum dolore veniam nostrud proident sint consectetur eiusmod irure adipisicing.
- Passionfruit
[[Top]](#top)
# <a name="Horizontal"></a>Horizontal rule
In dolore velit aliquip labore mollit minim tempor veniam eu veniam ad in sint aliquip mollit mollit. Ex occaecat non deserunt elit laborum sunt tempor sint consequat culpa culpa qui sit. Irure ad commodo eu voluptate mollit cillum cupidatat veniam proident amet minim reprehenderit.
---
In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi. Dolor amet cillum excepteur consequat sint non sint.
[[Top]](#top)
# <a name="Table"></a>Table
Duis sunt ut pariatur reprehenderit mollit mollit magna dolore in pariatur nulla commodo sit dolor ad fugiat. Laboris amet ea occaecat duis eu enim exercitation deserunt ea laborum occaecat reprehenderit. Et incididunt dolor commodo consequat mollit nisi proident non pariatur in et incididunt id. Eu ut et Lorem ea ex magna minim ipsum ipsum do.
| Table Heading 1 | Table Heading 2 | Center align | Right align | Table Heading 5 |
| :-------------- | :-------------- | :----------: | ----------: | :-------------- |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
Minim id consequat adipisicing cupidatat laborum culpa veniam non consectetur et duis pariatur reprehenderit eu ex consectetur. Sunt nisi qui eiusmod ut cillum laborum Lorem officia aliquip laboris ullamco nostrud laboris non irure laboris. Cillum dolore labore Lorem deserunt mollit voluptate esse incididunt ex dolor.
[[Top]](#top)
# <a name="Code"></a>Code
## Inline code
Ad amet irure est magna id mollit Lorem in do duis enim. Excepteur velit nisi magna ea pariatur pariatur ullamco fugiat deserunt sint non sint. Duis duis est `code in text` velit velit aute culpa ex quis pariatur pariatur laborum aute pariatur duis tempor sunt ad. Irure magna voluptate dolore consectetur consectetur irure esse. Anim magna `<strong>in culpa qui officia</strong>` dolor eiusmod esse amet aute cupidatat aliqua do id voluptate cupidatat reprehenderit amet labore deserunt.
## Highlighted
Et fugiat ad nisi amet magna labore do cillum fugiat occaecat cillum Lorem proident. In sint dolor ullamco ad do adipisicing amet id excepteur Lorem aliquip sit irure veniam laborum duis cillum. Aliqua occaecat minim cillum deserunt magna sunt laboris do do irure ea nostrud consequat ut voluptate ex.
```html
<div class="md:flex">
<div class="md:flex-shrink-0">
<img
class="rounded-lg md:w-56"
src="https://images.unsplash.com/photo-1556740738-b6a63e27c4df?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=448&q=80"
alt="Woman paying for a purchase"
/>
</div>
<div class="mt-4 md:mt-0 md:ml-6">
<div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">
Marketing
</div>
<a
href="#"
class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline"
>Finding customers for your new business</a
>
<p class="mt-2 text-gray-600">
Getting a new business off the ground is a lot of hard work. Here are five
ideas you can use to find your first customers.
</p>
</div>
</div>
```
Ex amet id ex aliquip id do laborum excepteur exercitation elit sint commodo occaecat nostrud est. Nostrud pariatur esse veniam laborum non sint magna sit laboris minim in id. Aliqua pariatur pariatur excepteur adipisicing irure culpa consequat commodo et ex id ad.
```html
<script>
let count = 0
function handleClick() {
count += 1
}
</script>
<button on:click="{handleClick}">
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
```
[[Top]](#top)
# <a name="Inline"></a>Inline elements
Sint ea anim ipsum ad commodo cupidatat do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla. Ullamco ad laborum ea qui et anim in laboris exercitation tempor sit officia laborum reprehenderit culpa velit quis. **Consequat commodo** reprehenderit duis [irure](#) esse esse exercitation minim enim Lorem dolore duis irure. Nisi Lorem reprehenderit ea amet excepteur dolor excepteur magna labore proident voluptate ipsum. Reprehenderit ex esse deserunt aliqua ea officia mollit Lorem nulla magna enim. Et ad ipsum labore enim ipsum **cupidatat consequat**. Commodo non ea cupidatat magna deserunt dolore ipsum velit nulla elit veniam nulla eiusmod proident officia.
![Super wide](/assets/blog/casual-life-3d-workspace.webp)
_Proident sit veniam in est proident officia adipisicing_ ea tempor cillum non cillum velit deserunt. Voluptate laborum incididunt sit consectetur Lorem irure incididunt voluptate nostrud. Commodo ut eiusmod tempor cupidatat esse enim minim ex anim consequat. Mollit sint culpa qui laboris quis consectetur ad sint esse. Amet anim anim minim ullamco et duis non irure. Sit tempor adipisicing ea laboris `culpa ex duis sint` anim aute reprehenderit id eu ea. Aute [excepteur proident](#) Lorem minim adipisicing nostrud mollit ad ut voluptate do nulla esse occaecat aliqua sint anim.
<img src="/assets/blog/casual-life-3d-girl-boy-poses.webp" alt="Not so wide" style="float: left" />
Incididunt in culpa cupidatat mollit cillum qui proident sit. In cillum aliquip incididunt voluptate magna amet cupidatat cillum pariatur sint aliqua est _enim **anim** voluptate_. Magna aliquip proident incididunt id duis pariatur eiusmod incididunt commodo culpa dolore sit. Culpa do nostrud elit ad exercitation anim pariatur non minim nisi **adipisicing sunt _officia_**. Do deserunt magna mollit Lorem commodo ipsum do cupidatat mollit enim ut elit veniam ea voluptate. Sint ea anim ipsum ad commodo cupidatat do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla.
<iframe width="100%" height="400" src="https://www.youtube.com/embed/PCp2iXA1uLE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Reprehenderit non eu quis in ad elit esse qui aute id [incididunt](#) dolore cillum. Esse laboris consequat dolor anim exercitation tempor aliqua deserunt velit magna laboris. Culpa culpa minim duis amet mollit do quis amet commodo nulla irure.
[Hello World](/blog/hello-world)

1
src/env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="astro/client" />

View file

@ -0,0 +1,50 @@
---
import BaseHead from '../components/BaseHead.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
export interface Props {
title: string;
description: string;
permalink: string;
current?: string;
}
const { title, description, permalink, current } = Astro.props;
---
<html lang="en">
<head>
<BaseHead title={title} description={description} permalink={permalink} />
</head>
<body>
<div class="layout">
<Header current={current} />
<main>
<slot />
</main>
<Footer />
</div>
</body>
</html>
<style>
.layout {
display: flex;
flex-direction: column;
min-height: 100%;
min-height: 100vh;
}
main {
flex: 1;
position: relative;
margin: 0 auto;
max-width: 1400px;
padding: 1em 2em;
box-sizing: border-box;
width: 100%;
display: flex;
flex-direction: column;
}
</style>

48
src/pages/about.astro Normal file
View file

@ -0,0 +1,48 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
const title = 'About';
const description = 'About your blog.';
const permalink = `${Astro.site.href}about`;
---
<BaseLayout title={title} description={description} permalink={permalink} current="about">
<div class="container">
<h1>About</h1>
<figure class="about-image">
<img src="/assets/about-illustration.webp" alt="Illustration of a notebook" width="330">
<figcaption>
Illustration by
<a href="https://icons8.com/illustrations/author/5c07e68d82bcbc0092519bb6" target="_blank" rel="noopener">Icons 8</a>
from
<a href="https://icons8.com/illustrations" target="_blank" rel="noopener">Ouch!</a>
</figcaption>
</figure>
<p>Text placeholder via <a href="https://jeffsum.com/" target="_blank">Jeffsum</a>.</p>
<p>So you two dig up, dig up dinosaurs? What do they got in there? King Kong? My dad once told me, laugh and the world laughs with you, Cry, and I'll give you something to cry about you little bastard! Life finds a way. God creates dinosaurs. God destroys dinosaurs. God creates Man. Man destroys God. Man creates Dinosaurs.</p>
<p>You really think you can fly that thing? You know what? It is beets. I've crashed into a beet truck. Forget the fat lady! You're obsessed with the fat lady! Drive us out of here! Is this my espresso machine? Wh-what is-h-how did you get my espresso machine?</p>
<p>Hey, you know how I'm, like, always trying to save the planet? Here's my chance. Hey, take a look at the earthlings. Goodbye! I was part of something special. Just my luck, no ice. You're a very talented young man, with your own clever thoughts and ideas. Do you need a manager?</p>
<p>Jaguar shark! So tell me - does it really exist? This thing comes fully loaded. AM/FM radio, reclining bucket seats, and... power windows. Yes, Yes, without the oops! You're a very talented young man, with your own clever thoughts and ideas. Do you need a manager?</p>
<p>Yes, Yes, without the oops! Do you have any idea how long it takes those cups to decompose. They're using our own satellites against us. And the clock is ticking. Do you have any idea how long it takes those cups to decompose. My dad once told me, laugh and the world laughs with you, Cry, and I'll give you something to cry about you little bastard!</p>
</div>
</BaseLayout>
<style>
.about-image {
float: right;
margin: -3em -7em 2em 2em;
max-width: 300px;
}
.about-image img {
border-radius: 8px;
margin-bottom: 1.5em;
}
@media (max-width: 1020px) {
.about-image {
float: none;
margin: 0 auto 2em;
}
}
</style>

View file

@ -0,0 +1,55 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import Bio from '../../components/Bio.astro';
import getPostData from '../../utils/getPostData';
export async function getStaticPaths() {
const posts = await Astro.glob('../../data/blog-posts/*.md');
return posts.map(p => ({
params: { slug: p.file.split('/').pop().split('.').shift() },
props: { post: p },
}));
}
const { Content, frontmatter } = Astro.props.post;
const { title, description, publishDate } = frontmatter;
const { slug, readingTime } = getPostData(Astro.props.post);
const permalink = `${Astro.site.href}blog/${slug}`;
---
<BaseLayout title={title} description={description} permalink={permalink} current="blog">
<header>
<p>{publishDate} ~ {readingTime}</p>
<h1>{title}</h1>
<hr />
</header>
<div class="container">
<article class="content">
<Content />
</article>
<hr />
<Bio />
</div>
</BaseLayout>
<style>
header {
text-align: center;
}
header h1 {
margin-bottom: 0.7em;
}
header p {
color: var(--text-secondary);
text-transform: uppercase;
font-family: var(--font-family-sans);
font-weight: 600;
}
header hr {
min-width: 100px;
width: 30%;
}
</style>

View file

@ -0,0 +1,52 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
const title = 'Blog';
const description = 'Latest articles.';
const permalink = `${Astro.site.href}blog`;
let allPosts = await Astro.glob('../../data/blog-posts/*.md');
allPosts = allPosts.sort((a, b) => new Date(b.frontmatter.publishDate).valueOf() - new Date(a.frontmatter.publishDate).valueOf());
---
<BaseLayout title={title} description={description} permalink={permalink} current="blog">
<div class="container">
<h1>Blog</h1>
{allPosts.map((post, index) => {
const href = `/blog/${post.file.split('/').pop().split('.').shift()}`;
return (
<div>
{ index !== 0 && <hr /> }
<div class="post-item">
<h2>
<a href={href}>{post.frontmatter.title}</a>
</h2>
<p>{post.frontmatter.description}</p>
<div class="post-item-footer">
<span class="post-item-date">— {post.frontmatter.publishDate}</span>
</div>
</div>
</div>
)
})}
</div>
</BaseLayout>
<style>
h2,
.post-item-footer {
font-family: var(--font-family-sans);
font-weight: 700;
}
.post-item-date {
color: var(--text-secondary);
text-align: left;
text-transform: uppercase;
margin-right: 16px;
}
hr {
margin: 60px auto;
}
</style>

99
src/pages/index.astro Normal file
View file

@ -0,0 +1,99 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
const title = 'My Astro Blog';
const description = 'The perfect starter for your perfect blog.';
const permalink = Astro.site.href;
---
<BaseLayout title={title} description={description} permalink={permalink}>
<div class="home-container">
<div class="home-copy">
<h1>Welcome to your new Astro Blog</h1>
<p>Check out the docs on <a href="https://www.github.com/Charca/astro-blog-template" target="_blank" rel="noopener">GitHub</a> to get started.</p>
</div>
<div class="hero-image-container">
<picture>
<source srcset="/assets/home-illustration.webp" media="(min-width: 600px)">
<img class="hero-image" alt="Illustration of person reading a book" src="/assets/home-illustration-small.webp" width="550" height="466">
</picture>
<p class="caption">
Illustration by
<a href="https://icons8.com/illustrations/author/5c07e68d82bcbc0092519bb6" target="_blank" rel="noopener">Icons 8</a>
from
<a href="https://icons8.com/illustrations" target="_blank" rel="noopener">Ouch!</a>
</p>
</div>
</div>
</BaseLayout>
<style>
.home-container {
align-items: center;
display: flex;
flex: 1;
justify-content: center;
margin: 2em 0;
min-height: 400px;
}
.home-copy {
flex: 1;
padding: 0 1em;
}
.home-copy h1 {
font-weight: 700;
margin-bottom: 0.5em;
line-height: 1.3;
}
.home-copy p {
font-size: 1.4em;
}
.hero-image-container {
margin: 0 1em;
text-align: center;
}
.hero-image-container picture {
display: block;
min-height: 250px;
}
.caption {
font-size: .8em;
font-style: italic;
text-align: left;
}
.hero-image {
width: 100%;
max-width: 550px;
margin-bottom: 1em;
}
@media (max-width: 1200px) {
p {
font-size: 1.2em;
}
.hero-image {
max-width: 400px;
}
}
@media (max-width: 800px) {
.home-container {
flex-direction: column;
}
.home-copy {
flex: 0;
padding-bottom: 2em;
text-align: center;
}
}
</style>

36
src/styles/fonts.css Normal file
View file

@ -0,0 +1,36 @@
/* latin */
@font-face {
font-family: 'Fira Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/assets/fonts/va9B4kDNxMZdWfMOD5VnLK3eRhf6Xl7Glw.woff2)
format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Merriweather';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/assets/fonts/u-440qyriQwlOrhSvowK_l5-fCZMdeX3rg.woff2)
format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Merriweather';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/assets/fonts/u-4n0qyriQwlOrhSvowK_l52xwNZWMf6hPvhPQ.woff2)
format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
}

279
src/styles/global.css Normal file
View file

@ -0,0 +1,279 @@
:root {
--background-body: #fff;
--text-main: #36393b;
--text-secondary: #6b6f72;
--primary-color: #548e9b;
--font-family-serif: Merriweather, serif;
--font-family-sans: 'Fira Sans', sans-serif;
}
:root.theme-dark {
--background-body: #202122;
--text-main: #fff;
--text-secondary: #ccc;
--primary-color: #548e9b;
}
html {
overflow-y: scroll;
}
@supports (scrollbar-gutter: stable) {
html {
overflow-y: auto;
scrollbar-gutter: stable;
}
}
*,
*:before,
*:after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--font-family-serif);
font-size: 16px;
line-height: 1.6;
background-color: var(--background-body);
color: var(--text-main);
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-family-sans);
font-weight: 700;
line-height: 1.2;
margin: 0 0 0.5em 0;
}
h1 {
font-family: var(--font-family-serif);
font-size: 4em;
margin: 0 0 1em 0;
}
h2 {
margin: 1.6em 0 0 0;
font-size: 1.8em;
}
h3 {
font-size: 1.5em;
}
h4 {
font-size: 1.4em;
}
h5 {
font-size: 1.3em;
}
h6 {
font-size: 1.2em;
}
p,
ul,
ol {
font-size: 1.3rem;
line-height: 1.75em;
margin: 1.2em 0;
}
ol,
ul {
padding-left: 2rem;
-webkit-padding-start: 5%;
-webkit-padding-end: 5%;
}
li {
margin: 1rem 0;
}
li p {
margin-bottom: 0.5rem;
margin-top: 0.5rem;
}
a {
color: inherit;
transition: color linear 0.15s;
}
a:hover {
color: var(--primary-color);
}
p a {
text-decoration: none;
box-shadow: inset 0 -0.12em 0 var(--primary-color);
-webkit-transition: box-shadow 0.2s ease-in-out, color 0.2s ease-in-out;
transition: box-shadow 0.2s ease-in-out, color 0.2s ease-in-out;
}
p a:hover {
box-shadow: inset 0 -1.5em 0 var(--primary-color);
color: #fff;
}
img {
max-width: 100%;
height: auto;
}
hr {
padding: 0;
border: 0;
height: 10px;
margin: 40px auto;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg width='20' height='10' viewBox='0 0 20 10' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd' clip-rule='evenodd' stroke-miterlimit='10'%3e%3cpath fill='none' d='M0 0h20v10H0z'/%3e%3cclipPath id='a'%3e%3cpath d='M0 0h20v10H0z'/%3e%3c/clipPath%3e%3cg clip-path='url(%23a)'%3e%3cpath d='M20 7.384c-4.999-.001-5-4.768-9.999-4.768C5 2.616 5 7.384 0 7.384' fill='none' stroke-width='3' stroke='%23548E9B'/%3e%3c/g%3e%3c/svg%3e");
}
code {
font-family: 'SF Mono', menlo, inconsolata, monospace;
font-size: calc(1em - 2px);
color: #555;
padding: 0.2em 0.4em;
border-radius: 2px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
pre {
border-radius: 8px !important;
margin: 1.2em 0 !important;
padding: 1.2em;
}
pre code {
padding: 0;
}
blockquote {
border-left: 4px solid #cccccc;
font-size: 1.4em;
font-style: italic;
margin: 2rem 0;
padding-left: 2rem;
padding-right: 2rem;
}
blockquote p {
padding-bottom: 6px;
}
blockquote footer {
font-size: 1.1rem;
text-align: right;
}
figure {
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
margin: 0 0 3em 0;
}
table {
border-collapse: collapse;
font-family: var(--font-family-sans);
font-size: 1.125em;
margin: 2em 0;
}
th {
border-bottom: 2px solid #cccccc;
padding: 0.4em 0.8em;
}
td {
padding: 0.4em 0.8em;
}
.container {
margin: 0 auto;
max-width: 42em;
width: 100%;
}
.content h1 {
font-size: 3em;
margin: 1em 0;
}
.theme-toggle {
display: inline-flex;
align-items: center;
height: 100%;
padding: 0.33em 0.67em;
padding-top: 8px;
margin-left: 10px;
gap: 0.6em;
border-radius: 99em;
background-color: var(--theme-code-inline-bg);
}
.theme-toggle > label:focus-within {
outline: 2px solid transparent;
box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
}
.theme-toggle > label {
color: var(--theme-code-inline-text);
position: relative;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.5;
cursor: pointer;
}
.theme-toggle .checked {
color: var(--theme-accent);
opacity: 1;
}
input[name='theme-toggle'] {
position: absolute;
opacity: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
}
@media (max-width: 1020px) {
h1 {
font-size: 3em;
}
.content h1 {
font-size: 2.4em;
}
}
@media (max-width: 480px) {
body {
font-size: 14px;
}
p,
ul,
ol {
font-size: 1.2rem;
margin: 1em 0;
}
}

14
src/utils/getPostData.ts Normal file
View file

@ -0,0 +1,14 @@
import readingTime from 'reading-time'
type Post = {
title: string
file: string
rawContent: () => string
}
export default function getPostData(post: Post) {
return {
slug: post.file.split('/').pop().split('.').shift(),
readingTime: readingTime(post.rawContent()).text,
}
}

15
tsconfig.json Normal file
View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
// Enable top-level await, and other modern ESM features.
"target": "ESNext",
"module": "ESNext",
// Enable node-style module resolution, for things like npm package imports.
"moduleResolution": "node",
// Enable JSON imports.
"resolveJsonModule": true,
// Enable stricter transpilation for better output.
"isolatedModules": true,
// Astro will directly run your TypeScript code, no transpilation needed.
"noEmit": true
}
}