diff --git a/src/main.tsx b/src/main.tsx
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,18 +1,18 @@
import "~/styles/app.css";
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
-import { QueryClient, QueryClientProvider, } from "@tanstack/react-query";
-import {
- persistQueryClient,
-} from "@tanstack/react-query-persist-client";
-import { createRouter,RouterProvider } from "@tanstack/react-router";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { persistQueryClient } from "@tanstack/react-query-persist-client";
+import { createRouter, RouterProvider } from "@tanstack/react-router";
+import { useSetAtom } from "jotai";
+import { useEffect } from "react";
//import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "./reportWebVitals.ts";
// Import the generated route tree
import { routeTree } from "./routeTree.gen";
-
+import { isAtTopAtom } from "./utils/atoms.ts";
const queryClient = new QueryClient({
defaultOptions: {
@@ -28,7 +28,7 @@
persistQueryClient({
queryClient,
persister: localStoragePersister,
-})
+});
// Create a new router instance
const router = createRouter({
@@ -54,9 +54,10 @@
root.render(
// double queries annoys me
//
-
-
-
+
+
+
+
//
);
}
@@ -65,3 +66,45 @@
// to log results (for example: reportWebVitals(// /*mass comment*/ console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
+
+export default function ScrollTopWatcher() {
+ const setIsAtTop = useSetAtom(isAtTopAtom);
+ useEffect(() => {
+ const meta = document.querySelector('meta[name="theme-color"]');
+ let lastAtTop = window.scrollY === 0;
+ let timeoutId: number | undefined;
+
+ const setVars = (atTop: boolean) => {
+ const root = document.documentElement;
+ root.style.setProperty("--is-top", atTop ? "1" : "0");
+
+ const bg = getComputedStyle(root).getPropertyValue("--header-bg").trim();
+ if (meta && bg) meta.setAttribute("content", bg);
+ setIsAtTop(atTop);
+ };
+
+ const check = () => {
+ const atTop = window.scrollY === 0;
+ if (atTop !== lastAtTop) {
+ lastAtTop = atTop;
+ setVars(atTop);
+ }
+ };
+
+ const handleScroll = () => {
+ if (timeoutId) clearTimeout(timeoutId);
+ timeoutId = window.setTimeout(check, 2);
+ };
+
+ // initialize
+ setVars(lastAtTop);
+ window.addEventListener("scroll", handleScroll, { passive: true });
+
+ return () => {
+ window.removeEventListener("scroll", handleScroll);
+ if (timeoutId) clearTimeout(timeoutId);
+ };
+ }, []);
+
+ return null;
+}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,4 +1,7 @@
import { Link, useRouter } from "@tanstack/react-router";
+import { useAtom } from "jotai";
+
+import { isAtTopAtom } from "~/utils/atoms";
export function Header({
backButtonCallback,
@@ -8,9 +11,10 @@
title?: string;
}) {
const router = useRouter();
+ const [isAtTop] = useAtom(isAtTopAtom);
//const what = router.history.
return (
-
+
{backButtonCallback ? (
- {isRefetching ?
:
}
+
>
);
diff --git a/src/components/UniversalPostRenderer.tsx b/src/components/UniversalPostRenderer.tsx
--- a/src/components/UniversalPostRenderer.tsx
+++ b/src/components/UniversalPostRenderer.tsx
@@ -1248,7 +1248,7 @@
// dont cursor: "pointer",
borderBottomWidth: bottomBorder ? (isQuote ? 0 : 1) : 0,
}}
- className="border-gray-300 dark:border-gray-600"
+ className="border-gray-300 dark:border-gray-800"
>
{isRepost && (
@@ -1521,7 +1521,7 @@
hydrate embeds this deep but the connection here is implicit
todo: idk make this a real part of the embed shim so its not implicit */
<>
-
+
(there is an embed here thats too deep to render)
>
@@ -1544,7 +1544,7 @@
borderBottomWidth: 1,
marginBottom: 8,
}} // important for height animation
- className="text-gray-500 dark:text-gray-400 border-gray-200 dark:border-gray-700"
+ className="text-gray-500 dark:text-gray-400 border-gray-200 dark:border-gray-800 was7"
>
{fullDateTimeFormat(post.indexedAt)}
@@ -1780,7 +1780,7 @@
//boxShadow: theme.cardShadow,
overflow: "hidden",
}}
- className="shadow border border-gray-200 dark:border-gray-700"
+ className="shadow border border-gray-200 dark:border-gray-800 was7"
>
{lightboxIndex !== null && (
{lightboxIndex !== null && (
{lightboxIndex !== null && (
{lightboxIndex !== null && (
{thumb && (
{
e.stopPropagation();
setPlaying(true);
@@ -2768,7 +2768,7 @@
100 / (aspect ? aspect.width / aspect.height : 16 / 9)
}%`, // 16:9 = 56.25%, 4:3 = 75%
}}
- className="border border-gray-200 dark:border-gray-700"
+ className="border border-gray-200 dark:border-gray-800 was7"
>
)}
-
+
{children}
@@ -448,7 +448,7 @@
{agent?.did ? (
-
+
) : (
-
+
@@ -682,8 +682,8 @@
{
onClickCallbback();
@@ -693,7 +693,7 @@
{active ? ActiveIcon : InactiveIcon}
{text}
@@ -732,7 +732,7 @@
{active ? ActiveIcon : InactiveIcon}
{text}
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -10,6 +10,7 @@
agentAtom,
authedAtom,
feedScrollPositionsAtom,
+ isAtTopAtom,
selectedFeedUriAtom,
store,
} from "~/utils/atoms";
@@ -350,12 +351,15 @@
authed && agent && identity?.pds && feedServiceDid;
const isReadyForUnauthedFeed = !authed && selectedFeed;
+
+ const [isAtTop] = useAtom(isAtTopAtom);
+
return (
{savedFeeds.length > 0 ? (
-
+
{savedFeeds.map((item: any, idx: number) => {
const label = item.value.split("/").pop() || item.value;
const isActive = selectedFeed === item.value;
diff --git a/src/styles/app.css b/src/styles/app.css
--- a/src/styles/app.css
+++ b/src/styles/app.css
@@ -86,4 +86,23 @@
}
.font-roboto {
font-family: "Roboto", sans-serif;
+}
+
+:root {
+ --header-bg-light: color-mix(in srgb, var(--color-white) calc(var(--is-top) * 100%), var(--color-gray-50));
+ --header-bg-dark: color-mix(in srgb, var(--color-gray-950) calc(var(--is-top) * 100%), var(--color-gray-900));
+}
+
+:root {
+ --header-bg: var(--header-bg-light);
+}
+@media (prefers-color-scheme: dark) {
+ :root {
+ --header-bg: var(--header-bg-dark);
+ }
+}
+
+:root {
+ --shadow-opacity: calc(1 - var(--is-top));
+ --tw-shadow-header: 0 2px 8px hsl(0 0% 0% / calc(var(--shadow-opacity) * 0.15));
}
\ No newline at end of file
diff --git a/src/utils/atoms.ts b/src/utils/atoms.ts
--- a/src/utils/atoms.ts
+++ b/src/utils/atoms.ts
@@ -21,5 +21,7 @@
{}
);
+export const isAtTopAtom = atom
(true);
+
export const agentAtom = atom(null);
export const authedAtom = atom(false);