···11+---
22+title: Welcome to the Cowsay!
33+date: 2023-10-15
44+summary: Welcome to the cowsay! A simple blog centered around cows.
55+cowsay: Hello World!
66+---
77+88+import CowSay from "@components/blog/CowSay.astro";
99+1010+## Hey there
1111+1212+Welcome to the cowsay! A simple blog centered around cows.
1313+I decided to make this blog as a way to track my progress in learning new things.
1414+I hope you enjoy your stay!
1515+1616+This first post is going into a bit of detail about how I made this blog.
1717+Currently the site uses Astro so I'm going to stick with that for now.
1818+1919+## Making The Basic Blog
2020+2121+Astro has a wonderful feature called the [content framework](https://docs.astro.build/en/guides/content-collections/), an extremely powerful way to easily
2222+create many pages with simple markdown and some frontmatter.
2323+2424+First thing you have to to do is create a folder called `content` in the src directory.
2525+I already had some content setup because of the projects parts of this site.
2626+2727+Inside the content folder you place a `config.ts` which will contain the schemas
2828+for your content's frontmatter. I'll just focus on my blog posts for now.
2929+3030+```ts
3131+const blogPostsCollection = defineCollection({
3232+ schema: z.object({
3333+ title: z.string(),
3434+ date: z.date(),
3535+ summary: z.string(),
3636+ cowsay: z.string()
3737+ })
3838+});
3939+```
4040+4141+This contains the metadata each blog post will need to have in order for my site
4242+to render it. That `cowsay` is a bit special.
4343+4444+Then, we simply export an object named `collections` which Astro will then pick
4545+up and generate TS bindings for.
4646+4747+```ts
4848+export const collections = {
4949+ posts: blogPostsCollection
5050+};
5151+```
5252+5353+Now we can get to writing some content! To do so simply make a folder with the
5454+same name as the _key_ of the collection you want to write for. In this case, `posts`.
5555+5656+Then create a markdown file within and start writing! Here's a little excerpt
5757+of what [this page looks like](https://github.com/Bwc9876/portfolio-site/tree/main/src/content/posts/hello_world.mdx):
5858+5959+```md
6060+---
6161+title: Welcome to the Cowsay!
6262+date: 2023-10-15
6363+summary: Welcome to the cowsay! A simple blog centered around cows.
6464+cowsay: Hello World!
6565+---
6666+6767+## Hey there!
6868+6969+Welcome to the cowsay! A simple blog centered around cows.
7070+I decided to make this blog as a way to track my progress in learning new things.
7171+I hope you enjoy your stay!
7272+```
7373+7474+The frontmatter is the part between the `---` and `---`. This is where you put
7575+metadata for the post. The `cowsay` field is a special one that I made up. It
7676+changes what the cow says in the header of the page. I'll get to that later.
7777+7878+Now that we have some content, we can start writing some code to render it!
7979+8080+I start off by making a `blog` folder in the `src/pages` directory. This is where
8181+all my blog related pages will go. I then make a `index.astro` file which will
8282+be a directory of all posts on the site.
8383+8484+```astro
8585+---
8686+import Layout from "@layouts/Layout.astro";
8787+import { getCollection } from "astro:content";
8888+8989+const blogEntries = await getCollection("posts");
9090+---
9191+9292+<Layout title="The Cowsay - Ben C's Blog">
9393+ <h1>The Cowsay - Ben C's Blog</h1>
9494+ <p>Here you'll find my blog posts, most recent first</p>
9595+ {
9696+ blogEntries.map((p, i) => (
9797+ <>
9898+ {i === 0 && <hr />}
9999+ <hgroup>
100100+ <h2>
101101+ <a href={`/blog/posts/${p.slug}`}>{p.data.title}</a>
102102+ </h2>
103103+ <h3>
104104+ {p.data.date.toLocaleDateString("en-us", {
105105+ weekday: "long",
106106+ year: "numeric",
107107+ month: "short",
108108+ day: "numeric"
109109+ })}
110110+ </h3>
111111+ </hgroup>
112112+ <p>
113113+ {p.data.summary} <a href={`/blog/posts/${p.slug}`}>Read More</a>
114114+ </p>
115115+ <hr />
116116+ </>
117117+ ))
118118+ }
119119+</Layout>
120120+```
121121+122122+
123123+124124+Great! I'll probably fiddle with it in the future but it's a good start. Now we need to make a page for each post.
125125+To make my URLs look nice I'm going to create a subfolder within `blog` called `posts` and then place a `[...slug].astro` in there.
126126+This will allow me to use `getStaticPaths()` to define the paths for each post.
127127+128128+```astro
129129+---
130130+import Layout from "@layouts/Layout.astro";
131131+import { CollectionEntry, getCollection } from "astro:content";
132132+export const getStaticPaths = async () => {
133133+ const posts = await getCollection("posts");
134134+ return posts.map((entry) => ({
135135+ params: { slug: entry.slug },
136136+ props: { entry }
137137+ }));
138138+};
139139+140140+const { entry } = Astro.props as { entry: CollectionEntry<"posts"> };
141141+const { Content } = await entry.render();
142142+---
143143+144144+<Layout title={entry.data.title} description={entry.data.summary}>
145145+ <h1>{entry.data.title}</h1>
146146+ <Content />
147147+</Layout>
148148+149149+<style is:global>
150150+ img {
151151+ border: solid 1px var(--text) !important;
152152+ border-radius: 5px;
153153+ }
154154+</style>
155155+```
156156+157157+Amazing! I'll spare you the image this time since... you're... look at it. But now we have a blog post page!
158158+Now anytime I want to make a new post I just have to make a new markdown file and it'll be rendered on the site.
159159+160160+## An Outline
161161+162162+Now that we have a basic blog, I want to add a few more features to it. First I want to add the ability to see all headers in a post.
163163+This should be pretty easy to do. Astro automatically parses all the headers for us and lets us access them in the `entry` object.
164164+165165+First we need to grab the headings from when we rendered the page:
166166+167167+```js
168168+const { Content, headings } = await entry.render();
169169+```
170170+171171+Then we need to map those to HTML
172172+173173+```astro
174174+<div class="toc">
175175+ <!-- Extra div so we can make it sticky -->
176176+ <div>
177177+ <span>On This Page</span>
178178+ <ul>
179179+ {
180180+ headings.map((h) => (
181181+ <li>
182182+ <a href={`#${h.slug}`}>{h.text}</a>
183183+ </li>
184184+ ))
185185+ }
186186+ </ul>
187187+ </div>
188188+</div>
189189+```
190190+191191+Finally some simple styles and layout
192192+193193+```astro
194194+<style>
195195+ /** Wrapper is going around everything to make the
196196+ table of contents appear on the right of the page **/
197197+ div.wrapper {
198198+ display: flex;
199199+ flex-direction: row;
200200+ gap: 4rem;
201201+ }
202202+203203+ div.toc {
204204+ width: 100%;
205205+ }
206206+207207+ div.toc div {
208208+ top: 5rem;
209209+ margin-top: 1rem;
210210+ position: sticky;
211211+ }
212212+213213+ div.toc ul li {
214214+ list-style-type: none;
215215+ }
216216+</style>
217217+```
218218+219219+Finally, I want to make sure on smaller screen sizes the table of contents appears at the top rather than the side so it doesn't take up too much space.
220220+221221+```css
222222+div.wrapper {
223223+ display: flex;
224224+ flex-direction: column-reverse;
225225+ gap: 1rem;
226226+}
227227+228228+@media (min-width: 1200px) {
229229+ div.wrapper {
230230+ flex-direction: row;
231231+ gap: 4rem;
232232+ }
233233+}
234234+```
235235+236236+
237237+238238+## The Cowsay
239239+240240+Now for the fun part. I want to make a little cow that says something in the header of each post. I'm going to use the `cowsay` field in the frontmatter to do this.
241241+I'll also provide a CowSay component that will render the cow and the text. This way I can use it MDX for admonitions.
242242+243243+First I need to make a component that will render the cow. I'm going to use the [cowsay](https://www.npmjs.com/package/cowsay) package to do this.
244244+245245+```astro
246246+---
247247+import * as cowsay from "cowsay";
248248+249249+type Props = {
250250+ color?: "warn" | "info";
251251+} & cowsay.IOptions;
252252+253253+const { color, ...cowOptions } = Astro.props;
254254+255255+const cowText = cowsay.say(cowOptions);
256256+---
257257+258258+<pre class={color}>
259259+{cowText}
260260+</pre>
261261+262262+<style>
263263+ pre {
264264+ padding: 1rem;
265265+ }
266266+267267+ pre.warn {
268268+ color: yellow;
269269+ background-color: rgb(25, 25, 0);
270270+ }
271271+272272+ pre.info {
273273+ color: cyan;
274274+ background-color: rgb(0, 25, 25);
275275+ }
276276+</style>
277277+```
278278+279279+Now I can link it up in my blog post page!
280280+281281+```astro
282282+<CowSay text={entry.data.cowsay} />
283283+```
284284+285285+Et voila! A cow that says something in the header of each post! I'll probably make it a bit more fancy in the future but for now it's good enough.
286286+287287+I can also use it for admonitions in MDX!
288288+289289+```mdx
290290+<CowSay color="warn" e="><" text="Warning!" />
291291+<CowSay color="info" e="^^" T="U" text="Info!" />
292292+```
293293+294294+<CowSay color="warn" e="><" text="Warning!" />
295295+<CowSay color="info" e="^^" T="U" text="Info!" />
296296+297297+I'll hold off on making an error one for now. Lest the cows get too angry.
298298+299299+## Conclusion
300300+301301+I'm really happy with how this blog turned out. I'm going to keep working on it and adding new features as I go.
302302+I'm also going to try and write more posts in the future. I hope you enjoyed this one!
303303+304304+<CowSay e="^^" text="Adiós!" />