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} +

+
+
+ ); +}