Numbered Cards #6

Merged
an.di merged 5 commits from adilallo/NumberedCards into main 2025-08-18 15:03:40 +00:00
16 changed files with 927 additions and 263 deletions
+126 -107
View File
@@ -2,10 +2,28 @@ import Logo from "./Logo";
import Separator from "./Separator";
export default function Footer() {
// Schema markup for organization information
const schemaData = {
"@context": "https://schema.org",
"@type": "Organization",
name: "Media Economies Design Lab",
email: "medlab@colorado.edu",
url: "https://communityrule.com",
sameAs: [
"https://bsky.app/profile/medlabboulder",
"https://gitlab.com/medlabboulder",
],
};
return (
<footer className="bg-[var(--color-surface-default-primary)] w-full">
<div
className="flex flex-col items-start mx-auto max-w-[1920px]
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaData) }}
/>
<footer className="bg-[var(--color-surface-default-primary)] w-full">
<div
className="flex flex-col items-start mx-auto max-w-[1920px]
px-[var(--spacing-measures-spacing-016)]
py-[var(--spacing-measures-spacing-040)]
gap-[var(--spacing-measures-spacing-040)]
@@ -15,124 +33,125 @@ export default function Footer() {
lg:px-[var(--spacing-measures-spacing-120,120px)]
lg:py-[var(--spacing-measures-spacing-096,96px)]
lg:gap-[var(--spacing-measures-spacing-060,60px)]"
>
{/* Logo*/}
<div className="block sm:hidden">
<Logo />
</div>
<div className="hidden sm:block lg:hidden">
<Logo size="footer" />
</div>
<div className="hidden lg:block">
<Logo size="footerLg" />
</div>
>
{/* Logo*/}
<div className="block sm:hidden">
<Logo />
</div>
<div className="hidden sm:block lg:hidden">
<Logo size="footer" />
</div>
<div className="hidden lg:block">
<Logo size="footerLg" />
</div>
{/* Content section */}
<div className="flex flex-col items-start w-full gap-[var(--spacing-measures-spacing-048,48px)] sm:flex-row sm:justify-between sm:gap-0">
{/* Branding Section */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-064,64px)] order-2 sm:order-1">
{/* Contact info */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-016,16px)]">
<div className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal">
Media Economies Design Lab
{/* Content section */}
<div className="flex flex-col items-start w-full gap-[var(--spacing-measures-spacing-048,48px)] sm:flex-row sm:justify-between sm:gap-0">
{/* Branding Section */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-064,64px)] order-2 sm:order-1">
{/* Contact info */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-016,16px)]">
<div className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal">
Media Economies Design Lab
</div>
<a
href="mailto:medlab@colorado.edu"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
medlab@colorado.edu
</a>
</div>
{/* Social media links */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-016,16px)]">
<a
href="#"
className="flex items-center gap-[var(--spacing-measures-spacing-06,6px)] hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer group"
aria-label="Follow us on Bluesky"
>
<img
src="/assets/Bluesky_Logo.svg"
alt="Bluesky"
width={24}
height={22}
className="flex-shrink-0 group-hover:scale-110 transition-transform"
/>
<div className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal">
medlabboulder
</div>
</a>
<a
href="#"
className="flex items-center gap-[var(--spacing-measures-spacing-06,6px)] hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer group"
aria-label="Follow us on GitLab"
>
<img
src="/assets/GitLab_Icon.png"
alt="GitLab"
width={22}
height={22}
className="flex-shrink-0 grayscale group-hover:scale-110 transition-transform"
/>
<div className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal">
medlabboulder
</div>
</a>
</div>
</div>
{/* Navigation Section */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-032,32px)] order-1 sm:order-2 sm:items-end">
<a
href="mailto:medlab@colorado.edu"
href="#"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
medlab@colorado.edu
</a>
</div>
{/* Social media links */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-016,16px)]">
<a
href="#"
className="flex items-center gap-[var(--spacing-measures-spacing-06,6px)] hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer group"
aria-label="Follow us on Bluesky"
>
<img
src="/assets/Bluesky_Logo.svg"
alt="Bluesky"
width={24}
height={22}
className="flex-shrink-0 group-hover:scale-110 transition-transform"
/>
<div className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal">
medlabboulder
</div>
Use cases
</a>
<a
href="#"
className="flex items-center gap-[var(--spacing-measures-spacing-06,6px)] hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer group"
aria-label="Follow us on GitLab"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
<img
src="/assets/GitLab_Icon.png"
alt="GitLab"
width={22}
height={22}
className="flex-shrink-0 grayscale group-hover:scale-110 transition-transform"
/>
<div className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal">
medlabboulder
</div>
Learn
</a>
<a
href="#"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
About
</a>
</div>
</div>
{/* Navigation Section */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-032,32px)] order-1 sm:order-2 sm:items-end">
<a
href="#"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Use cases
</a>
<a
href="#"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Learn
</a>
<a
href="#"
className="text-[var(--color-content-default-primary)] font-['Inter'] text-base leading-5 font-medium tracking-[0%] lg:text-2xl lg:leading-7 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-primary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
About
</a>
<Separator />
{/* Bottom section */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-032,32px)] sm:flex-row sm:justify-between sm:items-center w-full">
<div className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium sm:text-xs sm:leading-4 lg:text-sm lg:leading-5 lg:font-normal">
© All right reserved
</div>
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-040,40px)] sm:flex-row sm:gap-[var(--spacing-measures-spacing-040,40px)]">
<a
href="#"
className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium underline sm:text-xs sm:leading-4 sm:no-underline lg:text-sm lg:leading-5 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-secondary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Privacy Policy
</a>
<a
href="#"
className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium underline sm:text-xs sm:leading-4 sm:no-underline lg:text-sm lg:leading-5 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-secondary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Terms of Service
</a>
<a
href="#"
className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium underline sm:text-xs sm:leading-4 sm:no-underline lg:text-sm lg:leading-5 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-secondary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Cookies Settings
</a>
</div>
</div>
</div>
<Separator />
{/* Bottom section */}
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-032,32px)] sm:flex-row sm:justify-between sm:items-center w-full">
<div className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium sm:text-xs sm:leading-4 lg:text-sm lg:leading-5 lg:font-normal">
© All right reserved
</div>
<div className="flex flex-col items-start gap-[var(--spacing-measures-spacing-040,40px)] sm:flex-row sm:gap-[var(--spacing-measures-spacing-040,40px)]">
<a
href="#"
className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium underline sm:text-xs sm:leading-4 sm:no-underline lg:text-sm lg:leading-5 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-secondary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Privacy Policy
</a>
<a
href="#"
className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium underline sm:text-xs sm:leading-4 sm:no-underline lg:text-sm lg:leading-5 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-secondary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Terms of Service
</a>
<a
href="#"
className="text-[var(--color-content-default-secondary)] font-['Inter'] text-sm leading-5 font-medium underline sm:text-xs sm:leading-4 sm:no-underline lg:text-sm lg:leading-5 lg:font-normal hover:opacity-80 active:opacity-60 focus:opacity-80 focus:outline-none focus:ring-2 focus:ring-[var(--color-content-default-secondary)] focus:ring-offset-2 focus:ring-offset-[var(--color-surface-default-primary)] transition-opacity p-2 -m-2 cursor-pointer"
>
Cookies Settings
</a>
</div>
</div>
</div>
</footer>
</footer>
</>
);
}
+94 -74
View File
@@ -6,6 +6,18 @@ import AvatarContainer from "./AvatarContainer";
import Avatar from "./Avatar";
export default function Header({ onToggle }) {
// Schema markup for site navigation
const schemaData = {
"@context": "https://schema.org",
"@type": "WebSite",
name: "CommunityRule",
url: "https://communityrule.com",
potentialAction: {
"@type": "SearchAction",
target: "https://communityrule.com/search?q={search_term_string}",
"query-input": "required name=search_term_string",
},
};
const navigationItems = [
{ href: "#", text: "Use cases", extraPadding: true },
{ href: "#", text: "Learn" },
@@ -88,96 +100,104 @@ export default function Header({ onToggle }) {
};
return (
<header
className="bg-[var(--color-surface-default-primary)] w-full border-b border-[var(--border-color-default-tertiary)]"
role="banner"
aria-label="Main navigation header"
>
<nav
className="flex items-center justify-between mx-auto max-w-[1920px] h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]"
role="navigation"
aria-label="Main navigation"
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaData) }}
/>
<header
className="bg-[var(--color-surface-default-primary)] w-full border-b border-[var(--border-color-default-tertiary)]"
role="banner"
aria-label="Main navigation header"
>
{/* Logo - Consistent left positioning across all breakpoints */}
<div className="flex items-center">
{logoConfig.map((config, index) => (
<div key={index} className={config.breakpoint}>
{renderLogo(config.size, config.showText)}
<nav
className="flex items-center justify-between mx-auto max-w-[1920px] h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]"
role="navigation"
aria-label="Main navigation"
>
{/* Logo - Consistent left positioning across all breakpoints */}
<div className="flex items-center">
{logoConfig.map((config, index) => (
<div key={index} className={config.breakpoint}>
{renderLogo(config.size, config.showText)}
</div>
))}
</div>
{/* Navigation Links - Consistent center positioning */}
<div className="flex items-center">
{/* XSmall breakpoint - Navigation items moved to right section */}
<div className="block sm:hidden">
{/* Empty for XSmall - navigation moved to right */}
</div>
))}
</div>
{/* Navigation Links - Consistent center positioning */}
<div className="flex items-center">
{/* XSmall breakpoint - Navigation items moved to right section */}
<div className="block sm:hidden">
{/* Empty for XSmall - navigation moved to right */}
</div>
{/* Small breakpoint - All items grouped together, centered */}
<div className="hidden sm:block md:hidden">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
</MenuBar>
</div>
{/* Small breakpoint - All items grouped together, centered */}
<div className="hidden sm:block md:hidden">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
</MenuBar>
</div>
<div className="hidden md:block lg:hidden">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
</MenuBar>
</div>
<div className="hidden md:block lg:hidden">
<MenuBar size="default">{renderNavigationItems("xsmall")}</MenuBar>
</div>
<div className="hidden lg:block xl:hidden">
<MenuBar size="large">{renderNavigationItems("large")}</MenuBar>
</div>
<div className="hidden lg:block xl:hidden">
<MenuBar size="large">{renderNavigationItems("large")}</MenuBar>
</div>
<div className="hidden xl:block">
<MenuBar size="large">{renderNavigationItems("xlarge")}</MenuBar>
</div>
</div>
{/* Authentication Elements - Consistent right alignment across all breakpoints */}
<div className="flex items-center">
{/* XSmall breakpoint - All navigation items + Create Rule button */}
<div className="block sm:hidden">
<div className="flex items-center gap-[var(--spacing-scale-001)]">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
{renderCreateRuleButton("xsmall", "small", "small")}
<div className="hidden xl:block">
<MenuBar size="large">{renderNavigationItems("xlarge")}</MenuBar>
</div>
</div>
{/* Small breakpoint - Only Create Rule button */}
<div className="hidden sm:block md:hidden">
<div className="flex items-center gap-[var(--spacing-scale-004)]">
{renderCreateRuleButton("xsmall", "small", "small")}
{/* Authentication Elements - Consistent right alignment across all breakpoints */}
<div className="flex items-center">
{/* XSmall breakpoint - All navigation items + Create Rule button */}
<div className="block sm:hidden">
<div className="flex items-center gap-[var(--spacing-scale-001)]">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
{renderCreateRuleButton("xsmall", "small", "small")}
</div>
</div>
</div>
{/* Medium breakpoint */}
<div className="hidden md:block lg:hidden">
<div className="flex items-center gap-[var(--spacing-measures-spacing-010)]">
{renderLoginButton("xsmall")}
{renderCreateRuleButton("xsmall", "medium", "medium")}
{/* Small breakpoint - Only Create Rule button */}
<div className="hidden sm:block md:hidden">
<div className="flex items-center gap-[var(--spacing-scale-004)]">
{renderCreateRuleButton("xsmall", "small", "small")}
</div>
</div>
</div>
{/* Large breakpoint */}
<div className="hidden lg:block xl:hidden">
<div className="flex items-center gap-[var(--spacing-measures-spacing-004)]">
{renderLoginButton("large")}
{renderCreateRuleButton("large", "xlarge", "xlarge")}
{/* Medium breakpoint */}
<div className="hidden md:block lg:hidden">
<div className="flex items-center gap-[var(--spacing-measures-spacing-010)]">
{renderLoginButton("xsmall")}
{renderCreateRuleButton("xsmall", "medium", "medium")}
</div>
</div>
</div>
{/* XLarge breakpoint */}
<div className="hidden xl:block">
<div className="flex items-center gap-[var(--spacing-measures-spacing-004)]">
{renderLoginButton("xlarge")}
{renderCreateRuleButton("xlarge", "xlarge", "xlarge")}
{/* Large breakpoint */}
<div className="hidden lg:block xl:hidden">
<div className="flex items-center gap-[var(--spacing-measures-spacing-004)]">
{renderLoginButton("large")}
{renderCreateRuleButton("large", "xlarge", "xlarge")}
</div>
</div>
{/* XLarge breakpoint */}
<div className="hidden xl:block">
<div className="flex items-center gap-[var(--spacing-measures-spacing-004)]">
{renderLoginButton("xlarge")}
{renderCreateRuleButton("xlarge", "xlarge", "xlarge")}
</div>
</div>
</div>
</div>
</nav>
</header>
</nav>
</header>
</>
);
}
+1 -1
View File
@@ -22,7 +22,7 @@ export default function HeaderTab({
<img
src="/assets/Union_sm_md_lg.svg"
alt="Union"
className="absolute -bottom-[3.5px] -right-[53px] w-[61px] h-[24px] sm:w-[61px] sm:h-[31.5px] hidden sm:block xl:hidden -z-10"
className="absolute -bottom-[3.7px] -right-[53px] w-[61px] h-[24px] sm:w-[61px] sm:h-[31.5px] hidden sm:block xl:hidden -z-10"
/>
<img
src="/assets/Union_xlg.svg"
+100 -80
View File
@@ -13,6 +13,20 @@ import Header from "./Header";
export default function HomeHeader() {
const [showRegularHeader, setShowRegularHeader] = useState(false);
// Schema markup for site navigation (home page specific)
const schemaData = {
"@context": "https://schema.org",
"@type": "WebSite",
name: "CommunityRule",
url: "https://communityrule.com",
description: "Build operating manuals for successful communities",
potentialAction: {
"@type": "SearchAction",
target: "https://communityrule.com/search?q={search_term_string}",
"query-input": "required name=search_term_string",
},
};
const navigationItems = [
{ href: "#", text: "Use cases", extraPadding: true },
{ href: "#", text: "Learn" },
@@ -139,91 +153,97 @@ export default function HomeHeader() {
}
return (
<header
className="w-full bg-transparent overflow-hidden"
role="banner"
aria-label="Home page navigation header"
>
<nav
className="relative flex items-center justify-between mx-auto max-w-[1920px] h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
role="navigation"
aria-label="Main navigation"
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaData) }}
/>
<header
className="w-full bg-transparent overflow-hidden"
role="banner"
aria-label="Home page navigation header"
>
<HeaderTab className="flex items-center self-end" stretch={true}>
{/* Logo - Consistent left positioning within HeaderTab */}
<div>
{logoConfig.map((config, index) => (
<div key={index} className={config.breakpoint}>
{renderLogo(config.size, config.showText)}
<nav
className="relative flex items-center justify-between mx-auto max-w-[1920px] h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
role="navigation"
aria-label="Main navigation"
>
<HeaderTab className="flex items-center self-end" stretch={true}>
{/* Logo - Consistent left positioning within HeaderTab */}
<div>
{logoConfig.map((config, index) => (
<div key={index} className={config.breakpoint}>
{renderLogo(config.size, config.showText)}
</div>
))}
</div>
{/* XSmall menu bar - positioned next to logo */}
<div className="block sm:hidden -me-[2px]">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
</MenuBar>
</div>
</HeaderTab>
{/* Navigation Links - Centered in header for SM and up */}
<div className="absolute left-1/2 transform -translate-x-1/2 hidden sm:block">
<div className="hidden sm:block md:hidden">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
</MenuBar>
</div>
<div className="hidden md:block lg:hidden">
<MenuBar size="medium">{renderNavigationItems("homeMd")}</MenuBar>
</div>
<div className="hidden lg:block xl:hidden">
<MenuBar size="large">{renderNavigationItems("large")}</MenuBar>
</div>
<div className="hidden xl:block">
<MenuBar size="large">
{renderNavigationItems("homeXlarge")}
</MenuBar>
</div>
</div>
{/* Authentication Elements - Consistent right alignment outside HeaderTab */}
<div className="flex items-center">
{/* XSmall and Small breakpoints - create rule button outside HeaderTab */}
<div className="block md:hidden">
{renderCreateRuleButton("xsmall", "small", "small")}
</div>
{/* Medium breakpoint - login outside HeaderTab, create rule outside */}
<div className="hidden md:block lg:hidden absolute right-[var(--spacing-measures-spacing-016)]">
<div className="flex items-center gap-[var(--spacing-scale-010)]">
{renderLoginButton("homeMd")}
{renderCreateRuleButton("small", "medium", "medium")}
</div>
))}
</div>
</div>
{/* XSmall menu bar - positioned next to logo */}
<div className="block sm:hidden -me-[2px]">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
</MenuBar>
</div>
</HeaderTab>
{/* Large breakpoint */}
<div className="hidden lg:flex xl:hidden items-center">
<div className="flex items-center gap-[var(--spacing-scale-004)]">
{renderLoginButton("large")}
{renderCreateRuleButton("large", "large", "large")}
</div>
</div>
{/* Navigation Links - Centered in header for SM and up */}
<div className="absolute left-1/2 transform -translate-x-1/2 hidden sm:block">
<div className="hidden sm:block md:hidden">
<MenuBar size="default">
{renderNavigationItems("xsmall")}
{renderLoginButton("xsmall")}
</MenuBar>
</div>
<div className="hidden md:block lg:hidden">
<MenuBar size="medium">{renderNavigationItems("homeMd")}</MenuBar>
</div>
<div className="hidden lg:block xl:hidden">
<MenuBar size="large">{renderNavigationItems("large")}</MenuBar>
</div>
<div className="hidden xl:block">
<MenuBar size="large">
{renderNavigationItems("homeXlarge")}
</MenuBar>
</div>
</div>
{/* Authentication Elements - Consistent right alignment outside HeaderTab */}
<div className="flex items-center">
{/* XSmall and Small breakpoints - create rule button outside HeaderTab */}
<div className="block md:hidden">
{renderCreateRuleButton("xsmall", "small", "small")}
</div>
{/* Medium breakpoint - login outside HeaderTab, create rule outside */}
<div className="hidden md:block lg:hidden absolute right-[var(--spacing-measures-spacing-016)]">
<div className="flex items-center gap-[var(--spacing-scale-010)]">
{renderLoginButton("homeMd")}
{renderCreateRuleButton("small", "medium", "medium")}
{/* XLarge breakpoint */}
<div className="hidden xl:flex items-center">
<div className="flex items-center gap-[var(--spacing-scale-004)]">
{renderLoginButton("homeXlarge")}
{renderCreateRuleButton("xlarge", "xlarge", "xlarge")}
</div>
</div>
</div>
{/* Large breakpoint */}
<div className="hidden lg:flex xl:hidden items-center">
<div className="flex items-center gap-[var(--spacing-scale-004)]">
{renderLoginButton("large")}
{renderCreateRuleButton("large", "large", "large")}
</div>
</div>
{/* XLarge breakpoint */}
<div className="hidden xl:flex items-center">
<div className="flex items-center gap-[var(--spacing-scale-004)]">
{renderLoginButton("homeXlarge")}
{renderCreateRuleButton("xlarge", "xlarge", "xlarge")}
</div>
</div>
</div>
</nav>
</header>
</nav>
</header>
</>
);
}
+23
View File
@@ -0,0 +1,23 @@
"use client";
import SectionNumber from "./SectionNumber";
const NumberedCard = ({ number, text, iconShape, iconColor }) => {
return (
<div className="bg-[var(--color-surface-inverse-primary)] rounded-[12px] p-5 shadow-lg flex flex-col gap-4 sm:p-8 sm:gap-8 sm:flex-row sm:items-center lg:p-8 lg:gap-0 lg:flex-row lg:items-stretch lg:relative lg:h-[238px]">
{/* Section Number - Top right (lg breakpoint) */}
<div className="flex justify-end sm:justify-start sm:flex-shrink-0 lg:absolute lg:top-8 lg:right-8">
<SectionNumber number={number} />
</div>
{/* Card Content - Bottom left (lg breakpoint) */}
<div className="sm:flex-1 lg:absolute lg:bottom-8 lg:left-8 lg:right-16">
<p className="font-bricolage-grotesque font-medium text-[24px] leading-[32px] sm:font-normal sm:leading-[24px] sm:text-[24px] lg:text-[24px] lg:leading-[24px] xl:text-[32px] xl:leading-[32px] text-[#141414]">
{text}
</p>
</div>
</div>
);
};
export default NumberedCard;
+75
View File
@@ -0,0 +1,75 @@
"use client";
import NumberedCard from "./NumberedCard";
import SectionHeader from "./SectionHeader";
import Button from "./Button";
const NumberedCards = ({ title, subtitle, cards }) => {
// Schema markup for SEO
const schemaData = {
"@context": "https://schema.org",
"@type": "HowTo",
name: title,
description: subtitle,
step: cards.map((card, index) => ({
"@type": "HowToStep",
position: index + 1,
name: card.text,
text: card.text,
})),
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaData) }}
/>
<section className="bg-transparent py-[var(--spacing-scale-032)] px-[var(--spacing-scale-020)] sm:py-[var(--spacing-scale-048)] sm:px-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)] lg:px-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-076)] xl:px-[var(--spacing-scale-064)]">
<div className="max-w-[var(--spacing-measures-max-width-lg)] mx-auto">
<div className="grid grid-cols-1 gap-y-[var(--spacing-scale-032)] lg:gap-y-[var(--spacing-scale-056)]">
{/* Section Header */}
<div>
<SectionHeader
title={title}
subtitle={subtitle}
titleLg="How CommunityRule helps"
/>
</div>
{/* Cards Container */}
<div className="grid grid-cols-1 gap-y-[var(--spacing-scale-024)] lg:grid-cols-3 lg:gap-[var(--spacing-scale-024)]">
{cards.map((card, index) => (
<NumberedCard
key={index}
number={index + 1}
text={card.text}
iconShape={card.iconShape}
iconColor={card.iconColor}
/>
))}
</div>
{/* Call to Action Button */}
<div className="text-center sm:text-left lg:text-center">
{/* Default button for xsm and sm breakpoints */}
<div className="block lg:hidden">
<Button variant="default" size="large">
Create CommunityRule
</Button>
</div>
{/* Outlined button for lg and xlg breakpoints */}
<div className="hidden lg:block">
<Button variant="outlined" size="large">
See how it works
</Button>
</div>
</div>
</div>
</div>
</section>
</>
);
};
export default NumberedCards;
+24
View File
@@ -0,0 +1,24 @@
"use client";
const SectionHeader = ({ title, subtitle, titleLg }) => {
return (
<div className="flex flex-col gap-1 w-full lg:flex-row lg:justify-between lg:items-start xl:gap-[var(--spacing-scale-024)]">
{/* Title Container - Left side (lg breakpoint) */}
<div className="lg:w-[369px] lg:h-[120px] lg:flex lg:items-center xl:w-[452px] xl:h-[156px] xl:flex xl:items-center">
<h2 className="font-bricolage-grotesque font-bold text-[28px] leading-[36px] sm:text-[32px] sm:leading-[40px] lg:text-[32px] lg:leading-[40px] lg:w-[369px] lg:pr-24 xl:text-[40px] xl:leading-[52px] xl:w-[452px] xl:pr-24 text-[var(--color-content-default-primary)]">
<span className="block lg:hidden">{title}</span>
<span className="hidden lg:block">{titleLg || title}</span>
</h2>
</div>
{/* Subtitle Container */}
<div className="lg:w-[928px] lg:h-[120px] lg:flex lg:items-center lg:justify-end xl:w-[763px] xl:h-[156px] xl:flex xl:items-center xl:justify-end">
<p className="font-inter font-normal text-[18px] leading-[130%] sm:text-[18px] sm:leading-[32px] lg:text-[24px] lg:leading-[32px] xl:text-[32px] xl:leading-[40px] xl:text-right text-[#484848] sm:text-[var(--color-content-default-tertiary)] lg:text-[var(--color-content-default-tertiary)] xl:text-[var(--color-content-default-tertiary)] tracking-[0px]">
{subtitle}
</p>
</div>
</div>
);
};
export default SectionHeader;
+33
View File
@@ -0,0 +1,33 @@
"use client";
const SectionNumber = ({ number }) => {
const getImageSrc = (num) => {
switch (num) {
case 1:
return "/assets/SectionNumber_1.png";
case 2:
return "/assets/SectionNumber_2.png";
case 3:
return "/assets/SectionNumber_3.png";
default:
return "/assets/SectionNumber_1.png";
}
};
return (
<div className="relative size-[40px] overflow-visible -rotate-[15deg]">
<img
src={getImageSrc(number)}
alt={`Section ${number}`}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 size-[47.37px] max-w-none"
/>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-[var(--font-size-body-small)] font-[var(--font-weight-bold)] text-[var(--color-content-inverse-primary)]">
{number}
</span>
</div>
</div>
);
};
export default SectionNumber;
+29 -1
View File
@@ -1,3 +1,31 @@
import NumberedCards from "./components/NumberedCards";
export default function Page() {
return <div>{/* home page content will go here */}</div>;
const numberedCardsData = {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
cards: [
{
text: "Document how your community makes decisions",
iconShape: "blob",
iconColor: "green",
},
{
text: "Build an operating manual for a successful community",
iconShape: "gear",
iconColor: "purple",
},
{
text: "Get a link to your manual for your group to review and evolve",
iconShape: "star",
iconColor: "orange",
},
],
};
return (
<div>
<NumberedCards {...numberedCardsData} />
</div>
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+113
View File
@@ -0,0 +1,113 @@
import NumberedCard from "../app/components/NumberedCard";
export default {
title: "Components/NumberedCard",
component: NumberedCard,
parameters: {
layout: "centered",
docs: {
description: {
component:
"Individual numbered card component that displays a step in a process with a numbered icon and descriptive text. Supports responsive layouts across different breakpoints.",
},
},
},
argTypes: {
number: {
control: { type: "number", min: 1, max: 9 },
description: "The number to display on the card",
},
text: {
control: { type: "text" },
description: "The descriptive text for this step",
},
iconShape: {
control: { type: "select" },
options: ["blob", "gear", "star"],
description:
"The shape of the icon background (currently not used, uses PNG images)",
},
iconColor: {
control: { type: "select" },
options: ["green", "purple", "orange", "blue"],
description:
"The color theme for the icon (currently not used, uses PNG images)",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
number: 1,
text: "Document how your community makes decisions",
iconShape: "blob",
iconColor: "green",
},
};
export const AllNumbers = {
args: {
number: 1,
text: "Example card text",
iconShape: "blob",
iconColor: "green",
},
render: (args) => (
<div className="space-y-4">
<NumberedCard {...args} number={1} text="First step in the process" />
<NumberedCard
{...args}
number={2}
text="Second step with different content"
/>
<NumberedCard
{...args}
number={3}
text="Third and final step of the workflow"
/>
</div>
),
parameters: {
docs: {
description: {
story:
"Shows all three numbered cards with different content to demonstrate the visual hierarchy.",
},
},
},
};
export const LongText = {
args: {
number: 1,
text: "This is a much longer piece of text that demonstrates how the card handles content that spans multiple lines and requires more space to display properly",
iconShape: "blob",
iconColor: "green",
},
parameters: {
docs: {
description: {
story:
"Demonstrates how the card handles longer text content across different breakpoints.",
},
},
},
};
export const ResponsiveTest = {
args: {
number: 2,
text: "Test responsive behavior by resizing your browser window",
iconShape: "gear",
iconColor: "purple",
},
parameters: {
docs: {
description: {
story:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
},
},
},
};
+125
View File
@@ -0,0 +1,125 @@
import NumberedCards from "../app/components/NumberedCards";
export default {
title: "Components/NumberedCards",
component: NumberedCards,
parameters: {
layout: "fullscreen",
docs: {
description: {
component:
"A component system for visually communicating multi-step workflows, processes, or value propositions. The component's modular design with NumberedCard and SectionNumber sub-components makes it ideal for explaining any sequential process while maintaining brand consistency and accessibility standards across the design system.",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title for the section",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text below the main title",
},
cards: {
control: { type: "object" },
description:
"Array of card objects with text, iconShape, and iconColor properties",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
cards: [
{
text: "Document how your community makes decisions",
iconShape: "blob",
iconColor: "green",
},
{
text: "Build an operating manual for a successful community",
iconShape: "gear",
iconColor: "purple",
},
{
text: "Get a link to your manual for your group to review and evolve",
iconShape: "star",
iconColor: "orange",
},
],
},
};
export const CustomContent = {
args: {
title: "Our Process",
subtitle: "Follow these simple steps to get started with your project.",
cards: [
{
text: "Define your project requirements and goals",
iconShape: "blob",
iconColor: "green",
},
{
text: "Collaborate with our team to create the perfect solution",
iconShape: "gear",
iconColor: "purple",
},
{
text: "Launch and iterate based on user feedback",
iconShape: "star",
iconColor: "orange",
},
{
text: "Scale and optimize for continued success",
iconShape: "blob",
iconColor: "blue",
},
],
},
parameters: {
docs: {
description: {
story:
"Example with custom content and four cards to show flexibility.",
},
},
},
};
export const ResponsiveBreakpoints = {
args: {
title: "Responsive Design Test",
subtitle:
"This story demonstrates how the component adapts across different breakpoints: xsm, sm, lg, and xl.",
cards: [
{
text: "Mobile-first design approach",
iconShape: "blob",
iconColor: "green",
},
{
text: "Tablet and desktop optimization",
iconShape: "gear",
iconColor: "purple",
},
{
text: "Large screen layouts and spacing",
iconShape: "star",
iconColor: "orange",
},
],
},
parameters: {
docs: {
description: {
story:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
},
},
},
};
+106
View File
@@ -0,0 +1,106 @@
import SectionHeader from "../app/components/SectionHeader";
export default {
title: "Components/SectionHeader",
component: SectionHeader,
parameters: {
layout: "centered",
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.",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title text (used for xsm and sm breakpoints)",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text below the main title",
},
titleLg: {
control: { type: "text" },
description:
"The title text for lg and xl breakpoints (optional, falls back to title)",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
titleLg: "How CommunityRule helps",
},
};
export const CustomContent = {
args: {
title: "Our Mission",
subtitle:
"We're dedicated to helping communities thrive through better decision-making processes and transparent governance structures.",
titleLg: "Building Better Communities",
},
parameters: {
docs: {
description: {
story:
"Example with custom content to show the flexibility of the component.",
},
},
},
};
export const LongSubtitle = {
args: {
title: "Complex Process",
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",
},
parameters: {
docs: {
description: {
story:
"Demonstrates how the component handles longer subtitle text across different breakpoints.",
},
},
},
};
export const ResponsiveTest = {
args: {
title: "Responsive Design",
subtitle:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
titleLg: "Responsive Design Test",
},
parameters: {
docs: {
description: {
story:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
},
},
},
};
export const WithoutTitleLg = {
args: {
title: "Simple Header",
subtitle:
"This example doesn't specify a titleLg prop, so it will use the same title text across all breakpoints.",
},
parameters: {
docs: {
description: {
story:
"Shows the component without a titleLg prop, demonstrating the fallback behavior.",
},
},
},
};
+78
View File
@@ -0,0 +1,78 @@
import SectionNumber from "../app/components/SectionNumber";
export default {
title: "Components/SectionNumber",
component: SectionNumber,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A numbered icon component that displays a number overlaid on a PNG background image. The component uses different PNG images for numbers 1, 2, and 3, with the image extending beyond the 40px container size.",
},
},
},
argTypes: {
number: {
control: { type: "number", min: 1, max: 3 },
description: "The number to display (1, 2, or 3)",
},
},
tags: ["autodocs"],
};
export const NumberOne = {
args: {
number: 1,
},
};
export const NumberTwo = {
args: {
number: 2,
},
};
export const NumberThree = {
args: {
number: 3,
},
};
export const AllNumbers = {
render: () => (
<div className="flex space-x-4">
<SectionNumber number={1} />
<SectionNumber number={2} />
<SectionNumber number={3} />
</div>
),
parameters: {
docs: {
description: {
story:
"Shows all three numbered icons side by side to demonstrate the different PNG backgrounds.",
},
},
},
};
export const WithBackground = {
render: () => (
<div className="bg-gray-100 p-8 rounded-lg">
<div className="flex space-x-4">
<SectionNumber number={1} />
<SectionNumber number={2} />
<SectionNumber number={3} />
</div>
</div>
),
parameters: {
docs: {
description: {
story:
"Shows the numbered icons on a background to demonstrate how the PNG images extend beyond the container.",
},
},
},
};