diff --git a/apps/claim-db-worker/__tests__/claim.test.tsx b/apps/claim-db-worker/__tests__/claim.test.tsx
index 75ed59b..936c7be 100644
--- a/apps/claim-db-worker/__tests__/claim.test.tsx
+++ b/apps/claim-db-worker/__tests__/claim.test.tsx
@@ -57,7 +57,7 @@ describe("claim page", () => {
mockSearchParams.set("projectID", "test-project-123");
render(
+ *Your database will be{" "} + + deleted 24 hours after creation + {" "} + unless you claim it. +
+ ); } diff --git a/apps/claim-db-worker/app/db-unavailable/page.tsx b/apps/claim-db-worker/app/db-unavailable/page.tsx index a86b1d5..a22c346 100644 --- a/apps/claim-db-worker/app/db-unavailable/page.tsx +++ b/apps/claim-db-worker/app/db-unavailable/page.tsx @@ -1,7 +1,9 @@ "use client"; import { cookieUtils } from "@/lib/utils"; -import { Info } from "lucide-react"; +import { Mascot } from "@/components/brand/Brand"; +import { PrismButton } from "@/components/brand/PrismButton"; +import { StatePanel } from "@/components/brand/StatePanel"; const DB_KEY = "temp_db_info"; @@ -11,21 +13,17 @@ export default function DBUnavailablePage() { }; return ( -Please create a new one
- - Create New Database - +Please create a new one
++ + Something went wrong +
+- {message} -
++ {message} +
- {details && ( -
- {details}
-
- )}
-
+ {details}
+
+ )}
+
);
}
diff --git a/apps/claim-db-worker/app/favicon.ico b/apps/claim-db-worker/app/favicon.ico
index a4cea67..812fb4c 100644
Binary files a/apps/claim-db-worker/app/favicon.ico and b/apps/claim-db-worker/app/favicon.ico differ
diff --git a/apps/claim-db-worker/app/globals.css b/apps/claim-db-worker/app/globals.css
index dc33b9f..2e4e303 100644
--- a/apps/claim-db-worker/app/globals.css
+++ b/apps/claim-db-worker/app/globals.css
@@ -1,115 +1,317 @@
@import "tailwindcss";
+/* Brand tokens, ported from the prisma.io site (web/apps/site globals.css and
+ the /brand-kit page): ink and paper carry the surface, the three prism
+ colours (cyan / yellow / red) are the only accents. Keep these values in
+ step with the site rather than tuning them locally. */
:root {
- --background: #0a0a0a;
- --foreground: #ededed;
- --background-dark: #0f1217;
- --background-card: #161a20;
- --background-code: #13161b;
- --background-step: #181b21;
- --background-table-header: #1c1f26;
- --background-button: #2d3138;
- --background-button-hover: #2d3748;
-
- --brand-surface-main: #161d2b;
- --brand-surface-accent: #0d3a38;
- --brand-surface-highlight: #71e8df;
-
- --text-muted: #a0aec0;
- --text-dim: #3f4b5b;
- --text-accent: #71e8df;
- --text-error: #fc8181;
-
- --border-subtle: #202734;
- --border-accent: #71e8df;
-
- --gradient-start: #71e8df;
- --gradient-end: #3f827d;
-
- --button: #2d3138;
- --button-hover: #2d3748;
-
- --button-blue: #16a394;
- --button-blue-hover: #16a395ba;
+ --background: #ffffff;
+ --foreground: #3a3b3c; /* brand text primary */
+ --ink: #151515;
+ --paper: #f9faf5;
+ --muted-foreground: #646567; /* brand text secondary */
+ --border: #e5e5e5;
+ --code: #f6f7f2;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
- --font-barlow: var(--font-barlow);
- --color-dark: var(--background-dark);
- --color-card: var(--background-card);
- --color-code: var(--background-code);
- --color-step: var(--background-step);
- --color-table-header: var(--background-table-header);
- --color-button: var(--background-button);
+ --color-ink: var(--ink);
+ --color-paper: var(--paper);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-border: var(--border);
+ --color-code: var(--code);
- --color-white: var(--text-white);
- --color-muted: var(--text-muted);
- --color-dim: var(--text-dim);
- --color-accent: var(--text-accent);
- --color-error: var(--text-error);
+ --font-sans: var(--font-inter);
+ --font-heading: var(--font-sora);
+ /* The site maps mono to Inter; connection strings and commands need real
+ fixed-width glyphs, so code stays on the system mono stack. */
+ --font-mono:
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
- --color-brand-surface-main: var(--brand-surface-main);
- --color-brand-surface-accent: var(--brand-surface-accent);
- --color-brand-surface-highlight: var(--brand-surface-highlight);
+ --color-prism-cyan-50: #eeffff;
+ --color-prism-cyan-100: #dcfdff;
+ --color-prism-cyan-200: #b3f4f9;
+ --color-prism-cyan-300: #7be7f0;
+ --color-prism-cyan-400: #01d7e4;
+ --color-prism-cyan-500: #00bbcb;
+ --color-prism-cyan-600: #009aaa;
+ --color-prism-cyan-700: #007f8d;
+ --color-prism-cyan-800: #006772;
+ --color-prism-cyan-900: #00545d;
+ --color-prism-cyan-950: #00353c;
- --color-subtle: var(--border-subtle);
- --color-accent: var(--border-accent);
+ --color-prism-red-50: #ffeeee;
+ --color-prism-red-100: #ffdfdf;
+ --color-prism-red-200: #ffc2c4;
+ --color-prism-red-300: #ff9fa4;
+ --color-prism-red-400: #ff7682;
+ --color-prism-red-500: #f34a60;
+ --color-prism-red-600: #d02344;
+ --color-prism-red-700: #af0a33;
+ --color-prism-red-800: #8e0b29;
+ --color-prism-red-900: #740f22;
+ --color-prism-red-950: #4b0211;
- --color-button: var(--button);
- --color-button-hover: var(--button-hover);
+ --color-prism-yellow-50: #fff7e0;
+ --color-prism-yellow-100: #fcedc2;
+ --color-prism-yellow-200: #f8da85;
+ --color-prism-yellow-300: #f3c306;
+ --color-prism-yellow-400: #eaa700;
+ --color-prism-yellow-500: #d48700;
+ --color-prism-yellow-600: #b46700;
+ --color-prism-yellow-700: #965100;
+ --color-prism-yellow-800: #7a4200;
+ --color-prism-yellow-900: #633700;
+ --color-prism-yellow-950: #402000;
- --color-button-blue: var(--button-blue);
- --color-button-blue-hover: var(--button-blue-hover);
+ --animate-hero-rise: hero-rise 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.15s both;
+ @keyframes hero-rise {
+ from {
+ opacity: 0;
+ transform: translateY(16px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+}
- --color-gradient-start: var(--gradient-start);
- --color-gradient-end: var(--gradient-end);
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground font-sans antialiased;
+ /* Inter renders optically darker than SF Pro at 400; the site pulls
+ regular text back to 380 to match. */
+ font-weight: 380;
+ }
+ button:not(:disabled),
+ [role="button"]:not([aria-disabled="true"]) {
+ cursor: pointer;
+ }
}
-body {
- background: var(--background);
- color: var(--foreground);
- font-family: Arial, Helvetica, sans-serif;
+/* Brand rule (from the site): headings are always Sora at Medium with natural
+ tracking, in ink. Unlayered so it beats leftover weight/tracking utilities. */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: var(--font-heading);
+ font-weight: 500;
+ letter-spacing: normal;
+}
+@layer base {
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ color: var(--ink);
+ }
}
-.bg-custom-gradient {
- background:
- radial-gradient(circle at 30% 30%, rgba(0, 128, 128, 0.2), transparent 50%),
- radial-gradient(circle at 70% 70%, rgba(0, 128, 128, 0.2), transparent 50%),
- var(--color-dark);
+/* The brand spectrum: cyan → yellow → orange → red → magenta. */
+.prism-spectrum {
+ background-image: linear-gradient(
+ 85deg,
+ #01d7e4 0%,
+ #f3c306 25%,
+ #f37a03 50%,
+ #f43531 74%,
+ #f00e5c 100%
+ );
+}
+
+/* Primary CTA — CSS port of the site's PrismButton (components/PrismButton
+ renders the markup): a black pill with a spectrum glow drifting behind it,
+ which blooms and fills prismatic on hover. The site version runs on
+ framer-motion and a WebGL burst; this keeps the look without shipping
+ either to the worker. The glow and bloom sit in the wrapper, behind the
+ pill, so only their blurred spill shows around its edge. */
+@keyframes prism-drift {
+ 0%,
+ 100% {
+ transform: translateX(0);
+ }
+ 50% {
+ transform: translateX(-58%);
+ }
+}
+.prism-cta {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+}
+.prism-cta-glow {
+ position: absolute;
+ inset: 0;
+ overflow: hidden;
+ border-radius: 9999px;
+ filter: blur(3.2px);
+ pointer-events: none;
+}
+.prism-cta-glow > span {
+ position: absolute;
+ inset-block: 0;
+ left: 0;
+ width: 240%;
+ animation: prism-drift 7s ease-in-out infinite;
+}
+.prism-cta-bloom {
+ position: absolute;
+ inset: 0;
+ border-radius: 9999px;
+ filter: blur(10px);
+ opacity: 0;
+ transition: opacity 0.35s ease;
+ pointer-events: none;
+}
+.prism-cta-button {
+ position: relative;
+ display: flex;
+ align-self: stretch;
+ width: 100%;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ border-radius: 9999px;
+ background: #000;
+ color: #fff;
+ font-weight: 600;
+ line-height: 1.5;
+ white-space: nowrap;
+ transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
-button {
- cursor: pointer;
+/* The prismatic fill erupting from the centre — a radial reveal standing in
+ for the site's WebGL burst. */
+.prism-cta-fill {
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(
+ circle at 50% 50%,
+ #f00e5c 0%,
+ #f43531 18%,
+ #f37a03 34%,
+ #f3c306 52%,
+ #01d7e4 78%,
+ #01d7e4 100%
+ );
+ clip-path: circle(0% at 50% 50%);
+ transition: clip-path 0.5s cubic-bezier(0.22, 1, 0.36, 1);
+ pointer-events: none;
+}
+.prism-cta-label {
+ position: relative;
+ z-index: 1;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+.prism-cta:hover:not(:has(:disabled)) .prism-cta-bloom {
+ opacity: 0.7;
+}
+.prism-cta:hover:not(:has(:disabled)) .prism-cta-button {
+ transform: scale(1.04);
+}
+.prism-cta:hover:not(:has(:disabled)) .prism-cta-fill {
+ clip-path: circle(75% at 50% 50%);
+}
+.prism-cta-button:focus-visible {
+ outline: 2px solid var(--ink);
+ outline-offset: 3px;
+}
+.prism-cta:has(:disabled) {
+ opacity: 0.5;
+}
+.prism-cta-button:disabled {
+ cursor: not-allowed;
+}
+
+/* Secondary pill — thin neutral border that reveals the moving spectrum on
+ hover (the site's .spectrum-border). */
+@keyframes spectrum-slide {
+ from {
+ background-position: 0% 50%;
+ }
+ to {
+ background-position: 200% 50%;
+ }
+}
+.spectrum-border {
+ position: relative;
+}
+.spectrum-border::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ border-radius: inherit;
+ padding: 1px;
+ background-image: linear-gradient(85deg, #01d7e4, #f3c306, #f37a03, #f43531, #f00e5c, #01d7e4);
+ background-size: 200% 100%;
+ -webkit-mask:
+ linear-gradient(#fff 0 0) content-box,
+ linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask:
+ linear-gradient(#fff 0 0) content-box,
+ linear-gradient(#fff 0 0);
+ mask-composite: exclude;
+ opacity: 0;
+ transition: opacity 0.5s ease;
+ pointer-events: none;
+}
+.spectrum-border:hover::before,
+.spectrum-border:focus-visible::before {
+ opacity: 1;
+ animation: spectrum-slide 2.5s linear infinite;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .prism-cta-button,
+ .prism-cta-fill,
+ .prism-cta-bloom {
+ transition: none;
+ }
+ .prism-cta-glow > span,
+ .spectrum-border:hover::before,
+ .spectrum-border:focus-visible::before {
+ animation: none;
+ }
+ .prism-cta:hover .prism-cta-button {
+ transform: none;
+ }
}
html {
- /* Firefox - Hides track and changes thumb color */
- scrollbar-color: hsla(0, 0%, 46%, 0.5) transparent;
+ scrollbar-color: rgb(0 0 0 / 0.2) transparent;
}
-/* Chrome - Hides track and width/height set for vertical/horizontal scrollbars */
::-webkit-scrollbar {
background-color: transparent;
width: 7px;
height: 7px;
}
-/* Chrome - Changes thumb color and rounds scrollbar edges, in Chrome any
-scrollbar changes disables default styling and rounding so corners appear squared */
::-webkit-scrollbar-thumb {
- background-color: hsla(0, 0%, 46%, 0.5);
+ background-color: rgb(0 0 0 / 0.2);
border-radius: 8px;
}
-/* Utility class to hide scrollbars */
.scrollbar-hide {
- -ms-overflow-style: none; /* Internet Explorer 10+ */
- scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none;
+ scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
- display: none; /* Safari and Chrome */
+ display: none;
}
.monaco-editor {
diff --git a/apps/claim-db-worker/app/icon.svg b/apps/claim-db-worker/app/icon.svg
new file mode 100644
index 0000000..c9d58ef
--- /dev/null
+++ b/apps/claim-db-worker/app/icon.svg
@@ -0,0 +1,14 @@
+
diff --git a/apps/claim-db-worker/app/layout.tsx b/apps/claim-db-worker/app/layout.tsx
index baffa8b..3567564 100644
--- a/apps/claim-db-worker/app/layout.tsx
+++ b/apps/claim-db-worker/app/layout.tsx
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
-import { Barlow } from "next/font/google";
+import { Inter, Sora } from "next/font/google";
import Script from "next/script";
import "./globals.css";
import { Navbar } from "../components/Navbar";
@@ -8,10 +8,15 @@ import { DropProvider } from "./contexts/DropContext";
import { Toaster } from "react-hot-toast";
import { PostHogProvider } from "@/components/PostHogProvider";
-const barlow = Barlow({
- weight: ["400", "500", "700", "800", "900"],
+// Brand type: Sora for headings, Inter for everything else.
+const sora = Sora({
subsets: ["latin"],
- variable: "--font-barlow",
+ variable: "--font-sora",
+});
+
+const inter = Inter({
+ subsets: ["latin"],
+ variable: "--font-inter",
});
export const metadata: Metadata = {
@@ -23,7 +28,7 @@ export const metadata: Metadata = {
title: "Want a free, instant Prisma Postgres database?",
description:
"Get a temporary Prisma Postgres database instantly. No account or config needed. Just run npx create-db.",
- images: ["/og-image.png"],
+ images: ["/og-card.png"],
type: "website",
url: "https://create-db.prisma.io/",
},
@@ -32,10 +37,7 @@ export const metadata: Metadata = {
title: "Want a free, instant Prisma Postgres database?",
description:
"Get a temporary Prisma Postgres database instantly. No account or config needed. Just run npx create-db.",
- images: ["/og-image.png"],
- },
- icons: {
- shortcut: "/favicon.png",
+ images: ["/og-card.png"],
},
};
@@ -45,10 +47,12 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
-
+
+
npx create-db@latest{" "}
+ to get a Prisma Postgres database instantly. No account or setup required.
>
),
},
{
- number: "2",
title: "Connect and use",
description:
"Get your connection string and start building. Perfect for development, testing, prototypes, and AI applications.",
},
{
- number: "3",
title: "Keep it forever",
description: (
<>
- Claim your database to your Prisma account to keep it permanently.{" "}
-
- Unclaimed databases expire after 24 hours.
-
+ Claim your database to your Prisma account to keep it permanently. Unclaimed databases
+ expire after 24 hours.
>
),
},
@@ -58,23 +56,22 @@ const steps = [
const featuresTable = [
{
- icon: + No account or config needed. Spin one up in the browser or from your terminal, and + claim it when you want to keep it. +
++ Databases are deleted after 24 hours unless claimed. +
+- {feature.description} -
-- {feature.description} -
-- databases will be deleted in 24 hours unless claimed -
-0{index + 1}
+{step.description}
+ {feature.description} +
+ create-db utilizes the power of the Prisma Postgres Management API to create and + manage your database. If you'd like to build something similar, see the{" "} Prisma Partners page . -
- No account or config needed -
-- {feature.description} -
-{description}
- You have successfully claimed your database -
++ You have successfully claimed your database +
- +
+
{children}
);
const StepNumber = ({ number }: { number: number }) => (
-
+
{number}
);
@@ -97,13 +97,13 @@ const StepItem = ({
return (
-
- {title}
+
+ {title}
{content && {content}}
{code && (
-
- {displayCode}
+
+ {displayCode}
)}
@@ -135,7 +135,7 @@ export default function ConnectPage() {
};
return (
-
+
{/* Connection type toggle */}
{/*
{(["prisma", "direct"] as const).map((type) => (
@@ -143,8 +143,8 @@ export default function ConnectPage() {
key={type}
className={`flex-1 px-3 py-2 sm:py-1 text-sm rounded-md transition-colors font-medium border ${
connectionType === type
- ? "text-brand-surface-highlight border-brand-surface-highlight bg-brand-surface-highlight/10"
- : "text-muted hover:text-white border-transparent bg-table-header hover:bg-brand-surface-highlight/5"
+ ? "text-ink border-border bg-white"
+ : "text-muted-foreground hover:text-ink border-transparent hover:bg-black/[0.03]"
}`}
onClick={() => setConnectionType(type)}
>
@@ -155,12 +155,12 @@ export default function ConnectPage() {
{/* Connection string input */}
-
+