diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
index 62abf0f..5ba96c8 100644
--- a/.storybook/preview.tsx
+++ b/.storybook/preview.tsx
@@ -1,6 +1,31 @@
-import type { Preview } from "@storybook/react";
+import type { Decorator, Preview } from "@storybook/react";
+import React from "react";
import "../src/app/globals.css";
+/**
+ * Wraps every story in a div carrying the app's `.dark` theme class (the same
+ * mechanism `next-themes` uses on `` via `attribute="class"`, see
+ * `src/app/layout.tsx`), driven by the "Theme" toolbar toggle below. This
+ * makes every story — foundations and existing components alike — re-render
+ * with the correct design tokens for the selected theme.
+ */
+const withTheme: Decorator = (Story, context) => {
+ const theme = context.globals.theme === "light" ? "light" : "dark";
+ return (
+
+
+
+ );
+};
+
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
@@ -34,6 +59,22 @@ const preview: Preview = {
},
},
},
+ globalTypes: {
+ theme: {
+ name: "Theme",
+ description: "Global theme (light/dark) applied via the app's .dark class",
+ defaultValue: "dark",
+ toolbar: {
+ icon: "mirror",
+ items: [
+ { value: "light", title: "Light", icon: "sun" },
+ { value: "dark", title: "Dark", icon: "moon" },
+ ],
+ dynamicTitle: true,
+ },
+ },
+ },
+ decorators: [withTheme],
tags: ["autodocs"],
};
diff --git a/src/stories/foundations/Colors.stories.tsx b/src/stories/foundations/Colors.stories.tsx
new file mode 100644
index 0000000..9797abd
--- /dev/null
+++ b/src/stories/foundations/Colors.stories.tsx
@@ -0,0 +1,111 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import React from "react";
+import { FoundationSection, TokenCard, TokenGrid } from "./shared";
+
+const meta: Meta = {
+ title: "Foundations/Colors",
+ tags: ["autodocs"],
+ parameters: {
+ docs: {
+ description: {
+ component:
+ "Color tokens defined as CSS custom properties in `src/app/globals.css`. " +
+ "Theme-aware tokens are set on `:root` (light) and overridden under `.dark` " +
+ "(the class next-themes toggles on ``) — use the Theme toolbar toggle " +
+ "above to see them shift. A handful of static brand accents are used directly " +
+ "as hex values around the app rather than as tokens; they're listed for reference.",
+ },
+ },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const THEME_TOKENS: { name: string; cssVar: string }[] = [
+ { name: "Background", cssVar: "--color-background" },
+ { name: "Foreground", cssVar: "--color-foreground" },
+ { name: "Surface", cssVar: "--color-surface" },
+ { name: "Surface Raised", cssVar: "--color-surface-raised" },
+ { name: "Border", cssVar: "--color-border" },
+ { name: "Muted", cssVar: "--color-muted" },
+];
+
+const STATIC_TOKENS: { name: string; cssVar: string; hex: string }[] = [
+ { name: "Neon Green", cssVar: "--color-neon-green", hex: "#39ff14" },
+ { name: "Oracle Navy", cssVar: "--color-oracle-navy", hex: "#0a0f1e" },
+ { name: "Oracle Border", cssVar: "--color-oracle-border", hex: "#1b2a3b" },
+];
+
+const BRAND_ACCENTS: { name: string; hex: string; usage: string }[] = [
+ { name: "Wallet Button", hex: "#d9f99d", usage: ".wallet-btn background" },
+ { name: "Wallet Button Hover", hex: "#bef574", usage: ".wallet-btn:hover accent" },
+ { name: "Cyber Lime", hex: "#99dc1b", usage: "Admin tab active state / focus ring" },
+];
+
+function Swatch({ background }: { background: string }) {
+ return (
+
+ );
+}
+
+export const ThemeTokens: Story = {
+ render: () => (
+
+
+ {THEME_TOKENS.map((t) => (
+ }
+ />
+ ))}
+
+
+ ),
+};
+
+export const StaticBrandTokens: Story = {
+ render: () => (
+
+
+ {STATIC_TOKENS.map((t) => (
+ }
+ />
+ ))}
+
+
+ ),
+};
+
+export const OtherBrandAccents: Story = {
+ render: () => (
+
+
+ {BRAND_ACCENTS.map((t) => (
+ } />
+ ))}
+
+
+ ),
+};
diff --git a/src/stories/foundations/Radii.stories.tsx b/src/stories/foundations/Radii.stories.tsx
new file mode 100644
index 0000000..4355ac8
--- /dev/null
+++ b/src/stories/foundations/Radii.stories.tsx
@@ -0,0 +1,59 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import React from "react";
+import { FoundationSection, TokenGrid } from "./shared";
+
+const meta: Meta = {
+ title: "Foundations/Radii",
+ tags: ["autodocs"],
+ parameters: {
+ docs: {
+ description: {
+ component:
+ "`tailwind.config.js` does not extend `theme.borderRadius`, so every `rounded-*` " +
+ "utility in the app resolves to Tailwind v4's built-in default scale, reproduced here. " +
+ "Common app usages: `rounded-lg`/`rounded-xl` for cards and panels, `rounded-full` for " +
+ "toggles, badges, and avatars.",
+ },
+ },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const RADII: { name: string; valueRem: number | "full" }[] = [
+ { name: "rounded-none", valueRem: 0 },
+ { name: "rounded-sm", valueRem: 0.125 },
+ { name: "rounded (default)", valueRem: 0.25 },
+ { name: "rounded-md", valueRem: 0.375 },
+ { name: "rounded-lg", valueRem: 0.5 },
+ { name: "rounded-xl", valueRem: 0.75 },
+ { name: "rounded-2xl", valueRem: 1 },
+ { name: "rounded-3xl", valueRem: 1.5 },
+ { name: "rounded-full", valueRem: "full" },
+];
+
+export const DefaultScale: Story = {
+ render: () => (
+
+
+ {RADII.map((r) => (
+
+
+
{r.name}
+
+ {r.valueRem === "full" ? "9999px" : `${r.valueRem}rem`}
+
+
+ ))}
+
+
+ ),
+};
diff --git a/src/stories/foundations/Shadows.stories.tsx b/src/stories/foundations/Shadows.stories.tsx
new file mode 100644
index 0000000..804f825
--- /dev/null
+++ b/src/stories/foundations/Shadows.stories.tsx
@@ -0,0 +1,55 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import React from "react";
+import { FoundationSection, TokenGrid } from "./shared";
+
+const meta: Meta = {
+ title: "Foundations/Shadows",
+ tags: ["autodocs"],
+ parameters: {
+ docs: {
+ description: {
+ component:
+ "`tailwind.config.js` does not extend `theme.boxShadow`, so every `shadow-*` " +
+ "utility in the app resolves to Tailwind v4's built-in default scale, reproduced here.",
+ },
+ },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const SHADOWS: { name: string; value: string }[] = [
+ { name: "shadow-sm", value: "0 1px 2px 0 rgb(0 0 0 / 0.05)" },
+ { name: "shadow (default)", value: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)" },
+ { name: "shadow-md", value: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)" },
+ { name: "shadow-lg", value: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)" },
+ { name: "shadow-xl", value: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)" },
+ { name: "shadow-2xl", value: "0 25px 50px -12px rgb(0 0 0 / 0.25)" },
+ { name: "shadow-inner", value: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)" },
+];
+
+export const DefaultScale: Story = {
+ render: () => (
+
+
+ {SHADOWS.map((s) => (
+
+
+
{s.name}
+
+ {s.value}
+
+
+ ))}
+
+
+ ),
+};
diff --git a/src/stories/foundations/Typography.stories.tsx b/src/stories/foundations/Typography.stories.tsx
new file mode 100644
index 0000000..748e9b1
--- /dev/null
+++ b/src/stories/foundations/Typography.stories.tsx
@@ -0,0 +1,108 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import React from "react";
+import { FoundationSection } from "./shared";
+
+const meta: Meta = {
+ title: "Foundations/Typography",
+ tags: ["autodocs"],
+ parameters: {
+ docs: {
+ description: {
+ component:
+ "Font family tokens come from `src/app/globals.css` (`--font-sans` / `--font-mono`, " +
+ "both currently mapped to the Geist Sans variable loaded in `src/app/layout.tsx` via " +
+ "`next/font/google`). The type scale and weights below are Tailwind CSS v4's built-in " +
+ "defaults — `tailwind.config.js` does not override `theme.extend`, so these are exactly " +
+ "what's available to every component in the app.",
+ },
+ },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const FONT_FAMILY = "var(--font-sans, ui-sans-serif, system-ui, sans-serif)";
+
+const TYPE_SCALE: { name: string; sizeRem: number; lineHeightRem: number }[] = [
+ { name: "text-xs", sizeRem: 0.75, lineHeightRem: 1 },
+ { name: "text-sm", sizeRem: 0.875, lineHeightRem: 1.25 },
+ { name: "text-base", sizeRem: 1, lineHeightRem: 1.5 },
+ { name: "text-lg", sizeRem: 1.125, lineHeightRem: 1.75 },
+ { name: "text-xl", sizeRem: 1.25, lineHeightRem: 1.75 },
+ { name: "text-2xl", sizeRem: 1.5, lineHeightRem: 2 },
+ { name: "text-3xl", sizeRem: 1.875, lineHeightRem: 2.25 },
+ { name: "text-4xl", sizeRem: 2.25, lineHeightRem: 2.5 },
+ { name: "text-5xl", sizeRem: 3, lineHeightRem: 1 },
+];
+
+const WEIGHTS: { name: string; weight: number }[] = [
+ { name: "font-normal", weight: 400 },
+ { name: "font-medium", weight: 500 },
+ { name: "font-semibold", weight: 600 },
+ { name: "font-bold", weight: 700 },
+];
+
+export const FontFamilies: Story = {
+ render: () => (
+
+
+
+
+ --font-sans
+
+
+ The quick brown fox jumps over the lazy dog — Geist Sans
+
+
+
+
+ --font-mono (currently also Geist Sans, not a distinct monospace face)
+
+
0123456789 StellarFlow tx_hash
+
+
+
+ ),
+};
+
+export const TypeScale: Story = {
+ render: () => (
+
+
+ {TYPE_SCALE.map((t) => (
+
+
+ {t.name}
+
+
+ {t.sizeRem}rem / {t.lineHeightRem}rem
+
+
+ StellarFlow
+
+
+ ))}
+
+
+ ),
+};
+
+export const FontWeights: Story = {
+ render: () => (
+
+
+ {WEIGHTS.map((w) => (
+
+
+ {w.name} · {w.weight}
+
+
+ StellarFlow Network
+
+
+ ))}
+
+
+ ),
+};
diff --git a/src/stories/foundations/shared.tsx b/src/stories/foundations/shared.tsx
new file mode 100644
index 0000000..6ec7c24
--- /dev/null
+++ b/src/stories/foundations/shared.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+
+export function FoundationSection({
+ title,
+ description,
+ children,
+}: {
+ title: string;
+ description?: string;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {title}
+ {description && (
+ {description}
+ )}
+ {children}
+
+ );
+}
+
+export function TokenGrid({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export function TokenCard({
+ label,
+ value,
+ swatch,
+}: {
+ label: string;
+ value: string;
+ swatch?: React.ReactNode;
+}) {
+ return (
+
+ {swatch}
+
+
{label}
+
+ {value}
+
+
+
+ );
+}