-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1bb006c
commit 3f16da5
Showing
13 changed files
with
735 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
--- | ||
// Remove current url path and remove trailing slash if exists | ||
const currentUrlPath = Astro.url.pathname.replace(/\/+$/, ""); | ||
// Get url array from path | ||
// eg: /tags/tailwindcss => ['tags', 'tailwindcss'] | ||
const breadcrumbList = currentUrlPath.split("/").slice(1); | ||
// if breadcrumb is Home > Posts > 1 <etc> | ||
// replace Posts with Posts (page number) | ||
breadcrumbList[0] === "posts" && | ||
breadcrumbList.splice(0, 2, `Posts (page ${breadcrumbList[1] || 1})`); | ||
--- | ||
|
||
<nav class="breadcrumb" aria-label="breadcrumb"> | ||
<ul> | ||
<li> | ||
<a href="/">Home</a> | ||
<span aria-hidden="true">></span> | ||
</li> | ||
{ | ||
breadcrumbList.map((breadcrumb, index) => | ||
index + 1 === breadcrumbList.length ? ( | ||
<li> | ||
<span | ||
class={`${index > 0 ? "lowercase" : "capitalize"}`} | ||
aria-current="page" | ||
> | ||
{/* make the last part lowercase in Home > Tags > some-tag */} | ||
{breadcrumb} | ||
</span> | ||
</li> | ||
) : ( | ||
<li> | ||
<a href={`/${breadcrumb}`}>{breadcrumb}</a> | ||
<span aria-hidden="true">></span> | ||
</li> | ||
) | ||
) | ||
} | ||
</ul> | ||
</nav> | ||
|
||
<style> | ||
.breadcrumb { | ||
@apply mx-auto mb-1 mt-8 w-full max-w-3xl px-4; | ||
} | ||
.breadcrumb ul li { | ||
@apply inline; | ||
} | ||
.breadcrumb ul li a { | ||
@apply capitalize opacity-70; | ||
} | ||
.breadcrumb ul li span { | ||
@apply opacity-70; | ||
} | ||
.breadcrumb ul li:not(:last-child) a { | ||
@apply hover:opacity-100; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import Datetime from "./Datetime"; | ||
import type { BlogFrontmatter } from "@content/_schemas"; | ||
|
||
export interface Props { | ||
href?: string; | ||
frontmatter: BlogFrontmatter; | ||
secHeading?: boolean; | ||
} | ||
|
||
export default function Card({ href, frontmatter, secHeading = true }: Props) { | ||
const { title, pubDatetime, description } = frontmatter; | ||
return ( | ||
<li className="my-6"> | ||
<a | ||
href={href} | ||
className="mb-1 inline-block text-xl font-medium text-skin-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0" | ||
> | ||
{secHeading ? ( | ||
<h2 className="text-xl font-medium decoration-dashed hover:underline"> | ||
{title} | ||
</h2> | ||
) : ( | ||
<h3 className="text-xl font-medium decoration-dashed hover:underline"> | ||
{title} | ||
</h3> | ||
)} | ||
</a> | ||
<Datetime datetime={pubDatetime} className="mt-1"/> | ||
<p className="mt-2">{description}</p> | ||
</li> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { LOCALE } from "@config"; | ||
|
||
export interface Props { | ||
datetime: string | Date; | ||
size?: "sm" | "lg"; | ||
className?: string; | ||
} | ||
|
||
export default function Datetime({ datetime, size = "sm", className }: Props) { | ||
return ( | ||
<div className={`flex items-center space-x-2 opacity-80 ${className}`}> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
className={`${ | ||
size === "sm" ? "scale-90" : "scale-100" | ||
} inline-block h-6 w-6 fill-skin-base`} | ||
aria-hidden="true" | ||
> | ||
<path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path> | ||
<path d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2zM19 8l.001 12H5V8h14z"></path> | ||
</svg> | ||
<span className="sr-only">Posted on:</span> | ||
<span className={`italic ${size === "sm" ? "text-sm" : "text-base"}`}> | ||
<FormattedDatetime datetime={datetime} /> | ||
</span> | ||
</div> | ||
); | ||
} | ||
|
||
const FormattedDatetime = ({ datetime }: { datetime: string | Date }) => { | ||
const myDatetime = new Date(datetime); | ||
|
||
const date = myDatetime.toLocaleDateString(LOCALE, { | ||
year: "numeric", | ||
month: "long", | ||
day: "numeric", | ||
}); | ||
|
||
const time = myDatetime.toLocaleTimeString(LOCALE, { | ||
hour: "2-digit", | ||
minute: "2-digit", | ||
}); | ||
|
||
return ( | ||
<> | ||
{date} | ||
{/* <span aria-hidden="true"> | </span> */} | ||
{/* <span className="sr-only"> at </span> */} | ||
{/* {time} */} | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--- | ||
import Hr from "./Hr.astro"; | ||
import Socials from "./Socials.astro"; | ||
const currentYear = new Date().getFullYear(); | ||
export interface Props { | ||
noMarginTop?: boolean; | ||
} | ||
const { noMarginTop = false } = Astro.props; | ||
--- | ||
|
||
<footer class={`${noMarginTop ? "" : "mt-auto"}`}> | ||
<Hr noPadding /> | ||
<div class="footer-wrapper"> | ||
<Socials centered /> | ||
<div class="copyright-wrapper"> | ||
<span>Copyright © {currentYear}</span> | ||
<span class="separator"> | </span> | ||
<span>All rights reserved.</span> | ||
</div> | ||
</div> | ||
</footer> | ||
|
||
<style> | ||
footer { | ||
@apply w-full; | ||
} | ||
.footer-wrapper { | ||
@apply flex flex-col items-center justify-between py-6 sm:flex-row-reverse sm:py-4; | ||
} | ||
.link-button { | ||
@apply my-1 p-2 hover:rotate-6; | ||
} | ||
.link-button svg { | ||
@apply scale-125; | ||
} | ||
.copyright-wrapper { | ||
@apply my-2 flex flex-col items-center whitespace-nowrap sm:flex-row; | ||
} | ||
.separator { | ||
@apply hidden sm:inline; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
--- | ||
import { SITE } from "@config"; | ||
import Hr from "./Hr.astro"; | ||
import LinkButton from "./LinkButton.astro"; | ||
export interface Props { | ||
activeNav?: "posts" | "projects" | "about" | "search"; | ||
} | ||
const { activeNav } = Astro.props; | ||
--- | ||
|
||
<header> | ||
<a id="skip-to-content" href="#main-content">Skip to content</a> | ||
<div class="nav-container"> | ||
<div class="top-nav-wrap"> | ||
<a href="/" class="logo">{SITE.title}</a> | ||
<nav id="nav-menu"> | ||
<button | ||
class="hamburger-menu focus-outline" | ||
aria-label="Open Menu" | ||
aria-expanded="false" | ||
aria-controls="menu-items" | ||
> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
stroke-width="1.5" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
class="menu-icon" | ||
> | ||
<line x1="7" y1="12" x2="21" y2="12" class="line"></line> | ||
<line x1="3" y1="6" x2="21" y2="6" class="line"></line> | ||
<line x1="12" y1="18" x2="21" y2="18" class="line"></line> | ||
<line x1="18" y1="6" x2="6" y2="18" class="close"></line> | ||
<line x1="6" y1="6" x2="18" y2="18" class="close"></line> | ||
</svg> | ||
</button> | ||
<ul id="menu-items" class="display-none sm:flex"> | ||
<li> | ||
<a | ||
href="/projects" | ||
class={activeNav === "projects" ? "active" : ""} | ||
> | ||
Projects | ||
</a> | ||
</li> | ||
<li> | ||
<a href="/posts" class={activeNav === "posts" ? "active" : ""}> | ||
Posts | ||
</a> | ||
</li> | ||
<li> | ||
<a href="/about" class={activeNav === "about" ? "active" : ""}> | ||
About | ||
</a> | ||
</li> | ||
<li> | ||
<LinkButton | ||
href="/search" | ||
className={`focus-outline p-3 sm:p-1 ${ | ||
activeNav === "search" ? "active" : "" | ||
}`} | ||
ariaLabel="search" | ||
title="Search" | ||
> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
class="scale-125 sm:scale-100" | ||
><path | ||
d="M19.023 16.977a35.13 35.13 0 0 1-1.367-1.384c-.372-.378-.596-.653-.596-.653l-2.8-1.337A6.962 6.962 0 0 0 16 9c0-3.859-3.14-7-7-7S2 5.141 2 9s3.14 7 7 7c1.763 0 3.37-.66 4.603-1.739l1.337 2.8s.275.224.653.596c.387.363.896.854 1.384 1.367l1.358 1.392.604.646 2.121-2.121-.646-.604c-.379-.372-.885-.866-1.391-1.36zM9 14c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5z" | ||
></path> | ||
</svg> | ||
</LinkButton> | ||
</li> | ||
<li> | ||
{ | ||
SITE.lightAndDarkMode && ( | ||
<button | ||
id="theme-btn" | ||
class="focus-outline" | ||
title="Toggles light & dark" | ||
aria-label="auto" | ||
aria-live="polite" | ||
> | ||
<svg xmlns="http://www.w3.org/2000/svg" id="moon-svg"> | ||
<path d="M20.742 13.045a8.088 8.088 0 0 1-2.077.271c-2.135 0-4.14-.83-5.646-2.336a8.025 8.025 0 0 1-2.064-7.723A1 1 0 0 0 9.73 2.034a10.014 10.014 0 0 0-4.489 2.582c-3.898 3.898-3.898 10.243 0 14.143a9.937 9.937 0 0 0 7.072 2.93 9.93 9.93 0 0 0 7.07-2.929 10.007 10.007 0 0 0 2.583-4.491 1.001 1.001 0 0 0-1.224-1.224zm-2.772 4.301a7.947 7.947 0 0 1-5.656 2.343 7.953 7.953 0 0 1-5.658-2.344c-3.118-3.119-3.118-8.195 0-11.314a7.923 7.923 0 0 1 2.06-1.483 10.027 10.027 0 0 0 2.89 7.848 9.972 9.972 0 0 0 7.848 2.891 8.036 8.036 0 0 1-1.484 2.059z" /> | ||
</svg> | ||
<svg xmlns="http://www.w3.org/2000/svg" id="sun-svg"> | ||
<path d="M6.993 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007S14.761 6.993 12 6.993 6.993 9.239 6.993 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19h2v3h-2zm0-17h2v3h-2zm-9 9h3v2h-3zm17 0h3v2h-3zM4.219 18.363l2.12-2.122 1.415 1.414-2.12 2.122zM16.24 6.344l2.122-2.122 1.414 1.414-2.122 2.122zM6.342 7.759 4.22 5.637l1.415-1.414 2.12 2.122zm13.434 10.605-1.414 1.414-2.122-2.122 1.414-1.414z" /> | ||
</svg> | ||
</button> | ||
) | ||
} | ||
</li> | ||
</ul> | ||
</nav> | ||
</div> | ||
</div> | ||
<Hr /> | ||
</header> | ||
|
||
<style> | ||
#skip-to-content { | ||
@apply absolute -top-full left-16 z-50 bg-skin-accent px-3 py-2 text-skin-inverted transition-all focus:top-4; | ||
} | ||
.nav-container { | ||
@apply mx-auto flex max-w-3xl flex-col items-center justify-between sm:flex-row; | ||
} | ||
.top-nav-wrap { | ||
@apply relative flex w-full items-start justify-between p-4 sm:items-center sm:py-8; | ||
} | ||
.logo { | ||
@apply absolute py-1 text-xl font-semibold sm:static sm:text-2xl; | ||
} | ||
.hamburger-menu { | ||
@apply self-end p-2 sm:hidden; | ||
} | ||
.hamburger-menu svg { | ||
@apply h-6 w-6 scale-125 fill-skin-base; | ||
} | ||
|
||
nav { | ||
@apply flex w-full flex-col items-center bg-skin-fill sm:ml-2 sm:flex-row sm:justify-end sm:space-x-4 sm:py-0; | ||
} | ||
nav ul { | ||
@apply mt-4 grid w-44 grid-cols-2 grid-rows-4 gap-x-2 gap-y-2 sm:ml-0 sm:mt-0 sm:w-auto sm:gap-x-5 sm:gap-y-0; | ||
} | ||
nav ul li { | ||
@apply col-span-2 flex items-center justify-center; | ||
} | ||
nav ul li a { | ||
@apply w-full px-4 py-3 text-center font-medium hover:text-skin-accent sm:my-0 sm:px-2 sm:py-1; | ||
} | ||
nav ul li:nth-child(4) a { | ||
@apply w-auto; | ||
} | ||
nav ul li:nth-child(4), | ||
nav ul li:nth-child(5) { | ||
@apply col-span-1; | ||
} | ||
nav a.active { | ||
@apply underline decoration-wavy decoration-2 underline-offset-4; | ||
} | ||
nav a.active svg { | ||
@apply fill-skin-accent; | ||
} | ||
|
||
nav button { | ||
@apply p-1; | ||
} | ||
nav button svg { | ||
@apply h-6 w-6 fill-skin-base hover:fill-skin-accent; | ||
} | ||
#theme-btn { | ||
@apply p-3 sm:p-1; | ||
} | ||
#theme-btn svg { | ||
@apply scale-125 hover:rotate-12 sm:scale-100; | ||
} | ||
|
||
.menu-icon line { | ||
@apply transition-opacity duration-75 ease-in-out; | ||
} | ||
.menu-icon .close { | ||
opacity: 0; | ||
} | ||
.menu-icon.is-active .line { | ||
@apply opacity-0; | ||
} | ||
.menu-icon.is-active .close { | ||
@apply opacity-100; | ||
} | ||
</style> | ||
|
||
<script> | ||
// Toggle menu | ||
const menuBtn = document.querySelector(".hamburger-menu"); | ||
const menuIcon = document.querySelector(".menu-icon"); | ||
const menuItems = document.querySelector("#menu-items"); | ||
|
||
menuBtn?.addEventListener("click", () => { | ||
const menuExpanded = menuBtn.getAttribute("aria-expanded") === "true"; | ||
menuIcon?.classList.toggle("is-active"); | ||
menuBtn.setAttribute("aria-expanded", menuExpanded ? "false" : "true"); | ||
menuBtn.setAttribute( | ||
"aria-label", | ||
menuExpanded ? "Open Menu" : "Close Menu" | ||
); | ||
menuItems?.classList.toggle("display-none"); | ||
}); | ||
</script> |
Oops, something went wrong.