- Yappr is a decentralized social media platform built on Dash Platform. Unlike traditional social
- networks where a company owns and controls your data, Yappr stores everything on a blockchain.
- You truly own your identity, your content, and your social connections. No company can ban you,
- delete your posts, or shut down the service.
-
-
+const FEATURES: Feature[] = [
+ { title: 'Posts', desc: 'Share your thoughts in up to 500 characters' },
+ { title: 'Profiles', desc: 'Customize your name, bio, avatar, and banner' },
+ { title: 'Follow', desc: 'Build your network and see updates from people you follow' },
+ { title: 'Likes & Reposts', desc: 'Engage with content you enjoy' },
+ { title: 'Encrypted DMs', desc: 'Private conversations, encrypted end-to-end' },
+ { title: 'Private Feeds', desc: 'Encrypted posts visible only to approved followers', href: '/about/private-feeds', icon: LockClosedIcon },
+ { title: 'Store', desc: 'Create a storefront, list items, and sell with Dash', href: '/store', icon: ShoppingBagIcon },
+ { title: 'Tips', desc: 'Send Dash tips directly to users you appreciate', icon: CurrencyDollarIcon },
+ { title: 'Notifications', desc: 'Tabbed alerts with unread indicators and preferences', icon: BellIcon },
+ { title: 'Bookmarks', desc: 'Save posts to revisit later' },
+ { title: 'Blocking', desc: 'Control what you see in your feed' },
+]
+
+const RESOURCES = [
+ { href: 'https://dashplatform.readme.io', title: 'Dash Platform Docs', desc: 'Learn about the underlying technology' },
+ { href: 'https://www.dash.org', title: 'Dash.org', desc: 'The Dash cryptocurrency project' },
+]
+
+function FeatureCard({ feature }: { feature: Feature }) {
+ const card = (
+
-
+export default function AboutPage() {
+ return (
+
+
+
+ Yappr is a decentralized social media platform built on Dash Platform. Unlike traditional social networks where a company
+ owns and controls your data, Yappr stores everything on a blockchain. You truly own your identity, your content, and your
+ social connections. No company can ban you, delete your posts, or shut down the service.
+
+
- {/* No Central Server */}
-
-
-
-
No Central Server
-
-
- Yappr has no backend servers. This website is just a client application that runs entirely in your
- browser. It connects directly to Dash Platform's decentralized network of nodes. If this website
- went offline, your data would still exist on the blockchain, and other applications could access it.
-
-
+
+
+
When you use Yappr, you're interacting directly with the Dash Platform blockchain:
+
+
+ Your identity is a cryptographic key pair. Your private key is your password, your public key is your ID.
+
+
+ Your posts are documents stored on the blockchain, signed with your private key to prove you created them.
+
+
+ Your username comes from DPNS (Dash Platform Name Service), mapping a human-readable name to your identity.
+
+
+ Social actions like follows, likes, and reposts are all blockchain documents that anyone can verify.
+
+
+
+
- {/* Open Source */}
-
-
-
-
Open Source
-
-
- Yappr is open source software. Anyone can view the code, verify what it does, suggest improvements,
- or create their own version. Transparency builds trust.
-
- Yappr is a community project. There's no company behind it, no investors to please, no ads to
- sell. It exists because people believe in the idea of social media that users actually own.
- Contributions, feedback, and ideas are always welcome.
-
-
+
+
+ Yappr has no backend servers. This website is just a client application that runs entirely in your browser. It connects
+ directly to Dash Platform's decentralized network of nodes. If this website went offline, your data would still exist on
+ the blockchain, and other applications could access it.
+
+
- {/* Technical Details */}
-
-
-
-
Technical Details
-
-
-
-
Contract ID
-
{YAPPR_CONTRACT_ID}
-
-
-
Network
-
{process.env.NEXT_PUBLIC_NETWORK || 'testnet'}
-
-
- {/* The contract id above does not say which SHAPE of contract it
- is, and the two are not interchangeable — a v2 client pointed
- at a v3 contract queries fields that do not exist. Printing
- the topology the bundle was compiled with makes that
- verifiable from the served artifact (the e2e suite gates on it). */}
-
Interaction Topology
-
{getContractTopology()}
-
-
-
Document Types
-
23 types across 2 contracts (social + storefront)
-
-
-
-
- View Full Data Contract
-
-
-
-
+
+
+ Yappr is open source software. Anyone can view the code, verify what it does, suggest improvements, or create their own
+ version. Transparency builds trust.
+
+
+
+ View on GitHub
+
+
- {/* Resources */}
-
-
-
+
+
+ Yappr is a community project. There's no company behind it, no investors to please, no ads to sell. It exists because
+ people believe in the idea of social media that users actually own. Contributions, feedback, and ideas are always welcome.
+
+
- {/* Last Updated */}
-
-
- Last updated: January 2025
-
-
+
+
+
+
{YAPPR_CONTRACT_ID}
+
+
+
{process.env.NEXT_PUBLIC_NETWORK || 'testnet'}
+
+ {/* The contract id does not say which SHAPE of contract it is, and a v2
+ client pointed at a v3 contract queries fields that do not exist.
+ Printing the compiled-in topology makes that verifiable from the
+ served artifact; the e2e suite gates on it. */}
+
+
+
+
)
}
diff --git a/app/about/private-feeds/page.tsx b/app/about/private-feeds/page.tsx
index 29fdf598..b0dd14dd 100644
--- a/app/about/private-feeds/page.tsx
+++ b/app/about/private-feeds/page.tsx
@@ -1,8 +1,7 @@
'use client'
-import { ArrowLeftIcon, LockClosedIcon, KeyIcon, ShieldCheckIcon, QuestionMarkCircleIcon, CpuChipIcon, ArrowPathIcon, ExclamationTriangleIcon, CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/outline'
-import Link from 'next/link'
-import { motion } from 'framer-motion'
+import { LockClosedIcon, KeyIcon, ShieldCheckIcon, QuestionMarkCircleIcon, CpuChipIcon, ArrowPathIcon, ExclamationTriangleIcon, CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/outline'
+import { InfoPage } from '@/components/layout/info-page'
// Reusable Components
function CalloutBox({
@@ -116,37 +115,13 @@ function SubSection({ title, children }: { title: string; children: React.ReactN
export default function PrivateFeedsPage() {
return (
-
-
- {/* Back link */}
-
-
-
- Back to About
-
-
-
-
- {/* Header */}
-
-
-
-
How Private Feeds Work
-
-
- End-to-end encrypted content sharing with efficient revocation on a public blockchain
-
-
-
- {/* Content */}
-
+
{/* Section 1: The Challenge */}
@@ -1139,9 +1114,6 @@ export default function PrivateFeedsPage() {
Last updated: January 2026
-
-
-
-
+
)
}
diff --git a/app/contract/page.tsx b/app/contract/page.tsx
index 028b2fbb..c8b345ab 100644
--- a/app/contract/page.tsx
+++ b/app/contract/page.tsx
@@ -1,9 +1,8 @@
'use client'
import { useState } from 'react'
-import { ArrowLeftIcon, DocumentDuplicateIcon, CheckIcon, CodeBracketIcon } from '@heroicons/react/24/outline'
-import Link from 'next/link'
-import { motion } from 'framer-motion'
+import { DocumentDuplicateIcon, CheckIcon, CodeBracketIcon } from '@heroicons/react/24/outline'
+import { InfoPage } from '@/components/layout/info-page'
import toast from 'react-hot-toast'
import socialContractV2 from '@/contracts/yappr-social-contract-v2.json'
import socialContractV4 from '@/contracts/yappr-social-contract-v4.json'
@@ -47,45 +46,26 @@ export default function ContractPage() {
.reduce((acc, doc) => acc + (doc.indices?.length || 0), 0)
return (
-
-
-
-
-
- Back to Yappr
-
-
-
-
-
-
-
-
Yappr Data Contract
-
-
- Dash Platform data contract for the Yappr social media platform
-
-
-
- Version: {dataContract.version}
-
-
- Documents: {documentCount}
-
-
- Indices: {totalIndices}
-
-
+
+
+ Version: {dataContract.version}
-
-
+
+ Documents: {documentCount}
+
+
+ Indices: {totalIndices}
+
+
+ }
+ >
+
Contract Definition
-
-
-
+
)
}
\ No newline at end of file
diff --git a/app/cookies/page.tsx b/app/cookies/page.tsx
index 973151f0..555e3437 100644
--- a/app/cookies/page.tsx
+++ b/app/cookies/page.tsx
@@ -1,159 +1,98 @@
'use client'
-import { ArrowLeftIcon, CircleStackIcon } from '@heroicons/react/24/outline'
-import Link from 'next/link'
-import { motion } from 'framer-motion'
+import { CircleStackIcon } from '@heroicons/react/24/outline'
+import { InfoPage, InfoSection, Prose } from '@/components/layout/info-page'
-export default function CookiesPage() {
+function StorageCard({ tone, name, badge, summary, children }: { tone: 'green' | 'blue'; name: string; badge: string; summary: string; children: React.ReactNode }) {
+ const dot = tone === 'green' ? 'bg-green-500' : 'bg-blue-500'
+ const pill = tone === 'green' ? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300' : 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
return (
-
-
-
-
-
- Back to Yappr
-
-
-
-
-
-
-
-
Cookies & Storage
-
-
- How Yappr uses browser storage
-
-
-
-
- {/* No Tracking */}
-
-
No Tracking Cookies
-
- Yappr does not use cookies for tracking, analytics, or advertising. We don't set any third-party
- cookies, and we don't participate in any ad networks or tracking systems. Your browsing activity
- on Yappr is not monitored or recorded.
-
-
+
+
+
+
{name}
+ {badge}
+
+
{summary}
+ {children}
+
+ )
+}
- {/* What We Store */}
-
-
What We Store Locally
-
- For the app to function, we use your browser's built-in storage mechanisms. Here's exactly
- what we store and why:
-
- Automatically cleared when you close the browser tab. Isolated to each tab.
-
-
-
Private Key
-
- Your private key is stored here while you're logged in. This means:
-
-
-
Closing the tab logs you out automatically
-
Other tabs cannot access your key
-
The key is never written to disk
-
Refreshing the page keeps you logged in (same tab)
-
-
-
+export default function CookiesPage() {
+ return (
+
+
+
+ Yappr does not use cookies for tracking, analytics, or advertising. We don't set any third-party cookies, and we
+ don't participate in any ad networks or tracking systems. Your browsing activity on Yappr is not monitored or recorded.
+
+
- {/* Local Storage */}
-
-
-
-
Local Storage
-
- Persistent
-
-
-
- Persists until manually cleared. Shared across tabs on the same domain.
-
-
-
-
Session Metadata
-
- Your identity ID, cached profile data, and balance. This lets the app remember who
- you are when you reload the page (though you'll need to re-enter your key if you
- closed the tab).
-
-
-
-
App Preferences
-
- Theme settings (dark/light mode) and other UI preferences.
-
-
-
-
-
-
+
+
+ For the app to function, we use your browser's built-in storage mechanisms. Here's exactly what we store and why:
+
+
+
+
+
Your private key is stored here while you're logged in. This means:
+
+
Closing the tab logs you out automatically
+
Other tabs cannot access your key
+
The key is never written to disk
+
Refreshing the page keeps you logged in (same tab)
+
+
+
- {/* Why This Design */}
-
-
Why This Design?
-
-
- Security and convenience often conflict. We chose this approach because:
-
-
-
- Private keys are sensitive. They should never persist on disk where malware
- or other users might find them. Session storage provides tab-isolated, memory-only storage.
-
-
- Session continuity matters. We cache non-sensitive data in local storage so
- you don't have to fetch your profile every time you reload.
-
-
- No external dependencies. We don't rely on external cookie services or
- authentication providers. Everything happens locally in your browser.
-
-
-
-
+
+
+
+ Your identity ID, cached profile data, and balance. This lets the app remember who you are when you reload the page
+ (though you'll need to re-enter your key if you closed the tab).
+
+ Theme settings (dark/light mode) and other UI preferences.
+
+
+
+
- {/* Clearing Storage */}
-
-
Clearing Your Data
-
- You can clear all locally stored data by using your browser's "Clear site data" feature,
- or by logging out of the app. Clearing this data will log you out and remove all cached
- preferences. It will not affect your data on the blockchain.
-
-
+
+
+
Security and convenience often conflict. We chose this approach because:
+
+
+ Private keys are sensitive. They should never persist on disk where malware or other users might find
+ them. Session storage provides tab-isolated, memory-only storage.
+
+
+ Session continuity matters. We cache non-sensitive data in local storage so you don't have to fetch
+ your profile every time you reload.
+
+
+ No external dependencies. We don't rely on external cookie services or authentication providers.
+ Everything happens locally in your browser.
+
+
+
+
- {/* Last Updated */}
-
-
- Last updated: January 2025
-
-
-
-
-
-
+
+
+ You can clear all locally stored data by using your browser's "Clear site data" feature, or by logging out of the
+ app. Clearing this data will log you out and remove all cached preferences. It will not affect your data on the blockchain.
+
+
+
)
}
diff --git a/app/privacy/page.tsx b/app/privacy/page.tsx
index 888d45f3..de0a666a 100644
--- a/app/privacy/page.tsx
+++ b/app/privacy/page.tsx
@@ -1,167 +1,100 @@
'use client'
-import { ArrowLeftIcon, ShieldCheckIcon, ExclamationTriangleIcon, EyeIcon, LockClosedIcon } from '@heroicons/react/24/outline'
-import Link from 'next/link'
-import { motion } from 'framer-motion'
+import { ShieldCheckIcon, EyeIcon, LockClosedIcon } from '@heroicons/react/24/outline'
+import { InfoPage, InfoSection, Prose, TestnetNotice } from '@/components/layout/info-page'
+
+const PUBLIC_DATA = [
+ 'Your profile (display name, bio, website, location)',
+ 'Your avatar and banner images',
+ 'All posts you create',
+ 'Who you follow and who follows you',
+ 'Likes and reposts',
+ 'Bookmarks',
+ 'Your DPNS username',
+ 'Your identity ID (public key)',
+ 'Timestamps of all actions',
+ 'Transaction history',
+]
export default function PrivacyPage() {
return (
-
-
-
-
-
- Back to Yappr
-
-
+
+
+ Yappr is currently running on Dash Platform's testnet. All data may be wiped at any time during network resets. Do not
+ store sensitive information during the testnet phase.
+
-
-
-
-
-
Privacy Policy
-
-
- How your data works on a decentralized platform
-
-
+
+
+ Yappr is built on blockchain technology, which is inherently transparent. Unlike traditional social media platforms where a
+ company controls and can hide your data, blockchain data is stored on a public ledger that anyone can read. This is a
+ fundamental design choice that enables decentralization but means privacy works differently than you might expect.
+
+
-
- {/* Testnet Warning */}
-
-
-
-
-
Testnet Notice
-
- Yappr is currently running on Dash Platform's testnet. All data may be wiped at any time
- during network resets. Do not store sensitive information during the testnet phase.
-
-
-
-
+
+ The following data is stored on the blockchain and visible to anyone:
+
+ {PUBLIC_DATA.map((item) => (
+
+
+ {item}
+
+ ))}
+
+
- {/* Public by Design */}
-
-
Public by Design
-
- Yappr is built on blockchain technology, which is inherently transparent. Unlike traditional social
- media platforms where a company controls and can hide your data, blockchain data is stored on a
- public ledger that anyone can read. This is a fundamental design choice that enables decentralization
- but means privacy works differently than you might expect.
-
-
+
+
+ Direct messages are encrypted before being stored on the blockchain. Only you and the recipient can read the message content.
+ However, metadata such as who you're messaging and when is still visible on the blockchain.
+
+
- {/* What's Public */}
-
-
-
-
What's Public
-
-
- The following data is stored on the blockchain and visible to anyone:
+
+
+
Some data is stored locally in your browser for the app to function:
+
+
+
Session Storage (cleared when you close the tab)
+
+ Your private key is stored here temporarily while you're logged in. It's automatically deleted when you close the
+ browser tab, providing security against persistent access.
-
- {[
- 'Your profile (display name, bio, website, location)',
- 'Your avatar and banner images',
- 'All posts you create',
- 'Who you follow and who follows you',
- 'Likes and reposts',
- 'Bookmarks',
- 'Your DPNS username',
- 'Your identity ID (public key)',
- 'Timestamps of all actions',
- 'Transaction history',
- ].map((item, i) => (
-
-
- {item}
-
- ))}
-
-
-
- {/* What's Encrypted */}
-
-
-
-
What's Encrypted
-
-
- Direct messages are encrypted before being stored on the blockchain. Only you and the recipient
- can read the message content. However, metadata such as who you're messaging and when is still
- visible on the blockchain.
-
-
-
- {/* Local Storage */}
-
-
What's Stored on Your Device
-
-
- Some data is stored locally in your browser for the app to function:
-
-
-
-
Session Storage (cleared when you close the tab)
-
Your private key is stored here temporarily while you're logged in. It's automatically deleted when you close the browser tab, providing security against persistent access.
-
-
-
Local Storage (persists until cleared)
-
Session metadata like your identity ID, balance, and profile information to restore your session on page reload. No private keys are stored here.
-
-
-
-
-
- {/* No Tracking */}
-
-
No Tracking or Analytics
-
- Yappr does not use any tracking cookies, analytics services, or advertising networks. We don't
- collect usage data, track your behavior, or build profiles about you. The app simply connects
- your browser directly to the Dash Platform network.
+
+
+
Local Storage (persists until cleared)
+
+ Session metadata like your identity ID, balance, and profile information to restore your session on page reload. No
+ private keys are stored here.
-
+
+
+
+
- {/* No Deletion */}
-
-
Data Cannot Be Deleted
-
- Because data is stored on a blockchain, it cannot be deleted. There is no "delete my account"
- button because no one has the power to remove data from the blockchain. Once you post something,
- it exists permanently. This is a fundamental property of blockchain technology, not a choice we made.
-
-
+
+
+ Yappr does not use any tracking cookies, analytics services, or advertising networks. We don't collect usage data, track
+ your behavior, or build profiles about you. The app simply connects your browser directly to the Dash Platform network.
+
+
- {/* Third Parties */}
-
-
Third-Party Services
-
- To communicate with the Dash Platform blockchain, the app connects to DAPI (Decentralized API)
- gateway nodes. These are distributed nodes in the Dash network, not centralized servers we control.
- Your requests pass through these nodes to reach the blockchain. The decentralized nature of DAPI
- means no single party can monitor all your activity.
-
-
+
+
+ Because data is stored on a blockchain, it cannot be deleted. There is no "delete my account" button because no one
+ has the power to remove data from the blockchain. Once you post something, it exists permanently. This is a fundamental
+ property of blockchain technology, not a choice we made.
+
+
- {/* Last Updated */}
-
-
- Last updated: January 2025
-
-
-
-
-
-
+
+
+ To communicate with the Dash Platform blockchain, the app connects to DAPI (Decentralized API) gateway nodes. These are
+ distributed nodes in the Dash network, not centralized servers we control. Your requests pass through these nodes to reach
+ the blockchain. The decentralized nature of DAPI means no single party can monitor all your activity.
+
+
+
)
}
diff --git a/app/terms/page.tsx b/app/terms/page.tsx
index a89b75a1..8aff2f70 100644
--- a/app/terms/page.tsx
+++ b/app/terms/page.tsx
@@ -1,132 +1,76 @@
'use client'
-import { ArrowLeftIcon, DocumentTextIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'
-import Link from 'next/link'
-import { motion } from 'framer-motion'
+import { DocumentTextIcon } from '@heroicons/react/24/outline'
+import { InfoPage, InfoSection, Prose, TestnetNotice } from '@/components/layout/info-page'
export default function TermsPage() {
return (
-
-
-
-
-
- Back to Yappr
-
-
+
+
+ Yappr is currently running on Dash Platform's testnet. This means all data, including your posts, profile, and social
+ connections, may be wiped at any time when the network resets or when we migrate to mainnet. Do not rely on testnet data
+ being permanent.
+
-
-
-
-
-
Terms of Use
-
-
- Understanding how Yappr works as a decentralized platform
-
-
+
+
+ Yappr is open-source software that connects directly to Dash Platform, a decentralized blockchain network. There is no
+ company, organization, or central authority operating this platform. No one controls the network, moderates content, or
+ has special access to user data. The software is provided as a tool for interacting with the Dash Platform blockchain.
+
+
-
- {/* Testnet Warning */}
-
-
-
-
-
Testnet Notice
-
- Yappr is currently running on Dash Platform's testnet. This means all data, including your posts,
- profile, and social connections, may be wiped at any time when the network resets or when we
- migrate to mainnet. Do not rely on testnet data being permanent.
-
-
-
-
+
+
+
When you use Yappr, you are directly interacting with a blockchain. This comes with important responsibilities:
+
+
+ Your private key is your identity. If you lose it, there is no way to recover your account. No one can reset
+ your password or restore access.
+
+
+ You own your content. Everything you post is signed with your private key and stored on the blockchain
+ under your identity.
+
+
+ You are responsible for your actions. Content you post reflects on you and is permanently associated
+ with your identity.
+
+
+
+
- {/* No Central Authority */}
-
-
No Central Authority
-
- Yappr is open-source software that connects directly to Dash Platform, a decentralized blockchain network.
- There is no company, organization, or central authority operating this platform. No one controls the network,
- moderates content, or has special access to user data. The software is provided as a tool for interacting
- with the Dash Platform blockchain.
-
-
+
+
+ Posts, likes, follows, and other interactions are stored on the Dash Platform blockchain. Once something is written to the
+ blockchain, it cannot be deleted or modified. Think carefully before you post. Even if content is hidden in the app
+ interface, it remains on the blockchain and can be viewed by anyone with the technical knowledge to query it directly.
+
+
- {/* User Responsibility */}
-
-
You Are Responsible
-
-
- When you use Yappr, you are directly interacting with a blockchain. This comes with important responsibilities:
-
-
-
Your private key is your identity. If you lose it, there is no way to recover your account. No one can reset your password or restore access.
-
You own your content. Everything you post is signed with your private key and stored on the blockchain under your identity.
-
You are responsible for your actions. Content you post reflects on you and is permanently associated with your identity.
-
-
-
+
+
+ Because there is no central authority, there is no content moderation. No one can delete posts, ban users, or remove content
+ from the blockchain. Users have tools to manage their own experience (such as blocking), but these only affect what you see,
+ not what exists on the network. Other users and applications may still display content you've blocked.
+
+
- {/* Data Permanence */}
-
-
Data Is Permanent
-
- Posts, likes, follows, and other interactions are stored on the Dash Platform blockchain. Once something
- is written to the blockchain, it cannot be deleted or modified. Think carefully before you post.
- Even if content is hidden in the app interface, it remains on the blockchain and can be viewed by
- anyone with the technical knowledge to query it directly.
-
-
+
+
+ Creating posts, updating your profile, and other actions require Dash Platform credits. These credits are a form of
+ cryptocurrency used to pay for storing data on the blockchain. You are responsible for maintaining sufficient credits in
+ your identity to perform actions. On testnet, credits can be obtained from faucets for free.
+
+
- {/* No Content Moderation */}
-
-
No Content Moderation
-
- Because there is no central authority, there is no content moderation. No one can delete posts, ban users,
- or remove content from the blockchain. Users have tools to manage their own experience (such as blocking),
- but these only affect what you see, not what exists on the network. Other users and applications may still
- display content you've blocked.
-
-
-
- {/* Transaction Costs */}
-
-
Transaction Costs
-
- Creating posts, updating your profile, and other actions require Dash Platform credits. These credits
- are a form of cryptocurrency used to pay for storing data on the blockchain. You are responsible for
- maintaining sufficient credits in your identity to perform actions. On testnet, credits can be obtained
- from faucets for free.
-
-
-
- {/* No Warranty */}
-
-
No Warranty
-
- This software is provided "as is" without warranty of any kind. The developers make no guarantees
- about availability, reliability, or fitness for any particular purpose. Use at your own risk. The
- decentralized nature of the platform means that issues cannot always be fixed or data recovered.
-
-
-
- {/* Last Updated */}
-
-
- Last updated: January 2025
-
-
-
-
-
-
+
+
+ This software is provided "as is" without warranty of any kind. The developers make no guarantees about availability,
+ reliability, or fitness for any particular purpose. Use at your own risk. The decentralized nature of the platform means
+ that issues cannot always be fixed or data recovered.
+
+
+
)
}
diff --git a/components/layout/info-page.tsx b/components/layout/info-page.tsx
new file mode 100644
index 00000000..43b17f67
--- /dev/null
+++ b/components/layout/info-page.tsx
@@ -0,0 +1,110 @@
+'use client'
+
+import type { ComponentType, ReactNode } from 'react'
+import Link from 'next/link'
+import { motion } from 'framer-motion'
+import { ArrowLeftIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'
+import { cn } from '@/lib/utils'
+
+type Icon = ComponentType<{ className?: string }>
+
+interface InfoPageProps {
+ icon: Icon
+ title: string
+ subtitle: string
+ /** Extra header content under the subtitle, e.g. a stats row. */
+ headerExtra?: ReactNode
+ /** Where the top-left link goes and what it says. */
+ back?: { href: string; label: string }
+ /** Centre column width; the contract viewer needs the wide one. */
+ width?: 'prose' | 'wide'
+ /** Printed in the footer as "Last updated: …". */
+ updated?: string
+ /** Vertical gap between sections. */
+ spacing?: 'normal' | 'loose'
+ children: ReactNode
+}
+
+/**
+ * The standalone card page used for about, legal and contract content: a
+ * back link, a gradient title band, and the sections a page passes in.
+ */
+export function InfoPage({ icon: TitleIcon, title, subtitle, headerExtra, back = { href: '/', label: 'Back to Yappr' }, width = 'prose', updated, spacing = 'normal', children }: InfoPageProps) {
+ return (
+
+
+
+
+
+ {back.label}
+
+
+
+
+
+
+
+
{title}
+
+
{subtitle}
+ {headerExtra}
+
+
+
+ {children}
+ {updated && (
+
+
Last updated: {updated}
+
+ )}
+
+
+
+
+ )
+}
+
+interface InfoSectionProps {
+ title: string
+ icon?: Icon
+ /** Icon colour; the default is neutral. */
+ iconClassName?: string
+ children: ReactNode
+}
+
+/** A titled section of an InfoPage; body copy is styled by the page. */
+export function InfoSection({ title, icon: SectionIcon, iconClassName, children }: InfoSectionProps) {
+ return (
+
+ {SectionIcon ? (
+
+}
+
+/** The amber "this is testnet" box at the top of the legal pages. */
+export function TestnetNotice({ children }: { children: ReactNode }) {
+ return (
+