+
+ );
+};
+
+export default QuoteBlock;
diff --git a/app/components/QuoteDecor.js b/app/components/QuoteDecor.js
new file mode 100644
index 0000000..cd2a5f4
--- /dev/null
+++ b/app/components/QuoteDecor.js
@@ -0,0 +1,73 @@
+"use client";
+
+const QuoteDecor = ({ className = "" }) => {
+ return (
+
+ );
+};
+
+export default QuoteDecor;
diff --git a/app/components/RuleStack.js b/app/components/RuleStack.js
index 0e43a6b..f5b9733 100644
--- a/app/components/RuleStack.js
+++ b/app/components/RuleStack.js
@@ -1,26 +1,32 @@
"use client";
-import SectionHeader from "./SectionHeader";
+import React from "react";
+import Image from "next/image";
import RuleCard from "./RuleCard";
import Button from "./Button";
-import Image from "next/image";
-const RuleStack = ({ children, className = "" }) => {
+const RuleStack = ({ className = "" }) => {
const handleTemplateClick = (templateName) => {
- console.log(`Template selected: ${templateName}`);
- // This would typically navigate to template details or open a modal
- // For now, we'll just log the selection
+ // Basic analytics tracking
+ if (typeof window !== "undefined") {
+ if (window.gtag) {
+ window.gtag("event", "template_click", {
+ template_name: templateName,
+ });
+ }
+ if (window.analytics) {
+ window.analytics.track("Template Clicked", {
+ templateName: templateName,
+ });
+ }
+ }
+ console.log(`${templateName} template clicked`);
};
return (
-
-
{
See all templates
-
+
);
};
diff --git a/app/page.js b/app/page.js
index e6a48ad..ac8d1fd 100644
--- a/app/page.js
+++ b/app/page.js
@@ -2,6 +2,7 @@ import NumberedCards from "./components/NumberedCards";
import HeroBanner from "./components/HeroBanner";
import LogoWall from "./components/LogoWall";
import RuleStack from "./components/RuleStack";
+import QuoteBlock from "./components/QuoteBlock";
export default function Page() {
const heroBannerData = {
@@ -41,6 +42,7 @@ export default function Page() {
+
);
}
diff --git a/public/assets/Quote_Avatar.svg b/public/assets/Quote_Avatar.svg
new file mode 100644
index 0000000..50c8704
--- /dev/null
+++ b/public/assets/Quote_Avatar.svg
@@ -0,0 +1,9 @@
+
diff --git a/stories/QuoteBlock.stories.js b/stories/QuoteBlock.stories.js
new file mode 100644
index 0000000..3b4f0f6
--- /dev/null
+++ b/stories/QuoteBlock.stories.js
@@ -0,0 +1,146 @@
+import QuoteBlock from "../app/components/QuoteBlock";
+
+export default {
+ title: "Components/QuoteBlock",
+ component: QuoteBlock,
+ parameters: {
+ layout: "fullscreen",
+ docs: {
+ description: {
+ component: `
+A responsive quote section component that displays inspirational governance quotes with author attribution and decorative geometric elements.
+
+## Features
+- **Three variants**: compact, standard, and extended layouts
+- **Responsive design**: Adapts across all breakpoints
+- **Error handling**: Graceful fallbacks for image loading failures
+- **Accessibility**: WCAG 2.1 AA compliant with proper ARIA labels
+- **Design system integration**: Uses design tokens for consistent styling
+
+## Usage
+\`\`\`jsx
+
+\`\`\`
+ `,
+ },
+ },
+ },
+ argTypes: {
+ variant: {
+ control: { type: "select" },
+ options: ["compact", "standard", "extended"],
+ description: "Layout variant for different use cases",
+ },
+ quote: {
+ control: { type: "text" },
+ description: "The quote text to display",
+ },
+ author: {
+ control: { type: "text" },
+ description: "Author name for attribution",
+ },
+ source: {
+ control: { type: "text" },
+ description: "Source title (book, article, etc.)",
+ },
+ avatarSrc: {
+ control: { type: "text" },
+ description: "Path to author avatar image",
+ },
+ fallbackAvatarSrc: {
+ control: { type: "text" },
+ description: "Fallback avatar image path",
+ },
+ onError: {
+ action: "error",
+ description: "Error callback function",
+ },
+ },
+};
+
+// Default story
+export const Default = {
+ args: {
+ variant: "standard",
+ quote:
+ "The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized.",
+ author: "Jo Freeman",
+ source: "The Tyranny of Structurelessness",
+ avatarSrc: "assets/Quote_Avatar.svg",
+ },
+};
+
+// All variants comparison
+export const AllVariants = {
+ render: () => (
+
+
+
Compact Variant
+
+
+
+
+
Standard Variant
+
+
+
+
+
Extended Variant
+
+
+
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Side-by-side comparison of all three variants to show the differences in layout, typography, and spacing.",
+ },
+ },
+ },
+};
+
+// Error state simulation
+export const ErrorState = {
+ args: {
+ variant: "standard",
+ quote:
+ "The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized.",
+ author: "Jo Freeman",
+ source: "The Tyranny of Structurelessness",
+ avatarSrc: "invalid-image-path.jpg", // This will trigger error state
+ onError: (error) => console.log("QuoteBlock error:", error),
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Error state when avatar image fails to load. Shows initials fallback and error handling.",
+ },
+ },
+ },
+};