diff --git a/app/components/RuleStack.js b/app/components/RuleStack.js
index c0d6fbf..c85fb1d 100644
--- a/app/components/RuleStack.js
+++ b/app/components/RuleStack.js
@@ -6,6 +6,12 @@ import Button from "./Button";
import Image from "next/image";
const RuleStack = ({ children, 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
+ };
+
return (
{
/>
}
backgroundColor="bg-[var(--color-surface-default-brand-lime)]"
+ onClick={() => handleTemplateClick("Consensus clusters")}
/>
{
/>
}
backgroundColor="bg-[var(--color-surface-default-brand-rust)]"
+ onClick={() => handleTemplateClick("Consensus")}
/>
{
/>
}
backgroundColor="bg-[var(--color-surface-default-brand-red)]"
+ onClick={() => handleTemplateClick("Elected Board")}
/>
{
/>
}
backgroundColor="bg-[var(--color-surface-default-brand-teal)]"
+ onClick={() => handleTemplateClick("Petition")}
/>
--
2.43.0
From f22cde728a392bdbd1d8b1c33d8d13b6e6dea7ea Mon Sep 17 00:00:00 2001
From: adilallo <39313955+adilallo@users.noreply.github.com>
Date: Sun, 24 Aug 2025 22:01:52 -0600
Subject: [PATCH 18/27] Rule Stack storyboard
---
stories/RuleCard.stories.js | 176 +++++++++++++++++++++++++++++++
stories/RuleStack.stories.js | 39 +++++++
stories/SectionHeader.stories.js | 47 ++++++++-
3 files changed, 261 insertions(+), 1 deletion(-)
create mode 100644 stories/RuleCard.stories.js
create mode 100644 stories/RuleStack.stories.js
diff --git a/stories/RuleCard.stories.js b/stories/RuleCard.stories.js
new file mode 100644
index 0000000..5d42faf
--- /dev/null
+++ b/stories/RuleCard.stories.js
@@ -0,0 +1,176 @@
+import RuleCard from "../app/components/RuleCard";
+import Image from "next/image";
+
+export default {
+ title: "Components/RuleCard",
+ component: RuleCard,
+ parameters: {
+ layout: "centered",
+ docs: {
+ description: {
+ component:
+ "An interactive card component that displays governance templates and decision-making patterns. Features hover states, keyboard navigation, analytics tracking, and accessibility support. Use Tab key to test focus indicators and Enter/Space to activate.",
+ },
+ },
+ },
+ argTypes: {
+ title: {
+ control: { type: "text" },
+ description: "The title of the governance template",
+ },
+ description: {
+ control: { type: "text" },
+ description: "The description of the governance pattern",
+ },
+ backgroundColor: {
+ control: { type: "select" },
+ options: [
+ "bg-[var(--color-surface-default-brand-lime)]",
+ "bg-[var(--color-surface-default-brand-rust)]",
+ "bg-[var(--color-surface-default-brand-red)]",
+ "bg-[var(--color-surface-default-brand-teal)]",
+ "bg-[var(--color-community-teal-100)]",
+ ],
+ description: "The background color variant for the card",
+ },
+ onClick: { action: "clicked" },
+ },
+ tags: ["autodocs"],
+};
+
+export const Default = {
+ args: {
+ title: "Consensus clusters",
+ description:
+ "Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council.",
+ backgroundColor: "bg-[var(--color-surface-default-brand-lime)]",
+ icon: (
+
+ ),
+ },
+};
+
+export const AllVariants = {
+ render: (args) => (
+
+
+ }
+ onClick={() => console.log("Consensus clusters selected")}
+ />
+
+ }
+ onClick={() => console.log("Consensus selected")}
+ />
+
+ }
+ onClick={() => console.log("Elected Board selected")}
+ />
+
+ }
+ onClick={() => console.log("Petition selected")}
+ />
+
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "All four governance template variants with their respective colors and icons. Test hover states, focus indicators, and click interactions.",
+ },
+ },
+ },
+};
+
+export const InteractiveStates = {
+ args: {
+ title: "Interactive Demo",
+ description:
+ "Hover over this card to see the scale and shadow effects. Use Tab to focus and Enter/Space to activate.",
+ backgroundColor: "bg-[var(--color-community-teal-100)]",
+ icon: (
+
+ ?
+
+ ),
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Demonstrates interactive states including hover effects, focus indicators, and keyboard navigation. Test with mouse hover and keyboard Tab/Enter/Space.",
+ },
+ },
+ },
+};
+
+export const AccessibilityTest = {
+ args: {
+ title: "Accessibility Demo",
+ description:
+ "This card is designed for accessibility testing. Use Tab to focus, Enter/Space to activate, and screen readers to test ARIA labels.",
+ backgroundColor: "bg-[var(--color-surface-default-brand-teal)]",
+ icon: (
+
+ ♿
+
+ ),
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Specifically designed for testing accessibility features including keyboard navigation, screen reader support, and focus management.",
+ },
+ },
+ },
+};
diff --git a/stories/RuleStack.stories.js b/stories/RuleStack.stories.js
new file mode 100644
index 0000000..c5bca40
--- /dev/null
+++ b/stories/RuleStack.stories.js
@@ -0,0 +1,39 @@
+import RuleStack from "../app/components/RuleStack";
+
+export default {
+ title: "Components/RuleStack",
+ component: RuleStack,
+ parameters: {
+ layout: "fullscreen",
+ docs: {
+ description: {
+ component:
+ "A complete template library component that displays governance patterns in a responsive grid layout. Includes SectionHeader with multi-line variant, interactive RuleCard components, and a call-to-action button. Features comprehensive accessibility, analytics tracking, and responsive design across all breakpoints.\n\n" +
+ "**Testing Scenarios:**\n" +
+ "- **Responsive Testing**: Resize browser window to test layout adaptation from single column on mobile to 2x2 grid on larger screens\n" +
+ "- **Interactive Testing**: Hover over cards to see effects, use Tab to navigate between cards, and click to see analytics events in console\n" +
+ "- **Accessibility Testing**: Use screen readers to test ARIA labels, keyboard navigation to move between cards, and verify focus indicators\n" +
+ "- **Custom Styling**: Add className prop to customize background or other styling",
+ },
+ },
+ },
+ argTypes: {
+ className: {
+ control: { type: "text" },
+ description: "Additional CSS classes for custom styling",
+ },
+ },
+ tags: ["autodocs"],
+};
+
+export const Default = {
+ args: {},
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "The complete RuleStack component with all four governance templates, responsive grid layout, and interactive features. Test hover states, keyboard navigation, and responsive behavior across different screen sizes.",
+ },
+ },
+ },
+};
diff --git a/stories/SectionHeader.stories.js b/stories/SectionHeader.stories.js
index 40bf7b0..a821b94 100644
--- a/stories/SectionHeader.stories.js
+++ b/stories/SectionHeader.stories.js
@@ -8,7 +8,7 @@ export default {
docs: {
description: {
component:
- "A section header component that displays a title and subtitle with responsive typography and layout. Supports different title text for large breakpoints and maintains consistent spacing across all screen sizes.",
+ "A section header component that displays a title and subtitle with responsive typography and layout. Supports different title text for large breakpoints and maintains consistent spacing across all screen sizes. Includes 'default' and 'multi-line' variants with different layout behaviors.",
},
},
},
@@ -26,6 +26,12 @@ export default {
description:
"The title text for lg and xl breakpoints (optional, falls back to title)",
},
+ variant: {
+ control: { type: "select" },
+ options: ["default", "multi-line"],
+ description:
+ "The layout variant - 'default' for traditional layout, 'multi-line' for 50/50 split layout",
+ },
},
tags: ["autodocs"],
};
@@ -35,6 +41,24 @@ export const Default = {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
titleLg: "How CommunityRule helps",
+ variant: "default",
+ },
+};
+
+export const MultiLine = {
+ args: {
+ title: "Popular templates",
+ subtitle:
+ "These are popular patterns for making decisions in mutual aid and open source communities. You can use them as they are or as a starting place for customizing your own CommunityRule.",
+ variant: "multi-line",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "The multi-line variant creates a 50/50 split layout at lg and xl breakpoints, with the title on the left and subtitle on the right. This variant is used in the RuleStack component.",
+ },
+ },
},
};
@@ -44,6 +68,7 @@ export const CustomContent = {
subtitle:
"We're dedicated to helping communities thrive through better decision-making processes and transparent governance structures.",
titleLg: "Building Better Communities",
+ variant: "default",
},
parameters: {
docs: {
@@ -61,6 +86,7 @@ export const LongSubtitle = {
subtitle:
"This is a much longer subtitle that demonstrates how the component handles extended text content across different breakpoints and layout configurations.",
titleLg: "Complex Process Simplified",
+ variant: "default",
},
parameters: {
docs: {
@@ -78,6 +104,7 @@ export const ResponsiveTest = {
subtitle:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
titleLg: "Responsive Design Test",
+ variant: "default",
},
parameters: {
docs: {
@@ -94,6 +121,7 @@ export const WithoutTitleLg = {
title: "Simple Header",
subtitle:
"This example doesn't specify a titleLg prop, so it will use the same title text across all breakpoints.",
+ variant: "default",
},
parameters: {
docs: {
@@ -104,3 +132,20 @@ export const WithoutTitleLg = {
},
},
};
+
+export const MultiLineResponsive = {
+ args: {
+ title: "Multi-line Responsive Test",
+ subtitle:
+ "This multi-line variant demonstrates the 50/50 split layout at larger breakpoints. Resize your browser to see how the layout adapts from stacked on mobile to side-by-side on desktop.",
+ variant: "multi-line",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ "Test the responsive behavior of the multi-line variant. The layout changes from stacked on mobile to 50/50 split on lg and xl breakpoints.",
+ },
+ },
+ },
+};
--
2.43.0
From e76fcc620628f8e04a7307e197a66622f15d6e8a Mon Sep 17 00:00:00 2001
From: adilallo <39313955+adilallo@users.noreply.github.com>
Date: Sun, 24 Aug 2025 22:10:27 -0600
Subject: [PATCH 19/27] Update Rule Stack storybook image paths
---
app/components/RuleStack.js | 8 ++++----
stories/RuleCard.stories.js | 10 +++++-----
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/app/components/RuleStack.js b/app/components/RuleStack.js
index c85fb1d..0e43a6b 100644
--- a/app/components/RuleStack.js
+++ b/app/components/RuleStack.js
@@ -27,7 +27,7 @@ const RuleStack = ({ children, className = "" }) => {
description="Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council."
icon={
{
description="Decisions that affect the group collectively should involve participation of all participants."
icon={
{
description="An elected board determines policies and organizes their implementation."
icon={
{
description="All participants can propose and vote on proposals for the group."
icon={
Date: Mon, 25 Aug 2025 15:52:39 -0600
Subject: [PATCH 20/27] Quote Block default breakpoint added
---
app/components/QuoteBlock.js | 49 ++++++++++++++++++++++++++++++++++
app/components/QuoteDecor.js | 42 +++++++++++++++++++++++++++++
app/components/RuleStack.js | 32 +++++++++++++---------
app/page.js | 2 ++
public/assets/Quote_Avatar.svg | 9 +++++++
5 files changed, 121 insertions(+), 13 deletions(-)
create mode 100644 app/components/QuoteBlock.js
create mode 100644 app/components/QuoteDecor.js
create mode 100644 public/assets/Quote_Avatar.svg
diff --git a/app/components/QuoteBlock.js b/app/components/QuoteBlock.js
new file mode 100644
index 0000000..50149af
--- /dev/null
+++ b/app/components/QuoteBlock.js
@@ -0,0 +1,49 @@
+"use client";
+
+import React from "react";
+import Image from "next/image";
+import QuoteDecor from "./QuoteDecor";
+
+const QuoteBlock = ({ className = "" }) => {
+ return (
+
+ {/* DECORATIONS (behind content) */}
+
+
+
+
+
+
+
+ "The rules of decision-making must be open and
+ available to everyone, and this can happen only if they are
+ formalized."
+
+
+
+
+
+ Jo Freeman
+
+
+ "The Tyranny of Structurelessness"
+
+
+
+
+ );
+};
+
+export default QuoteBlock;
diff --git a/app/components/QuoteDecor.js b/app/components/QuoteDecor.js
new file mode 100644
index 0000000..6d7b9e9
--- /dev/null
+++ b/app/components/QuoteDecor.js
@@ -0,0 +1,42 @@
+"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 (
-
+
);
};
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 @@
+
--
2.43.0
From ccee34f5c866bc3634b67cf6b4aecde56e261e28 Mon Sep 17 00:00:00 2001
From: adilallo <39313955+adilallo@users.noreply.github.com>
Date: Mon, 25 Aug 2025 18:45:13 -0600
Subject: [PATCH 21/27] Quote Block md breakpoint
---
app/components/QuoteBlock.js | 64 ++++++++++++++-------------
app/components/QuoteDecor.js | 85 ++++++++++++++++++++++++------------
2 files changed, 92 insertions(+), 57 deletions(-)
diff --git a/app/components/QuoteBlock.js b/app/components/QuoteBlock.js
index 50149af..04c57dd 100644
--- a/app/components/QuoteBlock.js
+++ b/app/components/QuoteBlock.js
@@ -7,39 +7,43 @@ import QuoteDecor from "./QuoteDecor";
const QuoteBlock = ({ className = "" }) => {
return (