diff --git a/docs/src/pages/privacy.astro b/docs/src/pages/privacy.astro
index b206858..d55e729 100644
--- a/docs/src/pages/privacy.astro
+++ b/docs/src/pages/privacy.astro
@@ -10,7 +10,7 @@ const title = "Privacy Policy";
Note: We maintain this privacy policy because we believe in transparency
diff --git a/docs/src/scripts/nav.ts b/docs/src/scripts/nav.ts
index b7d1f4e..cdc8187 100644
--- a/docs/src/scripts/nav.ts
+++ b/docs/src/scripts/nav.ts
@@ -1,122 +1,155 @@
-// Handle both download and logo links
-document.querySelectorAll('a[href*="section=download"]').forEach((anchor) => {
- anchor.addEventListener("click", function (this: HTMLAnchorElement, e) {
- const href = this.getAttribute("href");
- const isIndexPage =
- window.location.pathname === "/" ||
- window.location.pathname.endsWith("index.html");
+class SiteNavigation {
+ private mobileButton: HTMLElement | null;
+ private mobileMenu: HTMLElement | null;
+ private logoLink: HTMLElement | null;
+ private mediaQuery: MediaQueryList;
+ private readonly HEADER_HEIGHT = 80;
+ private readonly ADDITIONAL_OFFSET = 200;
+
+ constructor() {
+ this.mobileButton = document.getElementById("mobile-menu-button");
+ this.mobileMenu = document.getElementById("mobile-menu");
+ this.logoLink = document.getElementById("logo-link");
+ this.mediaQuery = window.matchMedia("(min-width: 768px)");
- // Always prevent default if we're already on index page
- if (isIndexPage) {
- e.preventDefault();
- const target = document.querySelector("#download");
+ this.init();
+ }
- if (target) {
- const headerHeight = 80;
- const additionalOffset = 200;
- const totalOffset = headerHeight + additionalOffset;
+ private init(): void {
+ this.setupDownloadLinks();
+ this.setupLogoLink();
+ this.setupMobileMenu();
+ this.handleInitialLoad();
+ }
+
+ private isIndexPage(): boolean {
+ return (
+ window.location.pathname === "/" ||
+ window.location.pathname.endsWith("index.html")
+ );
+ }
- const elementPosition = target.getBoundingClientRect().top;
- const offsetPosition = elementPosition + window.scrollY - totalOffset;
+ private setupDownloadLinks(): void {
+ document
+ .querySelectorAll('a[href*="section=download"]')
+ .forEach((anchor) => {
+ anchor.addEventListener("click", (e: Event) => {
+ const link = e.currentTarget as HTMLAnchorElement; // Use currentTarget instead of casting anchor
+ const href = link.getAttribute("href");
- window.scrollTo({
- top: offsetPosition,
- behavior: "smooth",
+ if (this.isIndexPage() && href) {
+ e.preventDefault();
+ this.scrollToDownload();
+ history.pushState(null, "", href);
+ }
});
+ });
+ }
- // Update URL without reload
- history.pushState(null, "", href);
+ private setupLogoLink(): void {
+ this.logoLink?.addEventListener("click", (e: Event) => {
+ // Changed from MouseEvent to Event
+ if (this.isIndexPage()) {
+ e.preventDefault();
+ this.scrollToTop();
+ history.pushState(null, "", "/");
}
- return;
- }
- });
-});
+ });
+ }
+
+ private scrollToElement(element: Element, offset = 0): void {
+ requestAnimationFrame(() => {
+ const elementPosition = element.getBoundingClientRect().top;
+ const offsetPosition = elementPosition + window.scrollY - offset;
-// Handle logo click
-const logoLink = document.getElementById("logo-link");
-if (logoLink) {
- logoLink.addEventListener("click", function (e) {
- const isIndexPage =
- window.location.pathname === "/" ||
- window.location.pathname.endsWith("index.html");
- if (isIndexPage) {
- e.preventDefault();
window.scrollTo({
- top: 0,
+ top: offsetPosition,
behavior: "smooth",
});
- history.pushState(null, "", "/"); // Fixed null parameter
- }
- // If not on index page, let the normal navigation happen
- });
-}
+ });
+ }
-// Handle initial load with query param
-window.addEventListener("load", function () {
- const params = new URLSearchParams(window.location.search);
- const section = params.get("section");
- if (section === "download") {
+ private scrollToDownload(): void {
const target = document.querySelector("#download");
if (target) {
- const headerHeight = 80;
- const additionalOffset = 200;
- const totalOffset = headerHeight + additionalOffset;
- requestAnimationFrame(() => {
- const elementPosition = target.getBoundingClientRect().top;
- const offsetPosition = elementPosition + window.scrollY - totalOffset;
- window.scrollTo({
- top: offsetPosition,
- behavior: "smooth",
- });
- });
+ const totalOffset = this.HEADER_HEIGHT + this.ADDITIONAL_OFFSET;
+ this.scrollToElement(target, totalOffset);
}
}
-});
-// Mobile menu functionality
-const mobileMenuButton = document.getElementById("mobile-menu-button");
-const mobileMenu = document.getElementById("mobile-menu");
-
-if (mobileMenuButton && mobileMenu) {
- mobileMenuButton.addEventListener("click", () => {
- // Toggle menu visibility
- if (mobileMenu.classList.contains("hidden")) {
- mobileMenu.classList.remove("hidden");
- mobileMenu.classList.add("block");
- } else {
- mobileMenu.classList.add("hidden");
- mobileMenu.classList.remove("block");
- }
- });
-} // Close mobile menu when clicking outside
-document.addEventListener("click", (e: MouseEvent) => {
- const target = e.target as Node;
- if (
- mobileMenu &&
- !mobileMenu.contains(target) &&
- mobileMenuButton &&
- !mobileMenuButton.contains(target)
- ) {
- mobileMenu.classList.add("hidden");
- mobileMenu.classList.remove("block");
+ private scrollToTop(): void {
+ window.scrollTo({
+ top: 0,
+ behavior: "smooth",
+ });
}
-});
-// Close mobile menu when window is resized to desktop size
-window.addEventListener("resize", () => {
- if (window.innerWidth >= 768 && mobileMenu) {
- // 768px is the md breakpoint in Tailwind
- mobileMenu.classList.add("hidden");
- mobileMenu.classList.remove("block");
+ private handleInitialLoad(): void {
+ window.addEventListener("load", () => {
+ const params = new URLSearchParams(window.location.search);
+ if (params.get("section") === "download") {
+ this.scrollToDownload();
+ }
+ });
}
-});
-// Close mobile menu when clicking a link
-if (mobileMenu) {
- mobileMenu.querySelectorAll("a").forEach((link) => {
- link.addEventListener("click", () => {
- mobileMenu.classList.add("hidden");
- mobileMenu.classList.remove("block");
+ private setupMobileMenu(): void {
+ if (!this.mobileButton || !this.mobileMenu) return;
+
+ // Toggle menu
+ this.mobileButton.addEventListener("click", () => {
+ const isExpanded = !this.mobileMenu?.classList.contains("hidden");
+ this.setMenuState(!isExpanded);
});
- });
+
+ // Close on outside click
+ document.addEventListener("click", this.handleClickOutside.bind(this));
+
+ // Handle screen resize
+ this.mediaQuery.addEventListener("change", this.handleResize.bind(this));
+
+ // Close on link click
+ this.mobileMenu.querySelectorAll("a").forEach((link) => {
+ link.addEventListener("click", () => this.setMenuState(false));
+ });
+ }
+
+ private setMenuState(isOpen: boolean): void {
+ if (!this.mobileMenu || !this.mobileButton) return;
+
+ this.mobileMenu.classList.toggle("hidden", !isOpen);
+ this.mobileMenu.classList.toggle("block", isOpen);
+ this.mobileButton.setAttribute("aria-expanded", isOpen.toString());
+
+ if (isOpen) {
+ this.mobileMenu.focus();
+ }
+ }
+
+ private handleClickOutside(event: Event): void {
+ const target = event.target as Node;
+
+ if (
+ !this.mobileMenu?.contains(target) &&
+ !this.mobileButton?.contains(target)
+ ) {
+ this.setMenuState(false);
+ }
+ }
+
+ private handleResize(event: MediaQueryListEvent): void {
+ if (event.matches) {
+ this.setMenuState(false);
+ }
+ }
+
+ public destroy(): void {
+ this.mediaQuery.removeEventListener("change", this.handleResize.bind(this));
+ document.removeEventListener("click", this.handleClickOutside.bind(this));
+ }
}
+
+// Initialize when DOM is ready
+document.addEventListener("DOMContentLoaded", () => {
+ new SiteNavigation();
+});
diff --git a/docs/src/styles/global.scss b/docs/src/styles/global.scss
index bca2088..2500e93 100644
--- a/docs/src/styles/global.scss
+++ b/docs/src/styles/global.scss
@@ -1,4 +1,6 @@
+// CSS Custom Properties
:root {
+ // Colors
--color-text-base: #333333;
--color-text-muted: #555555;
--color-text-inverted: #ffffff;
@@ -6,6 +8,7 @@
--color-bg-card: rgba(255, 255, 255, 0.8);
}
+// Dark theme
.dark {
--color-text-base: #e0e0e0;
--color-text-muted: #a0a0a0;
@@ -14,8 +17,12 @@
--color-bg-card: rgba(255, 255, 255, 0.1);
}
+// Reset & Base Styles
html {
scroll-behavior: smooth;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-size-adjust: 100%;
}
* {
@@ -37,11 +44,14 @@ body {
line-height: 1.47059;
font-weight: 400;
letter-spacing: -0.022em;
+ text-rendering: optimizeLegibility;
+ background-color: var(--color-bg-fill);
+ color: var(--color-text-base);
}
.coming-soon-card {
- opacity: 0.6;
- filter: grayscale(40%) saturate(60%);
+ opacity: 0.75; // Increased from 0.6 for better visibility
+ filter: grayscale(30%); // Reduced grayscale for better appearance
pointer-events: none;
&::before {
@@ -49,14 +59,28 @@ body {
position: absolute;
top: 10px;
right: 10px;
- background-color: #2997ff;
- color: white;
+ background-color: var(
+ --color-text-muted
+ ); // Using theme color for better consistency
+ color: var(--color-bg-fill);
padding: 0.3rem 0.6rem;
font-size: 0.8rem;
border-radius: 0.3rem;
font-weight: bold;
pointer-events: none;
opacity: 1;
+ z-index: 10; // Ensure badge is always visible
+ }
+
+ // Add a subtle overlay to enhance the "disabled" appearance
+ &::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background-color: var(--color-bg-fill);
+ opacity: 0.1;
+ border-radius: 0.75rem;
+ pointer-events: none;
}
}
@@ -71,4 +95,42 @@ body {
font-size: 0.75rem;
font-weight: bold;
pointer-events: none;
+
+ @media (hover: hover) {
+ &:hover {
+ transform: scale(1.05);
+ }
+ }
+}
+
+// Focus Styles
+:focus-visible {
+ outline: 2px solid var(--color-text-base);
+ outline-offset: 2px;
+}
+
+// Selection Styles
+::selection {
+ background-color: var(--color-text-base);
+ color: var(--color-bg-fill);
+}
+
+// Scrollbar Styles
+@media (min-width: 768px) {
+ ::-webkit-scrollbar {
+ width: 10px;
+ }
+
+ ::-webkit-scrollbar-track {
+ background: var(--color-bg-fill);
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background: var(--color-text-muted);
+ border-radius: 5px;
+
+ &:hover {
+ background: var(--color-text-base);
+ }
+ }
}
diff --git a/docs/tailwind.config.mjs b/docs/tailwind.config.mjs
index 98b45af..9cfe42a 100644
--- a/docs/tailwind.config.mjs
+++ b/docs/tailwind.config.mjs
@@ -5,8 +5,8 @@ export default {
theme: {
extend: {
colors: {
- "primary-blue": "#2997ff",
- "deep-blue": "#0066cc",
+ "primary-blue": "#0071e3",
+ "deep-blue": "#004cc2",
"dark-gray": "#1a1a1a",
"light-gray": "#f0f0f0",
"coming-soon": "#71717a",