diff --git a/src/app/dev/buttons/page.tsx b/src/app/dev/buttons/page.tsx
new file mode 100644
index 0000000..5c63d78
--- /dev/null
+++ b/src/app/dev/buttons/page.tsx
@@ -0,0 +1,19 @@
+// Dev-only story page for the button system (task jhxk68cdy). Not linked from
+// the app and returns 404 in production builds. Shows every style in both themes.
+import type { CSSProperties } from "react";
+import { notFound } from "next/navigation";
+import { ButtonGallery } from "@/components/ButtonGallery";
+
+export const metadata = { title: "Buttons (dev)" };
+
+const grid: CSSProperties = { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(340px, 1fr))", minHeight: "100vh" };
+
+export default function ButtonsDevPage() {
+ if (process.env.NODE_ENV === "production") notFound();
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/globals.css b/src/app/globals.css
index 6a8e0bf..55ebebe 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -1,9 +1,11 @@
/* Global styles only (ADR 0001, section 6): layer order, tokens, reset, focus ring. */
@layer reset, base;
-/* Tokens. Dark is the default, so it applies even without data-theme. */
+/* Tokens. Dark is the default, so it applies even without data-theme.
+ A nested [data-theme] element re-scopes the theme tokens (used by the dev button page). */
:root,
-:root[data-theme="dark"] {
+:root[data-theme="dark"],
+[data-theme="dark"] {
--bg: #09090B;
--tile: #121215;
--tile-2: #19191D;
@@ -17,10 +19,16 @@
--danger: #FF6B70;
--glass: rgba(18,18,21,.72);
--shadow-pop: 0 10px 28px rgba(0,0,0,.5);
+ /* Buttons: drop shadows and the soft-depth (neumorphic) light and shade. */
+ --shadow-btn: 0 10px 20px -6px rgba(0,0,0,.35);
+ --shadow-float: 0 14px 28px -12px rgba(0,0,0,.55);
+ --neu-d: rgba(0,0,0,.8);
+ --neu-l: rgba(255,255,255,.05);
color-scheme: dark;
}
-:root[data-theme="light"] {
+:root[data-theme="light"],
+[data-theme="light"] {
--bg: #EFEDE9;
--tile: #FBFAF8;
--tile-2: #F3F1EE;
@@ -34,6 +42,10 @@
--danger: #B3121A;
--glass: rgba(251,250,248,.74);
--shadow-pop: 0 10px 28px rgba(26,28,31,.14);
+ --shadow-btn: 0 10px 20px -6px rgba(26,28,31,.22);
+ --shadow-float: 0 14px 28px -12px rgba(26,28,31,.3);
+ --neu-d: rgba(26,28,31,.17);
+ --neu-l: rgba(255,255,255,.95);
color-scheme: light;
}
@@ -42,6 +54,11 @@
--accent-hi: color-mix(in srgb, var(--accent), white 26%);
--accent-lo: color-mix(in srgb, var(--accent), black 30%);
--accent-soft: color-mix(in srgb, var(--accent), transparent 88%);
+ --accent-deep: color-mix(in srgb, var(--accent), black 55%);
+ --accent-glow: rgba(255,110,110,.75);
+ --accent-glow-0: rgba(255,110,110,0);
+ /* Text and marks on a crimson face: white in both themes. */
+ --on-accent: #FFFFFF;
/* HexAvatar initials: dark muted gradients, same in both themes. */
--hex-oxblood: linear-gradient(145deg, #6e1a22, #3a0d12);
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index c8b6545..e5b3f64 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,6 +4,7 @@ import { Cormorant_Garamond, Manrope } from "next/font/google";
import { DEFAULT_THEME, THEME_SCRIPT } from "@/lib/theme";
import "./globals.css";
import "../styles/type.css";
+import "../styles/buttons.css";
import "../styles/avatar.css";
import "../styles/legacy.css";
diff --git a/src/components/ButtonGallery.tsx b/src/components/ButtonGallery.tsx
new file mode 100644
index 0000000..dc7f109
--- /dev/null
+++ b/src/components/ButtonGallery.tsx
@@ -0,0 +1,79 @@
+// Every button style and state, in one theme scope. Used by the dev story page
+// (src/app/dev/buttons) and the button tests.
+import type { CSSProperties, ReactNode } from "react";
+import type { Theme } from "@/lib/theme";
+import { DepthButton, FlipButton, GrowButton, IconButton, SendIcon, SparkleButton, SweepButton } from "./Buttons";
+
+const scope: CSSProperties = {
+ background: "var(--bg)",
+ color: "var(--ink)",
+ padding: "32px 28px",
+ display: "flex",
+ flexDirection: "column",
+ gap: 28,
+};
+const row: CSSProperties = { display: "flex", flexWrap: "wrap", alignItems: "center", gap: 14 };
+const heading: CSSProperties = { margin: "0 0 10px", fontSize: 12, fontWeight: 700, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-3)" };
+
+function Section({ title, children }: { title: string; children: ReactNode }) {
+ return (
+
+ );
+}
+
+const FlameIcon = (
+
+);
+const TrashIcon = (
+
+);
+const PlayIcon = (
+
+);
+
+export function ButtonGallery({ theme }: { theme: Theme }) {
+ return (
+
+
+ {theme === "dark" ? "Dark" : "Light"} theme
+
+
+
+
+
+
+
+ } />
+
+
+ } disabled />
+
+
+ );
+}
diff --git a/src/components/Buttons.tsx b/src/components/Buttons.tsx
new file mode 100644
index 0000000..8ce2346
--- /dev/null
+++ b/src/components/Buttons.tsx
@@ -0,0 +1,239 @@
+// Button system (spec "Buttons", ADR 0001). Styles live in src/styles/buttons.css.
+// Each component renders a native