diff --git a/apps/blog/src/app/(blog)/layout.tsx b/apps/blog/src/app/(blog)/layout.tsx
index ec90473df7..dd8c53b1ed 100644
--- a/apps/blog/src/app/(blog)/layout.tsx
+++ b/apps/blog/src/app/(blog)/layout.tsx
@@ -1,23 +1,120 @@
-import { HomeLayout } from 'fumadocs-ui/layouts/home';
-import { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
-export function baseOptions(): BaseLayoutProps {
+import { Button } from "@prisma-docs/eclipse";
+import {
+ Logo,
+ NavigationMenu,
+ NavigationMenuContent,
+ NavigationMenuItem,
+ NavigationMenuLink,
+ NavigationMenuList,
+ NavigationMenuTrigger,
+ NavigationWrapper,
+ Socials,
+} from "@prisma-docs/ui/components/navigation-menu";
+import { StarCount } from "@prisma-docs/ui/components/star-count";
+export function baseOptions() {
return {
nav: {
- title: 'My App',
+ title: "My App",
},
links: [
{
- url: '/docs',
- text: 'Docs',
+ text: "Products",
+ sub: [
+ {
+ text: "Postgres",
+ url: "/postgres",
+ desc: "Managed Postgres for global workloads",
+ },
+ {
+ text: "ORM",
+ url: "/orm",
+ },
+ ],
},
{
- url: '/blog',
- text: 'Blog',
+ url: "/pricing",
+ text: "Pricing",
+ },
+ {
+ text: "Resources",
+ sub: [
+ {
+ text: "MCP",
+ url: "/mcp",
+ desc: "Managed Postgres for global workloads",
+ },
+ {
+ text: "Tutorials",
+ url: "/learn",
+ },
+ ],
+ },
+ {
+ url: "/partners",
+ text: "Partners",
+ },
+ {
+ url: "/blog",
+ text: "Blog",
},
],
};
}
-export default function Layout({ children, }: { children: React.ReactNode; }) {
- return {children};
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+ <>
+
+
+
+
+
+ {Logo}
+
+
+
+ {baseOptions().links.map((link: any) =>
+ link.url ? (
+
+
+ {link.text}
+
+
+ ) : link.sub ? (
+
+ {link.text}
+
+ {link.sub.map((sublink: any) => (
+
+ {sublink.text}
+
+ ))}
+
+
+ ) : null,
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+ >
+ );
}
diff --git a/apps/blog/src/app/layout.tsx b/apps/blog/src/app/layout.tsx
index 3f0c7dbe77..bc100fb775 100644
--- a/apps/blog/src/app/layout.tsx
+++ b/apps/blog/src/app/layout.tsx
@@ -1,26 +1,32 @@
-import { RootProvider } from 'fumadocs-ui/provider/next';
-import './global.css';
-import { Inter, Barlow } from 'next/font/google';
+import { RootProvider } from "fumadocs-ui/provider/next";
+import "./global.css";
+import { Inter, Barlow } from "next/font/google";
+import Script from "next/script";
const inter = Inter({
- subsets: ['latin'],
- variable: '--font-inter',
+ subsets: ["latin"],
+ variable: "--font-inter",
});
const barlow = Barlow({
- subsets: ['latin'],
- weight: ['400', '500', '600', '700'],
- variable: '--font-barlow',
+ subsets: ["latin"],
+ weight: ["400", "500", "600", "700"],
+ variable: "--font-barlow",
});
-export default function Layout({ children }: LayoutProps<'/'>) {
+export default function Layout({ children }: LayoutProps<"/">) {
return (
-
+
+
+
{children}
diff --git a/packages/eclipse/src/components/button.tsx b/packages/eclipse/src/components/button.tsx
index a5b1735fff..3e18871bd1 100644
--- a/packages/eclipse/src/components/button.tsx
+++ b/packages/eclipse/src/components/button.tsx
@@ -2,15 +2,15 @@ import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../lib/cn";
-const buttonVariants = cva("border", {
+const buttonVariants = cva("", {
variants: {
variant: {
ppg: "bg-background-ppg-reverse text-foreground-ppg-reverse hover:bg-background-ppg-reverse-strong",
orm: "bg-background-orm-reverse text-foreground-orm-reverse hover:bg-background-orm-reverse-strong",
default:
- "bg-background-default border-stroke-neutral text-foreground-neutral",
+ "bg-background-default border border-stroke-neutral text-foreground-neutral",
"default-stronger":
- "bg-background-neutral border-stroke-neutral text-foreground-neutral hover:bg-background-neutral-strong",
+ "bg-background-neutral text-foreground-neutral hover:bg-background-neutral-strong",
"default-weaker": "bg-background-neutral text-foreground-neutral-weaker",
error:
"bg-background-error-reverse text-foreground-error-reverse hover:bg-backΩground-error-reverse-strong focus-visible:ring-stroke-error",
diff --git a/packages/eclipse/src/static/fonts/monaspace_neon_var.woff b/packages/eclipse/src/static/fonts/monaspace_neon_var.woff
new file mode 100644
index 0000000000..0df0364fd8
Binary files /dev/null and b/packages/eclipse/src/static/fonts/monaspace_neon_var.woff differ
diff --git a/packages/eclipse/src/static/fonts/monaspace_neon_var.woff2 b/packages/eclipse/src/static/fonts/monaspace_neon_var.woff2
new file mode 100644
index 0000000000..9b7d48d487
Binary files /dev/null and b/packages/eclipse/src/static/fonts/monaspace_neon_var.woff2 differ
diff --git a/packages/eclipse/src/styles/globals.css b/packages/eclipse/src/styles/globals.css
index a34a0c667f..e6fab02853 100644
--- a/packages/eclipse/src/styles/globals.css
+++ b/packages/eclipse/src/styles/globals.css
@@ -3,6 +3,13 @@
@source "../components/**/*.tsx";
@source "../components/ui/**/*.tsx";
+@font-face {
+ font-family: "Monaspace Neon Var";
+ src:
+ url("../static/fonts/monaspace_neon_var.woff") format("woff"),
+ url("../static/fonts/monaspace_neon_var.woff2") format("woff2");
+}
+
@theme {
/* Box Shadow */
--shadow-drop-low: 0 1px 2px 0 rgba(0, 0, 0, 0.04);
@@ -75,7 +82,7 @@
--font-family-display: var(--font-mona-sans), Inter, sans-serif;
--font-family-sans-display: Inter, sans-serif;
--font-family-sans: Inter, system-ui, sans-serif;
- --font-family-mono: Jetbrains Mono, monospace;
+ --font-family-mono: Monaspace Neon Var;
/* Tailwind Font Family Mappings */
--font-display: var(--font-mona-sans), Inter, sans-serif;
diff --git a/packages/eclipse/src/tokens/index.ts b/packages/eclipse/src/tokens/index.ts
index 913a167f5d..a5817e9560 100644
--- a/packages/eclipse/src/tokens/index.ts
+++ b/packages/eclipse/src/tokens/index.ts
@@ -235,7 +235,7 @@ export const typography = {
fontFamily: {
"sans-display": "Inter",
sans: "Inter",
- monospace: "Jetbrains Mono",
+ monospace: "Monaspace Neon Var",
},
fontSize: {
"2xs": 11,
diff --git a/packages/ui/src/components/navigation-menu.tsx b/packages/ui/src/components/navigation-menu.tsx
new file mode 100644
index 0000000000..32c37e4674
--- /dev/null
+++ b/packages/ui/src/components/navigation-menu.tsx
@@ -0,0 +1,244 @@
+"use client";
+import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu";
+import { cva } from "class-variance-authority";
+
+import { cn } from "../lib/cn";
+import { ChevronDownIcon } from "lucide-react";
+import { useScrollThreshold } from "../hooks/use-scroll-threshold";
+import { StarCount } from "./star-count";
+
+const Logo = (
+
+);
+
+function NavigationMenu({
+ align = "start",
+ className,
+ children,
+ ...props
+}: NavigationMenuPrimitive.Root.Props &
+ Pick) {
+ return (
+
+ {children}
+
+
+ );
+}
+
+function NavigationWrapper({
+ className,
+ ...props
+}: React.ComponentPropsWithRef<"div">) {
+ const scroll = useScrollThreshold(64);
+
+ return (
+
+ {props.children}
+
+ );
+}
+
+function NavigationMenuList({
+ className,
+ ...props
+}: React.ComponentPropsWithRef) {
+ return (
+
+ );
+}
+
+function NavigationMenuItem({
+ className,
+ ...props
+}: React.ComponentPropsWithRef) {
+ return (
+
+ );
+}
+
+const navigationMenuTriggerStyle = cva(
+ "bg-background hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-none px-2.5 py-1.5 text-base font-semibold transition-all focus-visible:ring-1 focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center disabled:pointer-events-none outline-none",
+);
+
+function NavigationMenuTrigger({
+ className,
+ children,
+ ...props
+}: NavigationMenuPrimitive.Trigger.Props) {
+ return (
+
+ {children}{" "}
+
+
+ );
+}
+
+function NavigationMenuContent({
+ className,
+ ...props
+}: NavigationMenuPrimitive.Content.Props) {
+ return (
+
+ );
+}
+
+function NavigationMenuPositioner({
+ className,
+ side = "bottom",
+ sideOffset = 8,
+ align = "start",
+ alignOffset = 0,
+ ...props
+}: NavigationMenuPrimitive.Positioner.Props) {
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+function NavigationMenuLink({
+ className,
+ ...props
+}: NavigationMenuPrimitive.Link.Props) {
+ return (
+
+ );
+}
+
+function NavigationMenuIndicator({
+ className,
+ ...props
+}: React.ComponentPropsWithRef) {
+ return (
+
+
+
+ );
+}
+
+function Socials() {
+ const scroll = useScrollThreshold(64);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+export {
+ Logo,
+ NavigationMenu,
+ NavigationMenuContent,
+ NavigationMenuIndicator,
+ NavigationMenuItem,
+ NavigationMenuLink,
+ NavigationWrapper,
+ NavigationMenuList,
+ NavigationMenuTrigger,
+ navigationMenuTriggerStyle,
+ NavigationMenuPositioner,
+ Socials,
+};
diff --git a/packages/ui/src/components/star-count.tsx b/packages/ui/src/components/star-count.tsx
new file mode 100644
index 0000000000..a340bb8b35
--- /dev/null
+++ b/packages/ui/src/components/star-count.tsx
@@ -0,0 +1,20 @@
+"use client";
+import { useStarCount } from "../hooks/use-star-count";
+
+export const StarCount = ({ className }: any) => {
+ const { starCount } = useStarCount();
+
+ const getStarCount = () => {
+ const stars = (starCount / 1000).toFixed(1);
+ return Number(stars) % 1 ? stars : Number(stars).toFixed(0);
+ };
+
+ return (
+
+ {getStarCount()}K
+
+ );
+};
diff --git a/packages/ui/src/hooks/use-scroll-threshold.ts b/packages/ui/src/hooks/use-scroll-threshold.ts
new file mode 100644
index 0000000000..a01769ae75
--- /dev/null
+++ b/packages/ui/src/hooks/use-scroll-threshold.ts
@@ -0,0 +1,20 @@
+import { useEffect, useState } from "react";
+
+export const useScrollThreshold = (threshold: number = 64) => {
+ const [isScrolled, setIsScrolled] = useState(false);
+
+ useEffect(() => {
+ const scrollListener = () => {
+ if (window.scrollY >= threshold) {
+ setIsScrolled(true);
+ } else {
+ setIsScrolled(false);
+ }
+ };
+
+ window.addEventListener("scroll", scrollListener);
+ return () => window.removeEventListener("scroll", scrollListener);
+ }, [threshold]);
+
+ return isScrolled;
+};
diff --git a/packages/ui/src/hooks/use-star-count.ts b/packages/ui/src/hooks/use-star-count.ts
new file mode 100644
index 0000000000..b4fa604520
--- /dev/null
+++ b/packages/ui/src/hooks/use-star-count.ts
@@ -0,0 +1,39 @@
+import { useEffect, useState } from "react";
+
+const GITHUB_API_URL = "https://api.github.com/repos/prisma/prisma";
+
+type Repo = {
+ name: string;
+ stargazers_count: number;
+};
+
+export const useStarCount = () => {
+ const [starCount, setStarCount] = useState(0);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchStarCount = async () => {
+ try {
+ setIsLoading(true);
+ const response = await fetch(GITHUB_API_URL);
+
+ if (!response.ok) {
+ throw new Error("Error fetching GitHub repository data");
+ }
+
+ const data: Repo = await response.json();
+ setStarCount(data.stargazers_count);
+ setError(null);
+ } catch (err) {
+ setError((err as Error).message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchStarCount();
+ }, []);
+
+ return { starCount, isLoading, error };
+};
diff --git a/packages/ui/src/styles/globals.css b/packages/ui/src/styles/globals.css
index af02e31598..6f8f1fd195 100644
--- a/packages/ui/src/styles/globals.css
+++ b/packages/ui/src/styles/globals.css
@@ -4,6 +4,7 @@
@custom-variant dark (&:is(.dark *));
@theme inline {
+ --breakpoint-md: 874px;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
@@ -124,3 +125,7 @@
@apply bg-background text-foreground;
}
}
+
+.transition-navbar {
+ transition: max-width 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
+}