Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions src/components/ActionCard/ActionCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Meta, StoryObj } from "@storybook/nextjs"

import { ButtonLink } from "@/components/ui/buttons/Button"

import ActionCard from "."

import devBlocksImg from "@/public/images/developers-eth-blocks.png"
import enterpriseImg from "@/public/images/enterprise.png"
import communityHeroImg from "@/public/images/heroes/community-hero.png"

const meta = {
title: "Components / Cards / ActionCard",
component: ActionCard,
parameters: {
chromatic: { disableSnapshot: true },
docs: {
description: {
component:
"Large dual-pane action card with a tinted image area and content side, used to surface a single high-emphasis link (e.g. community subpage entry points). The whole card is a single link via `LinkBox` / `LinkOverlay`. Use `isRight` and `isBottom` to nudge the image alignment inside its pane.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-3xl">
<Story />
</div>
),
],
} satisfies Meta<typeof ActionCard>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
title: "Get involved",
description:
"The Ethereum community includes people from all backgrounds, working on many different projects.",
href: "/community/",
image: communityHeroImg,
alt: "Community illustration",
},
}

export const WithoutDescription: Story = {
args: {
title: "Get involved",
href: "/community/",
image: communityHeroImg,
alt: "Community illustration",
},
}

export const WithChildren: Story = {
args: {
title: "Enterprise on Ethereum",
description:
"Public, permissionless infrastructure for building secure, transparent business applications.",
href: "/enterprise/",
image: enterpriseImg,
alt: "Enterprise illustration",
children: (
<ButtonLink href="/enterprise/" variant="outline">
Learn more
</ButtonLink>
),
},
}

export const ImagePositioning: Story = {
args: {
title: "Image positioning",
description:
"Combine `isRight` and `isBottom` to control where the image sits inside its tinted pane.",
href: "#",
image: devBlocksImg,
alt: "Developer blocks illustration",
isRight: true,
isBottom: false,
},
}

export const CommunityPageStyle = {
render: () => (
<div className="grid gap-6 lg:grid-cols-2">
<ActionCard
className="m-0 flex-col rounded-xs border lg:m-4"
title="Get involved"
description="The Ethereum community is the best place to start contributing."
href="/community/"
image={communityHeroImg}
alt="Community illustration"
imageWidth={320}
/>
<ActionCard
className="m-0 flex-col rounded-xs border lg:m-4"
title="Build with Ethereum"
description="Documentation, tools and tutorials for builders of every skill level."
href="/developers/"
image={devBlocksImg}
alt="Developer blocks illustration"
imageWidth={320}
/>
</div>
),
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { StaticImageData } from "next/image"
import type { BaseHTMLAttributes, ElementType, ReactNode } from "react"

import { Image } from "@/components/Image"
import { Flex } from "@/components/ui/flex"
import InlineLink from "@/components/ui/Link"
import { LinkBox, LinkOverlay } from "@/components/ui/link-box"

import { cn } from "@/lib/utils/cn"

import { Flex } from "./ui/flex"
export type ActionCardProps = Omit<
BaseHTMLAttributes<HTMLDivElement>,
"title"
Expand Down
69 changes: 69 additions & 0 deletions src/components/CommentCard/CommentCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Meta, StoryObj } from "@storybook/nextjs"

import { VStack } from "@/components/ui/flex"

import CommentCard from "."

const meta = {
title: "Components / Cards / CommentCard",
component: CommentCard,
parameters: {
chromatic: { disableSnapshot: true },
docs: {
description: {
component:
"Quote-style attribution card used inline within long-form content to attribute a statement to a named person. The avatar circle shows the first letter of `name` over an accent fill.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-2xl">
<Story />
</div>
),
],
} satisfies Meta<typeof CommentCard>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
description:
"Ethereum is a global, decentralized platform for money and new kinds of applications.",
name: "Tim Beiko",
title: "Protocol Coordination, Ethereum Foundation",
},
}

export const LongDescription: Story = {
args: {
description:
"Layer 2 networks settle transactions on Ethereum mainnet while running execution off-chain, which gives users much lower fees and higher throughput without compromising on the security properties of the underlying network. This is the path the ecosystem has converged on for scaling.",
name: "Alex Smirnov",
title: "Co-founder, deBridge",
},
}

export const InlineWithProse = {
render: () => (
<VStack className="items-stretch gap-4">
<p>
Block proposers and attesters earn rewards for participating honestly in
consensus. The economic incentives have so far been sufficient to keep
the network secure under load.
</p>
<CommentCard
description="The validator set has grown faster than I expected. The security guarantees scale with it."
name="Justin Drake"
title="Researcher, Ethereum Foundation"
/>
<p>
Anyone with 32 ETH can run their own validator, and there are also
liquid staking options for smaller stakers.
</p>
</VStack>
),
}
78 changes: 78 additions & 0 deletions src/components/GhostCard/GhostCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Meta, StoryObj } from "@storybook/nextjs"

import Emoji from "@/components/Emoji"
import { VStack } from "@/components/ui/flex"

import GhostCard from "."

const meta = {
title: "Components / Cards / GhostCard",
component: GhostCard,
parameters: {
chromatic: { disableSnapshot: true },
docs: {
description: {
component:
"Card with a subtle offset shadow plate behind it (the 'ghost' layer), used to give a sidebar callout extra visual weight inside long-form content. Accepts arbitrary children -- the wrapper applies the layered look only.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-md">
<Story />
</div>
),
],
} satisfies Meta<typeof GhostCard>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
children: (
<p>A simple card surface with a layered shadow plate behind it.</p>
),
},
}

export const WithEmojiAndHeading: Story = {
args: {
children: (
<>
<Emoji text=":pizza:" className="text-5xl" />
<h3 className="mt-12 mb-8">Bitcoin pizza day</h3>
<p className="mb-0">
The first known commercial Bitcoin transaction in 2010 -- two pizzas
for 10,000 BTC -- is a reminder that volatile assets make poor units
of account. Stablecoins exist to fix that.
</p>
</>
),
},
}

export const SideBySide = {
render: () => (
<VStack className="items-stretch gap-6 md:flex-row md:items-stretch">
<GhostCard className="flex-1">
<Emoji text=":bank:" className="text-4xl" />
<h3 className="mt-6 mb-4">For savers</h3>
<p className="mb-0">
Hold value without exposure to short-term volatility against the US
dollar.
</p>
</GhostCard>
<GhostCard className="flex-1">
<Emoji text=":globe_with_meridians:" className="text-4xl" />
<h3 className="mt-6 mb-4">For payments</h3>
<p className="mb-0">
Move dollar-denominated value across borders in minutes, settled on
public infrastructure.
</p>
</GhostCard>
</VStack>
),
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from "react"

import { cn } from "@/lib/utils/cn"
import { Card } from "@/components/ui/card"

import { Card } from "./ui/card"
import { cn } from "@/lib/utils/cn"

interface GhostCardProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode
Expand Down
109 changes: 109 additions & 0 deletions src/components/HighlightCard/HighlightCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Castle, LockKeyhole, Shield } from "lucide-react"
import { Meta, StoryObj } from "@storybook/nextjs"

import { CardTitle } from "@/components/ui/card"

import { HighlightCard, HighlightCardContent, HighlightStack, IconBox } from "."

const meta = {
title: "Components / Cards / HighlightCard",
component: HighlightCard,
parameters: {
chromatic: { disableSnapshot: true },
docs: {
description: {
component:
"Composition family for the in-content 'highlight' layout: a coloured `IconBox`, an optional `CardTitle`, and `HighlightCardContent` body, optionally stacked through `HighlightStack` to produce the divided list seen on the 'What is Ethereum' / 'What is Ether' pages. None of the parts are linkable on their own -- this is purely a content layout.",
},
},
},
} satisfies Meta<typeof HighlightCard>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
render: () => (
<HighlightCard>
<IconBox>
<Shield className="text-accent-a" />
</IconBox>
<div>
<CardTitle className="mb-2">Censorship resistance</CardTitle>
<HighlightCardContent>
<p>
No government or company has control over Ethereum. Decentralization
makes it nearly impossible for anyone to stop you from receiving
payments or using services on Ethereum.
</p>
</HighlightCardContent>
</div>
</HighlightCard>
),
}

export const Stack: Story = {
render: () => (
<HighlightStack>
<HighlightCard>
<IconBox>
<Shield className="text-accent-a" />
</IconBox>
<div>
<CardTitle className="mb-2">Censorship resistance</CardTitle>
<HighlightCardContent>
<p>
No single entity can stop you from sending value or interacting
with applications on Ethereum.
</p>
</HighlightCardContent>
</div>
</HighlightCard>
<HighlightCard>
<IconBox>
<LockKeyhole className="text-accent-b" />
</IconBox>
<div>
<CardTitle className="mb-2">Strong security guarantees</CardTitle>
<HighlightCardContent>
<p>
Ethereum is secured by hundreds of thousands of validators
distributed worldwide.
</p>
</HighlightCardContent>
</div>
</HighlightCard>
<HighlightCard>
<IconBox>
<Castle className="text-accent-c" />
</IconBox>
<div>
<CardTitle className="mb-2">Reliability</CardTitle>
<HighlightCardContent>
<p>
The network has run continuously since 2015 and is designed for
long-term operation.
</p>
</HighlightCardContent>
</div>
</HighlightCard>
</HighlightStack>
),
}

export const IconBoxOnly: Story = {
render: () => (
<div className="flex gap-4">
<IconBox>
<Shield className="text-accent-a" />
</IconBox>
<IconBox>
<LockKeyhole className="text-accent-b" />
</IconBox>
<IconBox>
<Castle className="text-accent-c" />
</IconBox>
</div>
),
}
Loading
Loading