mirror of
https://github.com/ChenQihan666/MyBlog-Next.git
synced 2026-08-14 11:29:33 +08:00
feat: improve accessibility including voiceover
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# Except these files & folders
|
# Except these files & folders
|
||||||
!/src
|
!/src
|
||||||
|
!/public
|
||||||
!/.github
|
!/.github
|
||||||
!tsconfig.json
|
!tsconfig.json
|
||||||
!astro.config.mjs
|
!astro.config.mjs
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
const primaryColorScheme = ""; // "light" | "dark"
|
||||||
|
|
||||||
|
// Get theme data from local storage
|
||||||
|
const currentTheme = localStorage.getItem("theme");
|
||||||
|
|
||||||
|
function getPreferTheme() {
|
||||||
|
// return theme value in local storage if it is set
|
||||||
|
if (currentTheme) return currentTheme;
|
||||||
|
|
||||||
|
// return primary color scheme if it is set
|
||||||
|
if (primaryColorScheme) return primaryColorScheme;
|
||||||
|
|
||||||
|
// return user device's prefer color scheme
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
let themeValue = getPreferTheme();
|
||||||
|
|
||||||
|
function setPreference() {
|
||||||
|
localStorage.setItem("theme", themeValue);
|
||||||
|
reflectPreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reflectPreference() {
|
||||||
|
document.firstElementChild.setAttribute("data-theme", themeValue);
|
||||||
|
|
||||||
|
document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set early so no page flashes / CSS is made aware
|
||||||
|
reflectPreference();
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
// set on load so screen readers can get the latest value on the button
|
||||||
|
reflectPreference();
|
||||||
|
|
||||||
|
// now this script can find and listen for clicks on the control
|
||||||
|
document.querySelector("#theme-btn").addEventListener("click", () => {
|
||||||
|
themeValue = themeValue === "light" ? "dark" : "light";
|
||||||
|
setPreference();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// sync with system changes
|
||||||
|
window
|
||||||
|
.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
.addEventListener("change", ({ matches: isDark }) => {
|
||||||
|
themeValue = isDark ? "dark" : "light";
|
||||||
|
setPreference();
|
||||||
|
});
|
||||||
@@ -34,7 +34,7 @@ const socialIcons: SocialIcons = {
|
|||||||
<circle cx="12" cy="12" r="3"></circle>
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
<line x1="16.5" y1="7.5" x2="16.5" y2="7.501"></line>
|
<line x1="16.5" y1="7.5" x2="16.5" y2="7.501"></line>
|
||||||
</svg>`,
|
</svg>`,
|
||||||
Linkedin: `<svg
|
LinkedIn: `<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="icon-tabler"
|
class="icon-tabler"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ breadcrumbList[0] === "posts" &&
|
|||||||
|
|
||||||
<nav class="breadcrumb" aria-label="breadcrumb">
|
<nav class="breadcrumb" aria-label="breadcrumb">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">Home</a></li>
|
<li>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<span aria-hidden="true">></span>
|
||||||
|
</li>
|
||||||
{
|
{
|
||||||
breadcrumbList.map((breadcrumb, index) => (
|
breadcrumbList.map((breadcrumb, index) => (
|
||||||
<li>
|
<li>
|
||||||
@@ -36,12 +39,13 @@ breadcrumbList[0] === "posts" &&
|
|||||||
@apply max-w-3xl mx-auto px-4 w-full mt-8 mb-1;
|
@apply max-w-3xl mx-auto px-4 w-full mt-8 mb-1;
|
||||||
}
|
}
|
||||||
.breadcrumb ul li {
|
.breadcrumb ul li {
|
||||||
@apply inline opacity-70 capitalize;
|
@apply inline;
|
||||||
}
|
}
|
||||||
.breadcrumb ul li + li {
|
.breadcrumb ul li a,
|
||||||
@apply text-skin-base before:content-[">"];
|
.breadcrumb ul li span {
|
||||||
|
@apply opacity-70 capitalize;
|
||||||
}
|
}
|
||||||
.breadcrumb ul li:not(:last-child) {
|
.breadcrumb ul li:not(:last-child) a {
|
||||||
@apply hover:opacity-100 hover:before:opacity-70;
|
@apply hover:opacity-100;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import formatDatetime from "@utils/formatDatetime";
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
datetime: string;
|
datetime: string;
|
||||||
size?: "sm" | "lg";
|
size?: "sm" | "lg";
|
||||||
@@ -14,13 +12,39 @@ export default function Datetime({ datetime, size = "sm", className }: Props) {
|
|||||||
className={`${
|
className={`${
|
||||||
size === "sm" ? "scale-90" : "scale-100"
|
size === "sm" ? "scale-90" : "scale-100"
|
||||||
} w-6 h-6 inline-block fill-skin-base`}
|
} w-6 h-6 inline-block fill-skin-base`}
|
||||||
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path>
|
<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>
|
<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>
|
</svg>
|
||||||
|
<span className="sr-only">Posted on:</span>
|
||||||
<span className={`italic ${size === "sm" ? "text-sm" : "text-base"}`}>
|
<span className={`italic ${size === "sm" ? "text-sm" : "text-base"}`}>
|
||||||
{formatDatetime(datetime)}
|
<FormattedDatetime datetime={datetime} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FormattedDatetime = ({ datetime }: { datetime: string }) => {
|
||||||
|
const myDatetime = new Date(datetime);
|
||||||
|
|
||||||
|
const date = myDatetime.toLocaleDateString([], {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
|
||||||
|
const time = myDatetime.toLocaleTimeString([], {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{date}
|
||||||
|
<span aria-hidden="true"> | </span>
|
||||||
|
<span className="sr-only"> at </span>
|
||||||
|
{time}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
+13
-20
@@ -58,13 +58,13 @@ const { activeNav } = Astro.props;
|
|||||||
<div class="button-wrapper">
|
<div class="button-wrapper">
|
||||||
<LinkButton
|
<LinkButton
|
||||||
href="/search"
|
href="/search"
|
||||||
className={`focus-outline p-1 ${
|
className={`focus-outline p-3 sm:p-1 ${
|
||||||
activeNav === "search" ? "active" : ""
|
activeNav === "search" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
ariaLabel="search"
|
ariaLabel="search"
|
||||||
title="Search"
|
title="Search"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg" class="scale-125 sm:scale-100"
|
||||||
><path
|
><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"
|
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>
|
></path>
|
||||||
@@ -76,7 +76,9 @@ const { activeNav } = Astro.props;
|
|||||||
<button
|
<button
|
||||||
id="theme-btn"
|
id="theme-btn"
|
||||||
class="focus-outline"
|
class="focus-outline"
|
||||||
aria-label="switch theme"
|
title="Toggles light & dark"
|
||||||
|
aria-label="auto"
|
||||||
|
aria-live="polite"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" id="moon-svg">
|
<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" />
|
<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" />
|
||||||
@@ -117,16 +119,16 @@ const { activeNav } = Astro.props;
|
|||||||
@apply fill-skin-base w-6 h-6 scale-125;
|
@apply fill-skin-base w-6 h-6 scale-125;
|
||||||
}
|
}
|
||||||
nav {
|
nav {
|
||||||
@apply w-full py-2 sm:py-0 flex flex-col items-center sm:justify-end sm:flex-row sm:space-x-4 bg-skin-fill;
|
@apply w-full py-2 sm:py-0 sm:pr-4 flex flex-col items-center sm:justify-end sm:flex-row sm:space-x-4 bg-skin-fill;
|
||||||
}
|
}
|
||||||
nav ul {
|
nav ul {
|
||||||
@apply flex flex-col sm:flex-row sm:space-x-4;
|
@apply flex flex-col sm:flex-row sm:space-x-4;
|
||||||
}
|
}
|
||||||
nav li {
|
nav li {
|
||||||
@apply py-2 text-center my-2 sm:my-0;
|
@apply py-3 sm:py-2 text-center my-1 sm:my-0;
|
||||||
}
|
}
|
||||||
nav a {
|
nav a {
|
||||||
@apply py-2 px-3 font-medium hover:text-skin-accent;
|
@apply px-6 py-3 sm:py-2 sm:px-3 font-medium hover:text-skin-accent;
|
||||||
}
|
}
|
||||||
nav a.active {
|
nav a.active {
|
||||||
@apply underline underline-offset-4 decoration-wavy decoration-2;
|
@apply underline underline-offset-4 decoration-wavy decoration-2;
|
||||||
@@ -143,8 +145,12 @@ const { activeNav } = Astro.props;
|
|||||||
nav button svg {
|
nav button svg {
|
||||||
@apply w-6 h-6 fill-skin-base hover:fill-skin-accent;
|
@apply w-6 h-6 fill-skin-base hover:fill-skin-accent;
|
||||||
}
|
}
|
||||||
|
#theme-btn {
|
||||||
|
/* @apply w-12 h-12 sm:w-9 sm:h-9 flex justify-center items-center sm:inline-block; */
|
||||||
|
@apply p-3 sm:p-1;
|
||||||
|
}
|
||||||
#theme-btn svg {
|
#theme-btn svg {
|
||||||
@apply hover:rotate-12;
|
@apply hover:rotate-12 scale-125 sm:scale-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-container {
|
.icon-container {
|
||||||
@@ -165,19 +171,6 @@ const { activeNav } = Astro.props;
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Toggle Theme
|
|
||||||
const themeBtn = document.querySelector("#theme-btn");
|
|
||||||
const htmlClassList = document.querySelector("html")?.classList;
|
|
||||||
themeBtn?.addEventListener("click", function () {
|
|
||||||
if (htmlClassList?.contains("theme-dark")) {
|
|
||||||
localStorage.setItem("theme", "light");
|
|
||||||
htmlClassList?.remove("theme-dark");
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("theme", "dark");
|
|
||||||
htmlClassList?.add("theme-dark");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Toggle menu
|
// Toggle menu
|
||||||
const menuBtn = document.querySelector(".hamburger-menu");
|
const menuBtn = document.querySelector(".hamburger-menu");
|
||||||
const navClassList = document.querySelector("nav")?.classList;
|
const navClassList = document.querySelector("nav")?.classList;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
noPadding?: boolean;
|
noPadding?: boolean;
|
||||||
|
ariaHidden?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { noPadding = false } = Astro.props;
|
const { noPadding = false, ariaHidden = true } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class={`max-w-3xl mx-auto ${noPadding ? "px-0" : "px-4"}`}>
|
<div class={`max-w-3xl mx-auto ${noPadding ? "px-0" : "px-4"}`}>
|
||||||
<hr class="border-skin-line" />
|
<hr class="border-skin-line" aria-hidden={ariaHidden} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ export interface Props {
|
|||||||
className?: string;
|
className?: string;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
tabindex?: string;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { href, className, ariaLabel, title, tabindex } = Astro.props;
|
const { href, className, ariaLabel, title, disabled = false } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
type="button"
|
type="button"
|
||||||
href={href}
|
href={disabled ? "#" : href}
|
||||||
tabindex={tabindex}
|
tabindex={disabled ? "-1" : "0"}
|
||||||
class={`group ${className}`}
|
class={`group ${className}`}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
title={title}
|
title={title}
|
||||||
|
aria-disabled={disabled}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -73,9 +73,8 @@ export default function SearchBar({ searchList }: Props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label className="relative block">
|
<label className="relative block">
|
||||||
<span className="sr-only">Search</span>
|
|
||||||
<span className="absolute inset-y-0 left-0 flex items-center pl-2 opacity-75">
|
<span className="absolute inset-y-0 left-0 flex items-center pl-2 opacity-75">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
<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>
|
<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>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const { centered = false } = Astro.props;
|
|||||||
<LinkButton
|
<LinkButton
|
||||||
href={social.href}
|
href={social.href}
|
||||||
className="link-button"
|
className="link-button"
|
||||||
title={social.name}
|
title={social.linkTitle}
|
||||||
>
|
>
|
||||||
<Fragment set:html={socialIcons[social.name]} />
|
<Fragment set:html={socialIcons[social.name]} />
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
|
|||||||
+20
-1
@@ -20,97 +20,116 @@ export const LOGO_IMAGE = {
|
|||||||
export const SOCIALS: SocialsObject = [
|
export const SOCIALS: SocialsObject = [
|
||||||
{
|
{
|
||||||
name: "Github",
|
name: "Github",
|
||||||
|
linkTitle: ` ${SITE.title} on Github`,
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
active: true,
|
active: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Facebook",
|
name: "Facebook",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Facebook`,
|
||||||
active: true,
|
active: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Instagram",
|
name: "Instagram",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Instagram`,
|
||||||
active: true,
|
active: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Linkedin",
|
name: "LinkedIn",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on LinkedIn`,
|
||||||
active: true,
|
active: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mail",
|
name: "Mail",
|
||||||
href: "mailto:yourmail@gmail.com",
|
href: "mailto:yourmail@gmail.com",
|
||||||
|
linkTitle: `Send an email to ${SITE.title}`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Twitter",
|
name: "Twitter",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Twitter`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Twitch",
|
name: "Twitch",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Twitch`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "YouTube",
|
name: "YouTube",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on YouTube`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WhatsApp",
|
name: "WhatsApp",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on WhatsApp`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Snapchat",
|
name: "Snapchat",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Snapchat`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Pinterest",
|
name: "Pinterest",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Pinterest`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "TikTok",
|
name: "TikTok",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on TikTok`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CodePen",
|
name: "CodePen",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on CodePen`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Discord",
|
name: "Discord",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Discord`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GitLab",
|
name: "GitLab",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on GitLab`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Reddit",
|
name: "Reddit",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Reddit`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Skype",
|
name: "Skype",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Skype`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Steam",
|
name: "Steam",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Steam`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Telegram",
|
name: "Telegram",
|
||||||
href: "https://github.com/satnaing/astro-paper",
|
href: "https://github.com/satnaing/astro-paper",
|
||||||
|
linkTitle: `${SITE.title} on Telegram`,
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -61,45 +61,7 @@ const socialImageURL = new URL(
|
|||||||
onload="this.onload=null;this.rel='stylesheet'"
|
onload="this.onload=null;this.rel='stylesheet'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<script is:inline>
|
<script is:inline src="/toggle-theme.js"></script>
|
||||||
const primaryColorScheme = "none"; // "light" | "dark" | "none"
|
|
||||||
|
|
||||||
const darkModeMediaQuery = window.matchMedia(
|
|
||||||
"(prefers-color-scheme: dark)"
|
|
||||||
).matches;
|
|
||||||
|
|
||||||
// Get theme data from local storage
|
|
||||||
const currentTheme = localStorage.getItem("theme");
|
|
||||||
|
|
||||||
let theme;
|
|
||||||
|
|
||||||
// Set theme to 'theme-dark' if currentTheme is 'dark'
|
|
||||||
if (currentTheme) {
|
|
||||||
theme = currentTheme === "dark" ? "theme-dark" : "";
|
|
||||||
} else {
|
|
||||||
// If primary color scheme is dark
|
|
||||||
// or primary color scheme is not set and prefers-color-scheme is dark
|
|
||||||
// choose dark mode
|
|
||||||
if (
|
|
||||||
primaryColorScheme === "dark" ||
|
|
||||||
(primaryColorScheme === "none" && darkModeMediaQuery)
|
|
||||||
) {
|
|
||||||
theme = "theme-dark";
|
|
||||||
}
|
|
||||||
// If primary color scheme is light
|
|
||||||
// choose light mode
|
|
||||||
else if (primaryColorScheme === "light") {
|
|
||||||
theme = "";
|
|
||||||
}
|
|
||||||
// fallback to prefers-color-scheme
|
|
||||||
else {
|
|
||||||
theme = darkModeMediaQuery ? "theme-dark" : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put dark class on html tag to enable dark mode
|
|
||||||
document.querySelector("html").className = theme;
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -38,9 +38,10 @@ const next = pageNum < totalPages ? "" : "disabled";
|
|||||||
|
|
||||||
<nav class="pagination-wrapper" aria-label="Pagination Navigation">
|
<nav class="pagination-wrapper" aria-label="Pagination Navigation">
|
||||||
<LinkButton
|
<LinkButton
|
||||||
tabindex={prev === "disabled" ? "-1" : "0"}
|
disabled={prev === "disabled"}
|
||||||
href={`/posts${pageNum - 1 !== 1 ? "/" + (pageNum - 1) : ""}`}
|
href={`/posts${pageNum - 1 !== 1 ? "/" + (pageNum - 1) : ""}`}
|
||||||
className={`mr-4 select-none ${prev}`}
|
className={`mr-4 select-none ${prev}`}
|
||||||
|
ariaLabel="Previous"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class={`${prev}-svg`}>
|
<svg xmlns="http://www.w3.org/2000/svg" class={`${prev}-svg`}>
|
||||||
<path
|
<path
|
||||||
@@ -50,9 +51,10 @@ const next = pageNum < totalPages ? "" : "disabled";
|
|||||||
Prev
|
Prev
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
tabindex={next === "disabled" ? "-1" : "0"}
|
disabled={next === "disabled"}
|
||||||
href={`/posts/${pageNum + 1}`}
|
href={`/posts/${pageNum + 1}`}
|
||||||
className={`ml-4 select-none ${next}`}
|
className={`ml-4 select-none ${next}`}
|
||||||
|
ariaLabel="Next"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class={`${next}-svg`}>
|
<svg xmlns="http://www.w3.org/2000/svg" class={`${next}-svg`}>
|
||||||
|
|||||||
+2
-2
@@ -11,8 +11,8 @@ import LinkButton from "@components/LinkButton.astro";
|
|||||||
|
|
||||||
<main id="main-content">
|
<main id="main-content">
|
||||||
<div class="not-found-wrapper">
|
<div class="not-found-wrapper">
|
||||||
<h1>404</h1>
|
<h1 aria-label="404 Not Found">404</h1>
|
||||||
<span>¯\_(ツ)_/¯</span>
|
<span aria-hidden="true">¯\_(ツ)_/¯</span>
|
||||||
<p>Page Not Found</p>
|
<p>Page Not Found</p>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
href="/"
|
href="/"
|
||||||
|
|||||||
+7
-5
@@ -3,7 +3,8 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root,
|
||||||
|
html[data-theme="light"] {
|
||||||
--color-fill: 251, 254, 251;
|
--color-fill: 251, 254, 251;
|
||||||
--color-text-base: 40, 39, 40;
|
--color-text-base: 40, 39, 40;
|
||||||
--color-accent: 0, 108, 172;
|
--color-accent: 0, 108, 172;
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
--color-card-muted: 205, 205, 205;
|
--color-card-muted: 205, 205, 205;
|
||||||
--color-border: 236, 233, 233;
|
--color-border: 236, 233, 233;
|
||||||
}
|
}
|
||||||
.theme-dark {
|
html[data-theme="dark"] {
|
||||||
--color-fill: 47, 55, 65;
|
--color-fill: 47, 55, 65;
|
||||||
--color-text-base: 230, 230, 230;
|
--color-text-base: 230, 230, 230;
|
||||||
--color-accent: 26, 217, 217;
|
--color-accent: 26, 217, 217;
|
||||||
@@ -20,11 +21,11 @@
|
|||||||
--color-border: 59, 70, 85;
|
--color-border: 59, 70, 85;
|
||||||
}
|
}
|
||||||
#sun-svg,
|
#sun-svg,
|
||||||
.theme-dark #moon-svg {
|
html[data-theme="dark"] #moon-svg {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#moon-svg,
|
#moon-svg,
|
||||||
.theme-dark #sun-svg {
|
html[data-theme="dark"] #sun-svg {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@@ -36,7 +37,8 @@
|
|||||||
@apply max-w-3xl mx-auto px-4;
|
@apply max-w-3xl mx-auto px-4;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
@apply outline-offset-1 outline-skin-fill outline-2 focus-visible:outline-dashed focus-visible:no-underline;
|
@apply outline-offset-1 outline-skin-fill outline-2
|
||||||
|
focus-visible:outline-dashed focus-visible:no-underline;
|
||||||
}
|
}
|
||||||
svg {
|
svg {
|
||||||
@apply w-6 h-6 inline-block fill-skin-base group-hover:fill-skin-accent;
|
@apply w-6 h-6 inline-block fill-skin-base group-hover:fill-skin-accent;
|
||||||
|
|||||||
+2
-1
@@ -14,6 +14,7 @@ export type SocialsObject = {
|
|||||||
name: SocialMedia;
|
name: SocialMedia;
|
||||||
href: string;
|
href: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
linkTitle: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
export type SocialIcons = {
|
export type SocialIcons = {
|
||||||
@@ -24,7 +25,7 @@ export type SocialMedia =
|
|||||||
| "Github"
|
| "Github"
|
||||||
| "Facebook"
|
| "Facebook"
|
||||||
| "Instagram"
|
| "Instagram"
|
||||||
| "Linkedin"
|
| "LinkedIn"
|
||||||
| "Mail"
|
| "Mail"
|
||||||
| "Twitter"
|
| "Twitter"
|
||||||
| "Twitch"
|
| "Twitch"
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
const formatDatetime = (datetime: string) => {
|
|
||||||
const myDatetime = new Date(datetime);
|
|
||||||
return (
|
|
||||||
myDatetime.toLocaleDateString([], {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
}) +
|
|
||||||
" | " +
|
|
||||||
myDatetime.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default formatDatetime;
|
|
||||||
Reference in New Issue
Block a user